mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
Initial sys_config implementation
This commit is contained in:
parent
a98a2b79d0
commit
070c3af50f
5 changed files with 889 additions and 10 deletions
|
@ -35,6 +35,7 @@
|
|||
#include "sys_gamepad.h"
|
||||
#include "sys_ss.h"
|
||||
#include "sys_gpio.h"
|
||||
#include "sys_config.h"
|
||||
|
||||
extern std::string ppu_get_syscall_name(u64 code);
|
||||
|
||||
|
@ -490,16 +491,16 @@ const std::array<ppu_function_t, 1024> s_ppu_syscall_table
|
|||
null_func,//BIND_FUNC(sys_hid_manager_...) //513 (0x201)
|
||||
null_func,//BIND_FUNC(sys_hid_manager_...) //514 (0x202)
|
||||
uns_func, //515 (0x203) UNS
|
||||
null_func,//BIND_FUNC(sys_config_open) //516 (0x204)
|
||||
null_func,//BIND_FUNC(sys_config_close) //517 (0x205)
|
||||
null_func,//BIND_FUNC(sys_config_get_service_event) //518 (0x206)
|
||||
null_func,//BIND_FUNC(sys_config_add_service_listener) //519 (0x207)
|
||||
null_func,//BIND_FUNC(sys_config_remove_service_listener) //520 (0x208)
|
||||
null_func,//BIND_FUNC(sys_config_register_service) //521 (0x209)
|
||||
null_func,//BIND_FUNC(sys_config_unregister_service) //522 (0x20A)
|
||||
null_func,//BIND_FUNC(sys_config_io_event) //523 (0x20B)
|
||||
null_func,//BIND_FUNC(sys_config_register_io_error_listener) //524 (0x20C)
|
||||
null_func,//BIND_FUNC(sys_config_unregister_io_error_listener) //525 (0x20D)
|
||||
BIND_FUNC(sys_config_open), //516 (0x204)
|
||||
BIND_FUNC(sys_config_close), //517 (0x205)
|
||||
BIND_FUNC(sys_config_get_service_event), //518 (0x206)
|
||||
BIND_FUNC(sys_config_add_service_listener), //519 (0x207)
|
||||
BIND_FUNC(sys_config_remove_service_listener), //520 (0x208)
|
||||
BIND_FUNC(sys_config_register_service), //521 (0x209)
|
||||
BIND_FUNC(sys_config_unregister_service), //522 (0x20A)
|
||||
BIND_FUNC(sys_config_get_io_event), //523 (0x20B)
|
||||
BIND_FUNC(sys_config_register_io_error_listener), //524 (0x20C)
|
||||
BIND_FUNC(sys_config_unregister_io_error_listener), //525 (0x20D)
|
||||
uns_func, uns_func, uns_func, uns_func, //526-529 UNS
|
||||
BIND_FUNC(sys_usbd_initialize), //530 (0x212)
|
||||
BIND_FUNC(sys_usbd_finalize), //531 (0x213)
|
||||
|
|
437
rpcs3/Emu/Cell/lv2/sys_config.cpp
Normal file
437
rpcs3/Emu/Cell/lv2/sys_config.cpp
Normal file
|
@ -0,0 +1,437 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "Emu/Cell/lv2/sys_event.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
|
||||
#include "sys_config.h"
|
||||
|
||||
LOG_CHANNEL(sys_config);
|
||||
|
||||
|
||||
// Enums
|
||||
template<>
|
||||
void fmt_class_string<sys_config_service_id>::format(std::string& out, u64 id)
|
||||
{
|
||||
const s64 s_id = static_cast<s64>(id);
|
||||
|
||||
switch (s_id)
|
||||
{
|
||||
case SYS_CONFIG_SERVICE_PADMANAGER : out += "SYS_CONFIG_SERVICE_PADMANAGER"; return;
|
||||
case SYS_CONFIG_SERVICE_PADMANAGER2 : out += "SYS_CONFIG_SERVICE_PADMANAGER2"; return;
|
||||
case SYS_CONFIG_SERVICE_USER_LIBPAD : out += "SYS_CONFIG_SERVICE_USER_LIBPAD"; return;
|
||||
case SYS_CONFIG_SERVICE_USER_LIBKB : out += "SYS_CONFIG_SERVICE_USER_LIBKB"; return;
|
||||
case SYS_CONFIG_SERVICE_USER_LIBMOUSE: out += "SYS_CONFIG_SERVICE_USER_LIBMOUSE"; return;
|
||||
}
|
||||
|
||||
if (s_id < 0)
|
||||
{
|
||||
fmt::append(out, "SYS_CONFIG_SERVICE_USER_%llx", id & ~(1ull << 63));
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(out, "SYS_CONFIG_SERVICE_%llx", id);
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<sys_config_service_listener_type>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
STR_CASE(SYS_CONFIG_EVENT_SOURCE_SERVICE);
|
||||
STR_CASE(SYS_CONFIG_EVENT_SOURCE_IO);
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
// Utilities
|
||||
void dump_buffer(std::string& out, const std::vector<u8>& buffer)
|
||||
{
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
out.reserve(out.size() + buffer.size() * 2 + 1);
|
||||
fmt::append(out, "0x");
|
||||
|
||||
for (u8 x : buffer)
|
||||
{
|
||||
fmt::append(out, "%02x", x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(out, "EMPTY");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// LV2 Config
|
||||
void lv2_config::initialize() const
|
||||
{
|
||||
// Register padmanager service, notifying vsh that a controller is connected
|
||||
static const u8 hid_info[0x1a] = {
|
||||
0x01, 0x01, // 2 unk
|
||||
0x02, 0x02, // 4
|
||||
0x00, 0x00, // 6
|
||||
0x00, 0x00, // 8
|
||||
0x00, 0x00, // 10
|
||||
0x05, 0x4c, // 12 vid
|
||||
0x02, 0x68, // 14 pid
|
||||
0x00, 0x10, // 16 unk2
|
||||
0x91, 0x88, // 18
|
||||
0x04, 0x00, // 20
|
||||
0x00, 0x07, // 22
|
||||
0x00, 0x00, // 24
|
||||
0x00, 0x00 // 26
|
||||
};
|
||||
|
||||
// user_id for the padmanager seems to signify the controller port number, and the buffer contains some sort of HID descriptor
|
||||
lv2_config_service::create(SYS_CONFIG_SERVICE_PADMANAGER , 0, 1, 0, hid_info, 0x1a)->notify();
|
||||
lv2_config_service::create(SYS_CONFIG_SERVICE_PADMANAGER2, 0, 1, 0, hid_info, 0x1a)->notify();
|
||||
}
|
||||
|
||||
void lv2_config::add_service_event(const std::shared_ptr<lv2_config_service_event>& event)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
events.emplace(event->id, event);
|
||||
}
|
||||
|
||||
void lv2_config::remove_service_event(u32 id)
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
events.erase(id);
|
||||
}
|
||||
|
||||
|
||||
// LV2 Config Service Listener
|
||||
bool lv2_config_service_listener::check_service(const lv2_config_service& service)
|
||||
{
|
||||
// Filter by type
|
||||
if (type == SYS_CONFIG_SERVICE_LISTENER_ONCE && !service_events.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter by service ID or verbosity
|
||||
if (service_id != service.id || min_verbosity > service.verbosity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// realhw only seems to send the pad connected events to the listeners that provided 0x01 as the first byte of their data buffer
|
||||
// TODO: Figure out how this filter works more properly
|
||||
if (service_id == SYS_CONFIG_SERVICE_PADMANAGER && (data.size() == 0 || data[0] != 0x01))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Event applies to this listener!
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lv2_config_service_listener::notify(const std::shared_ptr<lv2_config_service_event>& event)
|
||||
{
|
||||
service_events.emplace_back(event);
|
||||
return event->notify();
|
||||
}
|
||||
|
||||
bool lv2_config_service_listener::notify(const std::shared_ptr<lv2_config_service>& service)
|
||||
{
|
||||
if (!check_service(*service))
|
||||
return false;
|
||||
|
||||
// Create service event and notify queue!
|
||||
auto event = lv2_config_service_event::create(handle, service, *this);
|
||||
return notify(event);
|
||||
}
|
||||
|
||||
void lv2_config_service_listener::notify_all()
|
||||
{
|
||||
std::vector<std::shared_ptr<lv2_config_service>> services;
|
||||
|
||||
// Grab all events
|
||||
idm::select<lv2_config_service>([&](u32 id, lv2_config_service& service) -> void {
|
||||
if (check_service(service))
|
||||
{
|
||||
services.push_back(service.get_shared_ptr());
|
||||
}
|
||||
}, 0);
|
||||
|
||||
// Sort services by timestamp
|
||||
sort(services.begin(), services.end(), [](const std::shared_ptr<lv2_config_service>& s1, const std::shared_ptr<lv2_config_service>& s2) -> bool {
|
||||
return s1->timestamp < s2->timestamp;
|
||||
});
|
||||
|
||||
// Notify listener (now with services in sorted order)
|
||||
for (auto& service : services)
|
||||
{
|
||||
this->notify(service);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// LV2 Config Service
|
||||
void lv2_config_service::unregister()
|
||||
{
|
||||
registered = false;
|
||||
|
||||
// Notify listeners
|
||||
notify();
|
||||
|
||||
// Allow this object to be destroyed by withdrawing it from the IDM
|
||||
// Note that it won't be destroyed while there are service events that hold a reference to it
|
||||
idm::remove<lv2_config_service>(idm_id);
|
||||
}
|
||||
|
||||
void lv2_config_service::notify() const
|
||||
{
|
||||
std::vector<std::shared_ptr<lv2_config_service_listener>> listeners;
|
||||
|
||||
auto sptr = wkptr.lock();
|
||||
|
||||
idm::select<lv2_config_service_listener>([&](u32 id, lv2_config_service_listener& listener) -> void {
|
||||
if (listener.check_service(*sptr))
|
||||
listeners.push_back(listener.get_shared_ptr());
|
||||
});
|
||||
|
||||
for (auto& listener : listeners)
|
||||
{
|
||||
listener->notify(this->get_shared_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
bool lv2_config_service_event::notify() const
|
||||
{
|
||||
auto _handle = handle.lock();
|
||||
if (!_handle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send event
|
||||
return _handle->notify(SYS_CONFIG_EVENT_SOURCE_SERVICE, (static_cast<u64>(service->is_registered()) << 32) | id, service->get_size());
|
||||
}
|
||||
|
||||
|
||||
// LV2 Config Service Event
|
||||
void lv2_config_service_event::write(sys_config_service_event_t *dst)
|
||||
{
|
||||
auto registered = service->is_registered();
|
||||
|
||||
dst->service_listener_handle = listener.get_id();
|
||||
dst->registered = registered;
|
||||
dst->service_id = service->id;
|
||||
dst->user_id = service->user_id;
|
||||
|
||||
if (registered)
|
||||
{
|
||||
dst->verbosity = service->verbosity;
|
||||
dst->padding = service->padding;
|
||||
|
||||
auto size = service->data.size();
|
||||
dst->data_size = static_cast<u32>(size);
|
||||
memcpy(dst->data, service->data.data(), size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Syscalls
|
||||
*/
|
||||
error_code sys_config_open(u32 equeue_hdl, vm::ptr<u32> out_config_hdl)
|
||||
{
|
||||
sys_config.trace("sys_config_open(equeue_hdl=0x%x, out_config_hdl=*0x%x)", equeue_hdl, out_config_hdl);
|
||||
|
||||
// Find queue with the given ID
|
||||
const auto queue = idm::get<lv2_obj, lv2_event_queue>(equeue_hdl);
|
||||
if (!queue)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Initialize lv2_config global state
|
||||
if (auto global = lv2_config::make())
|
||||
{
|
||||
global->initialize();
|
||||
}
|
||||
|
||||
// Create a lv2_config_handle object
|
||||
const auto config = lv2_config_handle::create(std::move(queue));
|
||||
if (config)
|
||||
{
|
||||
*out_config_hdl = idm::last_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// Failed to allocate sys_config object
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
error_code sys_config_close(u32 config_hdl)
|
||||
{
|
||||
sys_config.trace("sys_config_close(config_hdl=0x%x)", config_hdl);
|
||||
|
||||
if (!idm::remove<lv2_config_handle>(config_hdl))
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
error_code sys_config_get_service_event(u32 config_hdl, u32 event_id, vm::ptr<sys_config_service_event_t> dst, u64 size)
|
||||
{
|
||||
sys_config.trace("sys_config_get_service_event(config_hdl=0x%x, event_id=0x%llx, dst=*0x%llx, size=0x%llx)", config_hdl, event_id, dst, size);
|
||||
|
||||
// Find sys_config handle object with the given ID
|
||||
const auto cfg = idm::get<lv2_config_handle>(config_hdl);
|
||||
if (!cfg)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Find service_event object
|
||||
const auto event = lv2_config::get()->find_event(event_id);
|
||||
if (!event)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Check buffer fits
|
||||
if (!event->check_buffer_size(size))
|
||||
{
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
// Write event to buffer
|
||||
event->write(dst.get_ptr());
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
error_code sys_config_add_service_listener(u32 config_hdl, sys_config_service_id service_id, u64 min_verbosity, vm::ptr<void> in, u64 size, sys_config_service_listener_type type, vm::ptr<u32> out_listener_hdl)
|
||||
{
|
||||
sys_config.trace("sys_config_add_service_listener(config_hdl=0x%x, service_id=0x%llx, min_verbosity=0x%llx, in=*0x%x, size=%lld, type=0x%llx, out_listener_hdl=*0x%x)", config_hdl, service_id, min_verbosity, in, size, type, out_listener_hdl);
|
||||
|
||||
// Find sys_config handle object with the given ID
|
||||
auto cfg = idm::get<lv2_config_handle>(config_hdl);
|
||||
if (!cfg)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Create service listener
|
||||
const auto listener = lv2_config_service_listener::create(cfg, service_id, min_verbosity, type, static_cast<u8*>(in.get_ptr()), size);
|
||||
if (!listener)
|
||||
{
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
std::string buf_str;
|
||||
dump_buffer(buf_str, listener->data);
|
||||
sys_config.todo("Registered service listener for service %llx with non-zero buffer: %s", service_id, buf_str.c_str());
|
||||
}
|
||||
|
||||
// Notify listener with all past events
|
||||
listener->notify_all();
|
||||
|
||||
// Done!
|
||||
*out_listener_hdl = listener->get_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_remove_service_listener(u32 config_hdl, u32 listener_hdl)
|
||||
{
|
||||
sys_config.trace("sys_config_remove_service_listener(config_hdl=0x%x, listener_hdl=0x%x)", config_hdl, listener_hdl);
|
||||
|
||||
// Remove listener from IDM
|
||||
if (!idm::remove<lv2_config_service_listener>(listener_hdl))
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
error_code sys_config_register_service(u32 config_hdl, sys_config_service_id service_id, u64 user_id, u64 verbosity, vm::ptr<u8> data_buf, u64 size, vm::ptr<u32> out_service_hdl)
|
||||
{
|
||||
sys_config.trace("sys_config_register_service(config_hdl=0x%x, service_id=0x%llx, user_id=0x%llx, verbosity=0x%llx, data_but=*0x%llx, size=%lld, out_service_hdl=*0x%llx)", config_hdl, service_id, user_id, verbosity, data_buf, size, out_service_hdl);
|
||||
|
||||
// Find sys_config handle object with the given ID
|
||||
const auto cfg = idm::get<lv2_config_handle>(config_hdl);
|
||||
if (!cfg)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Create service
|
||||
auto service = lv2_config_service::create(service_id, user_id, verbosity, 0, data_buf.get_ptr(), size);
|
||||
if (!service)
|
||||
{
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
// Notify all listeners
|
||||
service->notify();
|
||||
|
||||
// Done!
|
||||
*out_service_hdl = service->get_id();
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_unregister_service(u32 config_hdl, u32 service_hdl)
|
||||
{
|
||||
sys_config.trace("sys_config_unregister_service(config_hdl=0x%x, service_hdl=0x%x)", config_hdl, service_hdl);
|
||||
|
||||
// Remove listener from IDM
|
||||
auto service = idm::withdraw<lv2_config_service>(service_hdl);
|
||||
if (!service)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
// Unregister service
|
||||
service->unregister();
|
||||
|
||||
// Done!
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* IO Events - TODO
|
||||
*/
|
||||
error_code sys_config_get_io_event(u32 config_hdl, u32 event_id /*?*/, vm::ptr<void> out_buf /*?*/, u64 size /*?*/)
|
||||
{
|
||||
sys_config.todo("sys_config_get_io_event(config_hdl=0x%x, event_id=0x%x, out_buf=*0x%x, size=%lld)", config_hdl, event_id, out_buf, size);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_register_io_error_listener(u32 config_hdl)
|
||||
{
|
||||
sys_config.todo("sys_config_register_io_error_listener(config_hdl=0x%x)", config_hdl);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_config_unregister_io_error_listener(u32 config_hdl)
|
||||
{
|
||||
sys_config.todo("sys_config_register_io_error_listener(config_hdl=0x%x)", config_hdl);
|
||||
return CELL_OK;
|
||||
}
|
433
rpcs3/Emu/Cell/lv2/sys_config.h
Normal file
433
rpcs3/Emu/Cell/lv2/sys_config.h
Normal file
|
@ -0,0 +1,433 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
|
||||
|
||||
/*
|
||||
* sys_config is a "subscription-based data storage API"
|
||||
*
|
||||
* It has the concept of services and listeners. Services provide data, listeners subscribe to registration/unregistration events from specific services.
|
||||
*
|
||||
* Services are divided into two classes: LV2 services (positive service IDs) and User services (negative service IDs).
|
||||
* LV2 services seem to be implictly "available", probably constructed on-demand with internal LV2 code generating the data. An example is PadManager (service ID 0x11).
|
||||
* User services may be registered through a syscall, and have negative IDs. An example is libPad (service ID 0x8000'0000'0000'0001).
|
||||
* Note that user-mode *cannot* register positive service IDs.
|
||||
*
|
||||
* To start with, you have to get a sys_config handle by calling sys_config_open and providing an event queue.
|
||||
* This event queue will be used for sys_config notifications if a subscribed config event is registered.
|
||||
*
|
||||
* With a sys_config handle, listeners can be added to specific services using sys_config_add_service_listener.
|
||||
* This syscall returns a service listener handle, which can be used to close the listener and stop further notifications.
|
||||
* Once subscribed, any matching past service registrations will be automatically sent to the supplied queue (thus the "data storage").
|
||||
*
|
||||
* Services exist "implicitly", and data may be registered *onto* a service by calling sys_config_register_service.
|
||||
* You can remove config events by calling sys_config_unregister_service and providing the handle returned when registering a service.
|
||||
*
|
||||
* If a service is registered (or unregistered) and matches any active listener, that listener will get an event sent to the event queue provided in the call to sys_config_open.
|
||||
*
|
||||
* This event will contain the type of config event ("service event" or "IO event", in event.source),
|
||||
* the corresponding sys_config handle (event.data1), the config event ID (event.data2 & 0xffff'ffff),
|
||||
* whether the service was registered or unregistered ('data2 >> 32'), and what buffer size will be needed to read the corresponding service event (event.data3).
|
||||
*
|
||||
* NOTE: if multiple listeners exist, each gets a separate event ID even though all events are the same!
|
||||
*
|
||||
* After receiving such an event from the event queue, the user should allocate enough buffer and call sys_config_get_service_event
|
||||
* (or sys_config_io_event) with the given event ID, in order to obtain a sys_config_service_event_t (or sys_config_io_event_t) structure
|
||||
* with the contents of the service that was (un)registered.
|
||||
*/
|
||||
|
||||
class lv2_config_handle;
|
||||
class lv2_config_service;
|
||||
class lv2_config_service_listener;
|
||||
class lv2_config_service_event;
|
||||
|
||||
|
||||
// Known sys_config service IDs
|
||||
enum sys_config_service_id : s64 {
|
||||
SYS_CONFIG_SERVICE_PADMANAGER = 0x11,
|
||||
SYS_CONFIG_SERVICE_PADMANAGER2 = 0x12, // lv2 seems to send padmanager events to both 0x11 and 0x12
|
||||
SYS_CONFIG_SERVICE_0x20 = 0x20,
|
||||
SYS_CONFIG_SERVICE_0x30 = 0x30,
|
||||
|
||||
SYS_CONFIG_SERVICE_USER_BASE = static_cast<s64>(UINT64_C(0x8000'0000'0000'0000)),
|
||||
SYS_CONFIG_SERVICE_USER_LIBPAD = SYS_CONFIG_SERVICE_USER_BASE + 1,
|
||||
SYS_CONFIG_SERVICE_USER_LIBKB = SYS_CONFIG_SERVICE_USER_BASE + 2,
|
||||
SYS_CONFIG_SERVICE_USER_LIBMOUSE = SYS_CONFIG_SERVICE_USER_BASE + 3,
|
||||
SYS_CONFIG_SERVICE_USER_0x1000 = SYS_CONFIG_SERVICE_USER_BASE + 0x1000,
|
||||
SYS_CONFIG_SERVICE_USER_0x1010 = SYS_CONFIG_SERVICE_USER_BASE + 0x1010,
|
||||
SYS_CONFIG_SERVICE_USER_0x1011 = SYS_CONFIG_SERVICE_USER_BASE + 0x1011,
|
||||
SYS_CONFIG_SERVICE_USER_0x1013 = SYS_CONFIG_SERVICE_USER_BASE + 0x1013,
|
||||
SYS_CONFIG_SERVICE_USER_0x1020 = SYS_CONFIG_SERVICE_USER_BASE + 0x1020,
|
||||
SYS_CONFIG_SERVICE_USER_0x1030 = SYS_CONFIG_SERVICE_USER_BASE + 0x1030,
|
||||
};
|
||||
|
||||
enum sys_config_service_listener_type : u32 {
|
||||
SYS_CONFIG_SERVICE_LISTENER_ONCE = 0,
|
||||
SYS_CONFIG_SERVICE_LISTENER_REPEATING = 1
|
||||
};
|
||||
|
||||
enum sys_config_event_source : u64 {
|
||||
SYS_CONFIG_EVENT_SOURCE_SERVICE = 1,
|
||||
SYS_CONFIG_EVENT_SOURCE_IO = 2
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Dynamic-sized struct to describe a sys_config_service_event
|
||||
* We never allocate it - the guest does it for us and provides a pointer
|
||||
*/
|
||||
struct sys_config_service_event_t {
|
||||
// Handle to the service listener for whom this event is destined
|
||||
be_t<u32> service_listener_handle;
|
||||
|
||||
// 1 if this service is currently registered or unregistered
|
||||
be_t<u32> registered;
|
||||
|
||||
// Service ID that triggered this event
|
||||
be_t<u64> service_id;
|
||||
|
||||
// Custom ID provided by the user, used to uniquely identify service events (provided to sys_config_register_event)
|
||||
// When a service is unregistered, this is the only value available to distinguish which service event was unregistered.
|
||||
be_t<u64> user_id;
|
||||
|
||||
/* if added==0, the structure ends here */
|
||||
|
||||
// Verbosity of this service event (provided to sys_config_register_event)
|
||||
be_t<u64> verbosity;
|
||||
|
||||
// Size of 'data'
|
||||
be_t<u32> data_size;
|
||||
|
||||
// Ignored, seems to be simply 32-bits of padding
|
||||
be_t<u32> padding;
|
||||
|
||||
// Buffer containing event data (copy of the buffer supplied to sys_config_register_service)
|
||||
// NOTE: This buffer size is dynamic, according to 'data_size', and can be 0. Here it is set to 1 since zero-sized buffers are not standards-compliant
|
||||
u8 data[1];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Event data structure for SYS_CONFIG_SERVICE_PADMANAGER
|
||||
* This is a guess
|
||||
*/
|
||||
struct sys_config_padmanager_data_t {
|
||||
be_t<u16> unk[5]; // hid device type ?
|
||||
be_t<u16> vid;
|
||||
be_t<u16> pid;
|
||||
be_t<u16> unk2[6]; // bluetooth address?
|
||||
};
|
||||
static_assert(sizeof(sys_config_padmanager_data_t) == 26);
|
||||
|
||||
|
||||
/*
|
||||
* Global (fxm-managed) sys_config state
|
||||
*/
|
||||
|
||||
class lv2_config {
|
||||
// LV2 Config mutex
|
||||
shared_mutex m_mutex;
|
||||
|
||||
// Map of LV2 Service Events
|
||||
std::unordered_map<u32, std::weak_ptr<lv2_config_service_event>> events;
|
||||
|
||||
public:
|
||||
void initialize() const;
|
||||
|
||||
// Service Events
|
||||
void add_service_event(const std::shared_ptr<lv2_config_service_event>& event);
|
||||
void remove_service_event(u32 id);
|
||||
|
||||
std::shared_ptr<lv2_config_service_event> find_event(u32 id)
|
||||
{
|
||||
reader_lock lock(m_mutex);
|
||||
|
||||
auto it = events.find(id);
|
||||
|
||||
if (it == events.end())
|
||||
return nullptr;
|
||||
|
||||
if (auto event = it->second.lock())
|
||||
{
|
||||
return event;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Utilities
|
||||
static std::shared_ptr<lv2_config> make()
|
||||
{
|
||||
return fxm::make<lv2_config>();
|
||||
}
|
||||
|
||||
static std::shared_ptr<lv2_config> get()
|
||||
{
|
||||
return fxm::get<lv2_config>();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Config Handle object, managed by IDM
|
||||
*/
|
||||
class lv2_config_handle
|
||||
{
|
||||
public:
|
||||
static const u32 id_base = 0x41000000;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 2048;
|
||||
|
||||
private:
|
||||
u32 idm_id;
|
||||
|
||||
// queue for service/io event notifications
|
||||
const std::weak_ptr<lv2_event_queue> queue;
|
||||
|
||||
bool send_queue_event(u64 source, u64 d1, u64 d2, u64 d3) const
|
||||
{
|
||||
if (auto sptr = queue.lock())
|
||||
{
|
||||
return sptr->send(source, d1, d2, d3);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_handle(std::weak_ptr<lv2_event_queue>&& _queue)
|
||||
: queue(std::move(_queue))
|
||||
{}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static std::shared_ptr<lv2_config_handle> create(Args&&... args)
|
||||
{
|
||||
auto cfg = std::make_shared<lv2_config_handle>(std::forward<Args>(args)...);
|
||||
|
||||
if (const u32 idm_id = idm::import_existing<lv2_config_handle>(cfg))
|
||||
{
|
||||
cfg->idm_id = idm_id;
|
||||
return std::move(cfg);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Notify event queue for this handle
|
||||
bool notify(u64 source, u64 data2, u64 data3) const
|
||||
{
|
||||
return send_queue_event(source, idm_id, data2, data3);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Service object, managed by IDM
|
||||
*/
|
||||
class lv2_config_service
|
||||
{
|
||||
public:
|
||||
static const u32 id_base = 0x43000000;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 2048;
|
||||
|
||||
private:
|
||||
// IDM data
|
||||
u32 idm_id;
|
||||
std::weak_ptr<lv2_config_service> wkptr;
|
||||
|
||||
// Whether this service is currently registered or not
|
||||
bool registered = true;
|
||||
|
||||
public:
|
||||
const u64 timestamp;
|
||||
const sys_config_service_id id;
|
||||
|
||||
const u64 user_id;
|
||||
const u64 verbosity;
|
||||
const u32 padding; // not used, but stored here just in case
|
||||
const std::vector<u8> data;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service(sys_config_service_id _id, u64 _user_id, u64 _verbosity, u32 _padding, const u8 _data[], size_t size)
|
||||
: timestamp(get_system_time())
|
||||
, id(_id)
|
||||
, user_id(_user_id)
|
||||
, verbosity(_verbosity)
|
||||
, padding(_padding)
|
||||
, data(&_data[0], &_data[size])
|
||||
{}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static std::shared_ptr<lv2_config_service> create(Args&&... args)
|
||||
{
|
||||
auto service = std::make_shared<lv2_config_service>(std::forward<Args>(args)...);
|
||||
|
||||
if (const u32 idm_id = idm::import_existing<lv2_config_service>(service))
|
||||
{
|
||||
service->wkptr = service;
|
||||
service->idm_id = idm_id;
|
||||
return std::move(service);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Registration
|
||||
bool is_registered() const { return registered; }
|
||||
void unregister();
|
||||
|
||||
// Notify listeners
|
||||
void notify() const;
|
||||
|
||||
// Utilities
|
||||
size_t get_size() const { return sizeof(sys_config_service_event_t)-1 + data.size(); }
|
||||
std::shared_ptr<lv2_config_service> get_shared_ptr () const { return wkptr.lock(); };
|
||||
u32 get_id() const { return idm_id; }
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Service Event Listener object, managed by IDM
|
||||
*/
|
||||
class lv2_config_service_listener
|
||||
{
|
||||
public:
|
||||
static const u32 id_base = 0x42000000;
|
||||
static const u32 id_step = 0x100;
|
||||
static const u32 id_count = 2048;
|
||||
|
||||
private:
|
||||
// IDM data
|
||||
u32 idm_id;
|
||||
std::weak_ptr<lv2_config_service_listener> wkptr;
|
||||
|
||||
// The service listener owns the service events - service events will not be freed as long as their corresponding listener exists
|
||||
// This has been confirmed to be the case in realhw
|
||||
std::vector<std::shared_ptr<lv2_config_service_event>> service_events;
|
||||
std::weak_ptr<lv2_config_handle> handle;
|
||||
|
||||
bool notify(const std::shared_ptr<lv2_config_service_event>& event);
|
||||
|
||||
public:
|
||||
const sys_config_service_id service_id;
|
||||
const u64 min_verbosity;
|
||||
const sys_config_service_listener_type type;
|
||||
|
||||
const std::vector<u8> data;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service_listener(std::shared_ptr<lv2_config_handle>& _handle, sys_config_service_id _service_id, u64 _min_verbosity, sys_config_service_listener_type _type, const u8 _data[], size_t size)
|
||||
: handle(_handle)
|
||||
, service_id(_service_id)
|
||||
, min_verbosity(_min_verbosity)
|
||||
, type(_type)
|
||||
, data(&_data[0], &_data[size])
|
||||
{}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static std::shared_ptr<lv2_config_service_listener> create(Args&&... args)
|
||||
{
|
||||
auto listener = std::make_shared<lv2_config_service_listener>(std::forward<Args>(args)...);
|
||||
|
||||
if (const u32 idm_id = idm::import_existing<lv2_config_service_listener>(listener))
|
||||
{
|
||||
listener->wkptr = listener;
|
||||
listener->idm_id = idm_id;
|
||||
return std::move(listener);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check whether service matches
|
||||
bool check_service(const lv2_config_service& service);
|
||||
|
||||
// Register new event, and notify queue
|
||||
bool notify(const std::shared_ptr<lv2_config_service>& service);
|
||||
|
||||
// (Re-)notify about all still-registered past events
|
||||
void notify_all();
|
||||
|
||||
// Utilities
|
||||
u32 get_id() const { return idm_id; }
|
||||
std::shared_ptr<lv2_config_service_listener> get_shared_ptr() const { return wkptr.lock(); };
|
||||
};
|
||||
|
||||
/*
|
||||
* LV2 Service Event object (*not* managed by IDM)
|
||||
*/
|
||||
class lv2_config_service_event
|
||||
{
|
||||
static u32 get_next_id()
|
||||
{
|
||||
static atomic_t<u32> next_id = 0;
|
||||
return next_id++;
|
||||
}
|
||||
|
||||
public:
|
||||
const u32 id;
|
||||
|
||||
// Note: Events hold a shared_ptr to their corresponding service - services only get freed once there are no more pending service events
|
||||
// This has been confirmed to be the case in realhw
|
||||
const std::weak_ptr<lv2_config_handle> handle;
|
||||
const std::shared_ptr<lv2_config_service> service;
|
||||
const lv2_config_service_listener& listener;
|
||||
|
||||
// Constructors (should not be used directly)
|
||||
lv2_config_service_event(const std::weak_ptr<lv2_config_handle>& _handle, const std::shared_ptr<lv2_config_service>& _service, const lv2_config_service_listener& _listener)
|
||||
: id(get_next_id())
|
||||
, handle(_handle)
|
||||
, service(_service)
|
||||
, listener(_listener)
|
||||
{}
|
||||
|
||||
lv2_config_service_event(const std::weak_ptr<lv2_config_handle>&& _handle, const std::shared_ptr<lv2_config_service>&& _service, const lv2_config_service_listener& _listener)
|
||||
: id(get_next_id())
|
||||
, handle(std::move(_handle))
|
||||
, service(std::move(_service))
|
||||
, listener(_listener)
|
||||
{}
|
||||
|
||||
// Factory
|
||||
template <typename... Args>
|
||||
static std::shared_ptr<lv2_config_service_event> create(Args&&... args)
|
||||
{
|
||||
auto ev = std::make_shared<lv2_config_service_event>(std::forward<Args>(args)...);
|
||||
|
||||
lv2_config::get()->add_service_event(ev);
|
||||
|
||||
return std::move(ev);
|
||||
}
|
||||
|
||||
// Destructor
|
||||
~lv2_config_service_event()
|
||||
{
|
||||
lv2_config::get()->remove_service_event(id);
|
||||
}
|
||||
|
||||
// Notify queue that this event exists
|
||||
bool notify() const;
|
||||
|
||||
// Write event to buffer
|
||||
void write(sys_config_service_event_t *dst);
|
||||
|
||||
// Check if the buffer can fit the current event, return false otherwise
|
||||
bool check_buffer_size(size_t size) const { return service->get_size() <= size; }
|
||||
};
|
||||
|
||||
/*
|
||||
* Syscalls
|
||||
*/
|
||||
/*516*/ error_code sys_config_open(u32 equeue_hdl, vm::ptr<u32> out_config_hdl);
|
||||
/*517*/ error_code sys_config_close(u32 config_hdl);
|
||||
/*518*/ error_code sys_config_get_service_event(u32 config_hdl, u32 event_id, vm::ptr<sys_config_service_event_t> dst, u64 size);
|
||||
/*519*/ error_code sys_config_add_service_listener(u32 config_hdl, sys_config_service_id service_id, u64 min_verbosity, vm::ptr<void> in, u64 size, sys_config_service_listener_type type, vm::ptr<u32> out_listener_hdl);
|
||||
/*520*/ error_code sys_config_remove_service_listener(u32 config_hdl, u32 listener_hdl);
|
||||
/*521*/ error_code sys_config_register_service(u32 config_hdl, sys_config_service_id service_id, u64 user_id, u64 verbosity, vm::ptr<u8> data_buf, u64 size, vm::ptr<u32> out_service_hdl);
|
||||
/*522*/ error_code sys_config_unregister_service(u32 config_hdl, u32 service_hdl);
|
||||
|
||||
// Following syscalls have not been REd yet
|
||||
/*523*/ error_code sys_config_get_io_event(u32 config_hdl, u32 event_id /*?*/, vm::ptr<void> out_buf /*?*/, u64 size /*?*/);
|
||||
/*524*/ error_code sys_config_register_io_error_listener(u32 config_hdl);
|
||||
/*525*/ error_code sys_config_unregister_io_error_listener(u32 config_hdl);
|
|
@ -112,6 +112,7 @@
|
|||
<ClCompile Include="..\Utilities\Thread.cpp" />
|
||||
<ClCompile Include="..\Utilities\version.cpp" />
|
||||
<ClCompile Include="..\Utilities\VirtualMemory.cpp" />
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_config.cpp" />
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_gpio.cpp" />
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_net.cpp" />
|
||||
<ClCompile Include="Emu\Cell\Modules\StaticHLE.cpp" />
|
||||
|
@ -410,6 +411,7 @@
|
|||
<ClInclude Include="Crypto\unself.h" />
|
||||
<ClInclude Include="Crypto\utils.h" />
|
||||
<ClInclude Include="define_new_memleakdetect.h" />
|
||||
<ClInclude Include="Emu\Cell\lv2\sys_config.h" />
|
||||
<ClInclude Include="Emu\Cell\lv2\sys_gpio.h" />
|
||||
<ClInclude Include="Emu\Cell\lv2\sys_net.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\cellCelp8Enc.h" />
|
||||
|
|
|
@ -779,6 +779,9 @@
|
|||
<ClCompile Include="Emu\RSX\Overlays\overlay_osk.cpp">
|
||||
<Filter>Emu\GPU\RSX\Overlays</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_config.cpp">
|
||||
<Filter>Emu\Cell\lv2</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_overlay.cpp">
|
||||
<Filter>Emu\Cell\lv2</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1522,6 +1525,9 @@
|
|||
<ClInclude Include="Emu\Audio\Null\NullAudioBackend.h">
|
||||
<Filter>Emu\Audio\Null</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Cell\lv2\sys_config.h">
|
||||
<Filter>Emu\Cell\lv2</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Common\surface_utils.h">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
|
|
Loading…
Add table
Reference in a new issue