Compare commits

...

43 Commits

Author SHA1 Message Date
bunnei
3258db29da time: Implement ISteadyClock::GetCurrentTimePoint. 2018-01-25 21:29:39 -05:00
bunnei
748c0de539 Merge pull request #137 from bunnei/improve-ipc
Improve IPC, unify Domains and Sessions, and add validation
2018-01-24 23:09:03 -05:00
bunnei
de177f6692 audout_u: Various cleanups. 2018-01-24 22:38:19 -05:00
bunnei
714a576113 ResponseBuilder: Use a bit field for customizing instead of always_move_handles. 2018-01-24 22:24:20 -05:00
bunnei
f0b6baf3ad time: Stub GetSystemClockContext function. 2018-01-24 22:24:18 -05:00
bunnei
7588b24f46 server_session: Fix scenario where all domain handlers are closed. 2018-01-24 22:24:16 -05:00
bunnei
1b1d399e5f hle: Rename RequestBuilder to ResponseBuilder. 2018-01-24 22:24:10 -05:00
bunnei
f9dae99006 service: Fix all incorrect IPC response headers. 2018-01-24 22:21:33 -05:00
bunnei
f0035420d7 ipc_helpers: Make interface domain agnostic and add header validation. 2018-01-24 22:19:01 -05:00
bunnei
27bad0598a hle: Integrate Domain handling into ServerSession. 2018-01-24 22:18:56 -05:00
bunnei
67758857e4 hle: Remove Domain and SyncObject kernel objects. 2018-01-24 22:18:54 -05:00
bunnei
932fa94af7 handle_table: Remove ConvertSessionToDomain. 2018-01-24 22:18:51 -05:00
st4rk
44eb840232 audout:u OpenAudioOut and IAudioOut (#138)
* Updated the audout:u and IAudioOut, now it might work with RetroArch without trigger an assert, however it's not the ideal implementation

* Updated the audout:u and IAudioOut, now it might work with RetroArch without trigger an assert, however it's not the ideal implementation

* audout:u OpenAudioOut implementation and IAudioOut cmd 1,2,3,4,5 implementation

* using an enum for audio_out_state as well as changing its initialize to member initializer list

* Minor fixes, added Service_Audio for LOG_*, changed PcmFormat enum to EnumClass

* Minor fixes, added Service_Audio for LOG_*, changed PcmFormat enum to EnumClass

* added missing Audio loggin subclass, minor fixes, clang comment breakline

* Solving backend logging conflict

* minor fix

* Fixed duplicated Service NVDRV in backend.cpp, my bad
2018-01-24 22:17:54 -05:00
bunnei
b35cf672c0 Merge pull request #140 from gdkchan/time_fix
Fix time returning epoch time in milliseconds rather than in seconds
2018-01-24 10:45:07 -05:00
gdkchan
703be1931a Fix time returning epoch time in milliseconds rather than in seconds 2018-01-24 11:54:47 -03:00
bunnei
a93ff5ed0f Merge pull request #139 from Rozelette/log_nvdrv
logging: add missing NVDRV subclass to macro list
2018-01-24 00:03:33 -05:00
Rozlette
ad64e7e86d logging: add missing NVDRV subclass to macro list 2018-01-23 22:18:23 -06:00
bunnei
337664ae7c Merge pull request #136 from N00byKing/patch-1
Correct Spelling
2018-01-23 20:42:45 -05:00
N00byKing
afa1ed6ad9 Correct Spelling 2018-01-23 14:20:18 +01:00
bunnei
d1b64cdc07 Merge pull request #135 from Subv/no_ports
IPC: Don't create unnecessary ports when returning sub interfaces.
2018-01-22 21:37:15 -05:00
Subv
b0489c9a64 Services: Added a todo about returning interfaces as domain objects in lm, hid and time. 2018-01-22 20:40:43 -05:00
bunnei
d8bd70d396 Merge pull request #133 from Subv/nvflinger2
AppletOE: Stubbed CreateManagedDisplayLayer to create a new layer in the default display.
2018-01-22 17:52:47 -05:00
Subv
7efa6e8801 Time: Don't create unnecessary ports when retrieving the clock service sessions. 2018-01-22 17:42:11 -05:00
Subv
1003996e80 HID: Don't create an unnecessary port in CreateAppletResource. 2018-01-22 17:41:31 -05:00
Subv
1a9c96e4de LM: Don't create an unnecessary port in Initialize. 2018-01-22 17:35:40 -05:00
Subv
67c43e9200 IPC: Don't create an unnecessary port when using PushIpcInterface outside of a domain. 2018-01-22 17:31:23 -05:00
bunnei
de2e5a0855 Merge pull request #134 from gdkchan/audout_hid_fix
Stub OpenAudioOut and fix a issue with HID IAppletResource
2018-01-22 17:09:09 -05:00
gdkchan
832009bfdb Stub OpenAudioOut and fix a issue with HID IAppletResource being created more than once 2018-01-22 17:27:55 -03:00
Subv
10c67bf395 AppletOE: Stubbed CreateManagedDisplayLayer to create a new layer in the Default display.
This function is used by libnx to obtain a new layer.
2018-01-22 13:50:22 -05:00
Subv
eb58f852f8 AppletOE: Make ISelfController keep a reference to nvflinger.
It'll be needed when we implement CreateManagedDisplayLayer.
2018-01-22 13:46:36 -05:00
Subv
42859461f3 Services: Vi shouldn't be responsible for creating nvflinger.
It is now created during Service initialization and passed to all the services that need it.
2018-01-22 13:40:02 -05:00
bunnei
7f19a7d305 Merge pull request #132 from Subv/nvflinger
VI: Move BufferQueue and NVFlinger to their own folder/namespace.
2018-01-22 13:22:20 -05:00
Subv
8d7686ff8e VI: Move BufferQueue and NVFlinger to their own folder/namespace. 2018-01-22 11:54:58 -05:00
st4rk
07355cf7cc Added stubs for audio services. (#116)
* stubs for audout:u, audin:u, audrec:u, audren:u, codecctl and decoding tables with nullptr for future implementations

* fixing the changes requested (remove private, explicit)
2018-01-21 22:03:36 -05:00
bunnei
fdbb039427 Merge pull request #131 from lioncash/enum
nvmap: Make IoctlCommands an enum class
2018-01-21 22:01:27 -05:00
bunnei
32d91fa6d2 Merge pull request #130 from MerryMage/dynarmic
externals: Update dynarmic
2018-01-21 18:08:18 -05:00
David
eeb3b5eed7 Added nvmemp, Added /dev/nvhost-ctrl, SetClientPID now stores pid (#114)
* Added nvmemp, Added /dev/nvhost-ctrl, SetClientPID now stores pid

* used clang-format-3.9 instead

* lowercase pid

* Moved nvmemp handlers to cpp

* Removed unnecessary logging for NvOsGetConfigU32. Cleaned up log and changed to LOG_DEBUG

* using std::arrays instead of c arrays

* nvhost get config now uses std::array completely

* added pid logging back

* updated cmakelist

* missing includes

* added array, removed memcpy

* clang-format6.0
2018-01-21 17:59:50 -05:00
bunnei
2403143ff1 Merge pull request #128 from Subv/parcel_query
VI: Implement the Query transaction of IHOSBinderDriver, and stubbed some results.
2018-01-21 17:56:06 -05:00
Lioncash
06d2e1bd23 nvmap: Add a return 0 underneath the UNIMPLEMENTED macro
This macro resolves to an empty macro in release builds.
2018-01-21 17:07:47 -05:00
Lioncash
687a17acae nvmap: Make IoctlCommands an enum class
Prevents the enum values from polluting the surrounding scope
2018-01-21 17:07:13 -05:00
MerryMage
e7cb20fbf0 externals: Update dynarmic
a6d17e A64: Implement AND (vector)
963453 tests/A64: Randomize vectors
adcd34 tests/A64/unicorn: Print interrupt number when InterruptHook is hit
304c91 tests/A64: Allow RunTestInstance to start from an arbitrary offset
d333b5 A64: Implement ADD (vector, vector)
1cf87a A64: Implement REV, REV32, and REV16 (#126)
9fc157 IR: Simplify types. F32 -> U32, F64 -> U64, F128 -> U128
50c181 reg_alloc: GetBitWidth: Add UNREACHABLE
adccbf reg_alloc: Consider bitwidth of data and registers when emitting instructions
7b7f23 A64: Implement CSEL
2f8413 IR: Implement Conditional Select
ebb3e8 A64/tests: Split unicorn sanity checking from other tests
5740a0 tests/A64: Single random instruction: Test branch instructions as well
0892b4 A64/translate/branch: bug: Read-after-write error in BLR
e77bc2 A64: Implement SBFM, BFM, UBFM
0c37ca A64: Implement MOVN, MOVZ, MOVK
b6bb59 travis: Print current test information
e77207 fuzz_thumb: Off by one error
a04ca2 ir/location_descriptor: Add missing <functional> header for std::hash
1e0f5c travis: Run A64 tests
2018-01-21 22:05:43 +00:00
bunnei
ab8525705b Merge pull request #123 from bunnei/fs
Initial implementation of RomFS filesystem and fsp-srv
2018-01-21 16:15:15 -05:00
Subv
749043c809 VI: Implement the Query transaction of IHOSBinderDriver, and stubbed some results. 2018-01-21 11:13:47 -05:00
64 changed files with 1517 additions and 809 deletions

View File

@@ -38,6 +38,8 @@ namespace Log {
SUB(Service, CFG) \
SUB(Service, DSP) \
SUB(Service, HID) \
SUB(Service, NVDRV) \
SUB(Service, Audio) \
CLS(HW) \
SUB(HW, Memory) \
SUB(HW, LCD) \

View File

@@ -55,6 +55,8 @@ enum class Class : ClassType {
Service_CFG, ///< The CFG (Configuration) service
Service_DSP, ///< The DSP (DSP control) service
Service_HID, ///< The HID (Human interface device) service
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_Audio, ///< The Audio (Audio control) service
HW, ///< Low-level hardware emulation
HW_Memory, ///< Memory-map and address translation
HW_LCD, ///< LCD register emulation

View File

@@ -36,8 +36,6 @@ add_library(core STATIC
hle/kernel/client_session.h
hle/kernel/condition_variable.cpp
hle/kernel/condition_variable.h
hle/kernel/domain.cpp
hle/kernel/domain.h
hle/kernel/errors.h
hle/kernel/event.cpp
hle/kernel/event.h
@@ -67,7 +65,6 @@ add_library(core STATIC
hle/kernel/svc.cpp
hle/kernel/svc.h
hle/kernel/svc_wrap.h
hle/kernel/sync_object.h
hle/kernel/thread.cpp
hle/kernel/thread.h
hle/kernel/timer.cpp
@@ -95,8 +92,18 @@ add_library(core STATIC
hle/service/apm/apm.h
hle/service/audio/audio.cpp
hle/service/audio/audio.h
hle/service/audio/audin_u.cpp
hle/service/audio/audin_u.h
hle/service/audio/audout_u.cpp
hle/service/audio/audout_u.h
hle/service/audio/audrec_u.cpp
hle/service/audio/audrec_u.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
hle/service/audio/audren_u.cpp
hle/service/audio/audren_u.h
hle/service/audio/codecctl.cpp
hle/service/audio/codecctl.h
hle/service/filesystem/filesystem.cpp
hle/service/filesystem/filesystem.h
hle/service/filesystem/fsp_srv.cpp
@@ -110,12 +117,20 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvdisp_disp0.h
hle/service/nvdrv/devices/nvhost_as_gpu.cpp
hle/service/nvdrv/devices/nvhost_as_gpu.h
hle/service/nvdrv/devices/nvhost_ctrl.cpp
hle/service/nvdrv/devices/nvhost_ctrl.h
hle/service/nvdrv/devices/nvmap.cpp
hle/service/nvdrv/devices/nvmap.h
hle/service/nvdrv/interface.cpp
hle/service/nvdrv/interface.h
hle/service/nvdrv/nvdrv.cpp
hle/service/nvdrv/nvdrv.h
hle/service/nvdrv/nvmemp.cpp
hle/service/nvdrv/nvmemp.h
hle/service/nvflinger/buffer_queue.cpp
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
hle/service/pctl/pctl.cpp
hle/service/pctl/pctl.h
hle/service/pctl/pctl_a.cpp

View File

@@ -51,13 +51,13 @@ public:
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
* update is requested (e.g. on a thread switch).
* @param tight_loop Number of instructions to execute.
* @return Result status, indicating whethor or not the operation succeeded.
* @return Result status, indicating whether or not the operation succeeded.
*/
ResultStatus RunLoop(int tight_loop = 100000);
/**
* Step the CPU one instruction
* @return Result status, indicating whethor or not the operation succeeded.
* @return Result status, indicating whether or not the operation succeeded.
*/
ResultStatus SingleStep();

View File

@@ -11,7 +11,6 @@
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/domain.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
@@ -31,11 +30,6 @@ public:
RequestHelperBase(Kernel::HLERequestContext& context)
: context(&context), cmdbuf(context.CommandBuffer()) {}
void ValidateHeader() {
// DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
// header.raw);
}
void Skip(unsigned size_in_words, bool set_to_null) {
if (set_to_null)
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
@@ -60,14 +54,30 @@ public:
}
};
class RequestBuilder : public RequestHelperBase {
class ResponseBuilder : public RequestHelperBase {
public:
RequestBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
u32 normal_params_size{};
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
/// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 {
None = 0,
/// Uses move handles to move objects in the response, even when in a domain. This is
/// required when PushMoveObjects is used.
AlwaysMoveHandles = 1,
};
ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
: RequestHelperBase(context), normal_params_size(normal_params_size),
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
RequestBuilder(Kernel::HLERequestContext& context, unsigned normal_params_size,
u32 num_handles_to_copy = 0, u32 num_handles_to_move = 0,
u32 num_domain_objects = 0)
: RequestHelperBase(context) {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
context.ClearIncomingObjects();
@@ -77,12 +87,19 @@ public:
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
if (context.IsDomain()) {
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
u32 num_handles_to_move{};
u32 num_domain_objects{};
const bool always_move_handles{
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
if (!context.Session()->IsDomain() || always_move_handles) {
num_handles_to_move = num_objects_to_move;
} else {
// If we're not in a domain, turn the domain object parameters into move handles.
num_handles_to_move += num_domain_objects;
num_domain_objects = 0;
num_domain_objects = num_objects_to_move;
}
if (context.Session()->IsDomain()) {
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
}
header.data_size.Assign(raw_data_size);
@@ -101,7 +118,7 @@ public:
AlignWithPadding();
if (context.IsDomain()) {
if (context.Session()->IsDomain()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
@@ -110,23 +127,43 @@ public:
IPC::DataPayloadHeader data_payload_header{};
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
PushRaw(data_payload_header);
datapayload_index = index;
}
template <class T>
void PushIpcInterface(std::shared_ptr<T> iface) {
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto sessions = Kernel::ServerSession::CreateSessionPair(iface->GetServiceName());
auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
iface->ClientConnected(server);
context->AddMoveObject(std::move(client));
}
}
template <class T, class... Args>
void PushIpcInterface(Args&&... args) {
auto iface = std::make_shared<T>(std::forward<Args>(args)...);
if (context->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
auto port = iface->CreatePort();
auto session = port->Connect();
ASSERT(session.Succeeded());
context->AddMoveObject(std::move(session).Unwrap());
}
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
}
void ValidateHeader() {
const size_t num_domain_objects = context->NumDomainObjects();
const size_t num_move_objects = context->NumMoveObjects();
ASSERT_MSG(!num_domain_objects || !num_move_objects,
"cannot move normal handles and domain objects");
ASSERT_MSG((index - datapayload_index) == normal_params_size,
"normal_params_size value is incorrect");
ASSERT_MSG((num_domain_objects + num_move_objects) == num_objects_to_move,
"num_objects_to_move value is incorrect");
ASSERT_MSG(context->NumCopyObjects() == num_handles_to_copy,
"num_handles_to_copy value is incorrect");
}
// Validate on destruction, as there shouldn't be any case where we don't want it
~RequestBuilder() {
~ResponseBuilder() {
ValidateHeader();
}
@@ -154,52 +191,52 @@ public:
/// Push ///
template <>
inline void RequestBuilder::Push(u32 value) {
inline void ResponseBuilder::Push(u32 value) {
cmdbuf[index++] = value;
}
template <typename T>
void RequestBuilder::PushRaw(const T& value) {
void ResponseBuilder::PushRaw(const T& value) {
std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length
}
template <>
inline void RequestBuilder::Push(ResultCode value) {
inline void ResponseBuilder::Push(ResultCode value) {
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
Push(value.raw);
Push<u32>(0);
}
template <>
inline void RequestBuilder::Push(u8 value) {
inline void ResponseBuilder::Push(u8 value) {
PushRaw(value);
}
template <>
inline void RequestBuilder::Push(u16 value) {
inline void ResponseBuilder::Push(u16 value) {
PushRaw(value);
}
template <>
inline void RequestBuilder::Push(u64 value) {
inline void ResponseBuilder::Push(u64 value) {
Push(static_cast<u32>(value));
Push(static_cast<u32>(value >> 32));
}
template <>
inline void RequestBuilder::Push(bool value) {
inline void ResponseBuilder::Push(bool value) {
Push(static_cast<u8>(value));
}
template <typename First, typename... Other>
void RequestBuilder::Push(const First& first_value, const Other&... other_values) {
void ResponseBuilder::Push(const First& first_value, const Other&... other_values) {
Push(first_value);
Push(other_values...);
}
template <typename... O>
inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
inline void ResponseBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddCopyObject(std::move(object));
@@ -207,7 +244,7 @@ inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
}
template <typename... O>
inline void RequestBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
auto objects = {pointers...};
for (auto& object : objects) {
context->AddMoveObject(std::move(object));
@@ -226,15 +263,10 @@ public:
Skip(CommandIdSize, false);
}
RequestBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move, u32 num_domain_objects,
bool validate_header = true) {
if (validate_header) {
ValidateHeader();
}
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move,
num_domain_objects};
ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
u32 num_handles_to_move,
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) {
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, flags};
}
template <typename T>

View File

@@ -7,7 +7,7 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/sync_object.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Kernel {
@@ -16,7 +16,7 @@ class ServerSession;
class Session;
class Thread;
class ClientSession final : public SyncObject {
class ClientSession final : public Object {
public:
friend class ServerSession;
@@ -33,7 +33,7 @@ public:
return HANDLE_TYPE;
}
ResultCode SendSyncRequest(SharedPtr<Thread> thread) override;
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
std::string name; ///< Name of client port (optional)

View File

@@ -1,63 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/domain.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
ResultVal<SharedPtr<Domain>> Domain::Create(std::string name) {
SharedPtr<Domain> domain(new Domain);
domain->name = std::move(name);
return MakeResult(std::move(domain));
}
ResultVal<SharedPtr<Domain>> Domain::CreateFromSession(const Session& session) {
auto res = Create(session.port->GetName() + "_Domain");
auto& domain = res.Unwrap();
domain->request_handlers.push_back(std::move(session.server->hle_handler));
Kernel::g_handle_table.ConvertSessionToDomain(session, domain);
return res;
}
ResultCode Domain::SendSyncRequest(SharedPtr<Thread> thread) {
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
auto& domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// If there is a DomainMessageHeader, then this is CommandType "Request"
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
return request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
request_handlers[object_id - 1] = nullptr;
IPC::RequestBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return RESULT_SUCCESS;
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
UNIMPLEMENTED();
}
return request_handlers.front()->HandleSyncRequest(context);
}
} // namespace Kernel

View File

@@ -1,45 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <string>
#include <vector>
#include "core/hle/kernel/sync_object.h"
#include "core/hle/result.h"
namespace Kernel {
class Session;
class SessionRequestHandler;
class Domain final : public SyncObject {
public:
std::string GetTypeName() const override {
return "Domain";
}
static const HandleType HANDLE_TYPE = HandleType::Domain;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
static ResultVal<SharedPtr<Domain>> CreateFromSession(const Session& server);
ResultCode SendSyncRequest(SharedPtr<Thread> thread) override;
/// The name of this domain (optional)
std::string name;
std::vector<std::shared_ptr<SessionRequestHandler>> request_handlers;
private:
Domain() = default;
~Domain() override = default;
static ResultVal<SharedPtr<Domain>> Create(std::string name = "Unknown");
};
} // namespace Kernel

View File

@@ -5,12 +5,10 @@
#include <utility>
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
@@ -55,14 +53,6 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
return Create(std::move(object));
}
void HandleTable::ConvertSessionToDomain(const Session& session, SharedPtr<Object> domain) {
for (auto& object : objects) {
if (DynamicObjectCast<ClientSession>(object) == session.client) {
object = domain;
}
}
}
ResultCode HandleTable::Close(Handle handle) {
if (!IsValid(handle))
return ERR_INVALID_HANDLE;

View File

@@ -17,8 +17,6 @@ enum KernelHandle : Handle {
CurrentProcess = 0xFFFF8001,
};
class Session;
/**
* This class allows the creation of Handles, which are references to objects that can be tested
* for validity and looked up. Here they are used to pass references to kernel objects to/from the
@@ -61,11 +59,6 @@ public:
*/
ResultVal<Handle> Duplicate(Handle handle);
/**
* Convert all handles of the specified Session to the specified Domain.
*/
void ConvertSessionToDomain(const Session& session, SharedPtr<Object> domain);
/**
* Closes a handle, removing it from the table and decreasing the object's ref-count.
* @return `RESULT_SUCCESS` or one of the following errors:

View File

@@ -7,7 +7,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/domain.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
@@ -26,10 +25,6 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s
boost::range::remove_erase(connected_sessions, server_session);
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::Domain> domain) : domain(std::move(domain)) {
cmd_buf[0] = 0;
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
: server_session(std::move(server_session)) {
cmd_buf[0] = 0;
@@ -87,7 +82,7 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
// Padding to align to 16 bytes
rp.AlignWithPadding();
if (IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
if (Session()->IsDomain() && (command_header->type == IPC::CommandType::Request || !incoming)) {
// If this is an incoming message, only CommandType "Request" has a domain header
// All outgoing domain messages have the domain header
domain_message_header =
@@ -200,12 +195,12 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P
// TODO(Subv): Translate the X/A/B/W buffers.
if (IsDomain()) {
if (Session()->IsDomain()) {
ASSERT(domain_message_header->num_objects == domain_objects.size());
// Write the domain objects to the command buffer, these go after the raw untranslated data.
// TODO(Subv): This completely ignores C buffers.
size_t domain_offset = size - domain_message_header->num_objects;
auto& request_handlers = domain->request_handlers;
auto& request_handlers = server_session->domain_request_handlers;
for (auto& object : domain_objects) {
request_handlers.emplace_back(object);

View File

@@ -86,7 +86,6 @@ protected:
*/
class HLERequestContext {
public:
HLERequestContext(SharedPtr<Kernel::Domain> domain);
HLERequestContext(SharedPtr<Kernel::ServerSession> session);
~HLERequestContext();
@@ -95,18 +94,11 @@ public:
return cmd_buf.data();
}
/**
* Returns the domain through which this request was made.
*/
const SharedPtr<Kernel::Domain>& Domain() const {
return domain;
}
/**
* Returns the session through which this request was made. This can be used as a map key to
* access per-client data on services.
*/
const SharedPtr<Kernel::ServerSession>& ServerSession() const {
const SharedPtr<Kernel::ServerSession>& Session() const {
return server_session;
}
@@ -151,10 +143,6 @@ public:
return domain_message_header;
}
bool IsDomain() const {
return domain != nullptr;
}
template <typename T>
SharedPtr<T> GetCopyObject(size_t index) {
ASSERT(index < copy_objects.size());
@@ -187,9 +175,20 @@ public:
domain_objects.clear();
}
size_t NumMoveObjects() const {
return move_objects.size();
}
size_t NumCopyObjects() const {
return copy_objects.size();
}
size_t NumDomainObjects() const {
return domain_objects.size();
}
private:
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::Domain> domain;
SharedPtr<Kernel::ServerSession> server_session;
// TODO(yuriks): Check common usage of this and optimize size accordingly
boost::container::small_vector<SharedPtr<Object>, 8> move_objects;

View File

@@ -31,7 +31,6 @@ enum class HandleType : u32 {
ServerPort,
ClientSession,
ServerSession,
Domain,
};
enum {
@@ -84,27 +83,12 @@ public:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
case HandleType::Domain:
return false;
}
UNREACHABLE();
}
/**
* Check if svcSendSyncRequest can be called on the object
* @return True svcSendSyncRequest can be called on the object, otherwise false
*/
bool IsSyncable() const {
switch (GetHandleType()) {
case HandleType::ClientSession:
case HandleType::Domain:
return true;
}
UNREACHABLE();
}
public:
static unsigned int next_object_id;

View File

@@ -4,6 +4,7 @@
#include <tuple>
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
@@ -61,6 +62,38 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar.
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
// If the session has been converted to a domain, handle the doomain request
if (IsDomain()) {
auto& domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// If there is a DomainMessageHeader, then this is CommandType "Request"
const u32 object_id{context.GetDomainMessageHeader()->object_id};
switch (domain_message_header->command) {
case IPC::DomainMessageHeader::CommandType::SendMessage:
return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
domain_request_handlers[object_id - 1] = nullptr;
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return RESULT_SUCCESS;
}
}
LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
ASSERT(false);
}
// If there is no domain header, the regular session handler is used
}
// If this ServerSession has an associated HLE handler, forward the request to it.
ResultCode result{RESULT_SUCCESS};
if (hle_handler != nullptr) {
@@ -69,11 +102,6 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
if (translate_result.IsError())
return translate_result;
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
Kernel::g_handle_table);
result = hle_handler->HandleSyncRequest(context);
} else {
// Add the thread to the list of threads that have issued a sync request with this
@@ -84,6 +112,15 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
// on it.
WakeupAllWaitingThreads();
// Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
// end of the command such that only commands following this one are handled as domains
if (convert_to_domain) {
ASSERT_MSG(domain_request_handlers.empty(), "already a domain");
domain_request_handlers = {hle_handler};
convert_to_domain = false;
}
return result;
}

View File

@@ -79,7 +79,10 @@ public:
std::string name; ///< The name of this session (optional)
std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint.
std::shared_ptr<SessionRequestHandler>
hle_handler; ///< This session's HLE request handler (optional)
hle_handler; ///< This session's HLE request handler (applicable when not a domain)
/// This is the list of domain request handlers (after conversion to a domain)
std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers;
/// List of threads that are pending a response after a sync request. This list is processed in
/// a LIFO manner, thus, the last request will be dispatched first.
@@ -91,6 +94,16 @@ public:
/// TODO(Subv): Find a better name for this.
SharedPtr<Thread> currently_handling;
/// Returns true if the session has been converted to a domain, otherwise False
bool IsDomain() const {
return !domain_request_handlers.empty();
}
/// Converts the session to a domain at the end of the current command
void ConvertToDomain() {
convert_to_domain = true;
}
private:
ServerSession();
~ServerSession() override;
@@ -102,6 +115,9 @@ private:
* @return The created server session
*/
static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
/// When set to True, converts the session to a domain at the end of the command
bool convert_to_domain{};
};
/**

View File

@@ -20,7 +20,6 @@
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/sync_object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -87,7 +86,7 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) {
SharedPtr<SyncObject> session = g_handle_table.Get<SyncObject>(handle);
SharedPtr<ClientSession> session = g_handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x%08X", handle);
return ERR_INVALID_HANDLE;

View File

@@ -1,35 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include "core/hle/kernel/kernel.h"
#include "core/hle/result.h"
namespace Kernel {
class Thread;
/// Class that represents a Kernel object that svcSendSyncRequest can be called on
class SyncObject : public Object {
public:
/**
* Handle a sync request from the emulated application.
* @param thread Thread that initiated the request.
* @returns ResultCode from the operation.
*/
virtual ResultCode SendSyncRequest(SharedPtr<Thread> thread) = 0;
};
// Specialization of DynamicObjectCast for SyncObjects
template <>
inline SharedPtr<SyncObject> DynamicObjectCast<SyncObject>(SharedPtr<Object> object) {
if (object != nullptr && object->IsSyncable()) {
return boost::static_pointer_cast<SyncObject>(std::move(object));
}
return nullptr;
}
} // namespace Kernel

View File

@@ -22,7 +22,7 @@ private:
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
ProfileBase profile_base{};
IPC::RequestBuilder rb{ctx, 16};
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
}
@@ -40,7 +40,7 @@ public:
private:
void CheckAvailability(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true); // TODO: Check when this is supposed to return true and when not
}
@@ -48,13 +48,13 @@ private:
void ACC_U0::GetUserExistence(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(true); // TODO: Check when this is supposed to return true and when not
}
void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IProfile>();
LOG_DEBUG(Service, "called");
@@ -62,12 +62,12 @@ void ACC_U0::GetProfile(Kernel::HLERequestContext& ctx) {
void ACC_U0::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ACC_U0::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerForApplication>();
LOG_DEBUG(Service, "called");

View File

@@ -8,8 +8,9 @@
namespace Service {
namespace AM {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AppletOE>()->InstallAsService(service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
}
} // namespace AM

View File

@@ -4,13 +4,19 @@
#pragma once
#include <memory>
#include "core/hle/service/service.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace AM {
/// Registers all AM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
} // namespace AM
} // namespace Service

View File

@@ -7,6 +7,7 @@
#include "core/hle/kernel/event.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service {
namespace AM {
@@ -24,14 +25,14 @@ public:
private:
void GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(0);
}
void AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
@@ -53,7 +54,8 @@ public:
class ISelfController final : public ServiceFramework<ISelfController> {
public:
ISelfController() : ServiceFramework("ISelfController") {
ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
@@ -65,6 +67,7 @@ public:
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
{16, &ISelfController::SetOutOfFocusSuspendingEnabled,
"SetOutOfFocusSuspendingEnabled"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
};
RegisterHandlers(functions);
}
@@ -83,14 +86,14 @@ private:
};
auto flags = rp.PopRaw<FocusHandlingModeParams>();
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
@@ -101,7 +104,7 @@ private:
bool flag = rp.Pop<bool>();
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag));
@@ -112,7 +115,7 @@ private:
bool flag = rp.Pop<bool>();
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called flag=%u", static_cast<u32>(flag));
@@ -125,25 +128,40 @@ private:
bool enabled = rp.Pop<bool>();
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called enabled=%u", static_cast<u32>(enabled));
}
void LockExit(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void UnlockExit(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
// TODO(Subv): Find out how AM determines the display to use, for now just create the layer
// in the Default display.
u64 display_id = nvflinger->OpenDisplay("Default");
u64 layer_id = nvflinger->CreateLayer(display_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
LOG_WARNING(Service, "(STUBBED) called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -175,7 +193,7 @@ private:
void GetEventHandle(Kernel::HLERequestContext& ctx) {
event->Signal();
IPC::RequestBuilder rb{ctx, 2, 1};
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
@@ -183,7 +201,7 @@ private:
}
void ReceiveMessage(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(15);
@@ -191,7 +209,7 @@ private:
}
void GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(FocusState::InFocus));
@@ -199,7 +217,7 @@ private:
}
void GetOperationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(OperationMode::Handheld));
@@ -207,7 +225,7 @@ private:
}
void GetPerformanceMode(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(APM::PerformanceMode::Handheld));
@@ -232,7 +250,7 @@ private:
std::vector<u8> buffer;
void GetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(buffer.size()));
@@ -251,7 +269,7 @@ private:
Memory::WriteBlock(output_buffer.Address(), buffer.data() + offset, output_buffer.Size());
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -273,7 +291,7 @@ private:
std::vector<u8> buffer;
void Open(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
@@ -310,7 +328,7 @@ private:
std::vector<u8> buffer(data, data + sizeof(data));
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(buffer);
@@ -325,34 +343,34 @@ private:
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called, result=0x%08X", result);
}
void GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(SystemLanguage::English);
LOG_WARNING(Service, "(STUBBED) called");
}
void InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called");
}
void NotifyRunning(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
@@ -367,7 +385,8 @@ public:
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
IApplicationProxy() : ServiceFramework("IApplicationProxy") {
IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
@@ -383,70 +402,73 @@ public:
private:
void GetAudioController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
LOG_DEBUG(Service, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
LOG_DEBUG(Service, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
LOG_DEBUG(Service, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
LOG_DEBUG(Service, "called");
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>();
rb.PushIpcInterface<ISelfController>(nvflinger);
LOG_DEBUG(Service, "called");
}
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ICommonStateGetter>();
LOG_DEBUG(Service, "called");
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
LOG_DEBUG(Service, "called");
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationFunctions>();
LOG_DEBUG(Service, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationProxy>();
rb.PushIpcInterface<IApplicationProxy>(nvflinger);
LOG_DEBUG(Service, "called");
}
AppletOE::AppletOE() : ServiceFramework("appletOE") {
AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
static const FunctionInfo functions[] = {
{0x00000000, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
};

View File

@@ -4,10 +4,15 @@
#pragma once
#include <memory>
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace AM {
// TODO: Add more languages
@@ -18,11 +23,13 @@ enum SystemLanguage {
class AppletOE final : public ServiceFramework<AppletOE> {
public:
AppletOE();
AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
~AppletOE() = default;
private:
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
};
} // namespace AM

View File

@@ -30,7 +30,7 @@ private:
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
u32 config = rp.Pop<u32>();
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service, "(STUBBED) called mode=%u config=%u", static_cast<u32>(mode), config);
@@ -41,7 +41,7 @@ private:
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Performance configuration
@@ -58,7 +58,7 @@ APM::APM() : ServiceFramework("apm") {
}
void APM::OpenSession(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
}

View File

@@ -0,0 +1,41 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audin_u.h"
namespace Service {
namespace Audio {
class IAudioIn final : public ServiceFramework<IAudioIn> {
public:
IAudioIn() : ServiceFramework("IAudioIn") {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetAudioInState"},
{0x1, nullptr, "StartAudioIn"},
{0x2, nullptr, "StopAudioIn"},
{0x3, nullptr, "AppendAudioInBuffer_1"},
{0x4, nullptr, "RegisterBufferEvent"},
{0x5, nullptr, "GetReleasedAudioInBuffer_1"},
{0x6, nullptr, "ContainsAudioInBuffer"},
{0x7, nullptr, "AppendAudioInBuffer_2"},
{0x8, nullptr, "GetReleasedAudioInBuffer_2"},
};
RegisterHandlers(functions);
}
~IAudioIn() = default;
};
AudInU::AudInU() : ServiceFramework("audin:u") {
static const FunctionInfo functions[] = {
{0x00000000, nullptr, "ListAudioIns"},
{0x00000001, nullptr, "OpenAudioIn"},
};
RegisterHandlers(functions);
}
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,23 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class AudInU final : public ServiceFramework<AudInU> {
public:
explicit AudInU();
~AudInU() = default;
};
} // namespace Audio
} // namespace Service

View File

@@ -2,14 +2,22 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/audio/audin_u.h"
#include "core/hle/service/audio/audio.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/hle/service/audio/audrec_u.h"
#include "core/hle/service/audio/audren_u.h"
#include "core/hle/service/audio/codecctl.h"
namespace Service {
namespace Audio {
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<AudOutU>()->InstallAsService(service_manager);
std::make_shared<AudInU>()->InstallAsService(service_manager);
std::make_shared<AudRecU>()->InstallAsService(service_manager);
std::make_shared<AudRenU>()->InstallAsService(service_manager);
std::make_shared<CodecCtl>()->InstallAsService(service_manager);
}
} // namespace Audio

View File

@@ -2,23 +2,197 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audout_u.h"
namespace Service {
namespace Audio {
/// Switch sample rate frequency
constexpr u32 sample_rate{48000};
/// TODO(st4rk): dynamic number of channels, as I think Switch has support
/// to more audio channels (probably when Docked I guess)
constexpr u32 audio_channels{2};
/// TODO(st4rk): find a proper value for the audio_ticks
constexpr u64 audio_ticks{static_cast<u64>(BASE_CLOCK_RATE / 500)};
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetAudioOutState"},
{0x1, &IAudioOut::StartAudioOut, "StartAudioOut"},
{0x2, &IAudioOut::StopAudioOut, "StopAudioOut"},
{0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"},
{0x4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"},
{0x5, &IAudioOut::GetReleasedAudioOutBuffer_1, "GetReleasedAudioOutBuffer_1"},
{0x6, nullptr, "ContainsAudioOutBuffer"},
{0x7, nullptr, "AppendAudioOutBuffer_2"},
{0x8, nullptr, "GetReleasedAudioOutBuffer_2"},
};
RegisterHandlers(functions);
// This is the event handle used to check if the audio buffer was released
buffer_event =
Kernel::Event::Create(Kernel::ResetType::OneShot, "IAudioOutBufferReleasedEvent");
// Register event callback to update the Audio Buffer
audio_event = CoreTiming::RegisterEvent(
"IAudioOut::UpdateAudioBuffersCallback", [this](u64 userdata, int cycles_late) {
UpdateAudioBuffersCallback();
CoreTiming::ScheduleEvent(audio_ticks - cycles_late, audio_event);
});
// Start the audio event
CoreTiming::ScheduleEvent(audio_ticks, audio_event);
}
~IAudioOut() = default;
private:
void StartAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
// Start audio
audio_out_state = AudioState::Started;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void StopAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
// Stop audio
audio_out_state = AudioState::Stopped;
queue_keys.clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void RegisterBufferEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_event);
}
void AppendAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const u64 key{rp.Pop<u64>()};
queue_keys.insert(queue_keys.begin(), key);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetReleasedAudioOutBuffer_1(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
const auto& buffer = ctx.BufferDescriptorB()[0];
// TODO(st4rk): This is how libtransistor currently implements the
// GetReleasedAudioOutBuffer, it should return the key (a VAddr) to the app and this address
// is used to know which buffer should be filled with data and send again to the service
// through AppendAudioOutBuffer. Check if this is the proper way to do it.
u64 key{0};
if (queue_keys.size()) {
key = queue_keys.back();
queue_keys.pop_back();
}
Memory::WriteBlock(buffer.Address(), &key, sizeof(u64));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
// TODO(st4rk): This might be the total of released buffers, needs to be verified on
// hardware
rb.Push<u32>(static_cast<u32>(queue_keys.size()));
}
void UpdateAudioBuffersCallback() {
if (audio_out_state != AudioState::Started) {
return;
}
if (queue_keys.empty()) {
return;
}
buffer_event->Signal();
}
enum class AudioState : u32 {
Started,
Stopped,
};
/// This is used to trigger the audio event callback that is going to read the samples from the
/// audio_buffer list and enqueue the samples using the sink (audio_core).
CoreTiming::EventType* audio_event;
/// This is the evend handle used to check if the audio buffer was released
Kernel::SharedPtr<Kernel::Event> buffer_event;
/// (st4rk): This is just a temporary workaround for the future implementation. Libtransistor
/// uses the key as an address in the App, so we need to return when the
/// GetReleasedAudioOutBuffer_1 is called, otherwise we'll run in problems, because
/// libtransistor uses the key returned as an pointer.
std::vector<u64> queue_keys;
AudioState audio_out_state;
};
void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
auto& buffer = ctx.BufferDescriptorB()[0];
const std::string audio_interface = "AudioInterface";
Memory::WriteBlock(buffer.Address(), &audio_interface[0], audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
rb.Push(RESULT_SUCCESS);
// TODO(st4rk): We're currently returning only one audio interface (stringlist size). However,
// it's highly possible to have more than one interface (despite that libtransistor requires
// only one).
rb.Push<u32>(1);
}
void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
if (!audio_out_interface) {
audio_out_interface = std::make_shared<IAudioOut>();
}
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(sample_rate);
rb.Push<u32>(audio_channels);
rb.Push<u32>(static_cast<u32>(PcmFormat::Int16));
rb.Push<u32>(0); // This field is unknown
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
}
AudOutU::AudOutU() : ServiceFramework("audout:u") {
static const FunctionInfo functions[] = {
{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
};
static const FunctionInfo functions[] = {{0x00000000, &AudOutU::ListAudioOuts, "ListAudioOuts"},
{0x00000001, &AudOutU::OpenAudioOut, "OpenAudioOut"},
{0x00000002, nullptr, "Unknown2"},
{0x00000003, nullptr, "Unknown3"}};
RegisterHandlers(functions);
}

View File

@@ -4,19 +4,37 @@
#pragma once
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class IAudioOut;
class AudOutU final : public ServiceFramework<AudOutU> {
public:
AudOutU();
~AudOutU() = default;
private:
std::shared_ptr<IAudioOut> audio_out_interface;
void ListAudioOuts(Kernel::HLERequestContext& ctx);
void OpenAudioOut(Kernel::HLERequestContext& ctx);
enum class PcmFormat : u32 {
Invalid = 0,
Int8 = 1,
Int16 = 2,
Int24 = 3,
Int32 = 4,
PcmFloat = 5,
Adpcm = 6,
};
};
} // namespace Audio

View File

@@ -0,0 +1,38 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audrec_u.h"
namespace Service {
namespace Audio {
class IFinalOutputRecorder final : public ServiceFramework<IFinalOutputRecorder> {
public:
IFinalOutputRecorder() : ServiceFramework("IFinalOutputRecorder") {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetFinalOutputRecorderState"},
{0x1, nullptr, "StartFinalOutputRecorder"},
{0x2, nullptr, "StopFinalOutputRecorder"},
{0x3, nullptr, "AppendFinalOutputRecorderBuffer"},
{0x4, nullptr, "RegisterBufferEvent"},
{0x5, nullptr, "GetReleasedFinalOutputRecorderBuffer"},
{0x6, nullptr, "ContainsFinalOutputRecorderBuffer"},
};
RegisterHandlers(functions);
}
~IFinalOutputRecorder() = default;
};
AudRecU::AudRecU() : ServiceFramework("audrec:u") {
static const FunctionInfo functions[] = {
{0x00000000, nullptr, "OpenFinalOutputRecorder"},
};
RegisterHandlers(functions);
}
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,23 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class AudRecU final : public ServiceFramework<AudRecU> {
public:
explicit AudRecU();
~AudRecU() = default;
};
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,44 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/audren_u.h"
namespace Service {
namespace Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
IAudioRenderer() : ServiceFramework("IAudioRenderer") {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetAudioRendererSampleRate"},
{0x1, nullptr, "GetAudioRendererSampleCount"},
{0x2, nullptr, "GetAudioRendererMixBufferCount"},
{0x3, nullptr, "GetAudioRendererState"},
{0x4, nullptr, "RequestUpdateAudioRenderer"},
{0x5, nullptr, "StartAudioRenderer"},
{0x6, nullptr, "StopAudioRenderer"},
{0x7, nullptr, "QuerySystemEvent"},
{0x8, nullptr, "SetAudioRendererRenderingTimeLimit"},
{0x9, nullptr, "GetAudioRendererRenderingTimeLimit"},
};
RegisterHandlers(functions);
}
~IAudioRenderer() = default;
};
AudRenU::AudRenU() : ServiceFramework("audren:u") {
static const FunctionInfo functions[] = {
{0x00000000, nullptr, "OpenAudioRenderer"},
{0x00000001, nullptr, "GetAudioRendererWorkBufferSize"},
{0x00000002, nullptr, "GetAudioRenderersProcessMasterVolume"},
{0x00000003, nullptr, "SetAudioRenderersProcessMasterVolume"},
};
RegisterHandlers(functions);
}
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,23 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class AudRenU final : public ServiceFramework<AudRenU> {
public:
explicit AudRenU();
~AudRenU() = default;
};
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,33 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/audio/codecctl.h"
namespace Service {
namespace Audio {
CodecCtl::CodecCtl() : ServiceFramework("codecctl") {
static const FunctionInfo functions[] = {
{0x00000000, nullptr, "InitializeCodecController"},
{0x00000001, nullptr, "FinalizeCodecController"},
{0x00000002, nullptr, "SleepCodecController"},
{0x00000003, nullptr, "WakeCodecController"},
{0x00000004, nullptr, "SetCodecVolume"},
{0x00000005, nullptr, "GetCodecVolumeMax"},
{0x00000006, nullptr, "GetCodecVolumeMin"},
{0x00000007, nullptr, "SetCodecActiveTarget"},
{0x00000008, nullptr, "Unknown"},
{0x00000009, nullptr, "BindCodecHeadphoneMicJackInterrupt"},
{0x0000000A, nullptr, "IsCodecHeadphoneMicJackInserted"},
{0x0000000B, nullptr, "ClearCodecHeadphoneMicJackInterrupt"},
{0x0000000C, nullptr, "IsCodecDeviceRequested"},
};
RegisterHandlers(functions);
}
} // namespace Audio
} // namespace Service

View File

@@ -0,0 +1,23 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Kernel {
class HLERequestContext;
}
namespace Service {
namespace Audio {
class CodecCtl final : public ServiceFramework<CodecCtl> {
public:
explicit CodecCtl();
~CodecCtl() = default;
};
} // namespace Audio
} // namespace Service

View File

@@ -40,12 +40,12 @@ private:
// Error checking
ASSERT_MSG(length == descriptor.Size(), "unexpected size difference");
if (length < 0) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
return;
}
if (offset < 0) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
return;
}
@@ -54,7 +54,7 @@ private:
std::vector<u8> output(length);
ResultVal<size_t> res = backend->Read(offset, length, output.data());
if (res.Failed()) {
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(res.Code());
return;
}
@@ -62,7 +62,7 @@ private:
// Write the data to memory
Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size());
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
@@ -91,14 +91,14 @@ void FSP_SRV::TryLoadRomFS() {
void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(5);
}
@@ -110,7 +110,7 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
if (!romfs) {
// TODO (bunnei): Find the right error code to use here
LOG_CRITICAL(Service_FS, "no file system interface available!");
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultCode(-1));
return;
}
@@ -119,12 +119,12 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
auto storage = romfs->OpenFile({}, {});
if (storage.Failed()) {
LOG_CRITICAL(Service_FS, "no storage interface available!");
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(storage.Code());
return;
}
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap()));
}

View File

@@ -46,7 +46,7 @@ public:
private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 1};
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_mem);
LOG_DEBUG(Service, "called");
@@ -162,18 +162,17 @@ public:
~Hid() = default;
private:
std::shared_ptr<IAppletResource> applet_resource;
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
auto client_port = std::make_shared<IAppletResource>()->CreatePort();
auto session = client_port->Connect();
if (session.Succeeded()) {
LOG_DEBUG(Service, "called, initialized IAppletResource -> session=%u",
(*session)->GetObjectId());
IPC::RequestBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushMoveObjects(std::move(session).Unwrap());
} else {
UNIMPLEMENTED();
if (applet_resource == nullptr) {
applet_resource = std::make_shared<IAppletResource>();
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAppletResource>(applet_resource);
LOG_DEBUG(Service, "called");
}
};

View File

@@ -65,7 +65,7 @@ private:
*/
void Log(Kernel::HLERequestContext& ctx) {
// This function only succeeds - Get that out of the way
IPC::RequestBuilder rb{ctx, 1};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
// Read MessageHeader, despite not doing anything with it right now
@@ -130,7 +130,7 @@ private:
}
output += message;
LOG_DEBUG(Debug_Emulated, "%s", output.c_str());
LOG_INFO(Debug_Emulated, "%s", output.c_str());
}
};
@@ -146,20 +146,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
* 0: ResultCode
*/
void LM::Initialize(Kernel::HLERequestContext& ctx) {
auto client_port = std::make_shared<Logger>()->CreatePort();
auto session = client_port->Connect();
if (session.Succeeded()) {
LOG_DEBUG(Service_SM, "called, initialized logger -> session=%u",
(*session)->GetObjectId());
IPC::RequestBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushMoveObjects(std::move(session).Unwrap());
registered_loggers.emplace_back(std::move(client_port));
} else {
UNIMPLEMENTED();
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<Logger>();
LOG_INFO(Service_SM, "called");
LOG_DEBUG(Service, "called");
}
LM::LM() : ServiceFramework("lm") {

View File

@@ -5,7 +5,6 @@
#pragma once
#include <vector>
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/service.h"
@@ -19,8 +18,6 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx);
std::vector<Kernel::SharedPtr<Kernel::ClientPort>> registered_loggers;
};
/// Registers all LM services with the specified service manager.

