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:
Yuri Kunde Schlesner 2017-06-06 21:20:52 -07:00
parent 6dc133c24a
commit 84c497292a
5 changed files with 269 additions and 4 deletions

View file

@ -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_) {