Rework VI + IHOSBinder

VI/IHOSBinder suffered from major inaccuracies in their function due to being quickly thrown together initially with little concern for accuracy, this has now been fixed with them being substantially more accurate now.
This commit is contained in:
PixelyIon 2021-07-04 05:21:19 +05:30 committed by ◱ Mark
parent ca4744c603
commit f1433ad0d9
24 changed files with 425 additions and 262 deletions

View File

@ -121,7 +121,6 @@ add_library(skyline SHARED
${source_DIR}/skyline/vfs/ticket.cpp ${source_DIR}/skyline/vfs/ticket.cpp
${source_DIR}/skyline/services/serviceman.cpp ${source_DIR}/skyline/services/serviceman.cpp
${source_DIR}/skyline/services/base_service.cpp ${source_DIR}/skyline/services/base_service.cpp
${source_DIR}/skyline/services/common/parcel.cpp
${source_DIR}/skyline/services/sm/IUserInterface.cpp ${source_DIR}/skyline/services/sm/IUserInterface.cpp
${source_DIR}/skyline/services/fatalsrv/IService.cpp ${source_DIR}/skyline/services/fatalsrv/IService.cpp
${source_DIR}/skyline/services/audio/IAudioOutManager.cpp ${source_DIR}/skyline/services/audio/IAudioOutManager.cpp
@ -182,12 +181,13 @@ add_library(skyline SHARED
${source_DIR}/skyline/services/nvdrv/devices/nvhost_channel.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvhost_channel.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvhost_as_gpu.cpp
${source_DIR}/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp ${source_DIR}/skyline/services/nvdrv/devices/nvhost_syncpoint.cpp
${source_DIR}/skyline/services/hosbinder/parcel.cpp
${source_DIR}/skyline/services/hosbinder/IHOSBinderDriver.cpp ${source_DIR}/skyline/services/hosbinder/IHOSBinderDriver.cpp
${source_DIR}/skyline/services/hosbinder/GraphicBufferProducer.cpp ${source_DIR}/skyline/services/hosbinder/GraphicBufferProducer.cpp
${source_DIR}/skyline/services/visrv/IDisplayService.cpp ${source_DIR}/skyline/services/visrv/IDisplayService.cpp
${source_DIR}/skyline/services/visrv/IApplicationDisplayService.cpp ${source_DIR}/skyline/services/visrv/IApplicationDisplayService.cpp
${source_DIR}/skyline/services/visrv/IManagerDisplayService.cpp ${source_DIR}/skyline/services/visrv/IManagerDisplayService.cpp
${source_DIR}/skyline/services/visrv/IManagerRootService.cpp ${source_DIR}/skyline/services/visrv/IRootService.cpp
${source_DIR}/skyline/services/visrv/ISystemDisplayService.cpp ${source_DIR}/skyline/services/visrv/ISystemDisplayService.cpp
${source_DIR}/skyline/services/pl/IPlatformServiceManager.cpp ${source_DIR}/skyline/services/pl/IPlatformServiceManager.cpp
${source_DIR}/skyline/services/aocsrv/IAddOnContentManager.cpp ${source_DIR}/skyline/services/aocsrv/IAddOnContentManager.cpp

View File

@ -218,11 +218,16 @@ namespace skyline {
/** /**
* @brief Returns a std::string_view from the payload * @brief Returns a std::string_view from the payload
* @param size The length of the string (0 means the string is null terminated) * @param size The length of the string (0 should only be used with nullTerminated and automatically determines size)
* @param nullTerminated If the returned view should only encapsulate a null terminated substring
*/ */
std::string_view PopString(size_t size = 0) { std::string_view PopString(size_t size = 0, bool nullTerminated = true) {
auto view{size ? std::string_view(reinterpret_cast<const char *>(payloadOffset), size) : std::string_view(reinterpret_cast<const char *>(payloadOffset))}; size = size ? size : cmdArgSz - reinterpret_cast<u64>(payloadOffset);
payloadOffset += view.length(); auto view{span(payloadOffset, size).as_string(nullTerminated)};
if (nullTerminated)
payloadOffset += size;
else
payloadOffset += view.length();
return view; return view;
} }

View File

@ -2,11 +2,11 @@
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/hosbinder/GraphicBufferProducer.h> #include <services/hosbinder/IHOSBinderDriver.h>
#include "ISelfController.h" #include "ISelfController.h"
namespace skyline::service::am { namespace skyline::service::am {
ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), BaseService(state, manager) {} ISelfController::ISelfController(const DeviceState &state, ServiceManager &manager) : libraryAppletLaunchableEvent(std::make_shared<type::KEvent>(state, false)), accumulatedSuspendedTickChangedEvent(std::make_shared<type::KEvent>(state, false)), hosbinder(manager.CreateOrGetService<hosbinder::IHOSBinderDriver>("dispdrv")), BaseService(state, manager) {}
Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ISelfController::LockExit(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
return {}; return {};
@ -47,18 +47,12 @@ namespace skyline::service::am {
} }
Result ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ISelfController::CreateManagedDisplayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.logger->Debug("Creating Managed Layer on Default Display"); auto layerId{hosbinder->CreateLayer(hosbinder::DisplayId::Default)};
state.logger->Debug("Creating Managed Layer #{} on 'Default' Display", layerId);
auto producer{hosbinder::producer.lock()}; response.Push(layerId);
if (producer->layerStatus != hosbinder::LayerStatus::Uninitialized)
throw exception("The application is creating more than one layer");
producer->layerStatus = hosbinder::LayerStatus::Managed;
response.Push<u64>(0);
return {}; return {};
} }
Result ISelfController::GetAccumulatedSuspendedTickValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result ISelfController::GetAccumulatedSuspendedTickValue(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
// TODO: Properly handle this after we implement game suspending // TODO: Properly handle this after we implement game suspending
response.Push<u64>(0); response.Push<u64>(0);

View File

@ -5,6 +5,10 @@
#include <services/serviceman.h> #include <services/serviceman.h>
namespace skyline::service::hosbinder {
class IHOSBinderDriver;
}
namespace skyline::service::am { namespace skyline::service::am {
/** /**
* @brief This has functions relating to an application's own current status * @brief This has functions relating to an application's own current status
@ -14,6 +18,7 @@ namespace skyline::service::am {
private: private:
std::shared_ptr<kernel::type::KEvent> libraryAppletLaunchableEvent; //!< This KEvent is triggered when the library applet is launchable std::shared_ptr<kernel::type::KEvent> libraryAppletLaunchableEvent; //!< This KEvent is triggered when the library applet is launchable
std::shared_ptr<kernel::type::KEvent> accumulatedSuspendedTickChangedEvent; //!< This KEvent is triggered when the time the system has spent in suspend is updated std::shared_ptr<kernel::type::KEvent> accumulatedSuspendedTickChangedEvent; //!< This KEvent is triggered when the time the system has spent in suspend is updated
std::shared_ptr<hosbinder::IHOSBinderDriver> hosbinder; //!< IHOSBinder service for managed display layers
public: public:
ISelfController(const DeviceState &state, ServiceManager &manager); ISelfController(const DeviceState &state, ServiceManager &manager);

View File

@ -70,7 +70,7 @@ namespace skyline::service::hosbinder {
} else { } else {
size_t index{}; size_t index{};
std::string bufferString; std::string bufferString;
for (auto& bufferSlot : queue) for (auto &bufferSlot : queue)
bufferString += util::Format("\n#{} - State: {}, Has Graphic Buffer: {}, Frame Number: {}", ++index, ToString(bufferSlot.state), static_cast<bool>(bufferSlot.graphicBuffer), bufferSlot.frameNumber); bufferString += util::Format("\n#{} - State: {}, Has Graphic Buffer: {}, Frame Number: {}", ++index, ToString(bufferSlot.state), static_cast<bool>(bufferSlot.graphicBuffer), bufferSlot.frameNumber);
state.logger->Warn("Cannot find any free buffers to dequeue:{}", bufferString); state.logger->Warn("Cannot find any free buffers to dequeue:{}", bufferString);
return AndroidStatus::InvalidOperation; return AndroidStatus::InvalidOperation;
@ -489,31 +489,4 @@ namespace skyline::service::hosbinder {
throw exception("An unimplemented transaction was called: {}", static_cast<u32>(code)); throw exception("An unimplemented transaction was called: {}", static_cast<u32>(code));
} }
} }
static frz::unordered_map<frz::string, DisplayId, 5> DisplayTypeMap{
{"Default", DisplayId::Default},
{"External", DisplayId::External},
{"Edid", DisplayId::Edid},
{"Internal", DisplayId::Internal},
{"Null", DisplayId::Null},
};
void GraphicBufferProducer::SetDisplay(const std::string &name) {
try {
if (displayId == DisplayId::Null)
displayId = DisplayTypeMap.at(frz::string(name.data(), name.size()));
else
throw exception("Trying to change display type from non-null type");
} catch (const std::out_of_range &) {
throw exception("The display with name: '{}' doesn't exist", name);
}
}
void GraphicBufferProducer::CloseDisplay() {
if (displayId == DisplayId::Null)
state.logger->Warn("Trying to close uninitiated display");
displayId = DisplayId::Null;
}
std::weak_ptr<GraphicBufferProducer> producer{};
} }

View File

@ -6,7 +6,7 @@
#pragma once #pragma once
#include <kernel/types/KEvent.h> #include <kernel/types/KEvent.h>
#include <services/common/parcel.h> #include "parcel.h"
#include "android_types.h" #include "android_types.h"
#include "native_window.h" #include "native_window.h"
@ -43,24 +43,6 @@ namespace skyline::service::hosbinder {
std::unique_ptr<GraphicBuffer> graphicBuffer{}; std::unique_ptr<GraphicBuffer> graphicBuffer{};
}; };
/**
* @brief An enumeration of all the possible display IDs
* @url https://switchbrew.org/wiki/Display_services#DisplayName
*/
enum class DisplayId : u64 {
Default, //!< Refers to the default display used by most applications
External, //!< Refers to an external display
Edid, //!< Refers to an external display with EDID capabilities
Internal, //!< Refers to the the internal display
Null, //!< Refers to the null display which is used for discarding data
};
enum class LayerStatus {
Uninitialized, //!< The layer hasn't been initialized
Stray, //!< The layer has been initialized as a stray layer
Managed, //!< The layer has been initialized as a managed layer
};
/** /**
* @brief An endpoint for the GraphicBufferProducer interface, it approximately implements BufferQueueProducer but also implements the functionality of interfaces called into by it such as GraphicBufferConsumer, Gralloc and so on * @brief An endpoint for the GraphicBufferProducer interface, it approximately implements BufferQueueProducer but also implements the functionality of interfaces called into by it such as GraphicBufferConsumer, Gralloc and so on
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h * @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/include/gui/IGraphicBufferProducer.h
@ -147,8 +129,6 @@ namespace skyline::service::hosbinder {
public: public:
std::shared_ptr<kernel::type::KEvent> bufferEvent; //!< Signalled every time a buffer in the queue is freed std::shared_ptr<kernel::type::KEvent> bufferEvent; //!< Signalled every time a buffer in the queue is freed
DisplayId displayId{DisplayId::Null}; //!< The ID of this display
LayerStatus layerStatus{LayerStatus::Uninitialized}; //!< The status of the single layer the display has
/** /**
* @brief The transactions supported by android.gui.IGraphicBufferProducer * @brief The transactions supported by android.gui.IGraphicBufferProducer
@ -178,18 +158,5 @@ namespace skyline::service::hosbinder {
* @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp;l=277-426 * @url https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:frameworks/native/libs/gui/IGraphicBufferProducer.cpp;l=277-426
*/ */
void OnTransact(TransactionCode code, Parcel &in, Parcel &out); void OnTransact(TransactionCode code, Parcel &in, Parcel &out);
/**
* @brief Sets displayId to a specific display type
* @note displayId has to be DisplayId::Null or this will throw an exception
*/
void SetDisplay(const std::string &name);
/**
* @brief Closes the display by setting displayId to DisplayId::Null
*/
void CloseDisplay();
}; };
extern std::weak_ptr<GraphicBufferProducer> producer; //!< A globally shared instance of the GraphicsBufferProducer
} }

View File

@ -7,43 +7,166 @@
#include "GraphicBufferProducer.h" #include "GraphicBufferProducer.h"
namespace skyline::service::hosbinder { namespace skyline::service::hosbinder {
IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : producer(hosbinder::producer.expired() ? std::make_shared<GraphicBufferProducer>(state) : hosbinder::producer.lock()), BaseService(state, manager) { IHOSBinderDriver::IHOSBinderDriver(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
if (hosbinder::producer.expired())
hosbinder::producer = producer;
}
Result IHOSBinderDriver::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IHOSBinderDriver::TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId{request.Pop<u32>()}; // We opted for just supporting a single layer and display as it's what basically all games use and wasting cycles on it is pointless
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request for it specifically
// There would also need to be an external compositor which composites all the graphics buffers submitted to every GraphicBufferProducer
auto binderHandle{request.Pop<u32>()};
if (binderHandle != DefaultBinderLayerHandle)
throw exception("Transaction on unknown binder object: #{}", binderHandle);
auto code{request.Pop<GraphicBufferProducer::TransactionCode>()}; auto code{request.Pop<GraphicBufferProducer::TransactionCode>()};
Parcel in(request.inputBuf.at(0), state, true); Parcel in(request.inputBuf.at(0), state, true);
Parcel out(state); Parcel out(state);
// We opted for just supporting a single layer and display as it's what basically all games use and wasting cycles on it is pointless if (!layer)
// If this was not done then we would need to maintain an array of GraphicBufferProducer objects for each layer and send the request for it specifically throw exception("Transacting parcel with non-existant layer");
// There would also need to be an external compositor which composites all the graphics buffers submitted to every GraphicBufferProducer layer->OnTransact(code, in, out);
state.logger->Debug("Layer ID: {}, Code: {}", layerId, code);
producer->OnTransact(code, in, out);
out.WriteParcel(request.outputBuf.at(0)); out.WriteParcel(request.outputBuf.at(0));
return {}; return {};
} }
Result IHOSBinderDriver::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IHOSBinderDriver::AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>(); auto binderHandle{request.Pop<u32>()};
auto addVal{request.Pop<i32>()}; if (binderHandle != DefaultBinderLayerHandle)
auto type{request.Pop<i32>()}; throw exception("Adjusting Binder object reference count for unknown object: #{}", binderHandle);
state.logger->Debug("Reference Change: {} {} reference", addVal, type ? "strong" : "weak");
auto value{request.Pop<i32>()};
bool isStrong{static_cast<bool>(request.Pop<u32>())};
if (isStrong) {
if (layerStrongReferenceCount != InitialStrongReferenceCount)
layerStrongReferenceCount += value;
else
layerStrongReferenceCount = value;
if (layerStrongReferenceCount < 0) {
state.logger->Warn("Strong reference count is lower than 0: {} + {} = {}", (layerStrongReferenceCount - value), value, layerStrongReferenceCount);
layerStrongReferenceCount = 0;
}
if (layerStrongReferenceCount == 0)
layer.reset();
} else {
layerWeakReferenceCount += value;
if (layerWeakReferenceCount < 0) {
state.logger->Warn("Weak reference count is lower than 0: {} + {} = {}", (layerWeakReferenceCount - value), value, layerWeakReferenceCount);
layerWeakReferenceCount = 0;
}
if (layerWeakReferenceCount == 0 && layerStrongReferenceCount < 1)
layer.reset();
}
state.logger->Debug("Reference Change: {} {} reference (S{} W{})", value, isStrong ? "strong" : "weak", layerStrongReferenceCount, layerWeakReferenceCount);
return {}; return {};
} }
Result IHOSBinderDriver::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IHOSBinderDriver::GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
KHandle handle{state.process->InsertItem(producer->bufferEvent)}; auto binderHandle{request.Pop<u32>()};
if (binderHandle != DefaultBinderLayerHandle)
throw exception("Getting handle from unknown binder object: #{}", binderHandle);
constexpr u32 BufferEventHandleId{0xF}; //!< The ID of the buffer event handle in the layer object
auto handleId{request.Pop<u32>()};
if (handleId != BufferEventHandleId)
throw exception("Getting unknown handle from binder object: 0x{:X}", handleId);
KHandle handle{state.process->InsertItem(layer->bufferEvent)};
state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle); state.logger->Debug("Display Buffer Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
return {}; return {};
} }
DisplayId IHOSBinderDriver::OpenDisplay(std::string_view name) {
if (name.length() > sizeof(u64))
throw exception("Opening display with name larger than sizeof(u64): '{}' ({})", name, name.length());
auto newDisplayId{[&]() -> DisplayId {
#define DISPLAY_CASE(id) case util::MakeMagic<u64>(#id): { return DisplayId::id; }
switch (util::MakeMagic<u64>(name)) {
DISPLAY_CASE(Default)
DISPLAY_CASE(External)
DISPLAY_CASE(Edid)
DISPLAY_CASE(Internal)
DISPLAY_CASE(Null)
default:
throw exception("Opening non-existent display: '{}'", name);
}
#undef DISPLAY_CASE
}()};
if (displayId != DisplayId::Null && displayId != newDisplayId)
throw exception("Opening a new display ({}) prior to closing opened display ({})", name, ToString(displayId));
return displayId = newDisplayId;
}
void IHOSBinderDriver::CloseDisplay(DisplayId id) {
if (displayId != id)
throw exception("Closing an unopened display: {} (Currently open display: {})", ToString(id), ToString(displayId));
displayId = DisplayId::Null;
}
u64 IHOSBinderDriver::CreateLayer(DisplayId pDisplayId) {
if (pDisplayId != displayId)
throw exception("Creating layer on unopened display: '{}'", ToString(pDisplayId));
else if (layer)
throw exception("Creation of multiple layers is not supported");
layerStrongReferenceCount = InitialStrongReferenceCount;
layerWeakReferenceCount = 0;
layer.emplace(state);
return DefaultLayerId;
}
Parcel IHOSBinderDriver::OpenLayer(DisplayId pDisplayId, u64 layerId) {
if (pDisplayId != displayId)
throw exception("Opening layer #{} with unopened display: '{}'", layerId, ToString(pDisplayId));
else if (layerId != DefaultLayerId)
throw exception("Attempting to open unrecognized layer #{}", layerId);
else if (!layer)
throw exception("Opening layer #{} prior to creation or after destruction", layerId);
Parcel parcel(state);
// Flat Binder with the layer's IGraphicBufferProducer
// https://cs.android.com/android/platform/superproject/+/android-5.1.1_r38:bionic/libc/kernel/uapi/linux/binder.h;l=47-57
parcel.Push<u32>(0x2); // Type of the IBinder
parcel.Push<u32>(0); // Flags
parcel.Push<u64>(DefaultBinderLayerHandle); // Handle
parcel.Push<u64>(0); // Cookie
// Unknown HOS-specific layer properties
parcel.Push(util::MakeMagic<u64>("dispdrv\0"));
parcel.Push<u64>({}); // Unknown
parcel.PushObject(0); // Offset of flattened IBinder relative to Parcel data
layerWeakReferenceCount++; // IBinder represents a weak reference to the layer
return parcel;
}
void IHOSBinderDriver::CloseLayer(u64 layerId) {
if (layerId != DefaultLayerId)
throw exception("Closing non-existent layer #{}", layerId);
else if (layerWeakReferenceCount == 0)
throw exception("Closing layer #{} which has no weak references to it", layerId);
if (--layerWeakReferenceCount == 0 && layerStrongReferenceCount < 1)
layer.reset();
}
void IHOSBinderDriver::DestroyLayer(u64 layerId) {
if (layerId != DefaultLayerId)
throw exception("Destroying non-existent layer #{}", layerId);
else if (layer)
throw exception("Destroying layer #{} which hasn't been closed: Weak References: {}, Strong References: {}", layerWeakReferenceCount, layerStrongReferenceCount);
}
} }