View File

@@ -0,0 +1,46 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
namespace Service {
namespace Nvidia {
namespace Devices {
u32 nvhost_ctrl::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, command=0x%08x, input_size=0x%lx, output_size=0x%lx", command,
input.size(), output.size());
switch (command) {
case IocGetConfigCommand:
return NvOsGetConfigU32(input, output);
}
UNIMPLEMENTED();
return 0;
}
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetConfigParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, setting=%s!%s", params.domain_str.data(),
params.param_str.data());
if (!strcmp(params.domain_str.data(), "nv")) {
if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) {
params.config_str[0] = '1';
} else {
UNIMPLEMENTED();
}
} else {
UNIMPLEMENTED();
}
std::memcpy(output.data(), &params, sizeof(params));
return 0;
}
} // namespace Devices
} // namespace Nvidia
} // namespace Service

View File

@@ -0,0 +1,48 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <cstdlib>
#include <cstring>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
namespace Service {
namespace Nvidia {
namespace Devices {
class nvhost_ctrl final : public nvdevice {
public:
nvhost_ctrl() = default;
~nvhost_ctrl() override = default;
u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override;
private:
enum IoctlCommands {
IocSyncptReadCommand = 0xC0080014,
IocSyncptIncrCommand = 0x40040015,
IocSyncptWaitCommand = 0xC00C0016,
IocModuleMutexCommand = 0x40080017,
IocModuleRegRDWRCommand = 0xC008010E,
IocSyncptWaitexCommand = 0xC0100019,
IocSyncptReadMaxCommand = 0xC008001A,
IocGetConfigCommand = 0xC183001B,
};
struct IocGetConfigParams {
std::array<char, 0x41> domain_str;
std::array<char, 0x41> param_str;
std::array<char, 0x101> config_str;
};
u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Devices
} // namespace Nvidia
} // namespace Service

View File

@@ -22,20 +22,21 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
}
u32 nvmap::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
switch (command) {
case IocCreateCommand:
switch (static_cast<IoctlCommand>(command)) {
case IoctlCommand::Create:
return IocCreate(input, output);
case IocAllocCommand:
case IoctlCommand::Alloc:
return IocAlloc(input, output);
case IocGetIdCommand:
case IoctlCommand::GetId:
return IocGetId(input, output);
case IocFromIdCommand:
case IoctlCommand::FromId:
return IocFromId(input, output);
case IocParamCommand:
case IoctlCommand::Param:
return IocParam(input, output);
}
UNIMPLEMENTED();
return 0;
}
u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {

View File

@@ -48,12 +48,12 @@ private:
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;
enum IoctlCommands {
IocCreateCommand = 0xC0080101,
IocFromIdCommand = 0xC0080103,
IocAllocCommand = 0xC0200104,
IocParamCommand = 0xC00C0109,
IocGetIdCommand = 0xC008010E
enum class IoctlCommand : u32 {
Create = 0xC0080101,
FromId = 0xC0080103,
Alloc = 0xC0200104,
Param = 0xC00C0109,
GetId = 0xC008010E
};
struct IocCreateParams {

View File

@@ -18,7 +18,7 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
std::string device_name = Memory::ReadCString(buffer.Address(), buffer.Size());
u32 fd = nvdrv->Open(device_name);
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(fd);
rb.Push<u32>(0);
@@ -43,7 +43,7 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
Memory::WriteBlock(output_buffer.Address(), output.data(), output_buffer.Size());
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(nv_result);
}
@@ -56,26 +56,25 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
auto result = nvdrv->Close(fd);
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
}
void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 pid = rp.Pop<u64>();
u64 unk = rp.Pop<u64>();
pid = rp.Pop<u64>();
LOG_WARNING(Service, "(STUBBED) called, pid=0x%llx, unk=0x%llx", pid, unk);
IPC::RequestBuilder rb{ctx, 2};
LOG_INFO(Service, "called, pid=0x%lx", pid);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)

View File

@@ -25,6 +25,8 @@ private:
void SetClientPID(Kernel::HLERequestContext& ctx);
std::shared_ptr<Module> nvdrv;
u64 pid{};
};
} // namespace Nvidia

