LV2/cellPad: Implement priority-based connection updates

This commit is contained in:
Eladash 2023-08-09 11:11:59 +03:00 committed by Elad Ashkenazi
parent a2416bf7f5
commit 4bbe885f35
8 changed files with 226 additions and 68 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<pad_info>();
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<s32, CELL_MAX_PADS> 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<CellPadData> 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<CellPadPeriphData> 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<CellPadData> 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<CellPadActParam> 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<CellPadInfo> 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<CellPadInfo> 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<CellPadInfo> info)
}
}
info->now_connect = now_connect;
return CELL_OK;
}
@ -658,9 +721,10 @@ error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> 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<CellPadInfo2> 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<CellPadCapabilityInfo>
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);

View File

@ -192,6 +192,7 @@ struct pad_info
{
atomic_t<u32> max_connect = 0;
std::array<u32, CELL_PAD_MAX_PORT_NUM> port_setting{ 0 };
std::array<u32, CELL_PAD_MAX_PORT_NUM> reported_statuses{};
SAVESTATE_INIT_POS(11);
@ -203,8 +204,15 @@ struct pad_info
{
return std::min<u32>(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<CellPadData> 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);

View File

@ -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<libio_sys_config>();
// Ensure awake
ppu.check_state();
while (!sys_event_queue_receive(ppu, cfg.queue_id, vm::null, 0))
{
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 send_sys_io_connect_event(u32 index, u32 state)
{
auto& cfg = g_fxo->get<libio_sys_config>();
std::lock_guard lock(cfg.mtx);
if (cfg.init_ctr)
{
if (auto port = idm::get<lv2_obj, lv2_event_queue>(cfg.queue_id))
{
port->send(0, 1, index, state);
}
}
}
error_code sys_config_start(ppu_thread& ppu)
{
sys_io.warning("sys_config_start()");
auto& cfg = g_fxo->get<libio_sys_config>();
std::lock_guard lock(cfg.mtx);
if (cfg.init_ctr++ == 0)
{
// Belongs to "_cfg_evt_hndlr" thread (8k stack)
cfg.stack_addr = ensure(vm::alloc(0x2000, vm::stack, 4096));
// Run thread
vm::var<u64> _tid;
vm::var<u32> queue_id;
vm::var<char[]> _name = vm::make_str("_cfg_evt_hndlr");
vm::var<sys_event_queue_attribute_t> 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<ppu_function_manager>().func_addr(FIND_FUNC(config_event_entry)), 0, 512, 0x2000, SYS_PPU_THREAD_CREATE_JOINABLE, +_name));
cfg.ppu_id = static_cast<u32>(*_tid);
cfg.queue_id = *queue_id;
}
}
extern void libio_sys_config_end()
{
auto& cfg = g_fxo->get<libio_sys_config>();
std::lock_guard lock(cfg.mtx);
if (cfg.init_ctr-- == 1)
{
ensure(vm::dealloc(std::exchange(cfg.stack_addr, 0), vm::stack));
}
}
error_code sys_config_start()
{
sys_io.todo("sys_config_start()");
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<libio_sys_config>();
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<u64>{}));
}
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);
});

View File

@ -12,7 +12,7 @@
LOG_CHANNEL(sys_hid);
error_code sys_hid_manager_open(u64 device_type, u64 port_no, vm::ptr<u32> handle)
error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr<u32> 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<u32> handl
if (device_type == 1)
{
cellPadInit(7);
cellPadInit(ppu, 7);
cellPadSetPortSetting(::narrow<u32>(port_no) /* 0 */, CELL_PAD_SETTING_LDD | CELL_PAD_SETTING_PRESS_ON | CELL_PAD_SETTING_SENSOR_ON);
}

View File

@ -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<u32> handle);
error_code sys_hid_manager_open(ppu_thread& ppu, u64 device_type, u64 port_no, vm::ptr<u32> handle);
error_code sys_hid_manager_ioctl(u32 hid_handle, u32 pkg_id, vm::ptr<void> buf, u64 buf_size);
error_code sys_hid_manager_add_hot_key_observer(u32 event_queue, vm::ptr<u32> unk);
error_code sys_hid_manager_check_focus();

View File

@ -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--;
}