From 4bbe885f35390194d0377195d9024cfb8499262b Mon Sep 17 00:00:00 2001 From: Eladash Date: Wed, 9 Aug 2023 11:11:59 +0300 Subject: [PATCH] LV2/cellPad: Implement priority-based connection updates --- rpcs3/Emu/Cell/Modules/cellKb.cpp | 12 +-- rpcs3/Emu/Cell/Modules/cellMouse.cpp | 12 +-- rpcs3/Emu/Cell/Modules/cellPad.cpp | 113 ++++++++++++++++++++++----- rpcs3/Emu/Cell/Modules/cellPad.h | 10 ++- rpcs3/Emu/Cell/Modules/sys_io_.cpp | 103 +++++++++++++++++++----- rpcs3/Emu/Cell/lv2/sys_hid.cpp | 4 +- rpcs3/Emu/Cell/lv2/sys_hid.h | 2 +- rpcs3/Emu/Io/PadHandler.cpp | 18 ++++- 8 files changed, 216 insertions(+), 58 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellKb.cpp b/rpcs3/Emu/Cell/Modules/cellKb.cpp index b90eef2f12c4..f6ae9433d25b 100644 --- a/rpcs3/Emu/Cell/Modules/cellKb.cpp +++ b/rpcs3/Emu/Cell/Modules/cellKb.cpp @@ -6,8 +6,8 @@ #include "Emu/Io/KeyboardHandler.h" #include "cellKb.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -61,7 +61,7 @@ void KeyboardHandlerBase::save(utils::serial& ar) ar(inited ? m_info.max_connect : 0); } -error_code cellKbInit(u32 max_connect) +error_code cellKbInit(ppu_thread& ppu, u32 max_connect) { sys_io.warning("cellKbInit(max_connect=%d)", max_connect); @@ -78,13 +78,13 @@ error_code cellKbInit(u32 max_connect) return CELL_KB_ERROR_INVALID_PARAMETER; } - libio_sys_config_init(); + sys_config_start(ppu); handler.Init(std::min(max_connect, 7u)); return CELL_OK; } -error_code cellKbEnd() +error_code cellKbEnd(ppu_thread& ppu) { sys_io.notice("cellKbEnd()"); @@ -96,7 +96,7 @@ error_code cellKbEnd() return CELL_KB_ERROR_UNINITIALIZED; // TODO - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellMouse.cpp b/rpcs3/Emu/Cell/Modules/cellMouse.cpp index 76437c2f460c..f7ae70bfb413 100644 --- a/rpcs3/Emu/Cell/Modules/cellMouse.cpp +++ b/rpcs3/Emu/Cell/Modules/cellMouse.cpp @@ -7,8 +7,8 @@ #include "cellMouse.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -61,7 +61,7 @@ void MouseHandlerBase::save(utils::serial& ar) ar(inited ? m_info.max_connect : 0); } -error_code cellMouseInit(u32 max_connect) +error_code cellMouseInit(ppu_thread& ppu, u32 max_connect) { sys_io.notice("cellMouseInit(max_connect=%d)", max_connect); @@ -78,7 +78,7 @@ error_code cellMouseInit(u32 max_connect) return CELL_MOUSE_ERROR_INVALID_PARAMETER; } - libio_sys_config_init(); + sys_config_start(ppu); handler.Init(std::min(max_connect, 7u)); return CELL_OK; @@ -121,7 +121,7 @@ error_code cellMouseClearBuf(u32 port_no) return CELL_OK; } -error_code cellMouseEnd() +error_code cellMouseEnd(ppu_thread& ppu) { sys_io.notice("cellMouseEnd()"); @@ -133,7 +133,7 @@ error_code cellMouseEnd() return CELL_MOUSE_ERROR_UNINITIALIZED; // TODO - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } diff --git a/rpcs3/Emu/Cell/Modules/cellPad.cpp b/rpcs3/Emu/Cell/Modules/cellPad.cpp index 075d5dbb4e27..71a1c54eeff2 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPad.cpp @@ -3,13 +3,14 @@ #include "Emu/system_config.h" #include "Emu/Cell/PPUModule.h" #include "Emu/Cell/lv2/sys_process.h" +#include "Emu/Cell/lv2/sys_sync.h" #include "Emu/Io/pad_types.h" #include "Input/pad_thread.h" #include "Input/product_info.h" #include "cellPad.h" -extern void libio_sys_config_init(); -extern void libio_sys_config_end(); +error_code sys_config_start(ppu_thread& ppu); +error_code sys_config_stop(ppu_thread& ppu); extern bool is_input_allowed(); @@ -63,8 +64,40 @@ void pad_info::save(utils::serial& ar) ar(max_connect, port_setting); } +extern void send_sys_io_connect_event(u32 index, u32 state); -error_code cellPadInit(u32 max_connect) +void cellPad_NotifyStateChange(u32 index, u32 state) +{ + auto info = g_fxo->try_get(); + + if (!info) + { + return; + } + + std::lock_guard lock(pad::g_pad_mutex); + + if (!info->max_connect) + { + return; + } + + const u32 old = info->reported_statuses[index]; + + if (~(old ^ state) & CELL_PAD_STATUS_CONNECTED) + { + return; + } + + info->reported_statuses[index] = (state & CELL_PAD_STATUS_CONNECTED) | CELL_PAD_STATUS_ASSIGN_CHANGES; +} + +extern void pad_state_notify_state_change(u32 index, u32 state) +{ + cellPad_NotifyStateChange(index, state); +} + +error_code cellPadInit(ppu_thread& ppu, u32 max_connect) { sys_io.warning("cellPadInit(max_connect=%d)", max_connect); @@ -78,13 +111,33 @@ error_code cellPadInit(u32 max_connect) if (max_connect == 0 || max_connect > CELL_MAX_PADS) return CELL_PAD_ERROR_INVALID_PARAMETER; - libio_sys_config_init(); + sys_config_start(ppu); + config.max_connect = max_connect; config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF); + config.reported_statuses = {}; + + std::array statuses{}; + + const auto handler = pad::get_current_handler(); + + const auto& pads = handler->GetPads(); + + for (u32 i = 0; i < statuses.size(); ++i) + { + if (i >= config.get_max_connect()) + break; + + if (pads[i]->m_port_status & CELL_PAD_STATUS_CONNECTED) + { + send_sys_io_connect_event(i, CELL_PAD_STATUS_CONNECTED); + } + } + return CELL_OK; } -error_code cellPadEnd() +error_code cellPadEnd(ppu_thread& ppu) { sys_io.notice("cellPadEnd()"); @@ -95,7 +148,7 @@ error_code cellPadEnd() if (!config.max_connect.exchange(0)) return CELL_PAD_ERROR_UNINITIALIZED; - libio_sys_config_end(); + sys_config_stop(ppu); return CELL_OK; } @@ -143,7 +196,7 @@ error_code cellPadClearBuf(u32 port_no) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); clear_pad_buffer(pad); @@ -176,7 +229,7 @@ error_code cellPadGetData(u32 port_no, vm::ptr data) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); pad_get_data(port_no, data.get_ptr()); @@ -452,7 +505,7 @@ error_code cellPadPeriphGetData(u32 port_no, vm::ptr data) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); pad_get_data(port_no, &data->cellpad_data); @@ -487,7 +540,7 @@ error_code cellPadGetRawData(u32 port_no, vm::ptr data) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // ? @@ -553,7 +606,7 @@ error_code cellPadSetActDirect(u32 port_no, vm::ptr param) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // TODO: find out if this is checked here or later or at all @@ -585,9 +638,10 @@ error_code cellPadGetInfo(vm::ptr info) const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.max_connect; - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; + u32 now_connect = 0; + const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_MAX_PADS; ++i) @@ -595,8 +649,16 @@ error_code cellPadGetInfo(vm::ptr info) if (i >= config.get_max_connect()) break; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? - info->status[i] = pads[i]->m_port_status; + if (!config.is_reportedly_connected(i)) + continue; + + config.reported_statuses[i] &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here? + info->status[i] = config.reported_statuses[i]; + + if (config.reported_statuses[i] & CELL_PAD_STATUS_CONNECTED) + { + now_connect++; + } if (pads[i]->m_vendor_id == 0 || pads[i]->m_product_id == 0) { @@ -635,6 +697,7 @@ error_code cellPadGetInfo(vm::ptr info) } } + info->now_connect = now_connect; return CELL_OK; } @@ -658,9 +721,10 @@ error_code cellPadGetInfo2(vm::ptr info) const PadInfo& rinfo = handler->GetInfo(); info->max_connect = config.get_max_connect(); // Here it is forcibly clamped - info->now_connect = rinfo.now_connect; info->system_info = rinfo.system_info; + u32 now_connect = 0; + const auto& pads = handler->GetPads(); for (u32 i = 0; i < CELL_PAD_MAX_PORT_NUM; ++i) @@ -668,13 +732,22 @@ error_code cellPadGetInfo2(vm::ptr info) if (i >= config.get_max_connect()) break; - info->port_status[i] = pads[i]->m_port_status; - pads[i]->m_port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; + if (!config.is_reportedly_connected(i)) + continue; + + info->port_status[i] = config.reported_statuses[i]; + config.reported_statuses[i] &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; info->port_setting[i] = config.port_setting[i]; info->device_capability[i] = pads[i]->m_device_capability; info->device_type[i] = pads[i]->m_device_type; + + if (config.reported_statuses[i] & CELL_PAD_STATUS_CONNECTED) + { + now_connect++; + } } + info->now_connect = now_connect; return CELL_OK; } @@ -701,7 +774,7 @@ error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); // Should return the same as device capability mask, psl1ght has it backwards in pad->h @@ -759,7 +832,7 @@ error_code cellPadInfoPressMode(u32 port_no) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) ? 1 : 0); @@ -788,7 +861,7 @@ error_code cellPadInfoSensorMode(u32 port_no) const auto& pad = pads[port_no]; - if (!(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) + if (!config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED)) return not_an_error(CELL_PAD_ERROR_NO_DEVICE); return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) ? 1 : 0); diff --git a/rpcs3/Emu/Cell/Modules/cellPad.h b/rpcs3/Emu/Cell/Modules/cellPad.h index 0b378773f90f..f4603a80e675 100644 --- a/rpcs3/Emu/Cell/Modules/cellPad.h +++ b/rpcs3/Emu/Cell/Modules/cellPad.h @@ -192,6 +192,7 @@ struct pad_info { atomic_t max_connect = 0; std::array port_setting{ 0 }; + std::array reported_statuses{}; SAVESTATE_INIT_POS(11); @@ -203,8 +204,15 @@ struct pad_info { return std::min(max_connect, CELL_PAD_MAX_PORT_NUM); } + + // Unreliable way the firmware uses to optimize away pad calls for disconnected pads + // This result relies on data updates from config events on a dedicated thread to receive them + bool is_reportedly_connected(u32 port_no) const + { + return port_no < get_max_connect() && !!(reported_statuses[port_no] & CELL_PAD_STATUS_CONNECTED); + } }; error_code cellPadGetData(u32 port_no, vm::ptr data); -error_code cellPadInit(u32 max_connect); +error_code cellPadInit(ppu_thread& ppu, u32 max_connect); error_code cellPadSetPortSetting(u32 port_no, u32 port_setting); diff --git a/rpcs3/Emu/Cell/Modules/sys_io_.cpp b/rpcs3/Emu/Cell/Modules/sys_io_.cpp index abbdb53cd53a..c9874f1ffa2e 100644 --- a/rpcs3/Emu/Cell/Modules/sys_io_.cpp +++ b/rpcs3/Emu/Cell/Modules/sys_io_.cpp @@ -3,6 +3,10 @@ #include "Emu/IdManager.h" #include "Emu/Cell/PPUModule.h" +#include "Emu/Cell/lv2/sys_event.h" +#include "Emu/Cell/lv2/sys_ppu_thread.h" +#include "Emu/Cell/Modules/sysPrxForUser.h" + LOG_CHANNEL(sys_io); extern void cellPad_init(); @@ -13,53 +17,114 @@ struct libio_sys_config { shared_mutex mtx; s32 init_ctr = 0; - u32 stack_addr = 0; + u32 ppu_id = 0; + u32 queue_id = 0; ~libio_sys_config() noexcept { - if (stack_addr) - { - ensure(vm::dealloc(stack_addr, vm::stack)); - } } }; -// Only exists internally (has no name) -extern void libio_sys_config_init() +extern void cellPad_NotifyStateChange(u32 index, u32 state); + +void config_event_entry(ppu_thread& ppu) { auto& cfg = g_fxo->get(); - std::lock_guard lock(cfg.mtx); + // Ensure awake + ppu.check_state(); - if (cfg.init_ctr++ == 0) + while (!sys_event_queue_receive(ppu, cfg.queue_id, vm::null, 0)) { - // Belongs to "_cfg_evt_hndlr" thread (8k stack) - cfg.stack_addr = ensure(vm::alloc(0x2000, vm::stack, 4096)); + if (ppu.is_stopped()) + { + return; + } + + // Some delay + thread_ctrl::wait_for(10000); + + // Wakeup + ppu.check_state(); + + const u64 arg1 = ppu.gpr[5]; + const u64 arg2 = ppu.gpr[6]; + const u64 arg3 = ppu.gpr[7]; + + // TODO: Reverse-engineer proper event system + + if (arg1 == 1) + { + cellPad_NotifyStateChange(arg2, arg3); + } } + + ppu_execute<&sys_ppu_thread_exit>(ppu, 0); } -extern void libio_sys_config_end() +extern void send_sys_io_connect_event(u32 index, u32 state) { auto& cfg = g_fxo->get(); std::lock_guard lock(cfg.mtx); - if (cfg.init_ctr-- == 1) + if (cfg.init_ctr) { - ensure(vm::dealloc(std::exchange(cfg.stack_addr, 0), vm::stack)); + if (auto port = idm::get(cfg.queue_id)) + { + port->send(0, 1, index, state); + } } } -error_code sys_config_start() +error_code sys_config_start(ppu_thread& ppu) { - sys_io.todo("sys_config_start()"); + sys_io.warning("sys_config_start()"); + + auto& cfg = g_fxo->get(); + + std::lock_guard lock(cfg.mtx); + + if (cfg.init_ctr++ == 0) + { + // Run thread + vm::var _tid; + vm::var queue_id; + vm::var _name = vm::make_str("_cfg_evt_hndlr"); + + vm::var attr; + attr->protocol = SYS_SYNC_PRIORITY; + attr->type = SYS_PPU_QUEUE; + attr->name_u64 = 0; + + ensure(CELL_OK == sys_event_queue_create(ppu, queue_id, attr, 0, 0x20)); + ensure(CELL_OK == ppu_execute<&sys_ppu_thread_create>(ppu, +_tid, g_fxo->get().func_addr(FIND_FUNC(config_event_entry)), 0, 512, 0x2000, SYS_PPU_THREAD_CREATE_JOINABLE, +_name)); + + cfg.ppu_id = static_cast(*_tid); + cfg.queue_id = *queue_id; + } return CELL_OK; } -error_code sys_config_stop() +error_code sys_config_stop(ppu_thread& ppu) { - sys_io.todo("sys_config_stop()"); + sys_io.warning("sys_config_stop()"); + + auto& cfg = g_fxo->get(); + + std::lock_guard lock(cfg.mtx); + + if (cfg.init_ctr && cfg.init_ctr-- == 1) + { + ensure(CELL_OK == sys_event_queue_destroy(ppu, cfg.queue_id, SYS_EVENT_QUEUE_DESTROY_FORCE)); + ensure(CELL_OK == sys_ppu_thread_join(ppu, cfg.ppu_id, +vm::var{})); + } + else + { + // TODO: Unknown error + } + return CELL_OK; } @@ -114,4 +179,6 @@ DECLARE(ppu_module_manager::sys_io)("sys_io", []() REG_FUNC(sys_io, sys_config_register_service); REG_FUNC(sys_io, sys_config_unregister_io_error_handler); REG_FUNC(sys_io, sys_config_unregister_service); + + REG_HIDDEN_FUNC(config_event_entry); }); diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.cpp b/rpcs3/Emu/Cell/lv2/sys_hid.cpp index 0f297eaafbbd..e5b5a310aa13 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_hid.cpp @@ -12,7 +12,7 @@ LOG_CHANNEL(sys_hid); -error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handle) +error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr handle) { sys_hid.todo("sys_hid_manager_open(device_type=0x%llx, port_no=0x%llx, handle=*0x%llx)", device_type, port_no, handle); @@ -34,7 +34,7 @@ error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handl if (device_type == 1) { - cellPadInit(7); + cellPadInit(ppu, 7); cellPadSetPortSetting(::narrow(port_no) /* 0 */, CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON | CELL_PAD_SETTING_SENSOR_ON); } diff --git a/rpcs3/Emu/Cell/lv2/sys_hid.h b/rpcs3/Emu/Cell/lv2/sys_hid.h index c1c6754a94cd..66acb78006b7 100644 --- a/rpcs3/Emu/Cell/lv2/sys_hid.h +++ b/rpcs3/Emu/Cell/lv2/sys_hid.h @@ -34,7 +34,7 @@ struct sys_hid_manager_514_pkg_d // SysCalls -error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr handle); +error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr handle); error_code sys_hid_manager_ioctl(u32 hid_handle, u32 pkg_id, vm::ptr buf, u64 buf_size); error_code sys_hid_manager_add_hot_key_observer(u32 event_queue, vm::ptr unk); error_code sys_hid_manager_check_focus(); diff --git a/rpcs3/Emu/Io/PadHandler.cpp b/rpcs3/Emu/Io/PadHandler.cpp index 4f98110a8720..40eb6dc0005b 100644 --- a/rpcs3/Emu/Io/PadHandler.cpp +++ b/rpcs3/Emu/Io/PadHandler.cpp @@ -7,6 +7,8 @@ cfg_input g_cfg_input; +extern void pad_state_notify_state_change(u32 index, u32 state); + PadHandlerBase::PadHandlerBase(pad_handler type) : m_type(type) { } @@ -701,8 +703,10 @@ void PadHandlerBase::process() if (!last_connection_status[i]) { input_log.success("%s device %d connected", m_type, i); - pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_state_notify_state_change(i, CELL_PAD_STATUS_CONNECTED); + last_connection_status[i] = true; connected_devices++; } @@ -723,8 +727,10 @@ void PadHandlerBase::process() if (!last_connection_status[i]) { input_log.success("%s device %d connected by force", m_type, i); - pad->m_port_status |= CELL_PAD_STATUS_CONNECTED; - pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad->m_port_status |= CELL_PAD_STATUS_CONNECTED + CELL_PAD_STATUS_ASSIGN_CHANGES; + pad_state_notify_state_change(i, CELL_PAD_STATUS_CONNECTED); + last_connection_status[i] = true; connected_devices++; } @@ -734,8 +740,12 @@ void PadHandlerBase::process() if (last_connection_status[i]) { input_log.error("%s device %d disconnected", m_type, i); + pad->m_port_status &= ~CELL_PAD_STATUS_CONNECTED; pad->m_port_status |= CELL_PAD_STATUS_ASSIGN_CHANGES; + + pad_state_notify_state_change(i, CELL_PAD_STATUS_DISCONNECTED); + last_connection_status[i] = false; connected_devices--; }