View File

@@ -6,9 +6,11 @@
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl.h"
#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvmemp.h"
namespace Service {
namespace Nvidia {
@@ -19,6 +21,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module_ = std::make_shared<Module>();
std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager);
std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager);
std::make_shared<NVMEMP>()->InstallAsService(service_manager);
nvdrv = module_;
}
@@ -27,6 +30,7 @@ Module::Module() {
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>();
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(nvmap_dev);
devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>();
}
u32 Module::Open(std::string device_name) {

View File

@@ -0,0 +1,31 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvmemp.h"
namespace Service {
namespace Nvidia {
NVMEMP::NVMEMP() : ServiceFramework("nvmemp") {
static const FunctionInfo functions[] = {
{0, &NVMEMP::Unknown0, "Unknown0"},
{1, &NVMEMP::Unknown1, "Unknown1"},
};
RegisterHandlers(functions);
}
void NVMEMP::Unknown0(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED();
}
void NVMEMP::Unknown1(Kernel::HLERequestContext& ctx) {
UNIMPLEMENTED();
}
} // namespace Nvidia
} // namespace Service

View File

@@ -0,0 +1,23 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/service.h"
namespace Service {
namespace Nvidia {
class NVMEMP final : public ServiceFramework<NVMEMP> {
public:
NVMEMP();
~NVMEMP() = default;
private:
void Unknown0(Kernel::HLERequestContext& ctx);
void Unknown1(Kernel::HLERequestContext& ctx);
};
} // namespace Nvidia
} // namespace Service

View File

@@ -0,0 +1,96 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
namespace Service {
namespace NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
Buffer buffer{};
buffer.slot = slot;
buffer.igbp_buffer = igbp_buffer;
buffer.status = Buffer::Status::Free;
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
queue.emplace_back(buffer);
}
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
if (buffer.status != Buffer::Status::Free)
return false;
// Make sure that the parameters match.
auto& igbp_buffer = buffer.igbp_buffer;
return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
igbp_buffer.height == height;
});
ASSERT(itr != queue.end());
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
return itr->igbp_buffer;
}
void BufferQueue::QueueBuffer(u32 slot) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
itr->status = Buffer::Status::Queued;
}
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued;
});
if (itr == queue.end())
return boost::none;
itr->status = Buffer::Status::Acquired;
return *itr;
}
void BufferQueue::ReleaseBuffer(u32 slot) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
}
u32 BufferQueue::Query(QueryType type) {
LOG_WARNING(Service, "(STUBBED) called type=%u", static_cast<u32>(type));
switch (type) {
case QueryType::NativeWindowFormat:
// TODO(Subv): Use an enum for this
static constexpr u32 FormatABGR8 = 1;
return FormatABGR8;
}
UNIMPLEMENTED();
return 0;
}
} // namespace NVFlinger
} // namespace Service