View File

@ -3,17 +3,46 @@
#pragma once #pragma once
#include <services/serviceman.h> #include <services/base_service.h>
#include "GraphicBufferProducer.h"
namespace skyline::service::hosbinder { namespace skyline::service::hosbinder {
class GraphicBufferProducer; /**
* @brief A display identifier specific to HOS, translated to a corresponding Android display internally
* @url https://switchbrew.org/wiki/Display_services#DisplayName
*/
enum class DisplayId : u64 {
Default, //!< Automatically determines the default display
External, //!< Refers to an external display, if any
Edid, //!< Refers to an external display with EDID capabilities
Internal, //!< Refers to the internal display on the Switch
Null, //!< A placeholder display which doesn't refer to any display
};
ENUM_STRING(DisplayId, {
ENUM_CASE(Default);
ENUM_CASE(External);
ENUM_CASE(Edid);
ENUM_CASE(Internal);
ENUM_CASE(Null);
})
/** /**
* @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is a translation layer between Android Binder IPC and HOS IPC to communicate with the Android display stack * @brief nvnflinger:dispdrv or nns::hosbinder::IHOSBinderDriver is a translation layer between Android Binder IPC and HOS IPC to communicate with the Android display stack
*/ */
class IHOSBinderDriver : public BaseService { class IHOSBinderDriver : public BaseService {
private: private:
std::shared_ptr<GraphicBufferProducer> producer; DisplayId displayId{DisplayId::Null}; //!< The ID of the display that the layer is connected to
constexpr static u64 DefaultLayerId{1}; //!< The VI ID of the default (and only) layer in our surface stack
constexpr static u32 DefaultBinderLayerHandle{1}; //!< The handle as assigned by SurfaceFlinger of the default layer
constexpr static i32 InitialStrongReferenceCount{std::numeric_limits<i32>::min()}; //!< Initial value for the strong reference count, weak references will keep the object alive till the strong reference count is first mutated
i32 layerStrongReferenceCount; //!< The amount of strong references to the layer object
i32 layerWeakReferenceCount; //!< The amount of weak references to the layer object
std::optional<GraphicBufferProducer> layer; //!< The IGraphicBufferProducer backing the layer (NativeWindow)
void AddWeakLayerReference(u32 count = 1) {
}
public: public:
IHOSBinderDriver(const DeviceState &state, ServiceManager &manager); IHOSBinderDriver(const DeviceState &state, ServiceManager &manager);
@ -25,7 +54,7 @@ namespace skyline::service::hosbinder {
Result TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result TransactParcel(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/** /**
* @brief Adjusts the reference counts to the underlying binder, it's stubbed as we aren't using the real symbols * @brief Adjusts the reference counts to the underlying Android reference counted object
* @url https://switchbrew.org/wiki/Nvnflinger_services#AdjustRefcount * @url https://switchbrew.org/wiki/Nvnflinger_services#AdjustRefcount
*/ */
Result AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result AdjustRefcount(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
@ -36,6 +65,38 @@ namespace skyline::service::hosbinder {
*/ */
Result GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result GetNativeHandle(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
/**
* @note This will throw an exception if another display was opened and not closed
*/
DisplayId OpenDisplay(std::string_view name);
/**
* @note This **must** be called prior to opening a different Display
*/
void CloseDisplay(DisplayId id);
/**
* @return An ID that can be utilized to refer to the layer
* @note This will throw an exception if the specified display has not been opened
*/
u64 CreateLayer(DisplayId displayId);
/**
* @return A parcel with a flattened IBinder to the IGraphicBufferProducer of the layer
* @note This will throw an exception if the specified display has not been opened
*/
Parcel OpenLayer(DisplayId displayId, u64 layerId);
/**
* @note This **must** be called prior to destroying the layer
*/
void CloseLayer(u64 layerId);
/**
* @note This **must** be called prior to opening a different Display
*/
void DestroyLayer(u64 layerId);
SERVICE_DECL( SERVICE_DECL(
SFUNC(0x0, IHOSBinderDriver, TransactParcel), SFUNC(0x0, IHOSBinderDriver, TransactParcel),
SFUNC(0x1, IHOSBinderDriver, AdjustRefcount), SFUNC(0x1, IHOSBinderDriver, AdjustRefcount),

View File

@ -3,7 +3,7 @@
#include "parcel.h" #include "parcel.h"
namespace skyline::service { namespace skyline::service::hosbinder {
Parcel::Parcel(span<u8> buffer, const DeviceState &state, bool hasToken) : state(state) { Parcel::Parcel(span<u8> buffer, const DeviceState &state, bool hasToken) : state(state) {
header = buffer.as<ParcelHeader>(); header = buffer.as<ParcelHeader>();

View File

@ -5,7 +5,7 @@
#include <kernel/ipc.h> #include <kernel/ipc.h>
namespace skyline::service { namespace skyline::service::hosbinder {
/** /**
* @brief This allows easy access and efficient serialization of an Android Parcel object * @brief This allows easy access and efficient serialization of an Android Parcel object
* @url https://switchbrew.org/wiki/Display_services#Parcel * @url https://switchbrew.org/wiki/Display_services#Parcel

View File

@ -18,6 +18,9 @@
#include "services/timesrv/core.h" #include "services/timesrv/core.h"
#include "fssrv/IFileSystemProxy.h" #include "fssrv/IFileSystemProxy.h"
#include "services/nvdrv/INvDrvServices.h" #include "services/nvdrv/INvDrvServices.h"
#include "hosbinder/IHOSBinderDriver.h"
#include "visrv/IApplicationRootService.h"
#include "visrv/ISystemRootService.h"
#include "visrv/IManagerRootService.h" #include "visrv/IManagerRootService.h"
#include "pl/IPlatformServiceManager.h" #include "pl/IPlatformServiceManager.h"
#include "aocsrv/IAddOnContentManager.h" #include "aocsrv/IAddOnContentManager.h"
@ -35,7 +38,7 @@
#define SERVICE_CASE(class, name, ...) \ #define SERVICE_CASE(class, name, ...) \
case util::MakeMagic<ServiceName>(name): { \ case util::MakeMagic<ServiceName>(name): { \
std::shared_ptr<BaseService> serviceObject = std::make_shared<class>(state, *this __VA_OPT__(,) __VA_ARGS__); \ std::shared_ptr<BaseService> serviceObject{std::make_shared<class>(state, *this, ##__VA_ARGS__)}; \
serviceMap[util::MakeMagic<ServiceName>(name)] = serviceObject; \ serviceMap[util::MakeMagic<ServiceName>(name)] = serviceObject; \
return serviceObject; \ return serviceObject; \
} }
@ -49,7 +52,7 @@ namespace skyline::service {
ServiceManager::ServiceManager(const DeviceState &state) : state(state), smUserInterface(std::make_shared<sm::IUserInterface>(state, *this)), globalServiceState(std::make_shared<GlobalServiceState>(state)) {} ServiceManager::ServiceManager(const DeviceState &state) : state(state), smUserInterface(std::make_shared<sm::IUserInterface>(state, *this)), globalServiceState(std::make_shared<GlobalServiceState>(state)) {}
std::shared_ptr<BaseService> ServiceManager::CreateService(ServiceName name) { std::shared_ptr<BaseService> ServiceManager::CreateOrGetService(ServiceName name) {
auto serviceIter{serviceMap.find(name)}; auto serviceIter{serviceMap.find(name)};
if (serviceIter != serviceMap.end()) if (serviceIter != serviceMap.end())
return (*serviceIter).second; return (*serviceIter).second;
@ -74,9 +77,10 @@ namespace skyline::service {
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:a") SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:a")
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:s") SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:s")
SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:t") SERVICE_CASE(nvdrv::INvDrvServices, "nvdrv:t")
SERVICE_CASE(hosbinder::IHOSBinderDriver, "dispdrv")
SERVICE_CASE(visrv::IApplicationRootService, "vi:u")
SERVICE_CASE(visrv::ISystemRootService, "vi:s")
SERVICE_CASE(visrv::IManagerRootService, "vi:m") SERVICE_CASE(visrv::IManagerRootService, "vi:m")
SERVICE_CASE(visrv::IManagerRootService, "vi:u")
SERVICE_CASE(visrv::IManagerRootService, "vi:s")
SERVICE_CASE(pl::IPlatformServiceManager, "pl:u") SERVICE_CASE(pl::IPlatformServiceManager, "pl:u")
SERVICE_CASE(aocsrv::IAddOnContentManager, "aoc:u") SERVICE_CASE(aocsrv::IAddOnContentManager, "aoc:u")
SERVICE_CASE(pctl::IParentalControlServiceFactory, "pctl") SERVICE_CASE(pctl::IParentalControlServiceFactory, "pctl")
@ -100,7 +104,7 @@ namespace skyline::service {
std::shared_ptr<BaseService> ServiceManager::NewService(ServiceName name, type::KSession &session, ipc::IpcResponse &response) { std::shared_ptr<BaseService> ServiceManager::NewService(ServiceName name, type::KSession &session, ipc::IpcResponse &response) {
std::lock_guard serviceGuard(mutex); std::lock_guard serviceGuard(mutex);
auto serviceObject{CreateService(name)}; auto serviceObject{CreateOrGetService(name)};
KHandle handle{}; KHandle handle{};
if (session.isDomain) { if (session.isDomain) {
session.domains.push_back(serviceObject); session.domains.push_back(serviceObject);

View File

@ -21,13 +21,6 @@ namespace skyline::service {
std::unordered_map<ServiceName, std::shared_ptr<BaseService>> serviceMap; //!< A mapping from a Service to the underlying object std::unordered_map<ServiceName, std::shared_ptr<BaseService>> serviceMap; //!< A mapping from a Service to the underlying object
std::mutex mutex; //!< Synchronizes concurrent access to services to prevent crashes std::mutex mutex; //!< Synchronizes concurrent access to services to prevent crashes
/**
* @brief Creates an instance of the service if it doesn't already exist, otherwise returns an existing instance
* @param name The name of the service to create
* @return A shared pointer to an instance of the service
*/
std::shared_ptr<BaseService> CreateService(ServiceName name);
public: public:
std::shared_ptr<BaseService> smUserInterface; //!< Used by applications to open connections to services std::shared_ptr<BaseService> smUserInterface; //!< Used by applications to open connections to services
std::shared_ptr<GlobalServiceState> globalServiceState; std::shared_ptr<GlobalServiceState> globalServiceState;
@ -58,19 +51,13 @@ namespace skyline::service {
} }
/** /**
* @param serviceType The type of the service * @brief Creates an instance of the service if it doesn't already exist, otherwise returns an existing instance
* @tparam The class of the service
* @return A shared pointer to an instance of the service
* @note This only works for services created with `NewService` as sub-interfaces used with `RegisterService` can have multiple instances
*/ */
template<typename Type> std::shared_ptr<BaseService> CreateOrGetService(ServiceName name);
std::shared_ptr<Type> GetService(ServiceName name) {
return std::static_pointer_cast<Type>(serviceMap.at(name));
}
template<typename Type> template<typename Type>
constexpr std::shared_ptr<Type> GetService(std::string_view name) { constexpr std::shared_ptr<Type> CreateOrGetService(std::string_view name) {
return GetService<Type>(util::MakeMagic<ServiceName>(name)); return std::static_pointer_cast<Type>(CreateOrGetService(util::MakeMagic<ServiceName>(name)));
} }
/** /**

View File

@ -3,101 +3,85 @@
#include <gpu.h> #include <gpu.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/serviceman.h>
#include <services/hosbinder/IHOSBinderDriver.h> #include <services/hosbinder/IHOSBinderDriver.h>
#include <services/hosbinder/GraphicBufferProducer.h>
#include "IApplicationDisplayService.h" #include "IApplicationDisplayService.h"
#include "ISystemDisplayService.h" #include "ISystemDisplayService.h"
#include "IManagerDisplayService.h" #include "IManagerDisplayService.h"
#include "results.h"
namespace skyline::service::visrv { namespace skyline::service::visrv {
IApplicationDisplayService::IApplicationDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager) {} IApplicationDisplayService::IApplicationDisplayService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level) : level(level), IDisplayService(state, manager) {}
Result IApplicationDisplayService::GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::GetRelayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response); manager.RegisterService(hosbinder, session, response);
return {}; return {};
} }
Result IApplicationDisplayService::GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::GetIndirectDisplayTransactionService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(hosbinder::IHOSBinderDriver), session, response); if (level < PrivilegeLevel::System)
return result::IllegalOperation;
manager.RegisterService(hosbinder, session, response);
return {}; return {};
} }
Result IApplicationDisplayService::GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::GetSystemDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (level < PrivilegeLevel::System)
return result::IllegalOperation;
manager.RegisterService(SRVREG(ISystemDisplayService), session, response); manager.RegisterService(SRVREG(ISystemDisplayService), session, response);
return {}; return {};
} }
Result IApplicationDisplayService::GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::GetManagerDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
if (level < PrivilegeLevel::Manager)
return result::IllegalOperation;
manager.RegisterService(SRVREG(IManagerDisplayService), session, response); manager.RegisterService(SRVREG(IManagerDisplayService), session, response);
return {}; return {};
} }
Result IApplicationDisplayService::OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::OpenDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
std::string displayName(request.PopString()); auto displayName(request.PopString());
state.logger->Debug("Setting display as: {}", displayName); state.logger->Debug("Opening display: {}", displayName);
response.Push(hosbinder->OpenDisplay(displayName));
auto producer{hosbinder::producer.lock()};
producer->SetDisplay(displayName);
response.Push<u64>(0); // There's only one display
return {}; return {};
} }
Result IApplicationDisplayService::CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::CloseDisplay(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
state.logger->Debug("Closing the display"); auto displayId{request.Pop<hosbinder::DisplayId>()};
auto producer{hosbinder::producer.lock()}; state.logger->Debug("Closing display: {}", hosbinder::ToString(displayId));
producer->CloseDisplay(); hosbinder->CloseDisplay(displayId);
return {}; return {};
} }
Result IApplicationDisplayService::OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::OpenLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
struct InputStruct { auto displayName{request.PopString(0x40)};
char displayName[0x40]; auto layerId{request.Pop<u64>()};
u64 layerId; state.logger->Debug("Opening layer #{} on display: {}", layerId, displayName);
u64 userId;
} input = request.Pop<InputStruct>();
state.logger->Debug("Opening Layer: Display Name: {}, Layer ID: {}, User ID: {}", input.displayName, input.layerId, input.userId);
std::string name(input.displayName);
Parcel parcel(state);
LayerParcel data{
.type = 0x2,
.pid = 0,
.bufferId = 0, // As we only have one layer and buffer
.string = "dispdrv"
};
parcel.Push(data);
parcel.objects.resize(4);
auto displayId{hosbinder->OpenDisplay(displayName)};
auto parcel{hosbinder->OpenLayer(displayId, layerId)};
response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0))); response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
return {}; return {};
} }
Result IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::CloseLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
u64 layerId{request.Pop<u64>()}; u64 layerId{request.Pop<u64>()};
state.logger->Debug("Closing Layer: {}", layerId); state.logger->Debug("Closing layer #{}", layerId);
hosbinder->CloseLayer(layerId);
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
producer->layerStatus = hosbinder::LayerStatus::Uninitialized;
return {}; return {};
} }
Result IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::SetLayerScalingMode(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto scalingMode{request.Pop<u64>()}; auto scalingMode{request.Pop<u64>()};
auto layerId{request.Pop<u64>()}; auto layerId{request.Pop<u64>()};
state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId); state.logger->Debug("Setting Layer Scaling mode to '{}' for layer {}", scalingMode, layerId);
return {}; return {};
} }
Result IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IApplicationDisplayService::GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
KHandle handle{state.process->InsertItem(state.gpu->presentation.vsyncEvent)}; KHandle handle{state.process->InsertItem(state.gpu->presentation.vsyncEvent)};
state.logger->Debug("VSync Event Handle: 0x{:X}", handle); state.logger->Debug("V-Sync Event Handle: 0x{:X}", handle);
response.copyHandles.push_back(handle); response.copyHandles.push_back(handle);
return {}; return {};
} }

View File

@ -4,6 +4,7 @@
#pragma once #pragma once
#include "IDisplayService.h" #include "IDisplayService.h"
#include "IRootService.h"
namespace skyline::service::visrv { namespace skyline::service::visrv {
/** /**
@ -11,8 +12,11 @@ namespace skyline::service::visrv {
* @url https://switchbrew.org/wiki/Display_services#IApplicationDisplayService * @url https://switchbrew.org/wiki/Display_services#IApplicationDisplayService
*/ */
class IApplicationDisplayService : public IDisplayService { class IApplicationDisplayService : public IDisplayService {
private:
PrivilegeLevel level;
public: public:
IApplicationDisplayService(const DeviceState &state, ServiceManager &manager); IApplicationDisplayService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level);
/** /**
* @brief Returns an handle to the 'nvnflinger' service * @brief Returns an handle to the 'nvnflinger' service
@ -74,19 +78,19 @@ namespace skyline::service::visrv {
*/ */
Result GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); Result GetDisplayVsyncEvent(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL( SERVICE_DECL(
SFUNC(0x64, IApplicationDisplayService, GetRelayService), SFUNC(0x64, IApplicationDisplayService, GetRelayService),
SFUNC(0x65, IApplicationDisplayService, GetSystemDisplayService), SFUNC(0x65, IApplicationDisplayService, GetSystemDisplayService),
SFUNC(0x66, IApplicationDisplayService, GetManagerDisplayService), SFUNC(0x66, IApplicationDisplayService, GetManagerDisplayService),
SFUNC(0x67, IApplicationDisplayService, GetIndirectDisplayTransactionService), SFUNC(0x67, IApplicationDisplayService, GetIndirectDisplayTransactionService),
SFUNC(0x3F2, IApplicationDisplayService, OpenDisplay), SFUNC(0x3F2, IApplicationDisplayService, OpenDisplay),
SFUNC(0x3FC, IApplicationDisplayService, CloseDisplay), SFUNC(0x3FC, IApplicationDisplayService, CloseDisplay),
SFUNC(0x7E4, IApplicationDisplayService, OpenLayer), SFUNC(0x7E4, IApplicationDisplayService, OpenLayer),
SFUNC(0x7E5, IApplicationDisplayService, CloseLayer), SFUNC(0x7E5, IApplicationDisplayService, CloseLayer),
SFUNC_BASE(0x7EE, IApplicationDisplayService, IDisplayService, CreateStrayLayer), SFUNC_BASE(0x7EE, IApplicationDisplayService, IDisplayService, CreateStrayLayer),
SFUNC_BASE(0x7EF, IApplicationDisplayService, IDisplayService, DestroyStrayLayer), SFUNC_BASE(0x7EF, IApplicationDisplayService, IDisplayService, DestroyStrayLayer),
SFUNC(0x835, IApplicationDisplayService, SetLayerScalingMode), SFUNC(0x835, IApplicationDisplayService, SetLayerScalingMode),
SFUNC(0x1452, IApplicationDisplayService, GetDisplayVsyncEvent) SFUNC(0x1452, IApplicationDisplayService, GetDisplayVsyncEvent)
) )
}; };
} }

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "IRootService.h"
namespace skyline::service::visrv {
/**
* @url https://switchbrew.org/wiki/Display_services#vi:u
*/
class IApplicationRootService : public IRootService {
public:
IApplicationRootService(const DeviceState &state, ServiceManager &manager) : IRootService(state, manager, PrivilegeLevel::Application) {}
SERVICE_DECL(
SFUNC_BASE(0x0, IApplicationRootService, IRootService, GetDisplayService)
)
};
}

View File

@ -1,46 +1,33 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/hosbinder/GraphicBufferProducer.h> #include <services/serviceman.h>
#include <services/hosbinder/IHOSBinderDriver.h>
#include "IDisplayService.h" #include "IDisplayService.h"
namespace skyline::service::visrv { namespace skyline::service::visrv {
IDisplayService::IDisplayService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {} IDisplayService::IDisplayService(const DeviceState &state, ServiceManager &manager) : hosbinder(manager.CreateOrGetService<hosbinder::IHOSBinderDriver>("dispdrv")), BaseService(state, manager) {}
Result IDisplayService::CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IDisplayService::CreateStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u64>(); request.Skip<u64>(); // VI Layer flags
auto displayId{request.Pop<u64>()}; auto displayId{request.Pop<hosbinder::DisplayId>()};
state.logger->Debug("Creating Stray Layer on Display: {}", displayId); auto layerId{hosbinder->CreateLayer(displayId)};
response.Push(layerId);
auto producer{hosbinder::producer.lock()}; state.logger->Debug("Creating Stray Layer #{} on Display: {}", layerId, hosbinder::ToString(displayId));
if (producer->layerStatus == hosbinder::LayerStatus::Stray)
throw exception("The application is creating more than one stray layer");
producer->layerStatus = hosbinder::LayerStatus::Stray;
response.Push<u64>(0); // There's only one layer
Parcel parcel(state);
LayerParcel data{
.type = 0x2,
.pid = 0,
.bufferId = 0, // As we only have one layer and buffer
.string = "dispdrv"
};
parcel.Push(data);
auto parcel{hosbinder->OpenLayer(displayId, layerId)};
response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0))); response.Push<u64>(parcel.WriteParcel(request.outputBuf.at(0)));
return {}; return {};
} }
Result IDisplayService::DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IDisplayService::DestroyStrayLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId{request.Pop<u64>()}; auto layerId{request.Pop<u64>()};
state.logger->Debug("Destroying Stray Layer: {}", layerId); state.logger->Debug("Destroying Stray Layer #{}", layerId);
auto producer{hosbinder::producer.lock()}; hosbinder->CloseLayer(layerId);
if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
producer->layerStatus = hosbinder::LayerStatus::Uninitialized;
return {}; return {};
} }

View File

@ -3,7 +3,11 @@
#pragma once #pragma once
#include <services/serviceman.h> #include <services/base_service.h>
namespace skyline::service::hosbinder {
class IHOSBinderDriver;
}
namespace skyline::service::visrv { namespace skyline::service::visrv {
/** /**
@ -11,18 +15,7 @@ namespace skyline::service::visrv {
*/ */
class IDisplayService : public BaseService { class IDisplayService : public BaseService {
protected: protected:
/** std::shared_ptr<hosbinder::IHOSBinderDriver> hosbinder; //!< The IHOSBinder relayed via this display class
* @brief This is the parcel used in OpenLayer/CreateStrayLayer
*/
struct LayerParcel {
u32 type; //!< The type of the layer
u32 pid; //!< The PID that the layer belongs to
u32 bufferId; //!< The buffer ID of the layer
u32 _pad0_[3];
u8 string[0x8]; //!< "dispdrv"
u64 _pad1_;
};
static_assert(sizeof(LayerParcel) == 0x28);
public: public:
IDisplayService(const DeviceState &state, ServiceManager &manager); IDisplayService(const DeviceState &state, ServiceManager &manager);

View File

@ -1,35 +1,27 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/hosbinder/GraphicBufferProducer.h> #include <services/hosbinder/IHOSBinderDriver.h>
#include "IManagerDisplayService.h" #include "IManagerDisplayService.h"
namespace skyline::service::visrv { namespace skyline::service::visrv {
IManagerDisplayService::IManagerDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager) {} IManagerDisplayService::IManagerDisplayService(const DeviceState &state, ServiceManager &manager) : IDisplayService(state, manager) {}
Result IManagerDisplayService::CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IManagerDisplayService::CreateManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
request.Skip<u32>(); request.Skip<u64>(); // VI Layer flags
auto displayId{request.Pop<u64>()}; auto displayId{request.Pop<hosbinder::DisplayId>()};
state.logger->Debug("Creating Managed Layer on Display: {}", displayId);
auto producer{hosbinder::producer.lock()}; auto layerId{hosbinder->CreateLayer(displayId)};
if (producer->layerStatus != hosbinder::LayerStatus::Uninitialized) state.logger->Debug("Creating Managed Layer #{} on Display: {}", layerId, hosbinder::ToString(displayId));
throw exception("The application is creating more than one layer"); response.Push(layerId);
producer->layerStatus = hosbinder::LayerStatus::Managed;
response.Push<u64>(0); // There's only one layer
return {}; return {};
} }
Result IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) { Result IManagerDisplayService::DestroyManagedLayer(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto layerId{request.Pop<u64>()}; auto layerId{request.Pop<u64>()};
state.logger->Debug("Destroying Managed Layer: {}", layerId); state.logger->Debug("Destroying Managed Layer #{}", layerId);
hosbinder->DestroyLayer(layerId);
auto producer{hosbinder::producer.lock()};
if (producer->layerStatus == hosbinder::LayerStatus::Uninitialized)
state.logger->Warn("The application is destroying an uninitialized layer");
producer->layerStatus = hosbinder::LayerStatus::Uninitialized;
return {}; return {};
} }

View File

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "IManagerRootService.h"
#include "IApplicationDisplayService.h"
namespace skyline::service::visrv {
IManagerRootService::IManagerRootService(const DeviceState &state, ServiceManager &manager) : BaseService(state, manager) {}
Result IManagerRootService::GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
manager.RegisterService(SRVREG(IApplicationDisplayService), session, response);
return {};
}
}

View File

@ -3,26 +3,18 @@
#pragma once #pragma once
#include <gpu.h> #include "IRootService.h"
#include <services/serviceman.h>
namespace skyline::service::visrv { namespace skyline::service::visrv {
/** /**
* @brief This service is used to get an handle to #IApplicationDisplayService
* @url https://switchbrew.org/wiki/Display_services#vi:m * @url https://switchbrew.org/wiki/Display_services#vi:m
*/ */
class IManagerRootService : public BaseService { class IManagerRootService : public IRootService {
public: public:
IManagerRootService(const DeviceState &state, ServiceManager &manager); IManagerRootService(const DeviceState &state, ServiceManager &manager) : IRootService(state, manager, PrivilegeLevel::Manager) {}
/** SERVICE_DECL(
* @brief Returns an handle to #IApplicationDisplayService SFUNC_BASE(0x2, IManagerRootService, IRootService, GetDisplayService)
* @url https://switchbrew.org/wiki/Display_services#GetDisplayService )
*/
Result GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
SERVICE_DECL(
SFUNC(0x2, IManagerRootService, GetDisplayService)
)
}; };
} }

View File

@ -0,0 +1,19 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <services/serviceman.h>
#include "IRootService.h"
#include "IApplicationDisplayService.h"
#include "results.h"
namespace skyline::service::visrv {
IRootService::IRootService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level) : level(level), BaseService(state, manager) {}
Result IRootService::GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response) {
auto requestedPrivileges{request.Pop<u32>()}; //!< A boolean indicating if the returned service should have higher privileges or not
if (requestedPrivileges && level < PrivilegeLevel::System)
return result::IllegalOperation;
manager.RegisterService(SRVREG(IApplicationDisplayService, level), session, response);
return {};
}
}

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <services/base_service.h>
namespace skyline::service::visrv {
/**
* @brief All privilege-based variants that a single service can have
*/
enum class PrivilegeLevel {
Application, //!< The service used by user applications (Lowest)
System, //!< The service used by system applications (Higher)
Manager, //!< The service used by system services internally (Highest)
};
/**
* @brief Manages allocation of VI to display services
* @url https://switchbrew.org/wiki/Display_services#vi:u
* @url https://switchbrew.org/wiki/Display_services#vi:s
* @url https://switchbrew.org/wiki/Display_services#vi:m
*/
class IRootService : public BaseService {
private:
PrivilegeLevel level;
public:
IRootService(const DeviceState &state, ServiceManager &manager, PrivilegeLevel level);
/**
* @brief Returns an handle to #IApplicationDisplayService
* @url https://switchbrew.org/wiki/Display_services#GetDisplayService
*/
Result GetDisplayService(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
};
}

View File

@ -0,0 +1,20 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include "IRootService.h"
namespace skyline::service::visrv {
/**
* @url https://switchbrew.org/wiki/Display_services#vi:s
*/
class ISystemRootService : public IRootService {
public:
ISystemRootService(const DeviceState &state, ServiceManager &manager) : IRootService(state, manager, PrivilegeLevel::System) {}
SERVICE_DECL(
SFUNC_BASE(0x3, ISystemRootService, IRootService, GetDisplayService)
)
};
}

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
namespace skyline::service::visrv::result {
constexpr Result IllegalOperation(114, 6);
}