rpcsx/rpcs3/Emu/Cell/Modules/cellPad.cpp

1340 lines
42 KiB
C++
Raw Normal View History

2020-12-05 13:08:24 +01:00
#include "stdafx.h"
2016-03-21 20:43:03 +01:00
#include "Emu/IdManager.h"
#include "Emu/system_config.h"
2016-03-21 20:43:03 +01:00
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_process.h"
#include "Emu/Cell/lv2/sys_sync.h"
2020-02-22 20:42:49 +01:00
#include "Emu/Io/pad_types.h"
#include "Emu/RSX/Overlays/overlay_debug_overlay.h"
#include "Input/pad_thread.h"
#include "Input/product_info.h"
2014-09-03 18:33:30 +02:00
#include "cellPad.h"
error_code sys_config_start(ppu_thread& ppu);
error_code sys_config_stop(ppu_thread& ppu);
extern bool is_input_allowed();
LOG_CHANNEL(cellPad);
2018-06-07 15:26:07 +02:00
template<>
void fmt_class_string<CellPadError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(CELL_PAD_ERROR_FATAL);
STR_CASE(CELL_PAD_ERROR_INVALID_PARAMETER);
STR_CASE(CELL_PAD_ERROR_ALREADY_INITIALIZED);
STR_CASE(CELL_PAD_ERROR_UNINITIALIZED);
STR_CASE(CELL_PAD_ERROR_RESOURCE_ALLOCATION_FAILED);
STR_CASE(CELL_PAD_ERROR_DATA_READ_FAILED);
STR_CASE(CELL_PAD_ERROR_NO_DEVICE);
STR_CASE(CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD);
STR_CASE(CELL_PAD_ERROR_TOO_MANY_DEVICES);
STR_CASE(CELL_PAD_ERROR_EBUSY);
}
return unknown;
});
}
2019-09-29 09:58:21 +02:00
template<>
void fmt_class_string<CellPadFilterError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](auto error)
{
switch (error)
{
STR_CASE(CELL_PADFILTER_ERROR_INVALID_PARAMETER);
}
return unknown;
});
}
extern void sys_io_serialize(utils::serial& ar);
pad_info::pad_info(utils::serial& ar)
: max_connect(ar)
, port_setting(ar)
, reported_info(ar)
{
2024-01-01 19:23:06 +01:00
//reported_info = {};
sys_io_serialize(ar);
}
void pad_info::save(utils::serial& ar)
{
USING_SERIALIZATION_VERSION(sys_io);
ar(max_connect, port_setting, reported_info);
sys_io_serialize(ar);
}
void show_debug_overlay(const CellPadData& data, const Pad& pad, const pad_info& config)
{
const u32 setting = config.port_setting[pad.m_player_id];
const u16 d1 = data.button[CELL_PAD_BTN_OFFSET_DIGITAL1];
const u16 d2 = data.button[CELL_PAD_BTN_OFFSET_DIGITAL2];
std::string text = fmt::format(
"> Name: Raw Value Pressure\n"
">\n"
"> Len: %13d\n"
"> Digital: %5s %5s\n"
"> Press: %5s %5s\n"
"> Sensor: %5s %5s\n"
">\n"
"> Digital 1: 0x%04x 0x%04x\n"
"> Digital 2: 0x%04x 0x%04x\n"
">\n"
"> D-Pad Up: %5d %5d %5d\n"
"> D-Pad Down: %5d %5d %5d\n"
"> D-Pad Left: %5d %5d %5d\n"
"> D-Pad Right: %5d %5d %5d\n"
"> Cross: %5d %5d %5d\n"
"> Square: %5d %5d %5d\n"
"> Circle: %5d %5d %5d\n"
"> Triangle: %5d %5d %5d\n"
"> Start: %5d %5d\n"
"> Select: %5d %5d\n"
"> PS: %5d %5d\n"
"> L1: %5d %5d %5d\n"
"> L2: %5d %5d %5d\n"
"> L3: %5d %5d\n"
"> R1: %5d %5d %5d\n"
"> R2: %5d %5d %5d\n"
"> R3: %5d %5d\n"
"> LS X: %5d %5d\n"
"> LS Y: %5d %5d\n"
"> RS X: %5d %5d\n"
"> RS Y: %5d %5d\n"
">\n"
"> Sensor X: %5d %5d\n"
"> Sensor Y: %5d %5d\n"
"> Sensor Z: %5d %5d\n"
"> Sensor G: %5d %5d\n"
">\n"
"> PID: 0x%04x\n"
"> VID: 0x%04x\n"
"> Device Type: 0x%08x\n"
"> Class Type: 0x%08x\n"
,
data.len,
"on", data.len >= CELL_PAD_LEN_CHANGE_DEFAULT ? "on" : "off",
(setting & CELL_PAD_SETTING_PRESS_ON) ? "on" : "off", data.len >= CELL_PAD_LEN_CHANGE_PRESS_ON ? "on" : "off",
(setting & CELL_PAD_SETTING_SENSOR_ON) ? "on" : "off", data.len >= CELL_PAD_LEN_CHANGE_SENSOR_ON ? "on" : "off",
pad.m_digital_1, d1,
pad.m_digital_2, d2,
pad.m_press_up, !!(d1 & CELL_PAD_CTRL_UP), data.button[CELL_PAD_BTN_OFFSET_PRESS_UP],
pad.m_press_down, !!(d1 & CELL_PAD_CTRL_DOWN), data.button[CELL_PAD_BTN_OFFSET_PRESS_DOWN],
pad.m_press_left, !!(d1 & CELL_PAD_CTRL_LEFT), data.button[CELL_PAD_BTN_OFFSET_PRESS_LEFT],
pad.m_press_right, !!(d1 & CELL_PAD_CTRL_RIGHT), data.button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT],
pad.m_press_cross, !!(d2 & CELL_PAD_CTRL_CROSS), data.button[CELL_PAD_BTN_OFFSET_PRESS_CROSS],
pad.m_press_square, !!(d2 & CELL_PAD_CTRL_SQUARE), data.button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE],
pad.m_press_circle, !!(d2 & CELL_PAD_CTRL_CIRCLE), data.button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE],
pad.m_press_triangle, !!(d2 & CELL_PAD_CTRL_TRIANGLE), data.button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE],
!!(pad.m_digital_1 & CELL_PAD_CTRL_START), !!(d1 & CELL_PAD_CTRL_START),
!!(pad.m_digital_1 & CELL_PAD_CTRL_SELECT), !!(d1 & CELL_PAD_CTRL_SELECT),
!!(pad.m_digital_1 & CELL_PAD_CTRL_PS), !!(d1 & CELL_PAD_CTRL_PS),
pad.m_press_L1, !!(d2 & CELL_PAD_CTRL_L1), data.button[CELL_PAD_BTN_OFFSET_PRESS_L1],
pad.m_press_L2, !!(d2 & CELL_PAD_CTRL_L2), data.button[CELL_PAD_BTN_OFFSET_PRESS_L2],
!!(pad.m_digital_1 & CELL_PAD_CTRL_L3), !!(d1 & CELL_PAD_CTRL_L3),
pad.m_press_R1, !!(d2 & CELL_PAD_CTRL_R1), data.button[CELL_PAD_BTN_OFFSET_PRESS_R1],
pad.m_press_R2, !!(d2 & CELL_PAD_CTRL_R2), data.button[CELL_PAD_BTN_OFFSET_PRESS_R2],
!!(pad.m_digital_1 & CELL_PAD_CTRL_R3), !!(d1 & CELL_PAD_CTRL_R3),
pad.m_analog_left_x, data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X],
pad.m_analog_left_y, data.button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y],
pad.m_analog_right_x, data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X],
pad.m_analog_right_y, data.button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y],
pad.m_sensor_x, data.button[CELL_PAD_BTN_OFFSET_SENSOR_X],
pad.m_sensor_y, data.button[CELL_PAD_BTN_OFFSET_SENSOR_Y],
pad.m_sensor_z, data.button[CELL_PAD_BTN_OFFSET_SENSOR_Z],
pad.m_sensor_g, data.button[CELL_PAD_BTN_OFFSET_SENSOR_G],
pad.m_product_id,
pad.m_vendor_id,
pad.m_device_type,
pad.m_class_type
);
rsx::overlays::set_debug_overlay_text(std::move(text));
}
extern void send_sys_io_connect_event(usz index, u32 state);
bool cellPad_NotifyStateChange(usz index, u64 /*state*/, bool locked, bool is_blocking = true)
{
auto info = g_fxo->try_get<pad_info>();
if (!info)
{
return true;
}
2024-01-01 08:18:55 +01:00
std::unique_lock lock(pad::g_pad_mutex, std::defer_lock);
if (locked)
{
if (is_blocking)
{
lock.lock();
}
else
{
if (!lock.try_lock())
{
return false;
}
}
2024-01-01 08:18:55 +01:00
}
if (index >= info->get_max_connect())
{
return true;
}
2023-08-19 12:29:46 +02:00
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[index];
2023-08-19 12:29:46 +02:00
if (pad->is_fake_pad)
{
return true;
}
pad_data_internal& reported_info = info->reported_info[index];
const u32 old_status = reported_info.port_status;
2023-08-19 12:29:46 +02:00
// Ignore sent status for now, use the latest instead
// NOTE 1: The state's CONNECTED bit should currently be identical to the current
// m_port_status CONNECTED bit when called from our pad handlers.
// NOTE 2: Make sure to propagate all other status bits to the reported status.
2024-03-26 12:44:25 +01:00
const u32 new_status = pad->m_port_status;
if (~(old_status ^ new_status) & CELL_PAD_STATUS_CONNECTED)
{
// old and new have the same connection status
return true;
}
reported_info.port_status = new_status | CELL_PAD_STATUS_ASSIGN_CHANGES;
reported_info.device_capability = pad->m_device_capability;
reported_info.device_type = pad->m_device_type;
reported_info.pclass_type = pad->m_class_type;
reported_info.pclass_profile = pad->m_class_profile;
2023-08-19 12:29:46 +02:00
if (pad->m_vendor_id == 0 || pad->m_product_id == 0)
2023-08-19 12:29:46 +02:00
{
// Fallback to defaults
const std::vector<input::product_info> input_products = input::get_products_by_class(pad->m_class_type);
const input::product_info& product = ::at32(input_products, 0);
reported_info.vendor_id = product.vendor_id;
reported_info.product_id = product.product_id;
2023-08-19 12:29:46 +02:00
}
else
{
reported_info.vendor_id = pad->m_vendor_id;
reported_info.product_id = pad->m_product_id;
2023-08-19 12:29:46 +02:00
}
return true;
}
extern void pad_state_notify_state_change(usz index, u32 state)
{
send_sys_io_connect_event(index, state);
}
error_code cellPadInit(ppu_thread& ppu, u32 max_connect)
{
cellPad.warning("cellPadInit(max_connect=%d)", max_connect);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (config.max_connect)
2016-03-21 20:43:03 +01:00
return CELL_PAD_ERROR_ALREADY_INITIALIZED;
2018-06-07 15:26:07 +02:00
if (max_connect == 0 || max_connect > CELL_MAX_PADS)
return CELL_PAD_ERROR_INVALID_PARAMETER;
sys_config_start(ppu);
config.max_connect = max_connect;
2021-03-02 12:59:19 +01:00
config.port_setting.fill(CELL_PAD_SETTING_PRESS_OFF | CELL_PAD_SETTING_SENSOR_OFF);
2023-08-19 12:29:46 +02:00
config.reported_info = {};
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
2024-03-26 12:44:25 +01:00
for (usz i = 0; i < config.get_max_connect(); ++i)
{
if (!pads[i]->is_fake_pad && (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(ppu_thread& ppu)
{
cellPad.notice("cellPadEnd()");
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (!config.max_connect.exchange(0))
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
sys_config_stop(ppu);
return CELL_OK;
}
2021-02-12 02:31:49 +01:00
void clear_pad_buffer(const std::shared_ptr<Pad>& pad)
{
if (!pad)
return;
// Set 'm_buffer_cleared' to force a resend of everything
// might as well also reset everything in our pad 'buffer' to nothing as well
pad->m_buffer_cleared = true;
pad->m_analog_left_x = pad->m_analog_left_y = pad->m_analog_right_x = pad->m_analog_right_y = 128;
pad->m_digital_1 = pad->m_digital_2 = 0;
pad->m_press_right = pad->m_press_left = pad->m_press_up = pad->m_press_down = 0;
pad->m_press_triangle = pad->m_press_circle = pad->m_press_cross = pad->m_press_square = 0;
pad->m_press_L1 = pad->m_press_L2 = pad->m_press_R1 = pad->m_press_R2 = 0;
pad->m_sensor_x = DEFAULT_MOTION_X;
pad->m_sensor_y = DEFAULT_MOTION_Y;
pad->m_sensor_z = DEFAULT_MOTION_Z;
pad->m_sensor_g = DEFAULT_MOTION_G;
}
2018-06-07 15:26:07 +02:00
error_code cellPadClearBuf(u32 port_no)
{
cellPad.trace("cellPadClearBuf(port_no=%d)", port_no);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
2014-05-07 06:09:10 +02:00
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
2014-05-07 06:09:10 +02:00
clear_pad_buffer(pad);
return CELL_OK;
}
void pad_get_data(u32 port_no, CellPadData* data, bool get_periph_data = false)
2021-05-28 11:35:03 +02:00
{
auto& config = g_fxo->get<pad_info>();
const auto handler = pad::get_current_handler();
const auto& pad = handler->GetPads()[port_no];
const PadInfo& rinfo = handler->GetInfo();
if (rinfo.system_info & CELL_PAD_INFO_INTERCEPTED)
{
data->len = CELL_PAD_LEN_NO_CHANGE;
2021-05-28 11:35:03 +02:00
return;
}
2019-09-20 08:11:30 +02:00
const u32 setting = config.port_setting[port_no];
bool btnChanged = false;
if (rinfo.ignore_input || !is_input_allowed())
{
// Needed for Hotline Miami and Ninja Gaiden Sigma after dialogs were closed and buttons are still pressed.
// Gran Turismo 6 would keep registering the Start button during OSK Dialogs if this wasn't cleared and if we'd return with len as CELL_PAD_LEN_NO_CHANGE.
clear_pad_buffer(pad);
}
else if (pad->ldd)
2019-04-28 23:35:11 +02:00
{
if (setting & CELL_PAD_SETTING_SENSOR_ON)
data->len = CELL_PAD_LEN_CHANGE_SENSOR_ON;
else
data->len = (setting & CELL_PAD_SETTING_PRESS_ON) ? CELL_PAD_LEN_CHANGE_PRESS_ON : CELL_PAD_LEN_CHANGE_DEFAULT;
2024-05-18 08:58:25 +02:00
std::memcpy(data->button, pad->ldd_data.button, data->len * sizeof(u16));
2021-05-28 11:35:03 +02:00
return;
2019-04-28 23:35:11 +02:00
}
else
{
2021-04-19 23:09:25 +02:00
const u16 d1Initial = pad->m_digital_1;
const u16 d2Initial = pad->m_digital_2;
// Check if this pad is configured as a skateboard which ignores sticks and pressure button values.
// Curiously it maps infrared on the press value of the face buttons for some reason.
const bool use_piggyback = pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD;
const auto set_value = [&btnChanged, use_piggyback, &pad](u16& value, u16 new_value, bool force_processing = false, u16 old_max_value = 255, u16 new_max_value = 255)
2023-02-10 02:41:32 +01:00
{
if (use_piggyback)
{
if (!force_processing)
return;
// Some piggyback values need to be scaled down on emulated devices
if (old_max_value != new_max_value)
{
if (pad->m_class_type == CELL_PAD_PCLASS_TYPE_SKATEBOARD && pad->m_pad_handler != pad_handler::skateboard)
{
new_value = static_cast<u16>(new_max_value * std::clamp(new_value / static_cast<f32>(old_max_value), 0.0f, 1.0f));
}
}
}
2023-02-10 02:41:32 +01:00
if (value != new_value)
{
btnChanged = true;
value = new_value;
}
};
for (Button& button : pad->m_buttons)
{
// here we check btns, and set pad accordingly,
// if something changed, set btnChanged
switch (button.m_offset)
{
case CELL_PAD_BTN_OFFSET_DIGITAL1:
{
if (button.m_pressed)
pad->m_digital_1 |= button.m_outKeyCode;
else
pad->m_digital_1 &= ~button.m_outKeyCode;
switch (button.m_outKeyCode)
{
2023-02-10 02:41:32 +01:00
case CELL_PAD_CTRL_LEFT: set_value(pad->m_press_left, button.m_value); break;
case CELL_PAD_CTRL_DOWN: set_value(pad->m_press_down, button.m_value); break;
case CELL_PAD_CTRL_RIGHT: set_value(pad->m_press_right, button.m_value); break;
case CELL_PAD_CTRL_UP: set_value(pad->m_press_up, button.m_value); break;
// These arent pressure btns
case CELL_PAD_CTRL_R3:
case CELL_PAD_CTRL_L3:
case CELL_PAD_CTRL_START:
case CELL_PAD_CTRL_SELECT:
default: break;
}
break;
}
case CELL_PAD_BTN_OFFSET_DIGITAL2:
{
if (button.m_pressed)
pad->m_digital_2 |= button.m_outKeyCode;
else
pad->m_digital_2 &= ~button.m_outKeyCode;
switch (button.m_outKeyCode)
{
2023-02-10 02:41:32 +01:00
case CELL_PAD_CTRL_SQUARE: set_value(pad->m_press_square, button.m_value); break;
case CELL_PAD_CTRL_CROSS: set_value(pad->m_press_cross, button.m_value); break;
case CELL_PAD_CTRL_CIRCLE: set_value(pad->m_press_circle, button.m_value); break;
case CELL_PAD_CTRL_TRIANGLE: set_value(pad->m_press_triangle, button.m_value); break;
case CELL_PAD_CTRL_R1: set_value(pad->m_press_R1, button.m_value); break;
case CELL_PAD_CTRL_L1: set_value(pad->m_press_L1, button.m_value); break;
case CELL_PAD_CTRL_R2: set_value(pad->m_press_R2, button.m_value); break;
case CELL_PAD_CTRL_L2: set_value(pad->m_press_L2, button.m_value); break;
default: break;
}
break;
}
case CELL_PAD_BTN_OFFSET_PRESS_PIGGYBACK:
{
switch (button.m_outKeyCode)
{
case CELL_PAD_CTRL_PRESS_RIGHT: set_value(pad->m_press_right, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_LEFT: set_value(pad->m_press_left, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_UP: set_value(pad->m_press_up, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_DOWN: set_value(pad->m_press_down, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_TRIANGLE: set_value(pad->m_press_triangle, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_CIRCLE: set_value(pad->m_press_circle, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_CROSS: set_value(pad->m_press_cross, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_SQUARE: set_value(pad->m_press_square, button.m_value, true, 255, 63); break; // Infrared on RIDE Skateboard
case CELL_PAD_CTRL_PRESS_L1: set_value(pad->m_press_L1, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_R1: set_value(pad->m_press_R1, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_L2: set_value(pad->m_press_L2, button.m_value, true); break;
case CELL_PAD_CTRL_PRESS_R2: set_value(pad->m_press_R2, button.m_value, true); break;
default: break;
}
break;
}
default:
break;
}
}
2014-05-08 01:08:16 +02:00
for (const AnalogStick& stick : pad->m_sticks)
{
switch (stick.m_offset)
{
2023-02-10 02:41:32 +01:00
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X: set_value(pad->m_analog_left_x, stick.m_value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y: set_value(pad->m_analog_left_y, stick.m_value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X: set_value(pad->m_analog_right_x, stick.m_value); break;
case CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y: set_value(pad->m_analog_right_y, stick.m_value); break;
default: break;
}
}
if (setting & CELL_PAD_SETTING_SENSOR_ON)
{
for (const AnalogSensor& sensor : pad->m_sensors)
{
switch (sensor.m_offset)
{
case CELL_PAD_BTN_OFFSET_SENSOR_X: set_value(pad->m_sensor_x, sensor.m_value, true); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Y: set_value(pad->m_sensor_y, sensor.m_value, true); break;
case CELL_PAD_BTN_OFFSET_SENSOR_Z: set_value(pad->m_sensor_z, sensor.m_value, true); break;
case CELL_PAD_BTN_OFFSET_SENSOR_G: set_value(pad->m_sensor_g, sensor.m_value, true); break;
default: break;
}
}
}
if (d1Initial != pad->m_digital_1 || d2Initial != pad->m_digital_2)
{
btnChanged = true;
}
}
if (setting & CELL_PAD_SETTING_SENSOR_ON)
{
// report back new data every ~10 ms even if the input doesn't change
// this is observed behaviour when using a Dualshock 3 controller
static std::array<steady_clock::time_point, CELL_PAD_MAX_PORT_NUM> last_update = { };
const auto now = steady_clock::now();
if (btnChanged || pad->m_buffer_cleared || now - last_update[port_no] >= 10ms)
{
data->len = CELL_PAD_LEN_CHANGE_SENSOR_ON;
last_update[port_no] = now;
}
else
{
data->len = CELL_PAD_LEN_NO_CHANGE;
}
}
else if (btnChanged || pad->m_buffer_cleared)
{
// only give back valid data if a controller state changed
data->len = (setting & CELL_PAD_SETTING_PRESS_ON) ? CELL_PAD_LEN_CHANGE_PRESS_ON : CELL_PAD_LEN_CHANGE_DEFAULT;
}
else
{
// report no state changes
data->len = CELL_PAD_LEN_NO_CHANGE;
}
pad->m_buffer_cleared = false;
// only update parts of the output struct depending on the controller setting
if (data->len > CELL_PAD_LEN_NO_CHANGE)
{
data->button[0] = 0x0; // always 0
// bits 15-8 reserved, 7-4 = 0x7, 3-0: data->len/2;
data->button[1] = (0x7 << 4) | std::min(data->len / 2, 15);
data->button[CELL_PAD_BTN_OFFSET_DIGITAL1] = pad->m_digital_1;
data->button[CELL_PAD_BTN_OFFSET_DIGITAL2] = pad->m_digital_2;
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_X] = pad->m_analog_right_x;
data->button[CELL_PAD_BTN_OFFSET_ANALOG_RIGHT_Y] = pad->m_analog_right_y;
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_X] = pad->m_analog_left_x;
data->button[CELL_PAD_BTN_OFFSET_ANALOG_LEFT_Y] = pad->m_analog_left_y;
if (setting & CELL_PAD_SETTING_PRESS_ON)
{
data->button[CELL_PAD_BTN_OFFSET_PRESS_RIGHT] = pad->m_press_right;
data->button[CELL_PAD_BTN_OFFSET_PRESS_LEFT] = pad->m_press_left;
data->button[CELL_PAD_BTN_OFFSET_PRESS_UP] = pad->m_press_up;
data->button[CELL_PAD_BTN_OFFSET_PRESS_DOWN] = pad->m_press_down;
data->button[CELL_PAD_BTN_OFFSET_PRESS_TRIANGLE] = pad->m_press_triangle;
data->button[CELL_PAD_BTN_OFFSET_PRESS_CIRCLE] = pad->m_press_circle;
data->button[CELL_PAD_BTN_OFFSET_PRESS_CROSS] = pad->m_press_cross;
data->button[CELL_PAD_BTN_OFFSET_PRESS_SQUARE] = pad->m_press_square;
data->button[CELL_PAD_BTN_OFFSET_PRESS_L1] = pad->m_press_L1;
data->button[CELL_PAD_BTN_OFFSET_PRESS_R1] = pad->m_press_R1;
data->button[CELL_PAD_BTN_OFFSET_PRESS_L2] = pad->m_press_L2;
data->button[CELL_PAD_BTN_OFFSET_PRESS_R2] = pad->m_press_R2;
}
else
{
// Clear area if setting is not used
constexpr u32 area_lengh = (CELL_PAD_LEN_CHANGE_PRESS_ON - CELL_PAD_LEN_CHANGE_DEFAULT) * sizeof(u16);
std::memset(&data->button[CELL_PAD_LEN_CHANGE_DEFAULT], 0, area_lengh);
}
if (data->len == CELL_PAD_LEN_CHANGE_SENSOR_ON)
{
data->button[CELL_PAD_BTN_OFFSET_SENSOR_X] = pad->m_sensor_x;
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Y] = pad->m_sensor_y;
data->button[CELL_PAD_BTN_OFFSET_SENSOR_Z] = pad->m_sensor_z;
data->button[CELL_PAD_BTN_OFFSET_SENSOR_G] = pad->m_sensor_g;
}
}
if (!get_periph_data || data->len <= CELL_PAD_LEN_CHANGE_SENSOR_ON)
{
return;
}
const auto get_pressure_value = [setting](u16 val, u16 min, u16 max) -> u16
{
if (setting & CELL_PAD_SETTING_PRESS_ON)
{
return std::clamp(val, min, max);
}
if (val > 0)
{
return max;
}
return 0;
};
// TODO: support for 'unique' controllers, which goes in offsets 24+ in padData (CELL_PAD_PCLASS_BTN_OFFSET)
// TODO: update data->len accordingly
switch (pad->m_class_profile)
{
default:
case CELL_PAD_PCLASS_TYPE_STANDARD:
case CELL_PAD_PCLASS_TYPE_NAVIGATION:
case CELL_PAD_PCLASS_TYPE_SKATEBOARD:
{
break;
}
case CELL_PAD_PCLASS_TYPE_GUITAR:
{
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_1] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_2] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_3] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_4] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_5] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_STRUM_UP] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_STRUM_DOWN] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_WHAMMYBAR] = 0x80; // 0x80 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H1] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H2] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H3] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H4] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_FRET_H5] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_5WAY_EFFECT] = 0x0019; // One of 5 values: 0x0019, 0x004C, 0x007F (or 0x0096), 0x00B2, 0x00E5 (or 0x00E2)
data->button[CELL_PAD_PCLASS_BTN_OFFSET_GUITAR_TILT_SENS] = get_pressure_value(0, 0x0, 0xFF);
break;
}
case CELL_PAD_PCLASS_TYPE_DRUM:
{
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_SNARE] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM2] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_TOM_FLOOR] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_KICK] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_HiHAT] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_CRASH] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_CYM_RIDE] = get_pressure_value(0, 0x0, 0xFF);
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DRUM_KICK2] = get_pressure_value(0, 0x0, 0xFF);
break;
}
case CELL_PAD_PCLASS_TYPE_DJ:
{
// First deck
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_ATTACK] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_CROSSFADER] = 0; // 0x0 - 0x3FF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_MIXER_DSP_DIAL] = 0; // 0x0 - 0x3FF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM1] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM2] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_STREAM3] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK1_PLATTER] = 0x80; // 0x0 - 0xFF (neutral: 0x80)
// Second deck
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM1] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM2] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_STREAM3] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DJ_DECK2_PLATTER] = 0x80; // 0x0 - 0xFF (neutral: 0x80)
break;
}
case CELL_PAD_PCLASS_TYPE_DANCEMAT:
{
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_CIRCLE] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_CROSS] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_TRIANGLE] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_SQUARE] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_RIGHT] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_LEFT] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_UP] = 0; // 0x0 or 0xFF
data->button[CELL_PAD_PCLASS_BTN_OFFSET_DANCEMAT_DOWN] = 0; // 0x0 or 0xFF
break;
}
}
}
error_code cellPadGetData(u32 port_no, vm::ptr<CellPadData> data)
{
cellPad.trace("cellPadGetData(port_no=%d, data=*0x%x)", port_no, data);
std::lock_guard lock(pad::g_pad_mutex);
auto& config = g_fxo->get<pad_info>();
if (!config.max_connect)
return CELL_PAD_ERROR_UNINITIALIZED;
if (port_no >= CELL_MAX_PADS || !data)
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (port_no >= config.get_max_connect())
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !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());
if (g_cfg.io.debug_overlay && !g_cfg.video.overlay && port_no == 0)
{
show_debug_overlay(*data, *pad, config);
}
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadPeriphGetInfo(vm::ptr<CellPadPeriphInfo> info)
2017-05-26 23:02:36 +02:00
{
cellPad.trace("cellPadPeriphGetInfo(info=*0x%x)", info);
2017-05-26 23:02:36 +02:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2017-05-26 23:02:36 +02:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2017-05-26 23:02:36 +02:00
return CELL_PAD_ERROR_UNINITIALIZED;
if (!info)
return CELL_PAD_ERROR_INVALID_PARAMETER;
const auto handler = pad::get_current_handler();
2017-05-26 23:02:36 +02:00
const PadInfo& rinfo = handler->GetInfo();
std::memset(info.get_ptr(), 0, sizeof(CellPadPeriphInfo));
2021-03-02 12:59:19 +01:00
info->max_connect = config.max_connect;
2017-05-26 23:02:36 +02:00
info->system_info = rinfo.system_info;
2023-08-19 12:29:46 +02:00
u32 now_connect = 0;
2024-03-26 12:44:25 +01:00
for (u32 i = 0; i < config.get_max_connect(); ++i)
2017-05-26 23:02:36 +02:00
{
2023-08-25 21:34:53 +02:00
pad_data_internal& reported_info = config.reported_info[i];
info->port_status[i] = reported_info.port_status;
2021-03-02 12:59:19 +01:00
info->port_setting[i] = config.port_setting[i];
2023-08-19 12:29:46 +02:00
2023-08-25 21:34:53 +02:00
reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED)
2023-08-19 12:29:46 +02:00
{
continue;
}
2023-08-25 21:34:53 +02:00
info->device_capability[i] = reported_info.device_capability;
info->device_type[i] = reported_info.device_type;
info->pclass_type[i] = reported_info.pclass_type;
info->pclass_profile[i] = reported_info.pclass_profile;
2023-08-19 12:29:46 +02:00
now_connect++;
2017-05-26 23:02:36 +02:00
}
2023-08-19 12:29:46 +02:00
info->now_connect = now_connect;
2017-05-26 23:02:36 +02:00
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadPeriphGetData(u32 port_no, vm::ptr<CellPadPeriphData> data)
2017-05-26 23:02:36 +02:00
{
cellPad.trace("cellPadPeriphGetData(port_no=%d, data=*0x%x)", port_no, data);
2017-05-26 23:02:36 +02:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2017-05-26 23:02:36 +02:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
// port_no can only be 0-6 in this function
if (port_no >= CELL_PAD_MAX_PORT_NUM || !data)
2017-05-26 23:02:36 +02:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
2021-05-28 11:35:03 +02:00
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
2022-11-27 08:44:30 +01:00
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
2017-05-26 23:02:36 +02:00
pad_get_data(port_no, &data->cellpad_data, true);
2021-05-28 11:35:03 +02:00
2019-02-25 18:47:16 +01:00
data->pclass_type = pad->m_class_type;
2020-04-25 02:12:19 +02:00
data->pclass_profile = pad->m_class_profile;
2018-06-07 15:26:07 +02:00
2021-05-28 11:35:03 +02:00
return CELL_OK;
2017-05-26 23:02:36 +02:00
}
2018-06-07 15:26:07 +02:00
error_code cellPadGetRawData(u32 port_no, vm::ptr<CellPadData> data)
2015-08-01 03:46:42 +02:00
{
cellPad.todo("cellPadGetRawData(port_no=%d, data=*0x%x)", port_no, data);
2018-06-07 15:26:07 +02:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2018-06-07 15:26:07 +02:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_UNINITIALIZED;
if (port_no >= CELL_MAX_PADS || !data)
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
2018-06-07 15:26:07 +02:00
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
2018-06-07 15:26:07 +02:00
// ?
return CELL_OK;
2015-08-01 03:46:42 +02:00
}
2018-06-07 15:26:07 +02:00
error_code cellPadGetDataExtra(u32 port_no, vm::ptr<u32> device_type, vm::ptr<CellPadData> data)
{
cellPad.trace("cellPadGetDataExtra(port_no=%d, device_type=*0x%x, data=*0x%x)", port_no, device_type, data);
2015-02-22 09:50:26 +01:00
// TODO: This is used just to get data from a BD/CEC remote,
// but if the port isnt a remote, device type is set to CELL_PAD_DEV_TYPE_STANDARD and just regular cellPadGetData is returned
2021-05-28 11:35:03 +02:00
if (auto err = cellPadGetData(port_no, data))
{
return err;
}
2018-06-07 15:26:07 +02:00
if (device_type) // no error is returned on NULL
{
*device_type = CELL_PAD_DEV_TYPE_STANDARD;
2018-06-07 15:26:07 +02:00
}
2021-05-28 11:35:03 +02:00
// Set BD data
data->button[24] = 0x0;
data->button[25] = 0x0;
2021-05-28 11:35:03 +02:00
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadSetActDirect(u32 port_no, vm::ptr<CellPadActParam> param)
{
cellPad.trace("cellPadSetActDirect(port_no=%d, param=*0x%x)", port_no, param);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS || !param)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
// Note: signed check unlike the usual unsigned check
2019-12-02 22:31:34 +01:00
if (static_cast<s32>(g_ps3_process_info.sdk_ver) > 0x1FFFFF)
{
// make sure reserved bits are 0
for (int i = 0; i < 6; i++)
{
if (param->reserved[i])
return CELL_PAD_ERROR_INVALID_PARAMETER;
}
}
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
2018-06-07 15:26:07 +02:00
// TODO: find out if this is checked here or later or at all
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_ACTUATOR))
return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD;
handler->SetRumble(port_no, param->motor[1], param->motor[0] > 0);
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadGetInfo(vm::ptr<CellPadInfo> info)
{
cellPad.trace("cellPadGetInfo(info=*0x%x)", info);
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
if (!info)
return CELL_PAD_ERROR_INVALID_PARAMETER;
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo));
const auto handler = pad::get_current_handler();
2016-03-21 20:43:03 +01:00
const PadInfo& rinfo = handler->GetInfo();
2021-03-02 12:59:19 +01:00
info->max_connect = config.max_connect;
2014-09-03 18:33:30 +02:00
info->system_info = rinfo.system_info;
u32 now_connect = 0;
2024-03-26 12:44:25 +01:00
for (u32 i = 0; i < config.get_max_connect(); ++i)
{
2023-08-25 21:34:53 +02:00
pad_data_internal& reported_info = config.reported_info[i];
reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES; // TODO: should ASSIGN flags be cleared here?
info->status[i] = reported_info.port_status;
2023-08-25 21:34:53 +02:00
if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED)
{
2023-08-19 12:29:46 +02:00
continue;
}
2023-08-25 21:34:53 +02:00
info->vendor_id[i] = reported_info.vendor_id;
info->product_id[i] = reported_info.product_id;
2023-08-19 12:29:46 +02:00
now_connect++;
}
info->now_connect = now_connect;
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadGetInfo2(vm::ptr<CellPadInfo2> info)
{
cellPad.trace("cellPadGetInfo2(info=*0x%x)", info);
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
if (!info)
return CELL_PAD_ERROR_INVALID_PARAMETER;
std::memset(info.get_ptr(), 0, sizeof(CellPadInfo2));
const auto handler = pad::get_current_handler();
2016-03-21 20:43:03 +01:00
const PadInfo& rinfo = handler->GetInfo();
info->max_connect = config.get_max_connect(); // Here it is forcibly clamped
2014-09-03 18:33:30 +02:00
info->system_info = rinfo.system_info;
u32 now_connect = 0;
const auto& pads = handler->GetPads();
2024-03-26 12:44:25 +01:00
for (u32 i = 0; i < config.get_max_connect(); ++i)
{
2023-08-25 21:34:53 +02:00
pad_data_internal& reported_info = config.reported_info[i];
info->port_status[i] = reported_info.port_status;
2023-08-19 12:29:46 +02:00
info->port_setting[i] = config.port_setting[i];
2023-08-25 21:34:53 +02:00
reported_info.port_status &= ~CELL_PAD_STATUS_ASSIGN_CHANGES;
if (~reported_info.port_status & CELL_PAD_STATUS_CONNECTED)
2023-08-19 12:29:46 +02:00
{
continue;
2023-08-19 12:29:46 +02:00
}
info->device_capability[i] = pads[i]->m_device_capability;
info->device_type[i] = pads[i]->m_device_type;
2023-08-19 12:29:46 +02:00
now_connect++;
}
info->now_connect = now_connect;
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadGetCapabilityInfo(u32 port_no, vm::ptr<CellPadCapabilityInfo> info)
{
cellPad.trace("cellPadGetCapabilityInfo(port_no=%d, data_addr:=0x%x)", port_no, info.addr());
2014-09-03 18:33:30 +02:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS || !info)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
2022-11-27 08:44:30 +01:00
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
2018-06-07 15:26:07 +02:00
// Should return the same as device capability mask, psl1ght has it backwards in pad->h
memset(info->info, 0, CELL_PAD_MAX_CAPABILITY_INFO * sizeof(u32));
info->info[0] = pad->m_device_capability;
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadSetPortSetting(u32 port_no, u32 port_setting)
{
cellPad.trace("cellPadSetPortSetting(port_no=%d, port_setting=0x%x)", port_no, port_setting);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
2018-06-07 15:26:07 +02:00
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
if (port_no >= CELL_PAD_MAX_PORT_NUM)
2018-06-07 15:26:07 +02:00
return CELL_OK;
2021-03-02 12:59:19 +01:00
config.port_setting[port_no] = port_setting;
// can also return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD <- Update: seems to be just internal and ignored
return CELL_OK;
}
error_code cellPadInfoPressMode(u32 port_no)
{
cellPad.trace("cellPadInfoPressMode(port_no=%d)", port_no);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
2022-11-27 08:44:30 +01:00
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE) ? 1 : 0);
}
error_code cellPadInfoSensorMode(u32 port_no)
{
cellPad.trace("cellPadInfoSensorMode(port_no=%d)", port_no);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
if (port_no >= config.get_max_connect())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_NO_DEVICE;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
if (pad->is_fake_pad || !config.is_reportedly_connected(port_no) || !(pad->m_port_status & CELL_PAD_STATUS_CONNECTED))
2022-11-27 08:44:30 +01:00
return not_an_error(CELL_PAD_ERROR_NO_DEVICE);
return not_an_error((pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE) ? 1 : 0);
2014-04-06 21:47:21 +02:00
}
2018-06-07 15:26:07 +02:00
error_code cellPadSetPressMode(u32 port_no, u32 mode)
2014-04-06 21:47:21 +02:00
{
cellPad.trace("cellPadSetPressMode(port_no=%d, mode=%d)", port_no, mode);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2016-03-21 20:43:03 +01:00
if (port_no >= CELL_PAD_MAX_PORT_NUM)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
2018-06-07 15:26:07 +02:00
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
if (port_no >= CELL_PAD_MAX_PORT_NUM)
2018-06-07 15:26:07 +02:00
return CELL_OK;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
2018-06-07 15:26:07 +02:00
// TODO: find out if this is checked here or later or at all
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_PRESS_MODE))
return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD;
if (mode)
2021-03-02 12:59:19 +01:00
config.port_setting[port_no] |= CELL_PAD_SETTING_PRESS_ON;
else
2021-03-02 12:59:19 +01:00
config.port_setting[port_no] &= ~CELL_PAD_SETTING_PRESS_ON;
2014-04-06 21:47:21 +02:00
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadSetSensorMode(u32 port_no, u32 mode)
2014-04-06 21:47:21 +02:00
{
cellPad.trace("cellPadSetSensorMode(port_no=%d, mode=%d)", port_no, mode);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2016-03-21 20:43:03 +01:00
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
2016-03-21 20:43:03 +01:00
2018-06-07 15:26:07 +02:00
if (port_no >= CELL_MAX_PADS)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
2018-06-07 15:26:07 +02:00
// CELL_PAD_ERROR_NO_DEVICE is not returned in this case.
if (port_no >= CELL_PAD_MAX_PORT_NUM)
2018-06-07 15:26:07 +02:00
return CELL_OK;
const auto handler = pad::get_current_handler();
const auto& pads = handler->GetPads();
const auto& pad = pads[port_no];
2018-06-07 15:26:07 +02:00
// TODO: find out if this is checked here or later or at all
if (!(pad->m_device_capability & CELL_PAD_CAPABILITY_SENSOR_MODE))
return CELL_PAD_ERROR_UNSUPPORTED_GAMEPAD;
if (mode)
2021-03-02 12:59:19 +01:00
config.port_setting[port_no] |= CELL_PAD_SETTING_SENSOR_ON;
else
2021-03-02 12:59:19 +01:00
config.port_setting[port_no] &= ~CELL_PAD_SETTING_SENSOR_ON;
2014-04-06 21:47:21 +02:00
return CELL_OK;
2014-07-12 09:46:14 +02:00
}
2014-09-03 18:33:30 +02:00
2018-06-07 15:26:07 +02:00
error_code cellPadLddRegisterController()
2015-02-22 09:50:26 +01:00
{
cellPad.warning("cellPadLddRegisterController()");
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
2019-04-28 23:35:11 +02:00
const s32 handle = handler->AddLddPad();
2018-06-07 15:26:07 +02:00
2019-04-28 23:35:11 +02:00
if (handle < 0)
return CELL_PAD_ERROR_TOO_MANY_DEVICES;
2021-03-02 12:59:19 +01:00
config.port_setting[handle] = 0;
2019-04-28 23:35:11 +02:00
2024-01-01 12:25:25 +01:00
cellPad_NotifyStateChange(handle, CELL_PAD_STATUS_CONNECTED, false);
2019-04-28 23:35:11 +02:00
return not_an_error(handle);
2015-02-22 09:50:26 +01:00
}
2018-06-07 15:26:07 +02:00
error_code cellPadLddDataInsert(s32 handle, vm::ptr<CellPadData> data)
2015-02-22 09:50:26 +01:00
{
cellPad.trace("cellPadLddDataInsert(handle=%d, data=*0x%x)", handle, data);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size() || !data) // data == NULL stalls on decr
return CELL_PAD_ERROR_INVALID_PARAMETER;
2019-04-28 23:35:11 +02:00
if (!pads[handle]->ldd)
return CELL_PAD_ERROR_NO_DEVICE;
pads[handle]->ldd_data = *data;
2018-06-07 15:26:07 +02:00
2015-02-22 09:50:26 +01:00
return CELL_OK;
}
2018-06-07 15:26:07 +02:00
error_code cellPadLddGetPortNo(s32 handle)
2015-02-22 09:50:26 +01:00
{
cellPad.trace("cellPadLddGetPortNo(handle=%d)", handle);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size())
2018-06-07 15:26:07 +02:00
return CELL_PAD_ERROR_INVALID_PARAMETER;
2019-04-28 23:35:11 +02:00
if (!pads[handle]->ldd)
return CELL_PAD_ERROR_FATAL; // might be incorrect
// Other possible return values: CELL_PAD_ERROR_EBUSY, CELL_EBUSY
return not_an_error(handle); // handle is port
2015-02-22 09:50:26 +01:00
}
2018-06-07 15:26:07 +02:00
error_code cellPadLddUnregisterController(s32 handle)
2015-02-22 09:50:26 +01:00
{
cellPad.warning("cellPadLddUnregisterController(handle=%d)", handle);
2015-02-22 09:50:26 +01:00
2018-12-17 19:13:35 +01:00
std::lock_guard lock(pad::g_pad_mutex);
2021-03-02 12:59:19 +01:00
auto& config = g_fxo->get<pad_info>();
2021-03-02 12:59:19 +01:00
if (!config.max_connect)
2015-02-22 09:50:26 +01:00
return CELL_PAD_ERROR_UNINITIALIZED;
const auto handler = pad::get_current_handler();
2019-04-28 23:35:11 +02:00
const auto& pads = handler->GetPads();
if (handle < 0 || static_cast<u32>(handle) >= pads.size())
return CELL_PAD_ERROR_INVALID_PARAMETER;
2019-04-28 23:35:11 +02:00
if (!pads[handle]->ldd)
return CELL_PAD_ERROR_NO_DEVICE;
handler->UnregisterLddPad(handle);
2024-01-01 08:18:55 +01:00
cellPad_NotifyStateChange(handle, CELL_PAD_STATUS_DISCONNECTED, false);
2018-06-07 15:26:07 +02:00
2015-02-22 09:50:26 +01:00
return CELL_OK;
}
2019-09-29 09:58:21 +02:00
error_code cellPadFilterIIRInit(vm::ptr<CellPadFilterIIRSos> pSos, s32 cutoff)
{
cellPad.todo("cellPadFilterIIRInit(pSos=*0x%x, cutoff=%d)", pSos, cutoff);
2019-09-29 09:58:21 +02:00
if (!pSos) // TODO: does this check for cutoff > 2 ?
{
return CELL_PADFILTER_ERROR_INVALID_PARAMETER;
}
return CELL_OK;
}
u32 cellPadFilterIIRFilter(vm::ptr<CellPadFilterIIRSos> pSos, u32 filterIn)
{
cellPad.todo("cellPadFilterIIRFilter(pSos=*0x%x, filterIn=%d)", pSos, filterIn);
2019-09-29 09:58:21 +02:00
// TODO: apply filter
return std::clamp(filterIn, 0u, 1023u);
}
2018-06-23 11:36:16 +02:00
s32 sys_io_3733EA3C(u32 port_no, vm::ptr<u32> device_type, vm::ptr<CellPadData> data)
{
2018-06-07 15:26:07 +02:00
// Used by the ps1 emulator built into the firmware
// Seems to call the same function that getdataextra does
cellPad.trace("sys_io_3733EA3C(port_no=%d, device_type=*0x%x, data=*0x%x)", port_no, device_type, data);
2018-06-23 11:36:16 +02:00
return cellPadGetDataExtra(port_no, device_type, data);
}
2014-09-03 18:33:30 +02:00
void cellPad_init()
{
REG_FUNC(sys_io, cellPadInit);
REG_FUNC(sys_io, cellPadEnd);
REG_FUNC(sys_io, cellPadClearBuf);
REG_FUNC(sys_io, cellPadGetData);
2015-08-01 03:46:42 +02:00
REG_FUNC(sys_io, cellPadGetRawData); //
REG_FUNC(sys_io, cellPadGetDataExtra);
REG_FUNC(sys_io, cellPadSetActDirect);
2015-08-01 03:46:42 +02:00
REG_FUNC(sys_io, cellPadGetInfo); //
REG_FUNC(sys_io, cellPadGetInfo2);
REG_FUNC(sys_io, cellPadPeriphGetInfo);
2015-08-01 03:46:42 +02:00
REG_FUNC(sys_io, cellPadPeriphGetData);
REG_FUNC(sys_io, cellPadSetPortSetting);
2015-08-01 03:46:42 +02:00
REG_FUNC(sys_io, cellPadInfoPressMode); //
REG_FUNC(sys_io, cellPadInfoSensorMode); //
REG_FUNC(sys_io, cellPadSetPressMode); //
REG_FUNC(sys_io, cellPadSetSensorMode); //
REG_FUNC(sys_io, cellPadGetCapabilityInfo); //
2015-02-22 09:58:18 +01:00
REG_FUNC(sys_io, cellPadLddRegisterController);
REG_FUNC(sys_io, cellPadLddDataInsert);
REG_FUNC(sys_io, cellPadLddGetPortNo);
REG_FUNC(sys_io, cellPadLddUnregisterController);
2018-06-23 11:36:16 +02:00
2019-09-29 09:58:21 +02:00
REG_FUNC(sys_io, cellPadFilterIIRInit);
REG_FUNC(sys_io, cellPadFilterIIRFilter);
2018-06-23 11:36:16 +02:00
REG_FNID(sys_io, 0x3733EA3C, sys_io_3733EA3C);
2014-09-03 18:33:30 +02:00
}