View File

@@ -0,0 +1,82 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include <boost/optional.hpp>
#include "common/swap.h"
#include "core/hle/kernel/event.h"
namespace CoreTiming {
struct EventType;
}
namespace Service {
namespace NVFlinger {
struct IGBPBuffer {
u32_le magic;
u32_le width;
u32_le height;
u32_le stride;
u32_le format;
u32_le usage;
INSERT_PADDING_WORDS(1);
u32_le index;
INSERT_PADDING_WORDS(3);
u32_le gpu_buffer_id;
INSERT_PADDING_WORDS(17);
u32_le nvmap_handle;
u32_le offset;
INSERT_PADDING_WORDS(60);
};
static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
class BufferQueue final {
public:
enum class QueryType {
NativeWindowWidth = 0,
NativeWindowHeight = 1,
NativeWindowFormat = 2,
};
BufferQueue(u32 id, u64 layer_id);
~BufferQueue() = default;
struct Buffer {
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
u32 slot;
Status status = Status::Free;
IGBPBuffer igbp_buffer;
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
u32 GetId() const {
return id;
}
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
return native_handle;
}
private:
u32 id;
u64 layer_id;
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
};
} // namespace NVFlinger
} // namespace Service

View File

@@ -0,0 +1,161 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include "common/alignment.h"
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
namespace Service {
namespace NVFlinger {
constexpr size_t SCREEN_REFRESH_RATE = 60;
constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
NVFlinger::NVFlinger() {
// Add the different displays to the list of displays.
Display default_{0, "Default"};
Display external{1, "External"};
Display edid{2, "Edid"};
Display internal{3, "Internal"};
displays.emplace_back(default_);
displays.emplace_back(external);
displays.emplace_back(edid);
displays.emplace_back(internal);
// Schedule the screen composition events
composition_event =
CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
Compose();
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
});
CoreTiming::ScheduleEvent(frame_ticks, composition_event);
}
NVFlinger::~NVFlinger() {
CoreTiming::UnscheduleEvent(composition_event, 0);
}
u64 NVFlinger::OpenDisplay(const std::string& name) {
LOG_WARNING(Service, "Opening display %s", name.c_str());
// TODO(Subv): Currently we only support the Default display.
ASSERT(name == "Default");
auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.name == name; });
ASSERT(itr != displays.end());
return itr->id;
}
u64 NVFlinger::CreateLayer(u64 display_id) {
auto& display = GetDisplay(display_id);
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
u64 layer_id = next_layer_id++;
u32 buffer_queue_id = next_buffer_queue_id++;
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
display.layers.emplace_back(layer_id, buffer_queue);
buffer_queues.emplace_back(std::move(buffer_queue));
return layer_id;
}
u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
const auto& layer = GetLayer(display_id, layer_id);
return layer.buffer_queue->GetId();
}
Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
const auto& display = GetDisplay(display_id);
return display.vsync_event;
}
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
[&](const auto& queue) { return queue->GetId() == id; });
ASSERT(itr != buffer_queues.end());
return *itr;
}
Display& NVFlinger::GetDisplay(u64 display_id) {
auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.id == display_id; });
ASSERT(itr != displays.end());
return *itr;
}
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
auto& display = GetDisplay(display_id);
auto itr = std::find_if(display.layers.begin(), display.layers.end(),
[&](const Layer& layer) { return layer.id == layer_id; });
ASSERT(itr != display.layers.end());
return *itr;
}
void NVFlinger::Compose() {
for (auto& display : displays) {
// Trigger vsync for this display at the end of drawing
SCOPE_EXIT({ display.vsync_event->Signal(); });
// Don't do anything for displays without layers.
if (display.layers.empty())
continue;
// TODO(Subv): Support more than 1 layer.
ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported");
Layer& layer = display.layers[0];
auto& buffer_queue = layer.buffer_queue;
// Search for a queued buffer and acquire it
auto buffer = buffer_queue->AcquireBuffer();
if (buffer == boost::none) {
// There was no queued buffer to draw, render previous frame
VideoCore::g_renderer->SwapBuffers({});
continue;
}
auto& igbp_buffer = buffer->igbp_buffer;
// Now send the buffer to the GPU for drawing.
auto nvdrv = Nvidia::nvdrv.lock();
ASSERT(nvdrv);
// TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc)
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride);
buffer_queue->ReleaseBuffer(buffer->slot);
}
}
Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event");
}
} // namespace NVFlinger
} // namespace Service

