Service: Add new ServiceFramework framework for writing HLE services
The old "Interface" class had a few problems such as using free functions (Which didn't allow you to write the service handler as if it were a regular class.) which weren't very extensible. (Only received one parameter with a pointer to the Interface object.) The new ServiceFramework aims to solve these problems by working with member functions and passing a generic context struct as parameter. This struct can be extended in the future without having to update all existing service implementations.
This commit is contained in:
parent
6dc133c24a
commit
84c497292a
5 changed files with 269 additions and 4 deletions
|
@ -2,6 +2,7 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
|
@ -45,6 +46,11 @@
|
|||
#include "core/hle/service/ssl_c.h"
|
||||
#include "core/hle/service/y2r_u.h"
|
||||
|
||||
using Kernel::ClientPort;
|
||||
using Kernel::ServerPort;
|
||||
using Kernel::ServerSession;
|
||||
using Kernel::SharedPtr;
|
||||
|
||||
namespace Service {
|
||||
|
||||
std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports;
|
||||
|
@ -102,9 +108,84 @@ void Interface::Register(const FunctionInfo* functions, size_t n) {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
|
||||
InvokerFn* handler_invoker)
|
||||
: service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
|
||||
|
||||
ServiceFrameworkBase::~ServiceFrameworkBase() = default;
|
||||
|
||||
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
|
||||
ASSERT(port == nullptr);
|
||||
port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
|
||||
port->SetHleHandler(shared_from_this());
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::InstallAsNamedPort() {
|
||||
ASSERT(port == nullptr);
|
||||
SharedPtr<ServerPort> server_port;
|
||||
SharedPtr<ClientPort> client_port;
|
||||
std::tie(server_port, client_port) = ServerPort::CreatePortPair(max_sessions, service_name);
|
||||
server_port->SetHleHandler(shared_from_this());
|
||||
AddNamedPort(service_name, std::move(client_port));
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) {
|
||||
handlers.reserve(handlers.size() + n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
// Usually this array is sorted by id already, so hint to insert at the end
|
||||
handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::ReportUnimplementedFunction(u32* cmd_buf, const FunctionInfoBase* info) {
|
||||
IPC::Header header{cmd_buf[0]};
|
||||
int num_params = header.normal_params_size + header.translate_params_size;
|
||||
std::string function_name = info == nullptr ? fmt::format("{:#08x}", cmd_buf[0]) : info->name;
|
||||
|
||||
fmt::MemoryWriter w;
|
||||
w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name,
|
||||
cmd_buf[0]);
|
||||
for (int i = 1; i <= num_params; ++i) {
|
||||
w.write(", [{}]={:#x}", i, cmd_buf[i]);
|
||||
}
|
||||
w << '}';
|
||||
|
||||
LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str());
|
||||
// TODO(bunnei): Hack - ignore error
|
||||
cmd_buf[1] = 0;
|
||||
}
|
||||
|
||||
void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_session) {
|
||||
u32* cmd_buf = Kernel::GetCommandBuffer();
|
||||
|
||||
// TODO(yuriks): The kernel should be the one handling this as part of translation after
|
||||
// everything else is migrated
|
||||
Kernel::HLERequestContext context;
|
||||
context.cmd_buf = cmd_buf;
|
||||
context.session = std::move(server_session);
|
||||
|
||||
u32 header_code = cmd_buf[0];
|
||||
auto itr = handlers.find(header_code);
|
||||
const FunctionInfoBase* info = itr == handlers.end() ? nullptr : &itr->second;
|
||||
if (info == nullptr || info->handler_callback == nullptr) {
|
||||
return ReportUnimplementedFunction(cmd_buf, info);
|
||||
}
|
||||
|
||||
LOG_TRACE(Service, "%s",
|
||||
MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str());
|
||||
handler_invoker(this, info->handler_callback, context);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Module interface
|
||||
|
||||
// TODO(yuriks): Move to kernel
|
||||
void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
|
||||
g_kernel_named_ports.emplace(std::move(name), std::move(port));
|
||||
}
|
||||
|
||||
static void AddNamedPort(Interface* interface_) {
|
||||
Kernel::SharedPtr<Kernel::ServerPort> server_port;
|
||||
Kernel::SharedPtr<Kernel::ClientPort> client_port;
|
||||
|
@ -112,7 +193,7 @@ static void AddNamedPort(Interface* interface_) {
|
|||
Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName());
|
||||
|
||||
server_port->SetHleHandler(std::shared_ptr<Interface>(interface_));
|
||||
g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port));
|
||||
AddNamedPort(interface_->GetPortName(), std::move(client_port));
|
||||
}
|
||||
|
||||
void AddService(Interface* interface_) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue