diff options
| author | 2016-12-21 20:05:56 +0200 | |
|---|---|---|
| committer | 2017-01-11 11:46:44 +0200 | |
| commit | cf3a272332b03640730d1434e9802e166ca931da (patch) | |
| tree | 7297bf1b38679cb84b5baa7c98b5b9e729560131 | |
| parent | Merge pull request #2369 from MerryMage/core-frontend (diff) | |
| download | yuzu-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.cpp | 15 | ||||
| -rw-r--r-- | src/citra/default_ini.h | 16 | ||||
| -rw-r--r-- | src/citra_qt/config.cpp | 32 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | src/core/frontend/camera/blank_camera.cpp | 31 | ||||
| -rw-r--r-- | src/core/frontend/camera/blank_camera.h | 28 | ||||
| -rw-r--r-- | src/core/frontend/camera/factory.cpp | 32 | ||||
| -rw-r--r-- | src/core/frontend/camera/factory.h | 41 | ||||
| -rw-r--r-- | src/core/frontend/camera/interface.cpp | 11 | ||||
| -rw-r--r-- | src/core/frontend/camera/interface.h | 61 | ||||
| -rw-r--r-- | src/core/hle/service/cam/cam.cpp | 1024 | ||||
| -rw-r--r-- | src/core/hle/service/cam/cam.h | 358 | ||||
| -rw-r--r-- | src/core/hle/service/cam/cam_u.cpp | 32 | ||||
| -rw-r--r-- | src/core/settings.h | 5 |
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 |
| 105 | region_value = | 105 | region_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 | ||
| 110 | camera_outer_right_name = | ||
| 111 | |||
| 112 | # A config string for the right outer camera. Its meaning is defined by the camera engine | ||
| 113 | camera_outer_right_config = | ||
| 114 | |||
| 115 | # ... for the left outer camera | ||
| 116 | camera_outer_left_name = | ||
| 117 | camera_outer_left_config = | ||
| 118 | |||
| 119 | # ... for the inner camera | ||
| 120 | camera_inner_name = | ||
| 121 | camera_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 | |||
| 7 | namespace Camera { | ||
| 8 | |||
| 9 | void BlankCamera::StartCapture() {} | ||
| 10 | |||
| 11 | void BlankCamera::StopCapture() {} | ||
| 12 | |||
| 13 | void BlankCamera::SetFormat(Service::CAM::OutputFormat output_format) { | ||
| 14 | output_rgb = output_format == Service::CAM::OutputFormat::RGB565; | ||
| 15 | } | ||
| 16 | |||
| 17 | void BlankCamera::SetResolution(const Service::CAM::Resolution& resolution) { | ||
| 18 | width = resolution.width; | ||
| 19 | height = resolution.height; | ||
| 20 | }; | ||
| 21 | |||
| 22 | void BlankCamera::SetFlip(Service::CAM::Flip) {} | ||
| 23 | |||
| 24 | void BlankCamera::SetEffect(Service::CAM::Effect) {} | ||
| 25 | |||
| 26 | std::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 | |||
| 10 | namespace Camera { | ||
| 11 | |||
| 12 | class BlankCamera final : public CameraInterface { | ||
| 13 | public: | ||
| 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 | |||
| 22 | private: | ||
| 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 | |||
| 10 | namespace Camera { | ||
| 11 | |||
| 12 | static std::unordered_map<std::string, std::unique_ptr<CameraFactory>> factories; | ||
| 13 | |||
| 14 | CameraFactory::~CameraFactory() = default; | ||
| 15 | |||
| 16 | void RegisterFactory(const std::string& name, std::unique_ptr<CameraFactory> factory) { | ||
| 17 | factories[name] = std::move(factory); | ||
| 18 | } | ||
| 19 | |||
| 20 | std::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 | |||
| 11 | namespace Camera { | ||
| 12 | |||
| 13 | class CameraFactory { | ||
| 14 | public: | ||
| 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 | */ | ||
| 31 | void 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 | */ | ||
| 39 | std::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 | |||
| 7 | namespace Camera { | ||
| 8 | |||
| 9 | CameraInterface::~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 | |||
| 11 | namespace Camera { | ||
| 12 | |||
| 13 | /// An abstract class standing for a camera. All camera implementations should inherit from this. | ||
| 14 | class CameraInterface { | ||
| 15 | public: | ||
| 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 | ||
| 14 | namespace Service { | 23 | namespace Service { |
| 15 | namespace CAM { | 24 | namespace CAM { |
| 16 | 25 | ||
| 17 | static const u32 TRANSFER_BYTES = 5 * 1024; | 26 | namespace { |
| 27 | |||
| 28 | struct ContextConfig { | ||
| 29 | Flip flip; | ||
| 30 | Effect effect; | ||
| 31 | OutputFormat format; | ||
| 32 | Resolution resolution; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct CameraConfig { | ||
| 36 | std::unique_ptr<Camera::CameraInterface> impl; | ||
| 37 | std::array<ContextConfig, 2> contexts; | ||
| 38 | int current_context; | ||
| 39 | FrameRate frame_rate; | ||
| 40 | }; | ||
| 41 | |||
| 42 | struct 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 | ||
| 86 | constexpr 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 | ||
| 98 | constexpr 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 | |||
| 114 | std::array<CameraConfig, NumCameras> cameras; | ||
| 115 | std::array<PortConfig, 2> ports; | ||
| 116 | int completion_event_callback; | ||
| 117 | |||
| 118 | const ResultCode ERROR_INVALID_ENUM_VALUE(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, | ||
| 119 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 120 | const ResultCode ERROR_OUT_OF_RANGE(ErrorDescription::OutOfRange, ErrorModule::CAM, | ||
| 121 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); | ||
| 122 | |||
| 123 | void 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. | ||
| 185 | void 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? | ||
| 205 | void 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. | ||
| 215 | static 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 | |||
| 225 | template <int max_index> | ||
| 226 | class CommandParamBitSet : public BitSet8 { | ||
| 227 | public: | ||
| 228 | explicit CommandParamBitSet(u32 command_param) | ||
| 229 | : BitSet8(static_cast<u8>(command_param & 0xFF)) {} | ||
| 18 | 230 | ||
| 19 | static Kernel::SharedPtr<Kernel::Event> completion_event_cam1; | 231 | bool IsValid() const { |
| 20 | static Kernel::SharedPtr<Kernel::Event> completion_event_cam2; | 232 | return m_val < (1 << max_index); |
| 21 | static Kernel::SharedPtr<Kernel::Event> interrupt_error_event; | 233 | } |
| 22 | static Kernel::SharedPtr<Kernel::Event> vsync_interrupt_error_event; | 234 | |
| 235 | bool IsSingle() const { | ||
| 236 | return IsValid() && Count() == 1; | ||
| 237 | } | ||
| 238 | }; | ||
| 239 | |||
| 240 | using PortSet = CommandParamBitSet<2>; | ||
| 241 | using ContextSet = CommandParamBitSet<2>; | ||
| 242 | using CameraSet = CommandParamBitSet<3>; | ||
| 243 | |||
| 244 | } // namespace | ||
| 23 | 245 | ||
| 24 | void StartCapture(Service::Interface* self) { | 246 | void 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 | ||
| 35 | void StopCapture(Service::Interface* self) { | 281 | void 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 | |||
| 307 | void 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 | |||
| 330 | void 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 | ||
| 46 | void GetVsyncInterruptEvent(Service::Interface* self) { | 341 | void 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 | ||
| 59 | void GetBufferErrorInterruptEvent(Service::Interface* self) { | 363 | void 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 | ||
| 72 | void SetReceiving(Service::Interface* self) { | 384 | void 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(); | 420 | void 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 | ||
| 94 | void SetTransferLines(Service::Interface* self) { | 439 | void 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 | ||
| 109 | void GetMaxLines(Service::Interface* self) { | 463 | void 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 | |||
| 495 | void 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 | ||
| 123 | void GetTransferBytes(Service::Interface* self) { | 519 | void 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 | |||
| 538 | void 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 | ||
| 135 | void SetTrimming(Service::Interface* self) { | 564 | void 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 | |||
| 585 | void 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 | |||
| 604 | void 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 | |||
| 632 | void 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 | ||
| 147 | void SetTrimmingParamsCenter(Service::Interface* self) { | 654 | void 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 | ||
| 163 | void Activate(Service::Interface* self) { | 682 | void 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 | |||
| 723 | void 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 | ||
| 174 | void FlipImage(Service::Interface* self) { | 752 | void 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 | |||
| 781 | void 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 | ||
| 188 | void SetSize(Service::Interface* self) { | 818 | void 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 | ||
| 202 | void SetFrameRate(Service::Interface* self) { | 847 | void 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 | |||
| 870 | void 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 | |||
| 899 | void 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 | |||
| 928 | void 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 | ||
| 215 | void GetStereoCameraCalibrationData(Service::Interface* self) { | 941 | void 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 | ||
| 968 | void 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 | |||
| 980 | template <typename PackageParameterType, int command_id> | ||
| 981 | static 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 | |||
| 1017 | Resolution PackageParameterWithContext::GetResolution() { | ||
| 1018 | return PRESET_RESOLUTION[static_cast<int>(size)]; | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | void SetPackageParameterWithContext(Service::Interface* self) { | ||
| 1022 | SetPackageParameter<PackageParameterWithContext, 0x34>(); | ||
| 1023 | } | ||
| 1024 | |||
| 1025 | void SetPackageParameterWithContextDetail(Service::Interface* self) { | ||
| 1026 | SetPackageParameter<PackageParameterWithContextDetail, 0x35>(); | ||
| 1027 | } | ||
| 1028 | |||
| 242 | void GetSuitableY2rStandardCoefficient(Service::Interface* self) { | 1029 | void 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) { | |||
| 263 | void DriverInitialize(Service::Interface* self) { | 1050 | void 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 | ||
| 277 | void DriverFinalize(Service::Interface* self) { | 1083 | void 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 | ||
| 286 | void Init() { | 1099 | void 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 | ||
| 304 | void Shutdown() { | 1118 | void 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 @@ | |||
| 13 | namespace Service { | 13 | namespace Service { |
| 14 | namespace CAM { | 14 | namespace CAM { |
| 15 | 15 | ||
| 16 | enum class Port : u8 { None = 0, Cam1 = 1, Cam2 = 2, Both = Cam1 | Cam2 }; | 16 | enum CameraIndex { |
| 17 | OuterRightCamera = 0, | ||
| 18 | InnerCamera = 1, | ||
| 19 | OuterLeftCamera = 2, | ||
| 17 | 20 | ||
| 18 | enum 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 | ||
| 29 | enum class Effect : u8 { | 24 | enum class Effect : u8 { |
| @@ -35,13 +30,6 @@ enum class Effect : u8 { | |||
| 35 | Sepia01 = 5, | 30 | Sepia01 = 5, |
| 36 | }; | 31 | }; |
| 37 | 32 | ||
| 38 | enum class Context : u8 { | ||
| 39 | None = 0, | ||
| 40 | A = 1, | ||
| 41 | B = 2, | ||
| 42 | Both = A | B, | ||
| 43 | }; | ||
| 44 | |||
| 45 | enum class Flip : u8 { | 33 | enum class Flip : u8 { |
| 46 | None = 0, | 34 | None = 0, |
| 47 | Horizontal = 1, | 35 | Horizontal = 1, |
| @@ -160,8 +148,23 @@ struct StereoCameraCalibrationData { | |||
| 160 | static_assert(sizeof(StereoCameraCalibrationData) == 64, | 148 | static_assert(sizeof(StereoCameraCalibrationData) == 64, |
| 161 | "StereoCameraCalibrationData structure size is wrong"); | 149 | "StereoCameraCalibrationData structure size is wrong"); |
| 162 | 150 | ||
| 163 | struct 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 | */ | ||
| 157 | struct 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 | |||
| 166 | struct 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 | ||
| 186 | static_assert(sizeof(PackageParameterCameraSelect) == 28, | 189 | static_assert(sizeof(PackageParameterWithoutContext) == 28, |
| 187 | "PackageParameterCameraSelect structure size is wrong"); | 190 | "PackageParameterCameraWithoutContext structure size is wrong"); |
| 191 | |||
| 192 | struct 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 | |||
| 203 | static_assert(sizeof(PackageParameterWithContext) == 8, | ||
| 204 | "PackageParameterWithContext structure size is wrong"); | ||
| 205 | |||
| 206 | struct 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 | |||
| 218 | static_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, | |||
| 198 | void StartCapture(Service::Interface* self); | 230 | void 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); | |||
| 209 | void StopCapture(Service::Interface* self); | 241 | void 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 | */ | ||
| 253 | void 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 | */ | ||
| 264 | void 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); | |||
| 255 | void SetReceiving(Service::Interface* self); | 310 | void 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 | */ | ||
| 322 | void 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 | */ |
| 269 | void SetTransferLines(Service::Interface* self); | 337 | void 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 | */ |
| 282 | void GetMaxLines(Service::Interface* self); | 351 | void 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 | */ | ||
| 366 | void 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 | */ |
| 294 | void GetTransferBytes(Service::Interface* self); | 379 | void 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 | */ | ||
| 393 | void 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); | |||
| 306 | void SetTrimming(Service::Interface* self); | 405 | void 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 | */ | ||
| 417 | void 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 | */ | ||
| 432 | void 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 | */ | ||
| 448 | void 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); | |||
| 332 | void Activate(Service::Interface* self); | 475 | void 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 | */ | ||
| 487 | void 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); | |||
| 345 | void FlipImage(Service::Interface* self); | 500 | void 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 | */ | ||
| 518 | void 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); | |||
| 358 | void SetSize(Service::Interface* self); | 531 | void 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); | |||
| 370 | void SetFrameRate(Service::Interface* self); | 543 | void 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 | */ | ||
| 556 | void 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 | */ | ||
| 569 | void 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 | */ | ||
| 581 | void 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); | |||
| 382 | void GetStereoCameraCalibrationData(Service::Interface* self); | 593 | void 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 | */ | ||
| 606 | void 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 | */ | ||
| 619 | void 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 | */ | ||
| 632 | void 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 { | |||
| 11 | const Interface::FunctionInfo FunctionTable[] = { | 11 | const 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 | ||
| 11 | namespace Settings { | 12 | namespace 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; |