View File

@@ -0,0 +1,84 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include <boost/optional.hpp>
#include "core/hle/kernel/event.h"
namespace CoreTiming {
struct EventType;
}
namespace Service {
namespace NVFlinger {
class BufferQueue;
struct Layer {
Layer(u64 id, std::shared_ptr<BufferQueue> queue);
~Layer() = default;
u64 id;
std::shared_ptr<BufferQueue> buffer_queue;
};
struct Display {
Display(u64 id, std::string name);
~Display() = default;
u64 id;
std::string name;
std::vector<Layer> layers;
Kernel::SharedPtr<Kernel::Event> vsync_event;
};
class NVFlinger final {
public:
NVFlinger();
~NVFlinger();
/// Opens the specified display and returns the id.
u64 OpenDisplay(const std::string& name);
/// Creates a layer on the specified display and returns the layer id.
u64 CreateLayer(u64 display_id);
/// Gets the buffer queue id of the specified layer in the specified display.
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
/// Gets the vsync event for the specified display.
Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
/// Obtains a buffer queue identified by the id.
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
void Compose();
private:
/// Returns the display identified by the specified id.
Display& GetDisplay(u64 display_id);
/// Returns the layer identified by the specified id in the desired display.
Layer& GetLayer(u64 display_id, u64 layer_id);
std::vector<Display> displays;
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
/// Id to use for the next layer that is created, this counter is shared among all displays.
u64 next_layer_id = 1;
/// Id to use for the next buffer queue that is created, this counter is shared among all
/// layers.
u32 next_buffer_queue_id = 1;
/// CoreTiming event that handles screen composition.
CoreTiming::EventType* composition_event;
};
} // namespace NVFlinger
} // namespace Service

View File

@@ -15,7 +15,7 @@ public:
};
void PCTL_A::GetService(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IParentalControlService>();
LOG_DEBUG(Service, "called");

View File

@@ -132,7 +132,7 @@ void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& context) {
switch (context.GetCommandType()) {
case IPC::CommandType::Close: {
IPC::RequestBuilder rb{context, 1};
IPC::ResponseBuilder rb{context, 2};
rb.Push(RESULT_SUCCESS);
return ResultCode(ErrorModule::HIPC, ErrorDescription::RemoteProcessDead);
}
@@ -165,11 +165,15 @@ void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
/// Initialize ServiceManager
void Init() {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
// here and pass it into the respective InstallInterfaces functions.
auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>();
SM::g_service_manager = std::make_shared<SM::ServiceManager>();
SM::ServiceManager::InstallInterfaces(SM::g_service_manager);
Account::InstallInterfaces(*SM::g_service_manager);
AM::InstallInterfaces(*SM::g_service_manager);
AM::InstallInterfaces(*SM::g_service_manager, nv_flinger);
AOC::InstallInterfaces(*SM::g_service_manager);
APM::InstallInterfaces(*SM::g_service_manager);
Audio::InstallInterfaces(*SM::g_service_manager);
@@ -180,7 +184,7 @@ void Init() {
PCTL::InstallInterfaces(*SM::g_service_manager);
Sockets::InstallInterfaces(*SM::g_service_manager);
Time::InstallInterfaces(*SM::g_service_manager);
VI::InstallInterfaces(*SM::g_service_manager);
VI::InstallInterfaces(*SM::g_service_manager, nv_flinger);
Set::InstallInterfaces(*SM::g_service_manager);
LOG_DEBUG(Service, "initialized OK");

View File

@@ -19,7 +19,7 @@ void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
Memory::WriteBlock(output_buffer.Address(), lang_codes.data(), lang_codes.size());
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u64>(lang_codes.size()));

View File

@@ -4,30 +4,26 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/domain.h"
#include "core/hle/service/sm/controller.h"
namespace Service {
namespace SM {
void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
auto domain = Kernel::Domain::CreateFromSession(*ctx.ServerSession()->parent).Unwrap();
ASSERT_MSG(!ctx.Session()->IsDomain(), "session is alread a domain");
ctx.Session()->ConvertToDomain();
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(domain->request_handlers.size()));
rb.Push<u32>(1); // Converted sessions start with 1 request handler
LOG_DEBUG(Service, "called, domain=%d", domain->GetObjectId());
LOG_DEBUG(Service, "called, server_session=%d", ctx.Session()->GetObjectId());
}
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(RESULT_SUCCESS);
// TODO(Subv): Check if this is correct
if (ctx.IsDomain())
rb.PushMoveObjects(ctx.Domain());
else
rb.PushMoveObjects(ctx.ServerSession());
rb.PushMoveObjects(ctx.Session());
LOG_DEBUG(Service, "called");
}
@@ -39,7 +35,7 @@ void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
}
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0x500);

View File

@@ -83,7 +83,7 @@ std::shared_ptr<ServiceManager> g_service_manager;
* 0: ResultCode
*/
void SM::Initialize(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 1};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_DEBUG(Service_SM, "called");
}
@@ -99,7 +99,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
auto client_port = service_manager->GetServicePort(name);
if (client_port.Failed()) {
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(client_port.Code());
LOG_ERROR(Service_SM, "called service=%s -> error 0x%08X", name.c_str(),
client_port.Code().raw);
@@ -112,7 +112,8 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
if (session.Succeeded()) {
LOG_DEBUG(Service_SM, "called service=%s -> session=%u", name.c_str(),
(*session)->GetObjectId());
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 1, 0);
IPC::ResponseBuilder rb =
rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles);
rb.Push(session.Code());
rb.PushMoveObjects(std::move(session).Unwrap());
}

