summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar wwylele2016-12-21 20:05:56 +0200
committerGravatar wwylele2017-01-11 11:46:44 +0200
commitcf3a272332b03640730d1434e9802e166ca931da (patch)
tree7297bf1b38679cb84b5baa7c98b5b9e729560131
parentMerge pull request #2369 from MerryMage/core-frontend (diff)
downloadyuzu-cf3a272332b03640730d1434e9802e166ca931da.tar.gz
yuzu-cf3a272332b03640730d1434e9802e166ca931da.tar.xz
yuzu-cf3a272332b03640730d1434e9802e166ca931da.zip
CAM: implement basic camera functions with a blank camera
-rw-r--r--src/citra/config.cpp15
-rw-r--r--src/citra/default_ini.h16
-rw-r--r--src/citra_qt/config.cpp32
-rw-r--r--src/core/CMakeLists.txt6
-rw-r--r--src/core/frontend/camera/blank_camera.cpp31
-rw-r--r--src/core/frontend/camera/blank_camera.h28
-rw-r--r--src/core/frontend/camera/factory.cpp32
-rw-r--r--src/core/frontend/camera/factory.h41
-rw-r--r--src/core/frontend/camera/interface.cpp11
-rw-r--r--src/core/frontend/camera/interface.h61
-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/settings.h5
14 files changed, 1520 insertions, 172 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 29462c982..2223bf0a0 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -91,6 +91,21 @@ void Config::ReadValues() {
91 Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false); 91 Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", false);
92 Settings::values.region_value = sdl2_config->GetInteger("System", "region_value", 1); 92 Settings::values.region_value = sdl2_config->GetInteger("System", "region_value", 1);
93 93
94 // Camera
95 using namespace Service::CAM;
96 Settings::values.camera_name[OuterRightCamera] =
97 sdl2_config->Get("Camera", "camera_outer_right_name", "blank");
98 Settings::values.camera_config[OuterRightCamera] =
99 sdl2_config->Get("Camera", "camera_outer_right_config", "");
100 Settings::values.camera_name[InnerCamera] =
101 sdl2_config->Get("Camera", "camera_inner_name", "blank");
102 Settings::values.camera_config[InnerCamera] =
103 sdl2_config->Get("Camera", "camera_inner_config", "");
104 Settings::values.camera_name[OuterLeftCamera] =
105 sdl2_config->Get("Camera", "camera_outer_left_name", "blank");
106 Settings::values.camera_config[OuterLeftCamera] =
107 sdl2_config->Get("Camera", "camera_outer_left_config", "");
108
94 // Miscellaneous 109 // Miscellaneous
95 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info"); 110 Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Info");
96 111
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 001b18ac2..618f1aeaa 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -104,6 +104,22 @@ is_new_3ds =
104# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan 104# 0: Japan, 1: USA (default), 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
105region_value = 105region_value =
106 106
107[Camera]
108# Which camera engine to use for the right outer camera
109# blank (default): a dummy camera that always returns black image
110camera_outer_right_name =
111
112# A config string for the right outer camera. Its meaning is defined by the camera engine
113camera_outer_right_config =
114
115# ... for the left outer camera
116camera_outer_left_name =
117camera_outer_left_config =
118
119# ... for the inner camera
120camera_inner_name =
121camera_inner_config =
122
107[Miscellaneous] 123[Miscellaneous]
108# A filter which removes logs below a certain logging level. 124# A filter which removes logs below a certain logging level.
109# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical 125# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
diff --git a/src/citra_qt/config.cpp b/src/citra_qt/config.cpp
index 06a4e9d25..58eb5a96e 100644
--- a/src/citra_qt/config.cpp
+++ b/src/citra_qt/config.cpp
@@ -66,6 +66,22 @@ void Config::ReadValues() {
66 qt_config->value("enable_audio_stretching", true).toBool(); 66 qt_config->value("enable_audio_stretching", true).toBool();
67 qt_config->endGroup(); 67 qt_config->endGroup();
68 68
69 using namespace Service::CAM;
70 qt_config->beginGroup("Camera");
71 Settings::values.camera_name[OuterRightCamera] =
72 qt_config->value("camera_outer_right_name", "blank").toString().toStdString();
73 Settings::values.camera_config[OuterRightCamera] =
74 qt_config->value("camera_outer_right_config", "").toString().toStdString();
75 Settings::values.camera_name[InnerCamera] =
76 qt_config->value("camera_inner_name", "blank").toString().toStdString();
77 Settings::values.camera_config[InnerCamera] =
78 qt_config->value("camera_inner_config", "").toString().toStdString();
79 Settings::values.camera_name[OuterLeftCamera] =
80 qt_config->value("camera_outer_left_name", "blank").toString().toStdString();
81 Settings::values.camera_config[OuterLeftCamera] =
82 qt_config->value("camera_outer_left_config", "").toString().toStdString();
83 qt_config->endGroup();
84
69 qt_config->beginGroup("Data Storage"); 85 qt_config->beginGroup("Data Storage");
70 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool(); 86 Settings::values.use_virtual_sd = qt_config->value("use_virtual_sd", true).toBool();
71 qt_config->endGroup(); 87 qt_config->endGroup();
@@ -171,6 +187,22 @@ void Config::SaveValues() {
171 qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching); 187 qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching);
172 qt_config->endGroup(); 188 qt_config->endGroup();
173 189
190 using namespace Service::CAM;
191 qt_config->beginGroup("Camera");
192 qt_config->setValue("camera_outer_right_name",
193 QString::fromStdString(Settings::values.camera_name[OuterRightCamera]));
194 qt_config->setValue("camera_outer_right_config",
195 QString::fromStdString(Settings::values.camera_config[OuterRightCamera]));
196 qt_config->setValue("camera_inner_name",
197 QString::fromStdString(Settings::values.camera_name[InnerCamera]));
198 qt_config->setValue("camera_inner_config",
199 QString::fromStdString(Settings::values.camera_config[InnerCamera]));
200 qt_config->setValue("camera_outer_left_name",
201 QString::fromStdString(Settings::values.camera_name[OuterLeftCamera]));
202 qt_config->setValue("camera_outer_left_config",
203 QString::fromStdString(Settings::values.camera_config[OuterLeftCamera]));
204 qt_config->endGroup();
205
174 qt_config->beginGroup("Data Storage"); 206 qt_config->beginGroup("Data Storage");
175 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd); 207 qt_config->setValue("use_virtual_sd", Settings::values.use_virtual_sd);
176 qt_config->endGroup(); 208 qt_config->endGroup();
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 3621449b3..2dd4158e5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -29,6 +29,9 @@ set(SRCS
29 file_sys/ivfc_archive.cpp 29 file_sys/ivfc_archive.cpp
30 file_sys/path_parser.cpp 30 file_sys/path_parser.cpp
31 file_sys/savedata_archive.cpp 31 file_sys/savedata_archive.cpp
32 frontend/camera/blank_camera.cpp
33 frontend/camera/factory.cpp
34 frontend/camera/interface.cpp
32 frontend/emu_window.cpp 35 frontend/emu_window.cpp
33 frontend/key_map.cpp 36 frontend/key_map.cpp
34 gdbstub/gdbstub.cpp 37 gdbstub/gdbstub.cpp
@@ -200,6 +203,9 @@ set(HEADERS
200 file_sys/ivfc_archive.h 203 file_sys/ivfc_archive.h
201 file_sys/path_parser.h 204 file_sys/path_parser.h
202 file_sys/savedata_archive.h 205 file_sys/savedata_archive.h
206 frontend/camera/blank_camera.h
207 frontend/camera/factory.h
208 frontend/camera/interface.h
203 frontend/emu_window.h 209 frontend/emu_window.h
204 frontend/key_map.h 210 frontend/key_map.h
205 gdbstub/gdbstub.h 211 gdbstub/gdbstub.h
diff --git a/src/core/frontend/camera/blank_camera.cpp b/src/core/frontend/camera/blank_camera.cpp
new file mode 100644
index 000000000..7995abcbd
--- /dev/null
+++ b/src/core/frontend/camera/blank_camera.cpp
@@ -0,0 +1,31 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/camera/blank_camera.h"
6
7namespace Camera {
8
9void BlankCamera::StartCapture() {}
10
11void BlankCamera::StopCapture() {}
12
13void BlankCamera::SetFormat(Service::CAM::OutputFormat output_format) {
14 output_rgb = output_format == Service::CAM::OutputFormat::RGB565;
15}
16
17void BlankCamera::SetResolution(const Service::CAM::Resolution& resolution) {
18 width = resolution.width;
19 height = resolution.height;
20};
21
22void BlankCamera::SetFlip(Service::CAM::Flip) {}
23
24void BlankCamera::SetEffect(Service::CAM::Effect) {}
25
26std::vector<u16> BlankCamera::ReceiveFrame() const {
27 // Note: 0x80008000 stands for two black pixels in YUV422
28 return std::vector<u16>(width * height, output_rgb ? 0 : 0x8000);
29}
30
31} // namespace Camera
diff --git a/src/core/frontend/camera/blank_camera.h b/src/core/frontend/camera/blank_camera.h
new file mode 100644
index 000000000..c6619bd88
--- /dev/null
+++ b/src/core/frontend/camera/blank_camera.h
@@ -0,0 +1,28 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/camera/factory.h"
8#include "core/frontend/camera/interface.h"
9
10namespace Camera {
11
12class BlankCamera final : public CameraInterface {
13public:
14 void StartCapture() override;
15 void StopCapture() override;
16 void SetResolution(const Service::CAM::Resolution&) override;
17 void SetFlip(Service::CAM::Flip) override;
18 void SetEffect(Service::CAM::Effect) override;
19 void SetFormat(Service::CAM::OutputFormat) override;
20 std::vector<u16> ReceiveFrame() const override;
21
22private:
23 int width = 0;
24 int height = 0;
25 bool output_rgb = false;
26};
27
28} // namespace Camera
diff --git a/src/core/frontend/camera/factory.cpp b/src/core/frontend/camera/factory.cpp
new file mode 100644
index 000000000..4b4da50dd
--- /dev/null
+++ b/src/core/frontend/camera/factory.cpp
@@ -0,0 +1,32 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <unordered_map>
6#include "common/logging/log.h"
7#include "core/frontend/camera/blank_camera.h"
8#include "core/frontend/camera/factory.h"
9
10namespace Camera {
11
12static std::unordered_map<std::string, std::unique_ptr<CameraFactory>> factories;
13
14CameraFactory::~CameraFactory() = default;
15
16void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> factory) {
17 factories[name] = std::move(factory);
18}
19
20std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config) {
21 auto pair = factories.find(name);
22 if (pair != factories.end()) {
23 return pair->second->Create(config);
24 }
25
26 if (name != "blank") {
27 LOG_ERROR(Service_CAM, "Unknown camera \"%s\"", name.c_str());
28 }
29 return std::make_unique<BlankCamera>();
30}
31
32} // namespace Camera
diff --git a/src/core/frontend/camera/factory.h b/src/core/frontend/camera/factory.h
new file mode 100644
index 000000000..d68be16e5
--- /dev/null
+++ b/src/core/frontend/camera/factory.h
@@ -0,0 +1,41 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <string>
9#include "core/frontend/camera/interface.h"
10
11namespace Camera {
12
13class CameraFactory {
14public:
15 virtual ~CameraFactory();
16
17 /**
18 * Creates a camera object based on the configuration string.
19 * @params config Configuration string to create the camera. The implementation can decide the
20 * meaning of this string.
21 * @returns a unique_ptr to the created camera object.
22 */
23 virtual std::unique_ptr<CameraInterface> Create(const std::string& config) const = 0;
24};
25
26/**
27 * Registers an external camera factory.
28 * @param name Identifier of the camera factory.
29 * @param factory Camera factory to register.
30 */
31void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> factory);
32
33/**
34 * Creates a camera from the factory.
35 * @param name Identifier of the camera factory.
36 * @param config Configuration string to create the camera. The meaning of this string is
37 * defined by the factory.
38 */
39std::unique_ptr<CameraInterface> CreateCamera(const std::string& name, const std::string& config);
40
41} // namespace Camera
diff --git a/src/core/frontend/camera/interface.cpp b/src/core/frontend/camera/interface.cpp
new file mode 100644
index 000000000..9aec9e7f1
--- /dev/null
+++ b/src/core/frontend/camera/interface.cpp
@@ -0,0 +1,11 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "core/frontend/camera/interface.h"
6
7namespace Camera {
8
9CameraInterface::~CameraInterface() = default;
10
11} // namespace Camera
diff --git a/src/core/frontend/camera/interface.h b/src/core/frontend/camera/interface.h
new file mode 100644
index 000000000..a55a495c9
--- /dev/null
+++ b/src/core/frontend/camera/interface.h
@@ -0,0 +1,61 @@
1// Copyright 2016 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <vector>
8#include "common/common_types.h"
9#include "core/hle/service/cam/cam.h"
10
11namespace Camera {
12
13/// An abstract class standing for a camera. All camera implementations should inherit from this.
14class CameraInterface {
15public:
16 virtual ~CameraInterface();
17
18 /// Starts the camera for video capturing.
19 virtual void StartCapture() = 0;
20
21 /// Stops the camera for video capturing.
22 virtual void StopCapture() = 0;
23
24 /**
25 * Sets the video resolution from raw CAM service parameters.
26 * For the meaning of the parameters, please refer to Service::CAM::Resolution. Note that the
27 * actual camera implementation doesn't need to respect all the parameters. However, the width
28 * and the height parameters must be respected and be used to determine the size of output
29 * frames.
30 * @param resolution The resolution parameters to set
31 */
32 virtual void SetResolution(const Service::CAM::Resolution& resolution) = 0;
33
34 /**
35 * Configures how received frames should be flipped by the camera.
36 * @param flip Flip applying to the frame
37 */
38 virtual void SetFlip(Service::CAM::Flip flip) = 0;
39
40 /**
41 * Configures what effect should be applied to received frames by the camera.
42 * @param effect Effect applying to the frame
43 */
44 virtual void SetEffect(Service::CAM::Effect effect) = 0;
45
46 /**
47 * Sets the output format of the all frames received after this function is called.
48 * @param format Output format of the frame
49 */
50 virtual void SetFormat(Service::CAM::OutputFormat format) = 0;
51
52 /**
53 * Receives a frame from the camera.
54 * This function should be only called between a StartCapture call and a StopCapture call.
55 * @returns A std::vector<u16> containing pixels. The total size of the vector is width * height
56 * where width and height are set by a call to SetResolution.
57 */
58 virtual std::vector<u16> ReceiveFrame() const = 0;
59};
60
61} // namespace Camera
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 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <array>
7#include <future>
8#include <memory>
9#include <vector>
10#include "common/bit_set.h"
5#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "core/core_timing.h"
13#include "core/frontend/camera/factory.h"
6#include "core/hle/kernel/event.h" 14#include "core/hle/kernel/event.h"
7#include "core/hle/service/cam/cam.h" 15#include "core/hle/service/cam/cam.h"
8#include "core/hle/service/cam/cam_c.h" 16#include "core/hle/service/cam/cam_c.h"
@@ -10,206 +18,924 @@
10#include "core/hle/service/cam/cam_s.h" 18#include "core/hle/service/cam/cam_s.h"
11#include "core/hle/service/cam/cam_u.h" 19#include "core/hle/service/cam/cam_u.h"
12#include "core/hle/service/service.h" 20#include "core/hle/service/service.h"
21#include "core/settings.h"
13 22
14namespace Service { 23namespace Service {
15namespace CAM { 24namespace CAM {
16 25
17static const u32 TRANSFER_BYTES = 5 * 1024; 26namespace {
27
28struct ContextConfig {
29 Flip flip;
30 Effect effect;
31 OutputFormat format;
32 Resolution resolution;
33};
34
35struct CameraConfig {
36 std::unique_ptr<Camera::CameraInterface> impl;
37 std::array<ContextConfig, 2> contexts;
38 int current_context;
39 FrameRate frame_rate;
40};
41
42struct PortConfig {
43 int camera_id;
44
45 bool is_active; // set when the port is activated by an Activate call.
46 bool is_pending_receiving; // set if SetReceiving is called when is_busy = false. When
47 // StartCapture is called then, this will trigger a receiving
48 // process and reset itself.
49 bool is_busy; // set when StartCapture is called and reset when StopCapture is called.
50 bool is_receiving; // set when there is an ongoing receiving process.
51
52 bool is_trimming;
53 u16 x0; // x-coordinate of starting position for trimming
54 u16 y0; // y-coordinate of starting position for trimming
55 u16 x1; // x-coordinate of ending position for trimming
56 u16 y1; // y-coordinate of ending position for trimming
57
58 u32 transfer_bytes;
59
60 Kernel::SharedPtr<Kernel::Event> completion_event;
61 Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event;
62 Kernel::SharedPtr<Kernel::Event> vsync_interrupt_event;
63
64 std::future<std::vector<u16>> capture_result; // will hold the received frame.
65 VAddr dest; // the destination address of a receiving process
66 u32 dest_size; // the destination size of a receiving process
67
68 void Clear() {
69 completion_event->Clear();
70 buffer_error_interrupt_event->Clear();
71 vsync_interrupt_event->Clear();
72 is_receiving = false;
73 is_active = false;
74 is_pending_receiving = false;
75 is_busy = false;
76 is_trimming = false;
77 x0 = 0;
78 y0 = 0;
79 x1 = 0;
80 y1 = 0;
81 transfer_bytes = 256;
82 }
83};
84
85// built-in resolution parameters
86constexpr std::array<Resolution, 8> PRESET_RESOLUTION{{
87 {640, 480, 0, 0, 639, 479}, // VGA
88 {320, 240, 0, 0, 639, 479}, // QVGA
89 {160, 120, 0, 0, 639, 479}, // QQVGA
90 {352, 288, 26, 0, 613, 479}, // CIF
91 {176, 144, 26, 0, 613, 479}, // QCIF
92 {256, 192, 0, 0, 639, 479}, // DS_LCD
93 {512, 384, 0, 0, 639, 479}, // DS_LCDx4
94 {400, 240, 0, 48, 639, 431}, // CTR_TOP_LCD
95}};
96
97// latency in ms for each frame rate option
98constexpr std::array<int, 13> LATENCY_BY_FRAME_RATE{{
99 67, // Rate_15
100 67, // Rate_15_To_5
101 67, // Rate_15_To_2
102 100, // Rate_10
103 118, // Rate_8_5
104 200, // Rate_5
105 50, // Rate_20
106 50, // Rate_20_To_5
107 33, // Rate_30
108 33, // Rate_30_To_5
109 67, // Rate_15_To_10
110 50, // Rate_20_To_10
111 33, // Rate_30_To_10
112}};
113
114std::array<CameraConfig, NumCameras> cameras;
115std::array<PortConfig, 2> ports;
116int completion_event_callback;
117
118const ResultCode ERROR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
119 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
120const ResultCode ERROR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::CAM,
121 ErrorSummary::InvalidArgument, ErrorLevel::Usage);
122
123void CompletionEventCallBack(u64 port_id, int) {
124 PortConfig& port = ports[port_id];
125 const CameraConfig& camera = cameras[port.camera_id];
126 const auto buffer = port.capture_result.get();
127
128 if (port.is_trimming) {
129 u32 trim_width;
130 u32 trim_height;
131 const int original_width = camera.contexts[camera.current_context].resolution.width;
132 const int original_height = camera.contexts[camera.current_context].resolution.height;
133 if (port.x1 <= port.x0 || port.y1 <= port.y0 || port.x1 > original_width ||
134 port.y1 > original_height) {
135 LOG_ERROR(Service_CAM, "Invalid trimming coordinates x0=%u, y0=%u, x1=%u, y1=%u",
136 port.x0, port.y0, port.x1, port.y1);
137 trim_width = 0;
138 trim_height = 0;
139 } else {
140 trim_width = port.x1 - port.x0;
141 trim_height = port.y1 - port.y0;
142 }
143
144 u32 trim_size = (port.x1 - port.x0) * (port.y1 - port.y0) * 2;
145 if (port.dest_size != trim_size) {
146 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%u)!",
147 port.dest_size, trim_size);
148 }
149
150 const u32 src_offset = port.y0 * original_width + port.x0;
151 const u16* src_ptr = buffer.data() + src_offset;
152 // Note: src_size_left is int because it can be negative if the buffer size doesn't match.
153 int src_size_left = static_cast<int>((buffer.size() - src_offset) * sizeof(u16));
154 VAddr dest_ptr = port.dest;
155 // Note: dest_size_left and line_bytes are int to match the type of src_size_left.
156 int dest_size_left = static_cast<int>(port.dest_size);
157 const int line_bytes = static_cast<int>(trim_width * sizeof(u16));
158
159 for (u32 y = 0; y < trim_height; ++y) {
160 int copy_length = std::min({line_bytes, dest_size_left, src_size_left});
161 if (copy_length <= 0) {
162 break;
163 }
164 Memory::WriteBlock(dest_ptr, src_ptr, copy_length);
165 dest_ptr += copy_length;
166 dest_size_left -= copy_length;
167 src_ptr += original_width;
168 src_size_left -= original_width * sizeof(u16);
169 }
170 } else {
171 std::size_t buffer_size = buffer.size() * sizeof(u16);
172 if (port.dest_size != buffer_size) {
173 LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!",
174 port.dest_size, buffer_size);
175 }
176 Memory::WriteBlock(port.dest, buffer.data(), std::min<u32>(port.dest_size, buffer_size));
177 }
178
179 port.is_receiving = false;
180 port.completion_event->Signal();
181}
182
183// Starts a receiving process on the specified port. This can only be called when is_busy = true and
184// is_receiving = false.
185void StartReceiving(int port_id) {
186 PortConfig& port = ports[port_id];
187 port.is_receiving = true;
188
189 // launches a capture task asynchronously
190 const CameraConfig& camera = cameras[port.camera_id];
191 port.capture_result =
192 std::async(std::launch::async, &Camera::CameraInterface::ReceiveFrame, camera.impl.get());
193
194 // schedules a completion event according to the frame rate. The event will block on the
195 // capture task if it is not finished within the expected time
196 CoreTiming::ScheduleEvent(
197 msToCycles(LATENCY_BY_FRAME_RATE[static_cast<int>(camera.frame_rate)]),
198 completion_event_callback, port_id);
199}
200
201// Cancels any ongoing receiving processes at the specified port. This is used by functions that
202// stop capturing.
203// TODO: what is the exact behaviour on real 3DS when stopping capture during an ongoing process?
204// Will the completion event still be signaled?
205void CancelReceiving(int port_id) {
206 if (!ports[port_id].is_receiving)
207 return;
208 LOG_WARNING(Service_CAM, "tries to cancel an ongoing receiving process.");
209 CoreTiming::UnscheduleEvent(completion_event_callback, port_id);
210 ports[port_id].capture_result.wait();
211 ports[port_id].is_receiving = false;
212}
213
214// Activates the specified port with the specfied camera.
215static void ActivatePort(int port_id, int camera_id) {
216 if (ports[port_id].is_busy && ports[port_id].camera_id != camera_id) {
217 CancelReceiving(port_id);
218 cameras[ports[port_id].camera_id].impl->StopCapture();
219 ports[port_id].is_busy = false;
220 }
221 ports[port_id].is_active = true;
222 ports[port_id].camera_id = camera_id;
223}
224
225template <int max_index>
226class CommandParamBitSet : public BitSet8 {
227public:
228 explicit CommandParamBitSet(u32 command_param)
229 : BitSet8(static_cast<u8>(command_param & 0xFF)) {}
18 230
19static Kernel::SharedPtr<Kernel::Event> completion_event_cam1; 231 bool IsValid() const {
20static Kernel::SharedPtr<Kernel::Event> completion_event_cam2; 232 return m_val < (1 << max_index);
21static Kernel::SharedPtr<Kernel::Event> interrupt_error_event; 233 }
22static Kernel::SharedPtr<Kernel::Event> vsync_interrupt_error_event; 234
235 bool IsSingle() const {
236 return IsValid() && Count() == 1;
237 }
238};
239
240using PortSet = CommandParamBitSet<2>;
241using ContextSet = CommandParamBitSet<2>;
242using CameraSet = CommandParamBitSet<3>;
243
244} // namespace
23 245
24void StartCapture(Service::Interface* self) { 246void StartCapture(Service::Interface* self) {
25 u32* cmd_buff = Kernel::GetCommandBuffer(); 247 u32* cmd_buff = Kernel::GetCommandBuffer();
26 248
27 u8 port = cmd_buff[1] & 0xFF; 249 const PortSet port_select(cmd_buff[1]);
250
251 if (port_select.IsValid()) {
252 for (int i : port_select) {
253 if (!ports[i].is_busy) {
254 if (!ports[i].is_active) {
255 // This doesn't return an error, but seems to put the camera in an undefined
256 // state
257 LOG_ERROR(Service_CAM, "port %u hasn't been activated", i);
258 } else {
259 cameras[ports[i].camera_id].impl->StartCapture();
260 ports[i].is_busy = true;
261 if (ports[i].is_pending_receiving) {
262 ports[i].is_pending_receiving = false;
263 StartReceiving(i);
264 }
265 }
266 } else {
267 LOG_WARNING(Service_CAM, "port %u already started", i);
268 }
269 }
270 cmd_buff[1] = RESULT_SUCCESS.raw;
271 } else {
272 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
273 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
274 }
28 275
29 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); 276 cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
30 cmd_buff[1] = RESULT_SUCCESS.raw;
31 277
32 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); 278 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
33} 279}
34 280
35void StopCapture(Service::Interface* self) { 281void StopCapture(Service::Interface* self) {
36 u32* cmd_buff = Kernel::GetCommandBuffer(); 282 u32* cmd_buff = Kernel::GetCommandBuffer();
37 283
38 u8 port = cmd_buff[1] & 0xFF; 284 const PortSet port_select(cmd_buff[1]);
285
286 if (port_select.IsValid()) {
287 for (int i : port_select) {
288 if (ports[i].is_busy) {
289 CancelReceiving(i);
290 cameras[ports[i].camera_id].impl->StopCapture();
291 ports[i].is_busy = false;
292 } else {
293 LOG_WARNING(Service_CAM, "port %u already stopped", i);
294 }
295 }
296 cmd_buff[1] = RESULT_SUCCESS.raw;
297 } else {
298 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
299 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
300 }
39 301
40 cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); 302 cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0);
303
304 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
305}
306
307void IsBusy(Service::Interface* self) {
308 u32* cmd_buff = Kernel::GetCommandBuffer();
309
310 const PortSet port_select(cmd_buff[1]);
311
312 if (port_select.IsValid()) {
313 bool is_busy = true;
314 // Note: the behaviour on no or both ports selected are verified against real 3DS.
315 for (int i : port_select) {
316 is_busy &= ports[i].is_busy;
317 }
318 cmd_buff[1] = RESULT_SUCCESS.raw;
319 cmd_buff[2] = is_busy ? 1 : 0;
320 } else {
321 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
322 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
323 }
324
325 cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0);
326
327 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
328}
329
330void ClearBuffer(Service::Interface* self) {
331 u32* cmd_buff = Kernel::GetCommandBuffer();
332
333 const PortSet port_select(cmd_buff[1]);
334
335 cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0);
41 cmd_buff[1] = RESULT_SUCCESS.raw; 336 cmd_buff[1] = RESULT_SUCCESS.raw;
42 337
43 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); 338 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
44} 339}
45 340
46void GetVsyncInterruptEvent(Service::Interface* self) { 341void GetVsyncInterruptEvent(Service::Interface* self) {
47 u32* cmd_buff = Kernel::GetCommandBuffer(); 342 u32* cmd_buff = Kernel::GetCommandBuffer();
48 343
49 u8 port = cmd_buff[1] & 0xFF; 344 const PortSet port_select(cmd_buff[1]);
345
346 if (port_select.IsSingle()) {
347 int port = *port_select.begin();
348 cmd_buff[1] = RESULT_SUCCESS.raw;
349 cmd_buff[2] = IPC::CopyHandleDesc();
350 cmd_buff[3] = Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom();
351 } else {
352 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
353 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
354 cmd_buff[2] = IPC::CopyHandleDesc();
355 cmd_buff[2] = 0;
356 }
50 357
51 cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2); 358 cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2);
52 cmd_buff[1] = RESULT_SUCCESS.raw;
53 cmd_buff[2] = IPC::CopyHandleDesc();
54 cmd_buff[3] = Kernel::g_handle_table.Create(vsync_interrupt_error_event).MoveFrom();
55 359
56 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); 360 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
57} 361}
58 362
59void GetBufferErrorInterruptEvent(Service::Interface* self) { 363void GetBufferErrorInterruptEvent(Service::Interface* self) {
60 u32* cmd_buff = Kernel::GetCommandBuffer(); 364 u32* cmd_buff = Kernel::GetCommandBuffer();
61 365
62 u8 port = cmd_buff[1] & 0xFF; 366 const PortSet port_select(cmd_buff[1]);
63 367
64 cmd_buff[0] = IPC::MakeHeader(0x6, 1, 2); 368 if (port_select.IsSingle()) {
65 cmd_buff[1] = RESULT_SUCCESS.raw; 369 int port = *port_select.begin();
66 cmd_buff[2] = IPC::CopyHandleDesc(); 370 cmd_buff[1] = RESULT_SUCCESS.raw;
67 cmd_buff[3] = Kernel::g_handle_table.Create(interrupt_error_event).MoveFrom(); 371 cmd_buff[2] = IPC::CopyHandleDesc();
68 372 cmd_buff[3] =
69 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); 373 Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom();
374 } else {
375 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
376 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
377 cmd_buff[2] = IPC::CopyHandleDesc();
378 cmd_buff[2] = 0;
379 }
380
381 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val);
70} 382}
71 383
72void SetReceiving(Service::Interface* self) { 384void SetReceiving(Service::Interface* self) {
73 u32* cmd_buff = Kernel::GetCommandBuffer(); 385 u32* cmd_buff = Kernel::GetCommandBuffer();
74 386
75 VAddr dest = cmd_buff[1]; 387 const VAddr dest = cmd_buff[1];
76 u8 port = cmd_buff[2] & 0xFF; 388 const PortSet port_select(cmd_buff[2]);
77 u32 image_size = cmd_buff[3]; 389 const u32 image_size = cmd_buff[3];
78 u16 trans_unit = cmd_buff[4] & 0xFFFF; 390 const u32 trans_unit = cmd_buff[4] & 0xFFFF;
391
392 if (port_select.IsSingle()) {
393 int port_id = *port_select.begin();
394 PortConfig& port = ports[port_id];
395 CancelReceiving(port_id);
396 port.completion_event->Clear();
397 port.dest = dest;
398 port.dest_size = image_size;
399
400 if (port.is_busy) {
401 StartReceiving(port_id);
402 } else {
403 port.is_pending_receiving = true;
404 }
405
406 cmd_buff[1] = RESULT_SUCCESS.raw;
407 cmd_buff[2] = IPC::CopyHandleDesc();
408 cmd_buff[3] = Kernel::g_handle_table.Create(port.completion_event).MoveFrom();
409 } else {
410 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
411 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
412 }
413
414 cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2);
79 415
80 Kernel::Event* completion_event = 416 LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest,
81 (Port)port == Port::Cam2 ? completion_event_cam2.get() : completion_event_cam1.get(); 417 port_select.m_val, image_size, trans_unit);
418}
82 419
83 completion_event->Signal(); 420void IsFinishedReceiving(Service::Interface* self) {
421 u32* cmd_buff = Kernel::GetCommandBuffer();
84 422
85 cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); 423 const PortSet port_select(cmd_buff[1]);
86 cmd_buff[1] = RESULT_SUCCESS.raw; 424
87 cmd_buff[2] = IPC::CopyHandleDesc(); 425 if (port_select.IsSingle()) {
88 cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); 426 int port = *port_select.begin();
427 cmd_buff[1] = RESULT_SUCCESS.raw;
428 cmd_buff[2] = (ports[port].is_receiving || ports[port].is_pending_receiving) ? 0 : 1;
429 } else {
430 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
431 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
432 }
89 433
90 LOG_WARNING(Service_CAM, "(STUBBED) called, addr=0x%X, port=%d, image_size=%d, trans_unit=%d", 434 cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0);
91 dest, port, image_size, trans_unit); 435
436 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
92} 437}
93 438
94void SetTransferLines(Service::Interface* self) { 439void SetTransferLines(Service::Interface* self) {
95 u32* cmd_buff = Kernel::GetCommandBuffer(); 440 u32* cmd_buff = Kernel::GetCommandBuffer();
96 441
97 u8 port = cmd_buff[1] & 0xFF; 442 const PortSet port_select(cmd_buff[1]);
98 u16 transfer_lines = cmd_buff[2] & 0xFFFF; 443 const u32 transfer_lines = cmd_buff[2] & 0xFFFF;
99 u16 width = cmd_buff[3] & 0xFFFF; 444 const u32 width = cmd_buff[3] & 0xFFFF;
100 u16 height = cmd_buff[4] & 0xFFFF; 445 const u32 height = cmd_buff[4] & 0xFFFF;
446
447 if (port_select.IsValid()) {
448 for (int i : port_select) {
449 ports[i].transfer_bytes = transfer_lines * width * 2;
450 }
451 cmd_buff[1] = RESULT_SUCCESS.raw;
452 } else {
453 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
454 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
455 }
101 456
102 cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); 457 cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
103 cmd_buff[1] = RESULT_SUCCESS.raw;
104 458
105 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, lines=%d, width=%d, height=%d", port, 459 LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u",
106 transfer_lines, width, height); 460 port_select.m_val, transfer_lines, width, height);
107} 461}
108 462
109void GetMaxLines(Service::Interface* self) { 463void GetMaxLines(Service::Interface* self) {
110 u32* cmd_buff = Kernel::GetCommandBuffer(); 464 u32* cmd_buff = Kernel::GetCommandBuffer();
111 465
112 u16 width = cmd_buff[1] & 0xFFFF; 466 const u32 width = cmd_buff[1] & 0xFFFF;
113 u16 height = cmd_buff[2] & 0xFFFF; 467 const u32 height = cmd_buff[2] & 0xFFFF;
468
469 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480
470 constexpr u32 MIN_TRANSFER_UNIT = 256;
471 constexpr u32 MAX_BUFFER_SIZE = 2560;
472 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) {
473 cmd_buff[1] = ERROR_OUT_OF_RANGE.raw;
474 } else {
475 u32 lines = MAX_BUFFER_SIZE / width;
476 if (lines > height) {
477 lines = height;
478 }
479 cmd_buff[1] = RESULT_SUCCESS.raw;
480 while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) {
481 --lines;
482 if (lines == 0) {
483 cmd_buff[1] = ERROR_OUT_OF_RANGE.raw;
484 break;
485 }
486 }
487 cmd_buff[2] = lines;
488 }
114 489
115 cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); 490 cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0);
116 cmd_buff[1] = RESULT_SUCCESS.raw;
117 cmd_buff[2] = TRANSFER_BYTES / (2 * width);
118 491
119 LOG_WARNING(Service_CAM, "(STUBBED) called, width=%d, height=%d, lines = %d", width, height, 492 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height);
120 cmd_buff[2]); 493}
494
495void SetTransferBytes(Service::Interface* self) {
496 u32* cmd_buff = Kernel::GetCommandBuffer();
497
498 const PortSet port_select(cmd_buff[1]);
499 const u32 transfer_bytes = cmd_buff[2] & 0xFFFF;
500 const u32 width = cmd_buff[3] & 0xFFFF;
501 const u32 height = cmd_buff[4] & 0xFFFF;
502
503 if (port_select.IsValid()) {
504 for (int i : port_select) {
505 ports[i].transfer_bytes = transfer_bytes;
506 }
507 cmd_buff[1] = RESULT_SUCCESS.raw;
508 } else {
509 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
510 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
511 }
512
513 cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0);
514
515 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u",
516 port_select.m_val, transfer_bytes, width, height);
121} 517}
122 518
123void GetTransferBytes(Service::Interface* self) { 519void GetTransferBytes(Service::Interface* self) {
124 u32* cmd_buff = Kernel::GetCommandBuffer(); 520 u32* cmd_buff = Kernel::GetCommandBuffer();
125 521
126 u8 port = cmd_buff[1] & 0xFF; 522 const PortSet port_select(cmd_buff[1]);
523
524 if (port_select.IsSingle()) {
525 int port = *port_select.begin();
526 cmd_buff[1] = RESULT_SUCCESS.raw;
527 cmd_buff[2] = ports[port].transfer_bytes;
528 } else {
529 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
530 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
531 }
127 532
128 cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); 533 cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
129 cmd_buff[1] = RESULT_SUCCESS.raw;
130 cmd_buff[2] = TRANSFER_BYTES;
131 534
132 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d", port); 535 LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val);
536}
537
538void GetMaxBytes(Service::Interface* self) {
539 u32* cmd_buff = Kernel::GetCommandBuffer();
540
541 const u32 width = cmd_buff[1] & 0xFFFF;
542 const u32 height = cmd_buff[2] & 0xFFFF;
543
544 // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480
545 constexpr u32 MIN_TRANSFER_UNIT = 256;
546 constexpr u32 MAX_BUFFER_SIZE = 2560;
547 if (width * height * 2 % MIN_TRANSFER_UNIT != 0) {
548 cmd_buff[1] = ERROR_OUT_OF_RANGE.raw;
549 } else {
550 u32 bytes = MAX_BUFFER_SIZE;
551
552 while (width * height * 2 % bytes != 0) {
553 bytes -= MIN_TRANSFER_UNIT;
554 }
555
556 cmd_buff[1] = RESULT_SUCCESS.raw;
557 cmd_buff[2] = bytes;
558 }
559 cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0);
560
561 LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height);
133} 562}
134 563
135void SetTrimming(Service::Interface* self) { 564void SetTrimming(Service::Interface* self) {
136 u32* cmd_buff = Kernel::GetCommandBuffer(); 565 u32* cmd_buff = Kernel::GetCommandBuffer();
137 566
138 u8 port = cmd_buff[1] & 0xFF; 567 const PortSet port_select(cmd_buff[1]);
139 bool trim = (cmd_buff[2] & 0xFF) != 0; 568 const bool trim = (cmd_buff[2] & 0xFF) != 0;
569
570 if (port_select.IsValid()) {
571 for (int i : port_select) {
572 ports[i].is_trimming = trim;
573 }
574 cmd_buff[1] = RESULT_SUCCESS.raw;
575 } else {
576 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
577 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
578 }
140 579
141 cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0); 580 cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0);
142 cmd_buff[1] = RESULT_SUCCESS.raw;
143 581
144 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trim=%d", port, trim); 582 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim);
583}
584
585void IsTrimming(Service::Interface* self) {
586 u32* cmd_buff = Kernel::GetCommandBuffer();
587
588 const PortSet port_select(cmd_buff[1]);
589
590 if (port_select.IsSingle()) {
591 int port = *port_select.begin();
592 cmd_buff[1] = RESULT_SUCCESS.raw;
593 cmd_buff[2] = ports[port].is_trimming;
594 } else {
595 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
596 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
597 }
598
599 cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
600
601 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
602}
603
604void SetTrimmingParams(Service::Interface* self) {
605 u32* cmd_buff = Kernel::GetCommandBuffer();
606
607 const PortSet port_select(cmd_buff[1]);
608 const u16 x0 = static_cast<u16>(cmd_buff[2] & 0xFFFF);
609 const u16 y0 = static_cast<u16>(cmd_buff[3] & 0xFFFF);
610 const u16 x1 = static_cast<u16>(cmd_buff[4] & 0xFFFF);
611 const u16 y1 = static_cast<u16>(cmd_buff[5] & 0xFFFF);
612
613 if (port_select.IsValid()) {
614 for (int i : port_select) {
615 ports[i].x0 = x0;
616 ports[i].y0 = y0;
617 ports[i].x1 = x1;
618 ports[i].y1 = y1;
619 }
620 cmd_buff[1] = RESULT_SUCCESS.raw;
621 } else {
622 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
623 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
624 }
625
626 cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0);
627
628 LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val,
629 x0, y0, x1, y1);
630}
631
632void GetTrimmingParams(Service::Interface* self) {
633 u32* cmd_buff = Kernel::GetCommandBuffer();
634
635 const PortSet port_select(cmd_buff[1]);
636
637 if (port_select.IsSingle()) {
638 int port = *port_select.begin();
639 cmd_buff[1] = RESULT_SUCCESS.raw;
640 cmd_buff[2] = ports[port].x0;
641 cmd_buff[3] = ports[port].y0;
642 cmd_buff[4] = ports[port].x1;
643 cmd_buff[5] = ports[port].y1;
644 } else {
645 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
646 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
647 }
648
649 cmd_buff[0] = IPC::MakeHeader(0x11, 5, 0);
650
651 LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val);
145} 652}
146 653
147void SetTrimmingParamsCenter(Service::Interface* self) { 654void SetTrimmingParamsCenter(Service::Interface* self) {
148 u32* cmd_buff = Kernel::GetCommandBuffer(); 655 u32* cmd_buff = Kernel::GetCommandBuffer();
149 656
150 u8 port = cmd_buff[1] & 0xFF; 657 const PortSet port_select(cmd_buff[1]);
151 s16 trimW = cmd_buff[2] & 0xFFFF; 658 const u16 trim_w = static_cast<u16>(cmd_buff[2] & 0xFFFF);
152 s16 trimH = cmd_buff[3] & 0xFFFF; 659 const u16 trim_h = static_cast<u16>(cmd_buff[3] & 0xFFFF);
153 s16 camW = cmd_buff[4] & 0xFFFF; 660 const u16 cam_w = static_cast<u16>(cmd_buff[4] & 0xFFFF);
154 s16 camH = cmd_buff[5] & 0xFFFF; 661 const u16 cam_h = static_cast<u16>(cmd_buff[5] & 0xFFFF);
662
663 if (port_select.IsValid()) {
664 for (int i : port_select) {
665 ports[i].x0 = (cam_w - trim_w) / 2;
666 ports[i].y0 = (cam_h - trim_h) / 2;
667 ports[i].x1 = ports[i].x0 + trim_w;
668 ports[i].y1 = ports[i].y0 + trim_h;
669 }
670 cmd_buff[1] = RESULT_SUCCESS.raw;
671 } else {
672 LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val);
673 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
674 }
155 675
156 cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); 676 cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
157 cmd_buff[1] = RESULT_SUCCESS.raw;
158 677
159 LOG_WARNING(Service_CAM, "(STUBBED) called, port=%d, trimW=%d, trimH=%d, camW=%d, camH=%d", 678 LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u",
160 port, trimW, trimH, camW, camH); 679 port_select.m_val, trim_w, trim_h, cam_w, cam_h);
161} 680}
162 681
163void Activate(Service::Interface* self) { 682void Activate(Service::Interface* self) {
164 u32* cmd_buff = Kernel::GetCommandBuffer(); 683 u32* cmd_buff = Kernel::GetCommandBuffer();
165 684
166 u8 cam_select = cmd_buff[1] & 0xFF; 685 const CameraSet camera_select(cmd_buff[1]);
686
687 if (camera_select.IsValid()) {
688 if (camera_select.m_val == 0) { // deactive all
689 for (int i = 0; i < 2; ++i) {
690 if (ports[i].is_busy) {
691 CancelReceiving(i);
692 cameras[ports[i].camera_id].impl->StopCapture();
693 ports[i].is_busy = false;
694 }
695 ports[i].is_active = false;
696 }
697 cmd_buff[1] = RESULT_SUCCESS.raw;
698 } else if (camera_select[0] && camera_select[1]) {
699 LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated");
700 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
701 } else {
702 if (camera_select[0]) {
703 ActivatePort(0, 0);
704 } else if (camera_select[1]) {
705 ActivatePort(0, 1);
706 }
707
708 if (camera_select[2]) {
709 ActivatePort(1, 2);
710 }
711 cmd_buff[1] = RESULT_SUCCESS.raw;
712 }
713 } else {
714 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val);
715 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
716 }
167 717
168 cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); 718 cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
169 cmd_buff[1] = RESULT_SUCCESS.raw;
170 719
171 LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d", cam_select); 720 LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val);
721}
722
723void SwitchContext(Service::Interface* self) {
724 u32* cmd_buff = Kernel::GetCommandBuffer();
725
726 const CameraSet camera_select(cmd_buff[1]);
727 const ContextSet context_select(cmd_buff[2]);
728
729 if (camera_select.IsValid() && context_select.IsSingle()) {
730 int context = *context_select.begin();
731 for (int camera : camera_select) {
732 cameras[camera].current_context = context;
733 const ContextConfig& context_config = cameras[camera].contexts[context];
734 cameras[camera].impl->SetFlip(context_config.flip);
735 cameras[camera].impl->SetEffect(context_config.effect);
736 cameras[camera].impl->SetFormat(context_config.format);
737 cameras[camera].impl->SetResolution(context_config.resolution);
738 }
739 cmd_buff[1] = RESULT_SUCCESS.raw;
740 } else {
741 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
742 context_select.m_val);
743 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
744 }
745
746 cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0);
747
748 LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val,
749 context_select.m_val);
172} 750}
173 751
174void FlipImage(Service::Interface* self) { 752void FlipImage(Service::Interface* self) {
175 u32* cmd_buff = Kernel::GetCommandBuffer(); 753 u32* cmd_buff = Kernel::GetCommandBuffer();
176 754
177 u8 cam_select = cmd_buff[1] & 0xFF; 755 const CameraSet camera_select(cmd_buff[1]);
178 u8 flip = cmd_buff[2] & 0xFF; 756 const Flip flip = static_cast<Flip>(cmd_buff[2] & 0xFF);
179 u8 context = cmd_buff[3] & 0xFF; 757 const ContextSet context_select(cmd_buff[3]);
758
759 if (camera_select.IsValid() && context_select.IsValid()) {
760 for (int camera : camera_select) {
761 for (int context : context_select) {
762 cameras[camera].contexts[context].flip = flip;
763 if (cameras[camera].current_context == context) {
764 cameras[camera].impl->SetFlip(flip);
765 }
766 }
767 }
768 cmd_buff[1] = RESULT_SUCCESS.raw;
769 } else {
770 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
771 context_select.m_val);
772 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
773 }
180 774
181 cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0); 775 cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0);
182 cmd_buff[1] = RESULT_SUCCESS.raw;
183 776
184 LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, flip=%d, context=%d", cam_select, 777 LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u",
185 flip, context); 778 camera_select.m_val, static_cast<int>(flip), context_select.m_val);
779}
780
781void SetDetailSize(Service::Interface* self) {
782 u32* cmd_buff = Kernel::GetCommandBuffer();
783
784 const CameraSet camera_select(cmd_buff[1]);
785 Resolution resolution;
786 resolution.width = static_cast<u16>(cmd_buff[2] & 0xFFFF);
787 resolution.height = static_cast<u16>(cmd_buff[3] & 0xFFFF);
788 resolution.crop_x0 = static_cast<u16>(cmd_buff[4] & 0xFFFF);
789 resolution.crop_y0 = static_cast<u16>(cmd_buff[5] & 0xFFFF);
790 resolution.crop_x1 = static_cast<u16>(cmd_buff[6] & 0xFFFF);
791 resolution.crop_y1 = static_cast<u16>(cmd_buff[7] & 0xFFFF);
792 const ContextSet context_select(cmd_buff[8]);
793
794 if (camera_select.IsValid() && context_select.IsValid()) {
795 for (int camera : camera_select) {
796 for (int context : context_select) {
797 cameras[camera].contexts[context].resolution = resolution;
798 if (cameras[camera].current_context == context) {
799 cameras[camera].impl->SetResolution(resolution);
800 }
801 }
802 }
803 cmd_buff[1] = RESULT_SUCCESS.raw;
804 } else {
805 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
806 context_select.m_val);
807 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
808 }
809
810 cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0);
811
812 LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, "
813 "crop_x1=%u, crop_y1=%u, context_select=%u",
814 camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0,
815 resolution.crop_y0, resolution.crop_x1, resolution.crop_y1, context_select.m_val);
186} 816}
187 817
188void SetSize(Service::Interface* self) { 818void SetSize(Service::Interface* self) {
189 u32* cmd_buff = Kernel::GetCommandBuffer(); 819 u32* cmd_buff = Kernel::GetCommandBuffer();
190 820
191 u8 cam_select = cmd_buff[1] & 0xFF; 821 const CameraSet camera_select(cmd_buff[1]);
192 u8 size = cmd_buff[2] & 0xFF; 822 const u32 size = cmd_buff[2] & 0xFF;
193 u8 context = cmd_buff[3] & 0xFF; 823 const ContextSet context_select(cmd_buff[3]);
824
825 if (camera_select.IsValid() && context_select.IsValid()) {
826 for (int camera : camera_select) {
827 for (int context : context_select) {
828 cameras[camera].contexts[context].resolution = PRESET_RESOLUTION[size];
829 if (cameras[camera].current_context == context) {
830 cameras[camera].impl->SetResolution(PRESET_RESOLUTION[size]);
831 }
832 }
833 }
834 cmd_buff[1] = RESULT_SUCCESS.raw;
835 } else {
836 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
837 context_select.m_val);
838 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
839 }
194 840
195 cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0); 841 cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0);
196 cmd_buff[1] = RESULT_SUCCESS.raw;
197 842
198 LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, size=%d, context=%d", cam_select, 843 LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u",
199 size, context); 844 camera_select.m_val, size, context_select.m_val);
200} 845}
201 846
202void SetFrameRate(Service::Interface* self) { 847void SetFrameRate(Service::Interface* self) {
203 u32* cmd_buff = Kernel::GetCommandBuffer(); 848 u32* cmd_buff = Kernel::GetCommandBuffer();
204 849
205 u8 cam_select = cmd_buff[1] & 0xFF; 850 const CameraSet camera_select(cmd_buff[1]);
206 u8 frame_rate = cmd_buff[2] & 0xFF; 851 const FrameRate frame_rate = static_cast<FrameRate>(cmd_buff[2] & 0xFF);
852
853 if (camera_select.IsValid()) {
854 for (int camera : camera_select) {
855 cameras[camera].frame_rate = frame_rate;
856 // TODO(wwylele): consider hinting the actual camera with the expected frame rate
857 }
858 cmd_buff[1] = RESULT_SUCCESS.raw;
859 } else {
860 LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val);
861 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
862 }
207 863
208 cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); 864 cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0);
865
866 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d",
867 camera_select.m_val, static_cast<int>(frame_rate));
868}
869
870void SetEffect(Service::Interface* self) {
871 u32* cmd_buff = Kernel::GetCommandBuffer();
872
873 const CameraSet camera_select(cmd_buff[1]);
874 const Effect effect = static_cast<Effect>(cmd_buff[2] & 0xFF);
875 const ContextSet context_select(cmd_buff[3]);
876
877 if (camera_select.IsValid() && context_select.IsValid()) {
878 for (int camera : camera_select) {
879 for (int context : context_select) {
880 cameras[camera].contexts[context].effect = effect;
881 if (cameras[camera].current_context == context) {
882 cameras[camera].impl->SetEffect(effect);
883 }
884 }
885 }
886 cmd_buff[1] = RESULT_SUCCESS.raw;
887 } else {
888 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
889 context_select.m_val);
890 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
891 }
892
893 cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0);
894
895 LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u",
896 camera_select.m_val, static_cast<int>(effect), context_select.m_val);
897}
898
899void SetOutputFormat(Service::Interface* self) {
900 u32* cmd_buff = Kernel::GetCommandBuffer();
901
902 const CameraSet camera_select(cmd_buff[1]);
903 const OutputFormat format = static_cast<OutputFormat>(cmd_buff[2] & 0xFF);
904 const ContextSet context_select(cmd_buff[3]);
905
906 if (camera_select.IsValid() && context_select.IsValid()) {
907 for (int camera : camera_select) {
908 for (int context : context_select) {
909 cameras[camera].contexts[context].format = format;
910 if (cameras[camera].current_context == context) {
911 cameras[camera].impl->SetFormat(format);
912 }
913 }
914 }
915 cmd_buff[1] = RESULT_SUCCESS.raw;
916 } else {
917 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val,
918 context_select.m_val);
919 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
920 }
921
922 cmd_buff[0] = IPC::MakeHeader(0x25, 1, 0);
923
924 LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u",
925 camera_select.m_val, static_cast<int>(format), context_select.m_val);
926}
927
928void SynchronizeVsyncTiming(Service::Interface* self) {
929 u32* cmd_buff = Kernel::GetCommandBuffer();
930
931 const u32 camera_select1 = cmd_buff[1] & 0xFF;
932 const u32 camera_select2 = cmd_buff[2] & 0xFF;
933
934 cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0);
209 cmd_buff[1] = RESULT_SUCCESS.raw; 935 cmd_buff[1] = RESULT_SUCCESS.raw;
210 936
211 LOG_WARNING(Service_CAM, "(STUBBED) called, cam_select=%d, frame_rate=%d", cam_select, 937 LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u",
212 frame_rate); 938 camera_select1, camera_select2);
213} 939}
214 940
215void GetStereoCameraCalibrationData(Service::Interface* self) { 941void GetStereoCameraCalibrationData(Service::Interface* self) {
@@ -239,6 +965,67 @@ void GetStereoCameraCalibrationData(Service::Interface* self) {
239 LOG_TRACE(Service_CAM, "called"); 965 LOG_TRACE(Service_CAM, "called");
240} 966}
241 967
968void SetPackageParameterWithoutContext(Service::Interface* self) {
969 u32* cmd_buff = Kernel::GetCommandBuffer();
970
971 PackageParameterWithoutContext package;
972 std::memcpy(&package, cmd_buff + 1, sizeof(package));
973
974 cmd_buff[0] = IPC::MakeHeader(0x33, 1, 0);
975 cmd_buff[1] = RESULT_SUCCESS.raw;
976
977 LOG_WARNING(Service_CAM, "(STUBBED) called");
978}
979
980template <typename PackageParameterType, int command_id>
981static void SetPackageParameter() {
982 u32* cmd_buff = Kernel::GetCommandBuffer();
983
984 PackageParameterType package;
985 std::memcpy(&package, cmd_buff + 1, sizeof(package));
986
987 const CameraSet camera_select(static_cast<u32>(package.camera_select));
988 const ContextSet context_select(static_cast<u32>(package.context_select));
989
990 if (camera_select.IsValid() && context_select.IsValid()) {
991 for (int camera_id : camera_select) {
992 CameraConfig& camera = cameras[camera_id];
993 for (int context_id : context_select) {
994 ContextConfig& context = camera.contexts[context_id];
995 context.effect = package.effect;
996 context.flip = package.flip;
997 context.resolution = package.GetResolution();
998 if (context_id == camera.current_context) {
999 camera.impl->SetEffect(context.effect);
1000 camera.impl->SetFlip(context.flip);
1001 camera.impl->SetResolution(context.resolution);
1002 }
1003 }
1004 }
1005 cmd_buff[1] = RESULT_SUCCESS.raw;
1006 } else {
1007 LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select,
1008 package.context_select);
1009 cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw;
1010 }
1011
1012 cmd_buff[0] = IPC::MakeHeader(command_id, 1, 0);
1013
1014 LOG_DEBUG(Service_CAM, "called");
1015}
1016
1017Resolution PackageParameterWithContext::GetResolution() {
1018 return PRESET_RESOLUTION[static_cast<int>(size)];
1019}
1020
1021void SetPackageParameterWithContext(Service::Interface* self) {
1022 SetPackageParameter<PackageParameterWithContext, 0x34>();
1023}
1024
1025void SetPackageParameterWithContextDetail(Service::Interface* self) {
1026 SetPackageParameter<PackageParameterWithContextDetail, 0x35>();
1027}
1028
242void GetSuitableY2rStandardCoefficient(Service::Interface* self) { 1029void GetSuitableY2rStandardCoefficient(Service::Interface* self) {
243 u32* cmd_buff = Kernel::GetCommandBuffer(); 1030 u32* cmd_buff = Kernel::GetCommandBuffer();
244 1031
@@ -263,24 +1050,50 @@ void PlayShutterSound(Service::Interface* self) {
263void DriverInitialize(Service::Interface* self) { 1050void DriverInitialize(Service::Interface* self) {
264 u32* cmd_buff = Kernel::GetCommandBuffer(); 1051 u32* cmd_buff = Kernel::GetCommandBuffer();
265 1052
266 completion_event_cam1->Clear(); 1053 for (int camera_id = 0; camera_id < NumCameras; ++camera_id) {
267 completion_event_cam2->Clear(); 1054 CameraConfig& camera = cameras[camera_id];
268 interrupt_error_event->Clear(); 1055 camera.current_context = 0;
269 vsync_interrupt_error_event->Clear(); 1056 for (int context_id = 0; context_id < 2; ++context_id) {
1057 // Note: the following default values are verified against real 3DS
1058 ContextConfig& context = camera.contexts[context_id];
1059 context.flip = camera_id == 1 ? Flip::Horizontal : Flip::None;
1060 context.effect = Effect::None;
1061 context.format = OutputFormat::YUV422;
1062 context.resolution =
1063 context_id == 0 ? PRESET_RESOLUTION[5 /*DS_LCD*/] : PRESET_RESOLUTION[0 /*VGA*/];
1064 }
1065 camera.impl = Camera::CreateCamera(Settings::values.camera_name[camera_id],
1066 Settings::values.camera_config[camera_id]);
1067 camera.impl->SetFlip(camera.contexts[0].flip);
1068 camera.impl->SetEffect(camera.contexts[0].effect);
1069 camera.impl->SetFormat(camera.contexts[0].format);
1070 camera.impl->SetResolution(camera.contexts[0].resolution);
1071 }
1072
1073 for (PortConfig& port : ports) {
1074 port.Clear();
1075 }
270 1076
271 cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0); 1077 cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0);
272 cmd_buff[1] = RESULT_SUCCESS.raw; 1078 cmd_buff[1] = RESULT_SUCCESS.raw;
273 1079
274 LOG_WARNING(Service_CAM, "(STUBBED) called"); 1080 LOG_DEBUG(Service_CAM, "called");
275} 1081}
276 1082
277void DriverFinalize(Service::Interface* self) { 1083void DriverFinalize(Service::Interface* self) {
278 u32* cmd_buff = Kernel::GetCommandBuffer(); 1084 u32* cmd_buff = Kernel::GetCommandBuffer();
279 1085
1086 CancelReceiving(0);
1087 CancelReceiving(1);
1088
1089 for (CameraConfig& camera : cameras) {
1090 camera.impl = nullptr;
1091 }
1092
280 cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0); 1093 cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0);
281 cmd_buff[1] = RESULT_SUCCESS.raw; 1094 cmd_buff[1] = RESULT_SUCCESS.raw;
282 1095
283 LOG_WARNING(Service_CAM, "(STUBBED) called"); 1096 LOG_DEBUG(Service_CAM, "called");
284} 1097}
285 1098
286void Init() { 1099void Init() {
@@ -291,21 +1104,28 @@ void Init() {
291 AddService(new CAM_S_Interface); 1104 AddService(new CAM_S_Interface);
292 AddService(new CAM_U_Interface); 1105 AddService(new CAM_U_Interface);
293 1106
294 completion_event_cam1 = 1107 for (PortConfig& port : ports) {
295 Kernel::Event::Create(ResetType::OneShot, "CAM_U::completion_event_cam1"); 1108 port.completion_event = Event::Create(ResetType::Sticky, "CAM_U::completion_event");
296 completion_event_cam2 = 1109 port.buffer_error_interrupt_event =
297 Kernel::Event::Create(ResetType::OneShot, "CAM_U::completion_event_cam2"); 1110 Event::Create(ResetType::OneShot, "CAM_U::buffer_error_interrupt_event");
298 interrupt_error_event = 1111 port.vsync_interrupt_event =
299 Kernel::Event::Create(ResetType::OneShot, "CAM_U::interrupt_error_event"); 1112 Event::Create(ResetType::OneShot, "CAM_U::vsync_interrupt_event");
300 vsync_interrupt_error_event = 1113 }
301 Kernel::Event::Create(ResetType::OneShot, "CAM_U::vsync_interrupt_error_event"); 1114 completion_event_callback =
1115 CoreTiming::RegisterEvent("CAM_U::CompletionEventCallBack", CompletionEventCallBack);
302} 1116}
303 1117
304void Shutdown() { 1118void Shutdown() {
305 completion_event_cam1 = nullptr; 1119 CancelReceiving(0);
306 completion_event_cam2 = nullptr; 1120 CancelReceiving(1);
307 interrupt_error_event = nullptr; 1121 for (PortConfig& port : ports) {
308 vsync_interrupt_error_event = nullptr; 1122 port.completion_event = nullptr;
1123 port.buffer_error_interrupt_event = nullptr;
1124 port.vsync_interrupt_event = nullptr;
1125 }
1126 for (CameraConfig& camera : cameras) {
1127 camera.impl = nullptr;
1128 }
309} 1129}
310 1130
311} // namespace CAM 1131} // namespace CAM
diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h
index c9b6f8acf..f6bff8bc6 100644
--- a/src/core/hle/service/cam/cam.h
+++ b/src/core/hle/service/cam/cam.h
@@ -13,17 +13,12 @@
13namespace Service { 13namespace Service {
14namespace CAM { 14namespace CAM {
15 15
16enum class Port : u8 { None = 0, Cam1 = 1, Cam2 = 2, Both = Cam1 | Cam2 }; 16enum CameraIndex {
17 OuterRightCamera = 0,
18 InnerCamera = 1,
19 OuterLeftCamera = 2,
17 20
18enum class CameraSelect : u8 { 21 NumCameras = 3,
19 None = 0,
20 Out1 = 1,
21 In1 = 2,
22 Out2 = 4,
23 In1Out1 = Out1 | In1,
24 Out1Out2 = Out1 | Out2,
25 In1Out2 = In1 | Out2,
26 All = Out1 | In1 | Out2,
27}; 22};
28 23
29enum class Effect : u8 { 24enum class Effect : u8 {
@@ -35,13 +30,6 @@ enum class Effect : u8 {
35 Sepia01 = 5, 30 Sepia01 = 5,
36}; 31};
37 32
38enum class Context : u8 {
39 None = 0,
40 A = 1,
41 B = 2,
42 Both = A | B,
43};
44
45enum class Flip : u8 { 33enum class Flip : u8 {
46 None = 0, 34 None = 0,
47 Horizontal = 1, 35 Horizontal = 1,
@@ -160,8 +148,23 @@ struct StereoCameraCalibrationData {
160static_assert(sizeof(StereoCameraCalibrationData) == 64, 148static_assert(sizeof(StereoCameraCalibrationData) == 64,
161 "StereoCameraCalibrationData structure size is wrong"); 149 "StereoCameraCalibrationData structure size is wrong");
162 150
163struct PackageParameterCameraSelect { 151/**
164 CameraSelect camera; 152 * Resolution parameters for the camera.
153 * The native resolution of 3DS camera is 640 * 480. The captured image will be cropped in the
154 * region [crop_x0, crop_x1] * [crop_y0, crop_y1], and then scaled to size width * height as the
155 * output image. Note that all cropping coordinates are inclusive.
156 */
157struct Resolution {
158 u16 width;
159 u16 height;
160 u16 crop_x0;
161 u16 crop_y0;
162 u16 crop_x1;
163 u16 crop_y1;
164};
165
166struct PackageParameterWithoutContext {
167 u8 camera_select;
165 s8 exposure; 168 s8 exposure;
166 WhiteBalance white_balance; 169 WhiteBalance white_balance;
167 s8 sharpness; 170 s8 sharpness;
@@ -183,14 +186,43 @@ struct PackageParameterCameraSelect {
183 s16 auto_white_balance_window_height; 186 s16 auto_white_balance_window_height;
184}; 187};
185 188
186static_assert(sizeof(PackageParameterCameraSelect) == 28, 189static_assert(sizeof(PackageParameterWithoutContext) == 28,
187 "PackageParameterCameraSelect structure size is wrong"); 190 "PackageParameterCameraWithoutContext structure size is wrong");
191
192struct PackageParameterWithContext {
193 u8 camera_select;
194 u8 context_select;
195 Flip flip;
196 Effect effect;
197 Size size;
198 INSERT_PADDING_BYTES(3);
199
200 Resolution GetResolution();
201};
202
203static_assert(sizeof(PackageParameterWithContext) == 8,
204 "PackageParameterWithContext structure size is wrong");
205
206struct PackageParameterWithContextDetail {
207 u8 camera_select;
208 u8 context_select;
209 Flip flip;
210 Effect effect;
211 Resolution resolution;
212
213 Resolution GetResolution() {
214 return resolution;
215 }
216};
217
218static_assert(sizeof(PackageParameterWithContextDetail) == 16,
219 "PackageParameterWithContextDetail structure size is wrong");
188 220
189/** 221/**
190 * Unknown 222 * Starts capturing at the selected port.
191 * Inputs: 223 * Inputs:
192 * 0: 0x00010040 224 * 0: 0x00010040
193 * 1: u8 Camera port (`Port` enum) 225 * 1: u8 selected port
194 * Outputs: 226 * Outputs:
195 * 0: 0x00010040 227 * 0: 0x00010040
196 * 1: ResultCode 228 * 1: ResultCode
@@ -198,10 +230,10 @@ static_assert(sizeof(PackageParameterCameraSelect) == 28,
198void StartCapture(Service::Interface* self); 230void StartCapture(Service::Interface* self);
199 231
200/** 232/**
201 * Unknown 233 * Stops capturing from the selected port.
202 * Inputs: 234 * Inputs:
203 * 0: 0x00020040 235 * 0: 0x00020040
204 * 1: u8 Camera port (`Port` enum) 236 * 1: u8 selected port
205 * Outputs: 237 * Outputs:
206 * 0: 0x00020040 238 * 0: 0x00020040
207 * 1: ResultCode 239 * 1: ResultCode
@@ -209,10 +241,33 @@ void StartCapture(Service::Interface* self);
209void StopCapture(Service::Interface* self); 241void StopCapture(Service::Interface* self);
210 242
211/** 243/**
244 * Gets whether the selected port is currently capturing.
245 * Inputs:
246 * 0: 0x00030040
247 * 1: u8 selected port
248 * Outputs:
249 * 0: 0x00030080
250 * 1: ResultCode
251 * 2: 0 if not capturing, 1 if capturing
252 */
253void IsBusy(Service::Interface* self);
254
255/**
256 * Clears the buffer of selected ports.
257 * Inputs:
258 * 0: 0x00040040
259 * 1: u8 selected port
260 * Outputs:
261 * 0: 0x00040040
262 * 2: ResultCode
263 */
264void ClearBuffer(Service::Interface* self);
265
266/**
212 * Unknown 267 * Unknown
213 * Inputs: 268 * Inputs:
214 * 0: 0x00050040 269 * 0: 0x00050040
215 * 1: u8 Camera port (`Port` enum) 270 * 1: u8 selected port
216 * Outputs: 271 * Outputs:
217 * 0: 0x00050042 272 * 0: 0x00050042
218 * 1: ResultCode 273 * 1: ResultCode
@@ -225,7 +280,7 @@ void GetVsyncInterruptEvent(Service::Interface* self);
225 * Unknown 280 * Unknown
226 * Inputs: 281 * Inputs:
227 * 0: 0x00060040 282 * 0: 0x00060040
228 * 1: u8 Camera port (`Port` enum) 283 * 1: u8 selected port
229 * Outputs: 284 * Outputs:
230 * 0: 0x00060042 285 * 0: 0x00060042
231 * 1: ResultCode 286 * 1: ResultCode
@@ -241,9 +296,9 @@ void GetBufferErrorInterruptEvent(Service::Interface* self);
241 * Inputs: 296 * Inputs:
242 * 0: 0x00070102 297 * 0: 0x00070102
243 * 1: Destination address in calling process 298 * 1: Destination address in calling process
244 * 2: u8 Camera port (`Port` enum) 299 * 2: u8 selected port
245 * 3: Image size (in bytes?) 300 * 3: Image size (in bytes)
246 * 4: u16 Transfer unit size (in bytes?) 301 * 4: u16 Transfer unit size (in bytes)
247 * 5: Descriptor: Handle 302 * 5: Descriptor: Handle
248 * 6: Handle to destination process 303 * 6: Handle to destination process
249 * Outputs: 304 * Outputs:
@@ -255,21 +310,34 @@ void GetBufferErrorInterruptEvent(Service::Interface* self);
255void SetReceiving(Service::Interface* self); 310void SetReceiving(Service::Interface* self);
256 311
257/** 312/**
258 * Unknown 313 * Gets whether the selected port finished receiving a frame.
314 * Inputs:
315 * 0: 0x00080040
316 * 1: u8 selected port
317 * Outputs:
318 * 0: 0x00080080
319 * 1: ResultCode
320 * 2: 0 if not finished, 1 if finished
321 */
322void IsFinishedReceiving(Service::Interface* self);
323
324/**
325 * Sets the number of lines the buffer contains.
259 * Inputs: 326 * Inputs:
260 * 0: 0x00090100 327 * 0: 0x00090100
261 * 1: u8 Camera port (`Port` enum) 328 * 1: u8 selected port
262 * 2: u16 Number of lines to transfer 329 * 2: u16 Number of lines to transfer
263 * 3: u16 Width 330 * 3: u16 Width
264 * 4: u16 Height 331 * 4: u16 Height
265 * Outputs: 332 * Outputs:
266 * 0: 0x00090040 333 * 0: 0x00090040
267 * 1: ResultCode 334 * 1: ResultCode
335 * @todo figure out how the "buffer" actually works.
268 */ 336 */
269void SetTransferLines(Service::Interface* self); 337void SetTransferLines(Service::Interface* self);
270 338
271/** 339/**
272 * Unknown 340 * Gets the maximum number of lines that fit in the buffer
273 * Inputs: 341 * Inputs:
274 * 0: 0x000A0080 342 * 0: 0x000A0080
275 * 1: u16 Width 343 * 1: u16 Width
@@ -277,27 +345,58 @@ void SetTransferLines(Service::Interface* self);
277 * Outputs: 345 * Outputs:
278 * 0: 0x000A0080 346 * 0: 0x000A0080
279 * 1: ResultCode 347 * 1: ResultCode
280 * 2: Maximum number of lines that fit in the buffer(?) 348 * 2: Maximum number of lines that fit in the buffer
349 * @todo figure out how the "buffer" actually works.
281 */ 350 */
282void GetMaxLines(Service::Interface* self); 351void GetMaxLines(Service::Interface* self);
283 352
284/** 353/**
285 * Unknown 354 * Sets the number of bytes the buffer contains.
355 * Inputs:
356 * 0: 0x000B0100
357 * 1: u8 selected port
358 * 2: u16 Number of bytes to transfer
359 * 3: u16 Width
360 * 4: u16 Height
361 * Outputs:
362 * 0: 0x000B0040
363 * 1: ResultCode
364 * @todo figure out how the "buffer" actually works.
365 */
366void SetTransferBytes(Service::Interface* self);
367
368/**
369 * Gets the number of bytes to the buffer contains.
286 * Inputs: 370 * Inputs:
287 * 0: 0x000C0040 371 * 0: 0x000C0040
288 * 1: u8 Camera port (`Port` enum) 372 * 1: u8 selected port
289 * Outputs: 373 * Outputs:
290 * 0: 0x000C0080 374 * 0: 0x000C0080
291 * 1: ResultCode 375 * 1: ResultCode
292 * 2: Total number of bytes for each frame with current settings(?) 376 * 2: The number of bytes the buffer contains
377 * @todo figure out how the "buffer" actually works.
293 */ 378 */
294void GetTransferBytes(Service::Interface* self); 379void GetTransferBytes(Service::Interface* self);
295 380
296/** 381/**
297 * Unknown 382 * Gets the maximum number of bytes that fit in the buffer.
383 * Inputs:
384 * 0: 0x000D0080
385 * 1: u16 Width
386 * 2: u16 Height
387 * Outputs:
388 * 0: 0x000D0080
389 * 1: ResultCode
390 * 2: Maximum number of bytes that fit in the buffer
391 * @todo figure out how the "buffer" actually works.
392 */
393void GetMaxBytes(Service::Interface* self);
394
395/**
396 * Enables or disables trimming.
298 * Inputs: 397 * Inputs:
299 * 0: 0x000E0080 398 * 0: 0x000E0080
300 * 1: u8 Camera port (`Port` enum) 399 * 1: u8 selected port
301 * 2: u8 bool Enable trimming if true 400 * 2: u8 bool Enable trimming if true
302 * Outputs: 401 * Outputs:
303 * 0: 0x000E0040 402 * 0: 0x000E0040
@@ -306,14 +405,58 @@ void GetTransferBytes(Service::Interface* self);
306void SetTrimming(Service::Interface* self); 405void SetTrimming(Service::Interface* self);
307 406
308/** 407/**
309 * Unknown 408 * Gets whether trimming is enabled.
409 * Inputs:
410 * 0: 0x000F0040
411 * 1: u8 selected port
412 * Outputs:
413 * 0: 0x000F0080
414 * 1: ResultCode
415 * 2: u8 bool Enable trimming if true
416 */
417void IsTrimming(Service::Interface* self);
418
419/**
420 * Sets the position to trim.
421 * Inputs:
422 * 0: 0x00100140
423 * 1: u8 selected port
424 * 2: x start
425 * 3: y start
426 * 4: x end (exclusive)
427 * 5: y end (exclusive)
428 * Outputs:
429 * 0: 0x00100040
430 * 1: ResultCode
431 */
432void SetTrimmingParams(Service::Interface* self);
433
434/**
435 * Gets the position to trim.
436 * Inputs:
437 * 0: 0x00110040
438 * 1: u8 selected port
439 *
440 * Outputs:
441 * 0: 0x00110140
442 * 1: ResultCode
443 * 2: x start
444 * 3: y start
445 * 4: x end (exclusive)
446 * 5: y end (exclusive)
447 */
448void GetTrimmingParams(Service::Interface* self);
449
450/**
451 * Sets the position to trim by giving the width and height. The trimming window is always at the
452 * center.
310 * Inputs: 453 * Inputs:
311 * 0: 0x00120140 454 * 0: 0x00120140
312 * 1: u8 Camera port (`Port` enum) 455 * 1: u8 selected port
313 * 2: s16 Trim width(?) 456 * 2: s16 Trim width
314 * 3: s16 Trim height(?) 457 * 3: s16 Trim height
315 * 4: s16 Camera width(?) 458 * 4: s16 Camera width
316 * 5: s16 Camera height(?) 459 * 5: s16 Camera height
317 * Outputs: 460 * Outputs:
318 * 0: 0x00120040 461 * 0: 0x00120040
319 * 1: ResultCode 462 * 1: ResultCode
@@ -324,7 +467,7 @@ void SetTrimmingParamsCenter(Service::Interface* self);
324 * Selects up to two physical cameras to enable. 467 * Selects up to two physical cameras to enable.
325 * Inputs: 468 * Inputs:
326 * 0: 0x00130040 469 * 0: 0x00130040
327 * 1: u8 Cameras to activate (`CameraSelect` enum) 470 * 1: u8 selected camera
328 * Outputs: 471 * Outputs:
329 * 0: 0x00130040 472 * 0: 0x00130040
330 * 1: ResultCode 473 * 1: ResultCode
@@ -332,12 +475,24 @@ void SetTrimmingParamsCenter(Service::Interface* self);
332void Activate(Service::Interface* self); 475void Activate(Service::Interface* self);
333 476
334/** 477/**
335 * Unknown 478 * Switches the context of camera settings.
479 * Inputs:
480 * 0: 0x00140080
481 * 1: u8 selected camera
482 * 2: u8 selected context
483 * Outputs:
484 * 0: 0x00140040
485 * 1: ResultCode
486 */
487void SwitchContext(Service::Interface* self);
488
489/**
490 * Sets flipping of images
336 * Inputs: 491 * Inputs:
337 * 0: 0x001D00C0 492 * 0: 0x001D00C0
338 * 1: u8 Camera select (`CameraSelect` enum) 493 * 1: u8 selected camera
339 * 2: u8 Type of flipping to perform (`Flip` enum) 494 * 2: u8 Type of flipping to perform (`Flip` enum)
340 * 3: u8 Context (`Context` enum) 495 * 3: u8 selected context
341 * Outputs: 496 * Outputs:
342 * 0: 0x001D0040 497 * 0: 0x001D0040
343 * 1: ResultCode 498 * 1: ResultCode
@@ -345,12 +500,30 @@ void Activate(Service::Interface* self);
345void FlipImage(Service::Interface* self); 500void FlipImage(Service::Interface* self);
346 501
347/** 502/**
348 * Unknown 503 * Sets camera resolution from custom parameters. For more details see the Resolution struct.
504 * Inputs:
505 * 0: 0x001E0200
506 * 1: u8 selected camera
507 * 2: width
508 * 3: height
509 * 4: crop x0
510 * 5: crop y0
511 * 6: crop x1
512 * 7: crop y1
513 * 8: u8 selected context
514 * Outputs:
515 * 0: 0x001E0040
516 * 1: ResultCode
517 */
518void SetDetailSize(Service::Interface* self);
519
520/**
521 * Sets camera resolution from preset resolution parameters. .
349 * Inputs: 522 * Inputs:
350 * 0: 0x001F00C0 523 * 0: 0x001F00C0
351 * 1: u8 Camera select (`CameraSelect` enum) 524 * 1: u8 selected camera
352 * 2: u8 Camera frame resolution (`Size` enum) 525 * 2: u8 Camera frame resolution (`Size` enum)
353 * 3: u8 Context id (`Context` enum) 526 * 3: u8 selected context
354 * Outputs: 527 * Outputs:
355 * 0: 0x001F0040 528 * 0: 0x001F0040
356 * 1: ResultCode 529 * 1: ResultCode
@@ -358,10 +531,10 @@ void FlipImage(Service::Interface* self);
358void SetSize(Service::Interface* self); 531void SetSize(Service::Interface* self);
359 532
360/** 533/**
361 * Unknown 534 * Sets camera framerate.
362 * Inputs: 535 * Inputs:
363 * 0: 0x00200080 536 * 0: 0x00200080
364 * 1: u8 Camera select (`CameraSelect` enum) 537 * 1: u8 selected camera
365 * 2: u8 Camera framerate (`FrameRate` enum) 538 * 2: u8 Camera framerate (`FrameRate` enum)
366 * Outputs: 539 * Outputs:
367 * 0: 0x00200040 540 * 0: 0x00200040
@@ -370,6 +543,44 @@ void SetSize(Service::Interface* self);
370void SetFrameRate(Service::Interface* self); 543void SetFrameRate(Service::Interface* self);
371 544
372/** 545/**
546 * Sets effect on the output image
547 * Inputs:
548 * 0: 0x002200C0
549 * 1: u8 selected camera
550 * 2: u8 image effect (`Effect` enum)
551 * 3: u8 selected context
552 * Outputs:
553 * 0: 0x00220040
554 * 1: ResultCode
555 */
556void SetEffect(Service::Interface* self);
557
558/**
559 * Sets format of the output image
560 * Inputs:
561 * 0: 0x002500C0
562 * 1: u8 selected camera
563 * 2: u8 image format (`OutputFormat` enum)
564 * 3: u8 selected context
565 * Outputs:
566 * 0: 0x00250040
567 * 1: ResultCode
568 */
569void SetOutputFormat(Service::Interface* self);
570
571/**
572 * Synchronizes the V-Sync timing of two cameras.
573 * Inputs:
574 * 0: 0x00290080
575 * 1: u8 selected camera 1
576 * 2: u8 selected camera 2
577 * Outputs:
578 * 0: 0x00280040
579 * 1: ResultCode
580 */
581void SynchronizeVsyncTiming(Service::Interface* self);
582
583/**
373 * Returns calibration data relating the outside cameras to eachother, for use in AR applications. 584 * Returns calibration data relating the outside cameras to eachother, for use in AR applications.
374 * 585 *
375 * Inputs: 586 * Inputs:
@@ -382,6 +593,45 @@ void SetFrameRate(Service::Interface* self);
382void GetStereoCameraCalibrationData(Service::Interface* self); 593void GetStereoCameraCalibrationData(Service::Interface* self);
383 594
384/** 595/**
596 * Batch-configures context-free settings.
597 *
598 * Inputs:
599 * 0: 0x003302C0
600 * 1-7: struct PachageParameterWithoutContext
601 * 8-11: unused
602 * Outputs:
603 * 0: 0x00330040
604 * 1: ResultCode
605 */
606void SetPackageParameterWithoutContext(Service::Interface* self);
607
608/**
609 * Batch-configures context-related settings with preset resolution parameters.
610 *
611 * Inputs:
612 * 0: 0x00340140
613 * 1-2: struct PackageParameterWithContext
614 * 3-5: unused
615 * Outputs:
616 * 0: 0x00340040
617 * 1: ResultCode
618 */
619void SetPackageParameterWithContext(Service::Interface* self);
620
621/**
622 * Batch-configures context-related settings with custom resolution parameters
623 *
624 * Inputs:
625 * 0: 0x003501C0
626 * 1-4: struct PackageParameterWithContextDetail
627 * 5-7: unused
628 * Outputs:
629 * 0: 0x00350040
630 * 1: ResultCode
631 */
632void SetPackageParameterWithContextDetail(Service::Interface* self);
633
634/**
385 * Unknown 635 * Unknown
386 * Inputs: 636 * Inputs:
387 * 0: 0x00360000 637 * 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 {
11const Interface::FunctionInfo FunctionTable[] = { 11const Interface::FunctionInfo FunctionTable[] = {
12 {0x00010040, StartCapture, "StartCapture"}, 12 {0x00010040, StartCapture, "StartCapture"},
13 {0x00020040, StopCapture, "StopCapture"}, 13 {0x00020040, StopCapture, "StopCapture"},
14 {0x00030040, nullptr, "IsBusy"}, 14 {0x00030040, IsBusy, "IsBusy"},
15 {0x00040040, nullptr, "ClearBuffer"}, 15 {0x00040040, ClearBuffer, "ClearBuffer"},
16 {0x00050040, GetVsyncInterruptEvent, "GetVsyncInterruptEvent"}, 16 {0x00050040, GetVsyncInterruptEvent, "GetVsyncInterruptEvent"},
17 {0x00060040, GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"}, 17 {0x00060040, GetBufferErrorInterruptEvent, "GetBufferErrorInterruptEvent"},
18 {0x00070102, SetReceiving, "SetReceiving"}, 18 {0x00070102, SetReceiving, "SetReceiving"},
19 {0x00080040, nullptr, "IsFinishedReceiving"}, 19 {0x00080040, IsFinishedReceiving, "IsFinishedReceiving"},
20 {0x00090100, SetTransferLines, "SetTransferLines"}, 20 {0x00090100, SetTransferLines, "SetTransferLines"},
21 {0x000A0080, GetMaxLines, "GetMaxLines"}, 21 {0x000A0080, GetMaxLines, "GetMaxLines"},
22 {0x000B0100, nullptr, "SetTransferBytes"}, 22 {0x000B0100, SetTransferBytes, "SetTransferBytes"},
23 {0x000C0040, GetTransferBytes, "GetTransferBytes"}, 23 {0x000C0040, GetTransferBytes, "GetTransferBytes"},
24 {0x000D0080, nullptr, "GetMaxBytes"}, 24 {0x000D0080, GetMaxBytes, "GetMaxBytes"},
25 {0x000E0080, SetTrimming, "SetTrimming"}, 25 {0x000E0080, SetTrimming, "SetTrimming"},
26 {0x000F0040, nullptr, "IsTrimming"}, 26 {0x000F0040, IsTrimming, "IsTrimming"},
27 {0x00100140, nullptr, "SetTrimmingParams"}, 27 {0x00100140, SetTrimmingParams, "SetTrimmingParams"},
28 {0x00110040, nullptr, "GetTrimmingParams"}, 28 {0x00110040, GetTrimmingParams, "GetTrimmingParams"},
29 {0x00120140, SetTrimmingParamsCenter, "SetTrimmingParamsCenter"}, 29 {0x00120140, SetTrimmingParamsCenter, "SetTrimmingParamsCenter"},
30 {0x00130040, Activate, "Activate"}, 30 {0x00130040, Activate, "Activate"},
31 {0x00140080, nullptr, "SwitchContext"}, 31 {0x00140080, SwitchContext, "SwitchContext"},
32 {0x00150080, nullptr, "SetExposure"}, 32 {0x00150080, nullptr, "SetExposure"},
33 {0x00160080, nullptr, "SetWhiteBalance"}, 33 {0x00160080, nullptr, "SetWhiteBalance"},
34 {0x00170080, nullptr, "SetWhiteBalanceWithoutBaseUp"}, 34 {0x00170080, nullptr, "SetWhiteBalanceWithoutBaseUp"},
@@ -38,18 +38,18 @@ const Interface::FunctionInfo FunctionTable[] = {
38 {0x001B0080, nullptr, "SetAutoWhiteBalance"}, 38 {0x001B0080, nullptr, "SetAutoWhiteBalance"},
39 {0x001C0040, nullptr, "IsAutoWhiteBalance"}, 39 {0x001C0040, nullptr, "IsAutoWhiteBalance"},
40 {0x001D00C0, FlipImage, "FlipImage"}, 40 {0x001D00C0, FlipImage, "FlipImage"},
41 {0x001E0200, nullptr, "SetDetailSize"}, 41 {0x001E0200, SetDetailSize, "SetDetailSize"},
42 {0x001F00C0, SetSize, "SetSize"}, 42 {0x001F00C0, SetSize, "SetSize"},
43 {0x00200080, SetFrameRate, "SetFrameRate"}, 43 {0x00200080, SetFrameRate, "SetFrameRate"},
44 {0x00210080, nullptr, "SetPhotoMode"}, 44 {0x00210080, nullptr, "SetPhotoMode"},
45 {0x002200C0, nullptr, "SetEffect"}, 45 {0x002200C0, SetEffect, "SetEffect"},
46 {0x00230080, nullptr, "SetContrast"}, 46 {0x00230080, nullptr, "SetContrast"},
47 {0x00240080, nullptr, "SetLensCorrection"}, 47 {0x00240080, nullptr, "SetLensCorrection"},
48 {0x002500C0, nullptr, "SetOutputFormat"}, 48 {0x002500C0, SetOutputFormat, "SetOutputFormat"},
49 {0x00260140, nullptr, "SetAutoExposureWindow"}, 49 {0x00260140, nullptr, "SetAutoExposureWindow"},
50 {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"}, 50 {0x00270140, nullptr, "SetAutoWhiteBalanceWindow"},
51 {0x00280080, nullptr, "SetNoiseFilter"}, 51 {0x00280080, nullptr, "SetNoiseFilter"},
52 {0x00290080, nullptr, "SynchronizeVsyncTiming"}, 52 {0x00290080, SynchronizeVsyncTiming, "SynchronizeVsyncTiming"},
53 {0x002A0080, nullptr, "GetLatestVsyncTiming"}, 53 {0x002A0080, nullptr, "GetLatestVsyncTiming"},
54 {0x002B0000, GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"}, 54 {0x002B0000, GetStereoCameraCalibrationData, "GetStereoCameraCalibrationData"},
55 {0x002C0400, nullptr, "SetStereoCameraCalibrationData"}, 55 {0x002C0400, nullptr, "SetStereoCameraCalibrationData"},
@@ -59,9 +59,9 @@ const Interface::FunctionInfo FunctionTable[] = {
59 {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"}, 59 {0x00300080, nullptr, "ReadMcuVariableI2cExclusive"},
60 {0x00310180, nullptr, "SetImageQualityCalibrationData"}, 60 {0x00310180, nullptr, "SetImageQualityCalibrationData"},
61 {0x00320000, nullptr, "GetImageQualityCalibrationData"}, 61 {0x00320000, nullptr, "GetImageQualityCalibrationData"},
62 {0x003302C0, nullptr, "SetPackageParameterWithoutContext"}, 62 {0x003302C0, SetPackageParameterWithoutContext, "SetPackageParameterWithoutContext"},
63 {0x00340140, nullptr, "SetPackageParameterWithContext"}, 63 {0x00340140, SetPackageParameterWithContext, "SetPackageParameterWithContext"},
64 {0x003501C0, nullptr, "SetPackageParameterWithContextDetail"}, 64 {0x003501C0, SetPackageParameterWithContextDetail, "SetPackageParameterWithContextDetail"},
65 {0x00360000, GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"}, 65 {0x00360000, GetSuitableY2rStandardCoefficient, "GetSuitableY2rStandardCoefficient"},
66 {0x00370202, nullptr, "PlayShutterSoundWithWave"}, 66 {0x00370202, nullptr, "PlayShutterSoundWithWave"},
67 {0x00380040, PlayShutterSound, "PlayShutterSound"}, 67 {0x00380040, PlayShutterSound, "PlayShutterSound"},
diff --git a/src/core/settings.h b/src/core/settings.h
index db4c8fada..fe47c364f 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -7,6 +7,7 @@
7#include <array> 7#include <array>
8#include <string> 8#include <string>
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "core/hle/service/cam/cam.h"
10 11
11namespace Settings { 12namespace Settings {
12 13
@@ -105,6 +106,10 @@ struct Values {
105 std::string sink_id; 106 std::string sink_id;
106 bool enable_audio_stretching; 107 bool enable_audio_stretching;
107 108
109 // Camera
110 std::array<std::string, Service::CAM::NumCameras> camera_name;
111 std::array<std::string, Service::CAM::NumCameras> camera_config;
112
108 // Debugging 113 // Debugging
109 bool use_gdbstub; 114 bool use_gdbstub;
110 u16 gdbstub_port; 115 u16 gdbstub_port;