From 070c3af50f6b42a736e9e746f9678fc607d2f907 Mon Sep 17 00:00:00 2001 From: Rui Pinheiro Date: Mon, 1 Apr 2019 22:37:56 +0100 Subject: [PATCH] Initial sys_config implementation --- rpcs3/Emu/Cell/lv2/lv2.cpp | 21 +- rpcs3/Emu/Cell/lv2/sys_config.cpp | 437 ++++++++++++++++++++++++++++++ rpcs3/Emu/Cell/lv2/sys_config.h | 433 +++++++++++++++++++++++++++++ rpcs3/emucore.vcxproj | 2 + rpcs3/emucore.vcxproj.filters | 6 + 5 files changed, 889 insertions(+), 10 deletions(-) create mode 100644 rpcs3/Emu/Cell/lv2/sys_config.cpp create mode 100644 rpcs3/Emu/Cell/lv2/sys_config.h diff --git a/rpcs3/Emu/Cell/lv2/lv2.cpp b/rpcs3/Emu/Cell/lv2/lv2.cpp index cf9f2ac734..06648146b6 100644 --- a/rpcs3/Emu/Cell/lv2/lv2.cpp +++ b/rpcs3/Emu/Cell/lv2/lv2.cpp @@ -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 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) diff --git a/rpcs3/Emu/Cell/lv2/sys_config.cpp b/rpcs3/Emu/Cell/lv2/sys_config.cpp new file mode 100644 index 0000000000..082a34d165 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_config.cpp @@ -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::format(std::string& out, u64 id) +{ + const s64 s_id = static_cast(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::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& 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& 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& event) +{ + service_events.emplace_back(event); + return event->notify(); +} + +bool lv2_config_service_listener::notify(const std::shared_ptr& 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> services; + + // Grab all events + idm::select([&](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& s1, const std::shared_ptr& 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(idm_id); +} + +void lv2_config_service::notify() const +{ + std::vector> listeners; + + auto sptr = wkptr.lock(); + + idm::select([&](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(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(size); + memcpy(dst->data, service->data.data(), size); + } +} + + + + +/* + * Syscalls + */ +error_code sys_config_open(u32 equeue_hdl, vm::ptr 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(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(config_hdl)) + { + return CELL_ESRCH; + } + + return CELL_OK; +} + + + +error_code sys_config_get_service_event(u32 config_hdl, u32 event_id, vm::ptr 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(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 in, u64 size, sys_config_service_listener_type type, vm::ptr 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(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(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(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 data_buf, u64 size, vm::ptr 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(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(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 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; +} \ No newline at end of file diff --git a/rpcs3/Emu/Cell/lv2/sys_config.h b/rpcs3/Emu/Cell/lv2/sys_config.h new file mode 100644 index 0000000000..c25aeeebb8 --- /dev/null +++ b/rpcs3/Emu/Cell/lv2/sys_config.h @@ -0,0 +1,433 @@ +#pragma once + +#include +#include + + +/* + * 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(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 service_listener_handle; + + // 1 if this service is currently registered or unregistered + be_t registered; + + // Service ID that triggered this event + be_t 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 user_id; + + /* if added==0, the structure ends here */ + + // Verbosity of this service event (provided to sys_config_register_event) + be_t verbosity; + + // Size of 'data' + be_t data_size; + + // Ignored, seems to be simply 32-bits of padding + be_t 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 unk[5]; // hid device type ? + be_t vid; + be_t pid; + be_t 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> events; + +public: + void initialize() const; + + // Service Events + void add_service_event(const std::shared_ptr& event); + void remove_service_event(u32 id); + + std::shared_ptr 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 make() + { + return fxm::make(); + } + + static std::shared_ptr get() + { + return fxm::get(); + } +}; + +/* + * 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 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&& _queue) + : queue(std::move(_queue)) + {} + + // Factory + template + static std::shared_ptr create(Args&&... args) + { + auto cfg = std::make_shared(std::forward(args)...); + + if (const u32 idm_id = idm::import_existing(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 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 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 + static std::shared_ptr create(Args&&... args) + { + auto service = std::make_shared(std::forward(args)...); + + if (const u32 idm_id = idm::import_existing(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 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 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> service_events; + std::weak_ptr handle; + + bool notify(const std::shared_ptr& event); + +public: + const sys_config_service_id service_id; + const u64 min_verbosity; + const sys_config_service_listener_type type; + + const std::vector data; + + // Constructors (should not be used directly) + lv2_config_service_listener(std::shared_ptr& _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 + static std::shared_ptr create(Args&&... args) + { + auto listener = std::make_shared(std::forward(args)...); + + if (const u32 idm_id = idm::import_existing(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& service); + + // (Re-)notify about all still-registered past events + void notify_all(); + + // Utilities + u32 get_id() const { return idm_id; } + std::shared_ptr 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 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 handle; + const std::shared_ptr service; + const lv2_config_service_listener& listener; + + // Constructors (should not be used directly) + lv2_config_service_event(const std::weak_ptr& _handle, const std::shared_ptr& _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&& _handle, const std::shared_ptr&& _service, const lv2_config_service_listener& _listener) + : id(get_next_id()) + , handle(std::move(_handle)) + , service(std::move(_service)) + , listener(_listener) + {} + + // Factory + template + static std::shared_ptr create(Args&&... args) + { + auto ev = std::make_shared(std::forward(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 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 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 in, u64 size, sys_config_service_listener_type type, vm::ptr 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 data_buf, u64 size, vm::ptr 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 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); \ No newline at end of file diff --git a/rpcs3/emucore.vcxproj b/rpcs3/emucore.vcxproj index 6e2db7cfc0..700be5d035 100644 --- a/rpcs3/emucore.vcxproj +++ b/rpcs3/emucore.vcxproj @@ -112,6 +112,7 @@ + @@ -410,6 +411,7 @@ + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index a78eb8d563..120f4f8191 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -779,6 +779,9 @@ Emu\GPU\RSX\Overlays + + Emu\Cell\lv2 + Emu\Cell\lv2 @@ -1522,6 +1525,9 @@ Emu\Audio\Null + + Emu\Cell\lv2 + Emu\GPU\RSX\Common