View File

@@ -11,7 +11,7 @@ namespace Sockets {
void BSD_U::RegisterClient(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // bsd errno
@@ -28,7 +28,7 @@ void BSD_U::Socket(Kernel::HLERequestContext& ctx) {
u32 fd = next_fd++;
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(fd);
@@ -38,7 +38,7 @@ void BSD_U::Socket(Kernel::HLERequestContext& ctx) {
void BSD_U::Connect(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret
@@ -48,7 +48,7 @@ void BSD_U::Connect(Kernel::HLERequestContext& ctx) {
void BSD_U::SendTo(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 4};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // ret

View File

@@ -4,6 +4,7 @@
#include <chrono>
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -19,25 +20,47 @@ public:
ISystemClock() : ServiceFramework("ISystemClock") {
static const FunctionInfo functions[] = {
{0, &ISystemClock::GetCurrentTime, "GetCurrentTime"},
};
{2, &ISystemClock::GetSystemClockContext, "GetSystemClockContext"}};
RegisterHandlers(functions);
}
private:
void GetCurrentTime(Kernel::HLERequestContext& ctx) {
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::milliseconds>(
const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
IPC::RequestBuilder rb{ctx, 4};
LOG_DEBUG(Service, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(time_since_epoch);
LOG_DEBUG(Service, "called");
}
void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
SystemClockContext system_clock_ontext{};
IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(system_clock_ontext);
}
};
class ISteadyClock final : public ServiceFramework<ISteadyClock> {
public:
ISteadyClock() : ServiceFramework("ISteadyClock") {}
ISteadyClock() : ServiceFramework("ISteadyClock") {
static const FunctionInfo functions[] = {
{0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
};
RegisterHandlers(functions);
}
private:
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service, "called");
SteadyClockTimePoint steady_clock_time_point{cyclesToMs(CoreTiming::GetTicks()) / 1000};
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(steady_clock_time_point);
}
};
class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
@@ -55,14 +78,14 @@ private:
void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
LocationName location_name{};
IPC::RequestBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(location_name);
}
void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
@@ -75,7 +98,7 @@ private:
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
CalendarAdditionalInfo additional_info{};
IPC::RequestBuilder rb{ctx, 10};
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(calendar_time);
rb.PushRaw(additional_info);
@@ -83,49 +106,28 @@ private:
};
void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
auto client_port = std::make_shared<ISystemClock>()->CreatePort();
auto session = client_port->Connect();
if (session.Succeeded()) {
LOG_DEBUG(Service, "called, initialized ISystemClock -> session=%u",
(*session)->GetObjectId());
IPC::RequestBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushMoveObjects(std::move(session).Unwrap());
} else {
UNIMPLEMENTED();
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>();
LOG_DEBUG(Service, "called");
}
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
auto client_port = std::make_shared<ISystemClock>()->CreatePort();
auto session = client_port->Connect();
if (session.Succeeded()) {
LOG_DEBUG(Service, "called, initialized ISystemClock -> session=%u",
(*session)->GetObjectId());
IPC::RequestBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushMoveObjects(std::move(session).Unwrap());
} else {
UNIMPLEMENTED();
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>();
LOG_DEBUG(Service, "called");
}
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
auto client_port = std::make_shared<ISteadyClock>()->CreatePort();
auto session = client_port->Connect();
if (session.Succeeded()) {
LOG_DEBUG(Service, "called, initialized ISteadyClock -> session=%u",
(*session)->GetObjectId());
IPC::RequestBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushMoveObjects(std::move(session).Unwrap());
} else {
UNIMPLEMENTED();
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISteadyClock>();
LOG_DEBUG(Service, "called");
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ITimeZoneService>();
LOG_DEBUG(Service, "called");

View File

@@ -33,6 +33,19 @@ struct CalendarAdditionalInfo {
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
"CalendarAdditionalInfo structure has incorrect size");
// TODO(bunnei) RE this structure
struct SystemClockContext {
INSERT_PADDING_BYTES(0x20);
};
static_assert(sizeof(SystemClockContext) == 0x20,
"SystemClockContext structure has incorrect size");
struct SteadyClockTimePoint {
u64 value;
INSERT_PADDING_WORDS(4);
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
class Module final {
public:
class Interface : public ServiceFramework<Interface> {

View File

@@ -8,8 +8,7 @@
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
#include "video_core/renderer_base.h"
@@ -18,9 +17,6 @@
namespace Service {
namespace VI {
constexpr size_t SCREEN_REFRESH_RATE = 60;
constexpr u64 frame_ticks = static_cast<u64>(BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
class Parcel {
public:
// This default size was chosen arbitrarily.
@@ -204,8 +200,8 @@ public:
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
data = Read<Data>();
ASSERT(data.graphic_buffer_length == sizeof(IGBPBuffer));
buffer = Read<IGBPBuffer>();
ASSERT(data.graphic_buffer_length == sizeof(NVFlinger::IGBPBuffer));
buffer = Read<NVFlinger::IGBPBuffer>();
}
struct Data {
@@ -216,7 +212,7 @@ public:
};
Data data;
IGBPBuffer buffer;
NVFlinger::IGBPBuffer buffer;
};
class IGBPSetPreallocatedBufferResponseParcel : public Parcel {
@@ -288,7 +284,8 @@ public:
class IGBPRequestBufferResponseParcel : public Parcel {
public:
explicit IGBPRequestBufferResponseParcel(IGBPBuffer buffer) : Parcel(), buffer(buffer) {}
explicit IGBPRequestBufferResponseParcel(NVFlinger::IGBPBuffer buffer)
: Parcel(), buffer(buffer) {}
~IGBPRequestBufferResponseParcel() override = default;
protected:
@@ -296,7 +293,7 @@ protected:
// TODO(Subv): Find out what this all means
Write<u32_le>(1);
Write<u32_le>(sizeof(IGBPBuffer));
Write<u32_le>(sizeof(NVFlinger::IGBPBuffer));
Write<u32_le>(0); // Unknown
Write(buffer);
@@ -304,7 +301,7 @@ protected:
Write<u32_le>(0);
}
IGBPBuffer buffer;
NVFlinger::IGBPBuffer buffer;
};
class IGBPQueueBufferRequestParcel : public Parcel {
@@ -356,9 +353,38 @@ private:
Data data{};
};
class IGBPQueryRequestParcel : public Parcel {
public:
explicit IGBPQueryRequestParcel(const std::vector<u8>& buffer) : Parcel(buffer) {
Deserialize();
}
~IGBPQueryRequestParcel() override = default;
void DeserializeData() override {
std::u16string token = ReadInterfaceToken();
type = Read<u32_le>();
}
u32 type;
};
class IGBPQueryResponseParcel : public Parcel {
public:
explicit IGBPQueryResponseParcel(u32 value) : Parcel(), value(value) {}
~IGBPQueryResponseParcel() override = default;
protected:
void SerializeData() override {
Write(value);
}
private:
u32_le value;
};
class IHOSBinderDriver final : public ServiceFramework<IHOSBinderDriver> {
public:
explicit IHOSBinderDriver(std::shared_ptr<NVFlinger> nv_flinger)
explicit IHOSBinderDriver(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("IHOSBinderDriver"), nv_flinger(std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{0, &IHOSBinderDriver::TransactParcel, "TransactParcel"},
@@ -445,12 +471,22 @@ private:
auto response_buffer = response.Serialize();
Memory::WriteBlock(output_buffer.Address(), response_buffer.data(),
output_buffer.Size());
} else if (transaction == TransactionId::Query) {
IGBPQueryRequestParcel request{input_data};
u32 value =
buffer_queue->Query(static_cast<NVFlinger::BufferQueue::QueryType>(request.type));
IGBPQueryResponseParcel response{value};
auto response_buffer = response.Serialize();
Memory::WriteBlock(output_buffer.Address(), response_buffer.data(),
output_buffer.Size());
} else {
ASSERT_MSG(false, "Unimplemented");
}
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -461,7 +497,7 @@ private:
u32 type = rp.Pop<u32>();
LOG_WARNING(Service, "(STUBBED) called id=%u, addval=%08X, type=%08X", id, addval, type);
IPC::RequestBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -475,12 +511,12 @@ private:
// TODO(Subv): Find out what this actually is.
LOG_WARNING(Service, "(STUBBED) called id=%u, unknown=%08X", id, unknown);
IPC::RequestBuilder rb{ctx, 2, 1};
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_queue->GetNativeHandle());
}
std::shared_ptr<NVFlinger> nv_flinger;
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
@@ -501,14 +537,14 @@ private:
u64 layer_id = rp.Pop<u64>();
u64 z_value = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
};
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
public:
explicit IManagerDisplayService(std::shared_ptr<NVFlinger> nv_flinger)
explicit IManagerDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("IManagerDisplayService"), nv_flinger(std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{1020, &IManagerDisplayService::CloseDisplay, "CloseDisplay"},
@@ -526,7 +562,7 @@ private:
IPC::RequestParser rp{ctx};
u64 display = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
@@ -540,7 +576,7 @@ private:
u64 layer_id = nv_flinger->CreateLayer(display);
IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
}
@@ -551,17 +587,17 @@ private:
u32 stack = rp.Pop<u32>();
u64 layer_id = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
std::shared_ptr<NVFlinger> nv_flinger;
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
}
@@ -569,7 +605,7 @@ void IApplicationDisplayService::GetRelayService(Kernel::HLERequestContext& ctx)
void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemDisplayService>();
}
@@ -577,7 +613,7 @@ void IApplicationDisplayService::GetSystemDisplayService(Kernel::HLERequestConte
void IApplicationDisplayService::GetManagerDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerDisplayService>(nv_flinger);
}
@@ -586,7 +622,7 @@ void IApplicationDisplayService::GetIndirectDisplayTransactionService(
Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHOSBinderDriver>(nv_flinger);
}
@@ -601,7 +637,7 @@ void IApplicationDisplayService::OpenDisplay(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(nv_flinger->OpenDisplay(name));
}
@@ -611,7 +647,7 @@ void IApplicationDisplayService::CloseDisplay(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
}
@@ -635,7 +671,7 @@ void IApplicationDisplayService::OpenLayer(Kernel::HLERequestContext& ctx) {
auto data = native_window.Serialize();
Memory::WriteBlock(buffer.Address(), data.data(), data.size());
IPC::RequestBuilder rb = rp.MakeBuilder(4, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(data.size());
}
@@ -658,7 +694,7 @@ void IApplicationDisplayService::CreateStrayLayer(Kernel::HLERequestContext& ctx
auto data = native_window.Serialize();
Memory::WriteBlock(buffer.Address(), data.data(), data.size());
IPC::RequestBuilder rb = rp.MakeBuilder(6, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
rb.Push<u64>(data.size());
@@ -670,7 +706,7 @@ void IApplicationDisplayService::DestroyStrayLayer(Kernel::HLERequestContext& ct
IPC::RequestParser rp{ctx};
u64 layer_id = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
@@ -680,7 +716,7 @@ void IApplicationDisplayService::SetLayerScalingMode(Kernel::HLERequestContext&
u32 scaling_mode = rp.Pop<u32>();
u64 unknown = rp.Pop<u64>();
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0);
rb.Push(RESULT_SUCCESS);
}
@@ -691,12 +727,13 @@ void IApplicationDisplayService::GetDisplayVsyncEvent(Kernel::HLERequestContext&
auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
IPC::RequestBuilder rb = rp.MakeBuilder(2, 1, 0, 0);
IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0);
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(vsync_event);
}
IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger)
IApplicationDisplayService::IApplicationDisplayService(
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("IApplicationDisplayService"), nv_flinger(std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{100, &IApplicationDisplayService::GetRelayService, "GetRelayService"},
@@ -716,212 +753,9 @@ IApplicationDisplayService::IApplicationDisplayService(std::shared_ptr<NVFlinger
RegisterHandlers(functions);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<VI_M>()->InstallAsService(service_manager);
}
NVFlinger::NVFlinger() {
// Add the different displays to the list of displays.
Display default_{0, "Default"};
Display external{1, "External"};
Display edid{2, "Edid"};
Display internal{3, "Internal"};
displays.emplace_back(default_);
displays.emplace_back(external);
displays.emplace_back(edid);
displays.emplace_back(internal);
// Schedule the screen composition events
composition_event =
CoreTiming::RegisterEvent("ScreenCompositioin", [this](u64 userdata, int cycles_late) {
Compose();
CoreTiming::ScheduleEvent(frame_ticks - cycles_late, composition_event);
});
CoreTiming::ScheduleEvent(frame_ticks, composition_event);
}
NVFlinger::~NVFlinger() {
CoreTiming::UnscheduleEvent(composition_event, 0);
}
u64 NVFlinger::OpenDisplay(const std::string& name) {
LOG_WARNING(Service, "Opening display %s", name.c_str());
// TODO(Subv): Currently we only support the Default display.
ASSERT(name == "Default");
auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.name == name; });
ASSERT(itr != displays.end());
return itr->id;
}
u64 NVFlinger::CreateLayer(u64 display_id) {
auto& display = GetDisplay(display_id);
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
u64 layer_id = next_layer_id++;
u32 buffer_queue_id = next_buffer_queue_id++;
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
display.layers.emplace_back(layer_id, buffer_queue);
buffer_queues.emplace_back(std::move(buffer_queue));
return layer_id;
}
u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
const auto& layer = GetLayer(display_id, layer_id);
return layer.buffer_queue->GetId();
}
Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
const auto& display = GetDisplay(display_id);
return display.vsync_event;
}
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
[&](const auto& queue) { return queue->GetId() == id; });
ASSERT(itr != buffer_queues.end());
return *itr;
}
Display& NVFlinger::GetDisplay(u64 display_id) {
auto itr = std::find_if(displays.begin(), displays.end(),
[&](const Display& display) { return display.id == display_id; });
ASSERT(itr != displays.end());
return *itr;
}
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
auto& display = GetDisplay(display_id);
auto itr = std::find_if(display.layers.begin(), display.layers.end(),
[&](const Layer& layer) { return layer.id == layer_id; });
ASSERT(itr != display.layers.end());
return *itr;
}
void NVFlinger::Compose() {
for (auto& display : displays) {
// Trigger vsync for this display at the end of drawing
SCOPE_EXIT({ display.vsync_event->Signal(); });
// Don't do anything for displays without layers.
if (display.layers.empty())
continue;
// TODO(Subv): Support more than 1 layer.
ASSERT_MSG(display.layers.size() == 1, "Max 1 layer per display is supported");
Layer& layer = display.layers[0];
auto& buffer_queue = layer.buffer_queue;
// Search for a queued buffer and acquire it
auto buffer = buffer_queue->AcquireBuffer();
if (buffer == boost::none) {
// There was no queued buffer to draw, render previous frame
VideoCore::g_renderer->SwapBuffers({});
continue;
}
auto& igbp_buffer = buffer->igbp_buffer;
// Now send the buffer to the GPU for drawing.
auto nvdrv = Nvidia::nvdrv.lock();
ASSERT(nvdrv);
// TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc)
auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>("/dev/nvdisp_disp0");
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride);
buffer_queue->ReleaseBuffer(buffer->slot);
}
}
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
native_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "BufferQueue NativeHandle");
}
void BufferQueue::SetPreallocatedBuffer(u32 slot, IGBPBuffer& igbp_buffer) {
Buffer buffer{};
buffer.slot = slot;
buffer.igbp_buffer = igbp_buffer;
buffer.status = Buffer::Status::Free;
LOG_WARNING(Service, "Adding graphics buffer %u", slot);
queue.emplace_back(buffer);
}
u32 BufferQueue::DequeueBuffer(u32 pixel_format, u32 width, u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
if (buffer.status != Buffer::Status::Free)
return false;
// Make sure that the parameters match.
auto& igbp_buffer = buffer.igbp_buffer;
return igbp_buffer.format == pixel_format && igbp_buffer.width == width &&
igbp_buffer.height == height;
});
ASSERT(itr != queue.end());
itr->status = Buffer::Status::Dequeued;
return itr->slot;
}
const IGBPBuffer& BufferQueue::RequestBuffer(u32 slot) const {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
return itr->igbp_buffer;
}
void BufferQueue::QueueBuffer(u32 slot) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Dequeued);
itr->status = Buffer::Status::Queued;
}
boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued;
});
if (itr == queue.end())
return boost::none;
itr->status = Buffer::Status::Acquired;
return *itr;
}
void BufferQueue::ReleaseBuffer(u32 slot) {
auto itr = std::find_if(queue.begin(), queue.end(),
[&](const Buffer& buffer) { return buffer.slot == slot; });
ASSERT(itr != queue.end());
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
}
Layer::Layer(u64 id, std::shared_ptr<BufferQueue> queue) : id(id), buffer_queue(std::move(queue)) {}
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
vsync_event = Kernel::Event::Create(Kernel::ResetType::Pulse, "Display VSync Event");
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger) {
std::make_shared<VI_M>(nv_flinger)->InstallAsService(service_manager);
}
} // namespace VI

View File

@@ -7,6 +7,7 @@
#include <memory>
#include <boost/optional.hpp>
#include "core/hle/kernel/event.h"
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/hle/service/service.h"
namespace CoreTiming {
@@ -16,127 +17,9 @@ struct EventType;
namespace Service {
namespace VI {
struct IGBPBuffer {
u32_le magic;
u32_le width;
u32_le height;
u32_le stride;
u32_le format;
u32_le usage;
INSERT_PADDING_WORDS(1);
u32_le index;
INSERT_PADDING_WORDS(3);
u32_le gpu_buffer_id;
INSERT_PADDING_WORDS(17);
u32_le nvmap_handle;
u32_le offset;
INSERT_PADDING_WORDS(60);
};
static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");
class BufferQueue {
public:
BufferQueue(u32 id, u64 layer_id);
~BufferQueue() = default;
struct Buffer {
enum class Status { Free = 0, Queued = 1, Dequeued = 2, Acquired = 3 };
u32 slot;
Status status = Status::Free;
IGBPBuffer igbp_buffer;
};
void SetPreallocatedBuffer(u32 slot, IGBPBuffer& buffer);
u32 DequeueBuffer(u32 pixel_format, u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot);
boost::optional<const Buffer&> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 GetId() const {
return id;
}
Kernel::SharedPtr<Kernel::Event> GetNativeHandle() const {
return native_handle;
}
private:
u32 id;
u64 layer_id;
std::vector<Buffer> queue;
Kernel::SharedPtr<Kernel::Event> native_handle;
};
struct Layer {
Layer(u64 id, std::shared_ptr<BufferQueue> queue);
~Layer() = default;
u64 id;
std::shared_ptr<BufferQueue> buffer_queue;
};
struct Display {
Display(u64 id, std::string name);
~Display() = default;
u64 id;
std::string name;
std::vector<Layer> layers;
Kernel::SharedPtr<Kernel::Event> vsync_event;
};
class NVFlinger {
public:
NVFlinger();
~NVFlinger();
/// Opens the specified display and returns the id.
u64 OpenDisplay(const std::string& name);
/// Creates a layer on the specified display and returns the layer id.
u64 CreateLayer(u64 display_id);
/// Gets the buffer queue id of the specified layer in the specified display.
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
/// Gets the vsync event for the specified display.
Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
/// Obtains a buffer queue identified by the id.
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
/// Performs a composition request to the emulated nvidia GPU and triggers the vsync events when
/// finished.
void Compose();
private:
/// Returns the display identified by the specified id.
Display& GetDisplay(u64 display_id);
/// Returns the layer identified by the specified id in the desired display.
Layer& GetLayer(u64 display_id, u64 layer_id);
std::vector<Display> displays;
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
/// Id to use for the next layer that is created, this counter is shared among all displays.
u64 next_layer_id = 1;
/// Id to use for the next buffer queue that is created, this counter is shared among all
/// layers.
u32 next_buffer_queue_id = 1;
/// CoreTiming event that handles screen composition.
CoreTiming::EventType* composition_event;
};
class IApplicationDisplayService final : public ServiceFramework<IApplicationDisplayService> {
public:
IApplicationDisplayService(std::shared_ptr<NVFlinger> nv_flinger);
IApplicationDisplayService(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~IApplicationDisplayService() = default;
private:
@@ -152,11 +35,12 @@ private:
void DestroyStrayLayer(Kernel::HLERequestContext& ctx);
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger> nv_flinger;
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
/// Registers all VI services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
} // namespace VI
} // namespace Service

View File

@@ -13,17 +13,18 @@ namespace VI {
void VI_M::GetDisplayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::RequestBuilder rb{ctx, 2, 0, 0, 1};
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationDisplayService>(nv_flinger);
}
VI_M::VI_M() : ServiceFramework("vi:m") {
VI_M::VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger)
: ServiceFramework("vi:m"), nv_flinger(std::move(nv_flinger)) {
static const FunctionInfo functions[] = {
{2, &VI_M::GetDisplayService, "GetDisplayService"},
{3, nullptr, "GetDisplayServiceWithProxyNameExchange"},
};
RegisterHandlers(functions);
nv_flinger = std::make_shared<NVFlinger>();
}
} // namespace VI

View File

@@ -8,17 +8,21 @@
#include "core/hle/service/service.h"
namespace Service {
namespace NVFlinger {
class NVFlinger;
}
namespace VI {
class VI_M final : public ServiceFramework<VI_M> {
public:
VI_M();
VI_M(std::shared_ptr<NVFlinger::NVFlinger> nv_flinger);
~VI_M() = default;
private:
void GetDisplayService(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger> nv_flinger;
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
};
} // namespace VI