mirror of https://github.com/RPCS3/rpcs3.git
Initial implementation of sys_uart
This commit is contained in:
parent
98e40d12ef
commit
e51d16aa37
|
@ -1,35 +1,588 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/ErrorCodes.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
|
||||
#include "sys_uart.h"
|
||||
|
||||
LOG_CHANNEL(sys_uart);
|
||||
|
||||
error_code sys_uart_initialize()
|
||||
struct av_get_monitor_info_cmd : public ps3av_cmd
|
||||
{
|
||||
sys_uart.todo("sys_uart_initialize()");
|
||||
u16 get_size(vuart_av_thread& /*vuart*/, const void* /*pkt_buf*/) override
|
||||
{
|
||||
return 12;
|
||||
}
|
||||
|
||||
bool execute(vuart_av_thread &vuart, const void *pkt_buf, u32 reply_max_size) override
|
||||
{
|
||||
auto pkt = static_cast<const ps3av_get_monitor_info *>(pkt_buf);
|
||||
|
||||
if (reply_max_size < sizeof(ps3av_get_monitor_info))
|
||||
{
|
||||
vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
auto evnt = std::make_unique<ps3av_get_monitor_info_reply>();
|
||||
evnt->avport = 0;
|
||||
// evnt->monitor_id =
|
||||
evnt->monitor_type = PS3AV_MONITOR_TYPE_HDMI;
|
||||
|
||||
// evnt->monitor_name;
|
||||
evnt->res_60.native = PS3AV_RESBIT_1280x720P;
|
||||
evnt->res_60.res_bits = 0xf;
|
||||
evnt->res_50.native = PS3AV_RESBIT_1280x720P;
|
||||
evnt->res_50.res_bits = 0xf;
|
||||
evnt->res_vesa.res_bits = 1;
|
||||
|
||||
evnt->cs.rgb = 1;
|
||||
evnt->cs.yuv444 = 1;
|
||||
evnt->cs.yuv422 = 1;
|
||||
|
||||
evnt->color.blue_x = 0xFFFF;
|
||||
evnt->color.blue_y = 0xFFFF;
|
||||
evnt->color.green_x = 0xFFFF;
|
||||
evnt->color.green_y = 0xFFFF;
|
||||
evnt->color.red_x = 0xFFFF;
|
||||
evnt->color.red_y = 0xFFFF;
|
||||
evnt->color.white_x = 0xFFFF;
|
||||
evnt->color.white_x = 0xFFFF;
|
||||
evnt->color.gamma = 100;
|
||||
|
||||
evnt->supported_ai = 0;
|
||||
evnt->speaker_info = 0;
|
||||
evnt->num_of_audio_block = 0;
|
||||
|
||||
vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS, evnt.get(), sizeof(ps3av_get_monitor_info_reply));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct av_get_hw_conf_cmd : public ps3av_cmd
|
||||
{
|
||||
u16 get_size(vuart_av_thread& /*vuart*/, const void* /*pkt_buf*/) override
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
bool execute(vuart_av_thread &vuart, const void *pkt_buf, u32 reply_max_size) override
|
||||
{
|
||||
auto pkt = static_cast<const ps3av_header *>(pkt_buf);
|
||||
|
||||
if (reply_max_size < sizeof(ps3av_get_hw_info_reply) + sizeof(ps3av_pkt_reply_hdr))
|
||||
{
|
||||
vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
auto out = std::make_unique<ps3av_get_hw_info_reply>();
|
||||
out->num_of_hdmi = 1;
|
||||
out->num_of_avmulti = 0;
|
||||
out->num_of_spdif = 1;
|
||||
out->resv = 0;
|
||||
|
||||
vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS, out.get(), sizeof(ps3av_get_hw_info_reply));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct generic_reply_cmd : public ps3av_cmd
|
||||
{
|
||||
u16 get_size(vuart_av_thread& /*vuart*/, const void* /*pkt_buf*/) override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool execute(vuart_av_thread &vuart, const void *pkt_buf, u32 reply_max_size) override
|
||||
{
|
||||
auto pkt = static_cast<const ps3av_header *>(pkt_buf);
|
||||
|
||||
if (reply_max_size < sizeof(ps3av_pkt_reply_hdr))
|
||||
{
|
||||
vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW);
|
||||
return false;
|
||||
}
|
||||
|
||||
vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
error_code sys_uart_initialize(ppu_thread &ppu)
|
||||
{
|
||||
ppu.state += cpu_flag::wait;
|
||||
|
||||
sys_uart.trace("sys_uart_initialize()");
|
||||
|
||||
auto &vuart_thread = g_fxo->get<vuart_av>();
|
||||
|
||||
if (vuart_thread.initialized.test_and_set())
|
||||
{
|
||||
return CELL_EPERM;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_uart_receive(vm::ptr<void> buffer, u64 size, u32 unk)
|
||||
error_code sys_uart_receive(ppu_thread &ppu, vm::ptr<void> buffer, u64 size, u32 mode)
|
||||
{
|
||||
sys_uart.todo("sys_uart_receive(buffer=*0x%x, size=0x%llx, unk=0x%x)", buffer, size, unk);
|
||||
sys_uart.trace("sys_uart_receive(buffer=*0x%x, size=0x%llx, mode=0x%x)", buffer, size, mode);
|
||||
|
||||
if (!size)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if ((mode & ~1UL) != 0)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
auto &vuart_thread = g_fxo->get<vuart_av>();
|
||||
|
||||
if (!vuart_thread.initialized)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (size > 0x20000U)
|
||||
{
|
||||
// kmalloc restriction
|
||||
fmt::throw_exception("Buffer is too big");
|
||||
}
|
||||
|
||||
const std::unique_ptr<u8[]> data = std::make_unique<u8[]>(size);
|
||||
u32 read_size = 0;
|
||||
|
||||
auto vuart_read = [&](u8 *buf, u32 buf_size) -> s32
|
||||
{
|
||||
const u32 ITER_SIZE = 4096;
|
||||
std::unique_lock lock(vuart_thread.rx_mutex, std::defer_lock);
|
||||
|
||||
if (!lock.try_lock())
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
u32 read_size = 0;
|
||||
u32 remaining = buf_size;
|
||||
|
||||
while (read_size < buf_size)
|
||||
{
|
||||
const u32 packet_size = std::min(remaining, ITER_SIZE);
|
||||
const u32 nread = vuart_thread.read_rx_data(buf + read_size, packet_size);
|
||||
read_size += nread;
|
||||
remaining -= nread;
|
||||
|
||||
if (nread < packet_size)
|
||||
break;
|
||||
}
|
||||
|
||||
return read_size;
|
||||
};
|
||||
|
||||
if (mode & BLOCKING_BIG_OP)
|
||||
{
|
||||
// Yield before checking for packets
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (ppu.is_stopped())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_lock<shared_mutex> lock(vuart_thread.rx_wake_m);
|
||||
const s32 read_result = vuart_read(data.get(), static_cast<u32>(size));
|
||||
|
||||
if (read_result > CELL_OK)
|
||||
{
|
||||
read_size = read_result;
|
||||
break;
|
||||
}
|
||||
|
||||
vuart_thread.rx_wake_c.wait_unlock(5000, lock);
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
}
|
||||
else // NOT_BLOCKING_BIG_OP
|
||||
{
|
||||
const s32 read_result = vuart_read(data.get(), static_cast<u32>(size));
|
||||
|
||||
if (read_result <= CELL_OK)
|
||||
{
|
||||
return read_result;
|
||||
}
|
||||
|
||||
read_size = read_result;
|
||||
}
|
||||
|
||||
if (!buffer || !vm::check_addr(buffer.addr(), vm::page_writable, read_size))
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
memcpy(buffer.get_ptr(), data.get(), read_size);
|
||||
return not_an_error(read_size);
|
||||
}
|
||||
|
||||
error_code sys_uart_send(ppu_thread &ppu, vm::cptr<void> buffer, u64 size, u32 mode)
|
||||
{
|
||||
sys_uart.trace("sys_uart_send(buffer=0x%x, size=0x%llx, mode=0x%x)", buffer, size, mode);
|
||||
|
||||
if (!size)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if ((mode & ~3ul) != 0)
|
||||
{
|
||||
return CELL_EINVAL;
|
||||
}
|
||||
|
||||
auto &vuart_thread = g_fxo->get<vuart_av>();
|
||||
|
||||
if (!vuart_thread.initialized)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (size > 0x20000U)
|
||||
{
|
||||
// kmalloc restriction
|
||||
fmt::throw_exception("Buffer is too big");
|
||||
}
|
||||
|
||||
if (!vm::check_addr(buffer.addr(), vm::page_readable, static_cast<u32>(size)))
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
const std::unique_ptr<u8[]> data = std::make_unique<u8[]>(size);
|
||||
memcpy(data.get(), buffer.get_ptr(), size);
|
||||
|
||||
std::unique_lock lock(vuart_thread.tx_mutex, std::defer_lock);
|
||||
|
||||
const u32 ITER_SIZE = 4096;
|
||||
|
||||
if (mode & BLOCKING_BIG_OP)
|
||||
{
|
||||
// Yield before sending packets
|
||||
lv2_obj::sleep(ppu);
|
||||
|
||||
lock.lock();
|
||||
|
||||
auto vuart_send_all = [&](const u8 *data, u32 data_sz)
|
||||
{
|
||||
u32 rem_size = data_sz;
|
||||
|
||||
while (rem_size)
|
||||
{
|
||||
if (ppu.is_stopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock<shared_mutex> lock(vuart_thread.tx_rdy_m);
|
||||
if (vuart_thread.get_tx_bytes() >= PS3AV_TX_BUF_SIZE)
|
||||
{
|
||||
vuart_thread.tx_rdy_c.wait_unlock(5000, lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
rem_size -= vuart_thread.enque_tx_data(data + data_sz - rem_size, rem_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
u32 sent_size = 0;
|
||||
u32 remaining = static_cast<u32>(size);
|
||||
|
||||
while (remaining)
|
||||
{
|
||||
const u32 packet_size = std::min(remaining, ITER_SIZE);
|
||||
if (!vuart_send_all(data.get() + sent_size, packet_size)) return {};
|
||||
sent_size += packet_size;
|
||||
remaining -= packet_size;
|
||||
}
|
||||
|
||||
ppu.check_state();
|
||||
}
|
||||
else if (mode & NOT_BLOCKING_OP)
|
||||
{
|
||||
if (!lock.try_lock())
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
if (PS3AV_TX_BUF_SIZE - vuart_thread.get_tx_bytes() < size)
|
||||
{
|
||||
return CELL_EAGAIN;
|
||||
}
|
||||
|
||||
return not_an_error(vuart_thread.enque_tx_data(data.get(), static_cast<u32>(size)));
|
||||
}
|
||||
else // NOT_BLOCKING_BIG_OP
|
||||
{
|
||||
if (!lock.try_lock())
|
||||
{
|
||||
return CELL_EBUSY;
|
||||
}
|
||||
|
||||
u32 sent_size = 0;
|
||||
u32 remaining = static_cast<u32>(size);
|
||||
|
||||
while (sent_size < size)
|
||||
{
|
||||
const u32 packet_size = std::min(remaining, ITER_SIZE);
|
||||
const u32 nsent = vuart_thread.enque_tx_data(data.get() + sent_size, packet_size);
|
||||
remaining -= nsent;
|
||||
|
||||
if (nsent < packet_size)
|
||||
{
|
||||
// Based on RE
|
||||
if (sent_size == 0)
|
||||
{
|
||||
return not_an_error(packet_size); // First iteration
|
||||
}
|
||||
else if (sent_size + nsent < size)
|
||||
{
|
||||
return not_an_error(sent_size + nsent);
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // Last iteration
|
||||
}
|
||||
}
|
||||
|
||||
sent_size += nsent;
|
||||
}
|
||||
}
|
||||
|
||||
return not_an_error(size);
|
||||
}
|
||||
|
||||
error_code sys_uart_get_params(vm::ptr<vuart_params> buffer)
|
||||
{
|
||||
sys_uart.trace("sys_uart_get_params(buffer=0x%x)", buffer);
|
||||
|
||||
auto &vuart_thread = g_fxo->get<vuart_av>();
|
||||
|
||||
if (!vuart_thread.initialized)
|
||||
{
|
||||
return CELL_ESRCH;
|
||||
}
|
||||
|
||||
if (!buffer || !vm::check_addr(buffer.addr(), vm::page_writable, sizeof(vuart_params)))
|
||||
{
|
||||
return CELL_EFAULT;
|
||||
}
|
||||
|
||||
buffer->rx_buf_size = PS3AV_RX_BUF_SIZE;
|
||||
buffer->tx_buf_size = PS3AV_TX_BUF_SIZE;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code sys_uart_send(vm::cptr<void> buffer, u64 size, u64 flags)
|
||||
void vuart_av_thread::operator()()
|
||||
{
|
||||
sys_uart.todo("sys_uart_send(buffer=0x%x, size=0x%llx, flags=0x%x)", buffer, size, flags);
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
std::unique_lock<shared_mutex> lock(tx_wake_m);
|
||||
if (!tx_buf.get_used_size())
|
||||
{
|
||||
tx_wake_c.wait_unlock(-1, lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
if (u32 read_size = read_tx_data(temp_tx_buf, sizeof(temp_tx_buf)))
|
||||
{
|
||||
parse_tx_buffer(read_size);
|
||||
commit_rx_buf();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
error_code sys_uart_get_params(vm::ptr<char> buffer)
|
||||
void vuart_av_thread::parse_tx_buffer(u32 buf_size)
|
||||
{
|
||||
sys_uart.todo("sys_uart_get_params(buffer=0x%x)", buffer);
|
||||
if (buf_size >= PS3AV_TX_BUF_SIZE)
|
||||
{
|
||||
while (read_tx_data(temp_tx_buf, sizeof(temp_tx_buf)) >= PS3AV_TX_BUF_SIZE);
|
||||
write_resp(reinterpret_cast<be_t<u16, 1>*>(temp_tx_buf)[3], PS3AV_STATUS_BUFFER_OVERFLOW);
|
||||
return;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
u32 read_ptr = 0;
|
||||
|
||||
while (buf_size)
|
||||
{
|
||||
const ps3av_header* const hdr = reinterpret_cast<ps3av_header*>(&temp_tx_buf[read_ptr]);
|
||||
const u16 pkt_size = hdr->length + 4;
|
||||
|
||||
if (hdr->length == 0xFFFCU)
|
||||
{
|
||||
write_resp(0xDEAD, PS3AV_STATUS_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hdr->version != PS3AV_VERSION)
|
||||
{
|
||||
if (hdr->version >= 0x100 && hdr->version < PS3AV_VERSION)
|
||||
{
|
||||
sys_uart.error("Unimplemented AV version: %x", hdr->version);
|
||||
}
|
||||
|
||||
write_resp(static_cast<u16>(hdr->cid.get()), PS3AV_STATUS_INVALID_COMMAND);
|
||||
return;
|
||||
}
|
||||
|
||||
const void* const pkt_storage = &temp_tx_buf[read_ptr];
|
||||
read_ptr += pkt_size;
|
||||
buf_size = buf_size < pkt_size ? 0 : buf_size - pkt_size;
|
||||
|
||||
auto cmd = get_cmd(hdr->cid);
|
||||
|
||||
if (!cmd.get())
|
||||
{
|
||||
sys_uart.error("Unknown AV cmd: 0x%x", hdr->cid);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto cmd_size = cmd->get_size(*this, pkt_storage);
|
||||
|
||||
if (cmd_size != pkt_size && cmd_size)
|
||||
{
|
||||
write_resp(static_cast<u16>(hdr->cid.get()), PS3AV_STATUS_INVALID_SAMPLE_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cmd->execute(*this, pkt_storage, sizeof(temp_rx_buf) - temp_rx_buf_size))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: handle HDMI events
|
||||
}
|
||||
|
||||
vuart_av_thread &vuart_av_thread::operator=(thread_state)
|
||||
{
|
||||
tx_wake_c.notify_all();
|
||||
return *this;
|
||||
}
|
||||
|
||||
u32 vuart_av_thread::enque_tx_data(const void *data, u32 data_sz)
|
||||
{
|
||||
std::unique_lock<shared_mutex> lock(tx_wake_m);
|
||||
if (auto size = tx_buf.push(data, data_sz))
|
||||
{
|
||||
lock.unlock();
|
||||
tx_wake_c.notify_all();
|
||||
return size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 vuart_av_thread::get_tx_bytes()
|
||||
{
|
||||
return tx_buf.get_used_size();
|
||||
}
|
||||
|
||||
u32 vuart_av_thread::read_rx_data(void *data, u32 data_sz)
|
||||
{
|
||||
return rx_buf.pop(data, data_sz);
|
||||
}
|
||||
|
||||
u32 vuart_av_thread::read_tx_data(void *data, u32 data_sz)
|
||||
{
|
||||
std::unique_lock<shared_mutex> lock(tx_rdy_m);
|
||||
if (auto size = tx_buf.pop(data, data_sz))
|
||||
{
|
||||
lock.unlock();
|
||||
tx_rdy_c.notify_all();
|
||||
return size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vuart_av_thread::write_resp(u32 cid, u32 status, const void *data, u32 data_size)
|
||||
{
|
||||
ps3av_pkt_reply_hdr pkt_hdr;
|
||||
|
||||
pkt_hdr.version = PS3AV_VERSION;
|
||||
pkt_hdr.status = status;
|
||||
pkt_hdr.length = data_size + 8;
|
||||
pkt_hdr.cid = cid | PS3AV_REPLY_BIT;
|
||||
|
||||
const u32 total_size = sizeof(pkt_hdr) + data_size;
|
||||
|
||||
if (temp_rx_buf_size + total_size <= PS3AV_RX_BUF_SIZE)
|
||||
{
|
||||
memcpy(&temp_rx_buf[temp_rx_buf_size], &pkt_hdr, sizeof(pkt_hdr));
|
||||
memcpy(&temp_rx_buf[temp_rx_buf_size + sizeof(pkt_hdr)], data, data_size);
|
||||
temp_rx_buf_size += total_size;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ps3av_cmd> vuart_av_thread::get_cmd(u32 cid)
|
||||
{
|
||||
switch (cid)
|
||||
{
|
||||
case PS3AV_CID_VIDEO_INIT:
|
||||
case PS3AV_CID_AUDIO_INIT:
|
||||
case PS3AV_CID_AV_GET_KSV_LIST_SIZE:
|
||||
case PS3AV_CID_VIDEO_FORMAT:
|
||||
case PS3AV_CID_VIDEO_PITCH:
|
||||
case PS3AV_CID_VIDEO_ROUTE:
|
||||
case PS3AV_CID_AV_VIDEO_DISABLE_SIG:
|
||||
case PS3AV_CID_AV_TV_MUTE:
|
||||
case PS3AV_CID_AV_UNK:
|
||||
case PS3AV_CID_AV_UNK2:
|
||||
case PS3AV_CID_VIDEO_GET_HW_CONF:
|
||||
case PS3AV_CID_AV_HDMI_MODE:
|
||||
case PS3AV_CID_AV_SET_ACP_PACKET:
|
||||
case PS3AV_CID_AV_VIDEO_UNK4:
|
||||
case PS3AV_CID_AV_ENABLE_EVENT:
|
||||
case PS3AV_CID_AV_AUDIO_MUTE:
|
||||
case PS3AV_CID_AUDIO_MUTE:
|
||||
case PS3AV_CID_AUDIO_MODE:
|
||||
case PS3AV_CID_AUDIO_CTRL:
|
||||
case PS3AV_CID_AUDIO_ACTIVE:
|
||||
case PS3AV_CID_AUDIO_INACTIVE:
|
||||
case PS3AV_CID_AUDIO_SPDIF_BIT:
|
||||
case PS3AV_CID_AVB_PARAM:
|
||||
case PS3AV_CID_AV_INIT:
|
||||
case 0xA0002:
|
||||
return std::make_shared<generic_reply_cmd>();
|
||||
|
||||
case PS3AV_CID_AV_GET_HW_CONF: return std::make_shared<av_get_hw_conf_cmd>();
|
||||
case PS3AV_CID_AV_GET_MONITOR_INFO: return std::make_shared<av_get_monitor_info_cmd>();
|
||||
default: return {};
|
||||
}
|
||||
}
|
||||
|
||||
void vuart_av_thread::commit_rx_buf()
|
||||
{
|
||||
std::unique_lock<shared_mutex> lock(rx_wake_m);
|
||||
rx_buf.push(temp_rx_buf, temp_rx_buf_size);
|
||||
temp_rx_buf_size = 0;
|
||||
|
||||
if (rx_buf.get_used_size())
|
||||
{
|
||||
lock.unlock();
|
||||
rx_wake_c.notify_all();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,326 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Memory/vm_ptr.h"
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Utilities/cond.h"
|
||||
|
||||
enum : u32
|
||||
{
|
||||
PS3AV_RX_BUF_SIZE = 0x800,
|
||||
PS3AV_TX_BUF_SIZE = 0x800,
|
||||
|
||||
PS3AV_VERSION = 0x205,
|
||||
|
||||
PS3AV_CID_AV_INIT = 0x00000001,
|
||||
PS3AV_CID_AV_FIN = 0x00000002,
|
||||
PS3AV_CID_AV_GET_HW_CONF = 0x00000003,
|
||||
PS3AV_CID_AV_GET_MONITOR_INFO = 0x00000004,
|
||||
PS3AV_CID_AV_GET_KSV_LIST_SIZE = 0x00000005,
|
||||
PS3AV_CID_AV_ENABLE_EVENT = 0x00000006,
|
||||
PS3AV_CID_AV_DISABLE_EVENT = 0x00000007,
|
||||
PS3AV_CID_AV_GET_ALL_EDID = 0x00000008,
|
||||
PS3AV_CID_AV_TV_MUTE = 0x0000000A,
|
||||
|
||||
PS3AV_CID_AV_VIDEO_CS = 0x00010001,
|
||||
PS3AV_CID_AV_VIDEO_MUTE = 0x00010002,
|
||||
PS3AV_CID_AV_VIDEO_DISABLE_SIG = 0x00010003,
|
||||
PS3AV_CID_AV_VIDEO_UNK4 = 0x00010004,
|
||||
PS3AV_CID_AV_AUDIO_PARAM = 0x00020001,
|
||||
PS3AV_CID_AV_AUDIO_MUTE = 0x00020002,
|
||||
PS3AV_CID_AV_ACP_CTRL = 0x00020003,
|
||||
PS3AV_CID_AV_SET_ACP_PACKET = 0x00020004,
|
||||
PS3AV_CID_AV_UNK = 0x00030001,
|
||||
PS3AV_CID_AV_UNK2 = 0x00030003,
|
||||
PS3AV_CID_AV_HDMI_MODE = 0x00040001,
|
||||
|
||||
PS3AV_CID_VIDEO_INIT = 0x01000001,
|
||||
PS3AV_CID_VIDEO_MODE = 0x01000002,
|
||||
PS3AV_CID_VIDEO_ROUTE = 0x01000003,
|
||||
PS3AV_CID_VIDEO_FORMAT = 0x01000004,
|
||||
PS3AV_CID_VIDEO_PITCH = 0x01000005,
|
||||
PS3AV_CID_VIDEO_GET_HW_CONF = 0x01000006,
|
||||
PS3AV_CID_VIDEO_REGVAL = 0x01000008,
|
||||
|
||||
PS3AV_CID_AUDIO_INIT = 0x02000001,
|
||||
PS3AV_CID_AUDIO_MODE = 0x02000002,
|
||||
PS3AV_CID_AUDIO_MUTE = 0x02000003,
|
||||
PS3AV_CID_AUDIO_ACTIVE = 0x02000004,
|
||||
PS3AV_CID_AUDIO_INACTIVE = 0x02000005,
|
||||
PS3AV_CID_AUDIO_SPDIF_BIT = 0x02000006,
|
||||
PS3AV_CID_AUDIO_CTRL = 0x02000007,
|
||||
|
||||
PS3AV_CID_EVENT_UNPLUGGED = 0x10000001,
|
||||
PS3AV_CID_EVENT_PLUGGED = 0x10000002,
|
||||
PS3AV_CID_EVENT_HDCP_DONE = 0x10000003,
|
||||
PS3AV_CID_EVENT_HDCP_FAIL = 0x10000004,
|
||||
PS3AV_CID_EVENT_HDCP_AUTH = 0x10000005,
|
||||
PS3AV_CID_EVENT_HDCP_ERROR = 0x10000006,
|
||||
|
||||
PS3AV_CID_AVB_PARAM = 0x04000001,
|
||||
|
||||
PS3AV_REPLY_BIT = 0x80000000,
|
||||
|
||||
PS3AV_RESBIT_720x480P = 0x0003, /* 0x0001 | 0x0002 */
|
||||
PS3AV_RESBIT_720x576P = 0x0003, /* 0x0001 | 0x0002 */
|
||||
PS3AV_RESBIT_1280x720P = 0x0004,
|
||||
PS3AV_RESBIT_1920x1080I = 0x0008,
|
||||
PS3AV_RESBIT_1920x1080P = 0x4000,
|
||||
|
||||
PS3AV_MONITOR_TYPE_HDMI = 1,
|
||||
PS3AV_MONITOR_TYPE_DVI = 2,
|
||||
|
||||
PS3AV_STATUS_SUCCESS = 0x00,
|
||||
PS3AV_STATUS_RECEIVE_VUART_ERROR = 0x01,
|
||||
PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL = 0x02,
|
||||
PS3AV_STATUS_INVALID_COMMAND = 0x03,
|
||||
PS3AV_STATUS_INVALID_PORT = 0x04,
|
||||
PS3AV_STATUS_INVALID_VID = 0x05,
|
||||
PS3AV_STATUS_INVALID_COLOR_SPACE = 0x06,
|
||||
PS3AV_STATUS_INVALID_FS = 0x07,
|
||||
PS3AV_STATUS_INVALID_AUDIO_CH = 0x08,
|
||||
PS3AV_STATUS_UNSUPPORTED_VERSION = 0x09,
|
||||
PS3AV_STATUS_INVALID_SAMPLE_SIZE = 0x0A,
|
||||
PS3AV_STATUS_FAILURE = 0x0B,
|
||||
PS3AV_STATUS_UNSUPPORTED_COMMAND = 0x0C,
|
||||
PS3AV_STATUS_BUFFER_OVERFLOW = 0x0D,
|
||||
PS3AV_STATUS_INVALID_VIDEO_PARAM = 0x0E,
|
||||
PS3AV_STATUS_NO_SEL = 0x0F,
|
||||
PS3AV_STATUS_INVALID_AV_PARAM = 0x10,
|
||||
PS3AV_STATUS_INVALID_AUDIO_PARAM = 0x11,
|
||||
PS3AV_STATUS_UNSUPPORTED_HDMI_MODE = 0x12,
|
||||
PS3AV_STATUS_NO_SYNC_HEAD = 0x13,
|
||||
};
|
||||
|
||||
enum PS3_AV_OP_MODE : u32
|
||||
{
|
||||
// BIG operation modes could send more then 4096 bytes
|
||||
|
||||
NOT_BLOCKING_BIG_OP = 0,
|
||||
BLOCKING_BIG_OP = 1,
|
||||
NOT_BLOCKING_OP = 2,
|
||||
};
|
||||
|
||||
class vuart_av_thread;
|
||||
|
||||
struct ps3av_cmd
|
||||
{
|
||||
virtual u16 get_size(vuart_av_thread &vuart, const void *pkt_buf) = 0;
|
||||
virtual bool execute(vuart_av_thread &vuart, const void *pkt_buf, u32 reply_max_size) = 0;
|
||||
};
|
||||
|
||||
template<u32 Size>
|
||||
class vuart_av_ringbuf
|
||||
{
|
||||
private:
|
||||
|
||||
atomic_t<u32> rd_ptr = 0;
|
||||
atomic_t<u32> wr_ptr = 0;
|
||||
u8 buf[Size + 1];
|
||||
|
||||
public:
|
||||
|
||||
u32 get_free_size()
|
||||
{
|
||||
const u32 rd = rd_ptr.observe();
|
||||
const u32 wr = wr_ptr.observe();
|
||||
|
||||
return wr >= rd ? Size - (wr - rd) : rd - wr - 1U;
|
||||
}
|
||||
|
||||
u32 get_used_size()
|
||||
{
|
||||
return Size - get_free_size();
|
||||
}
|
||||
|
||||
u32 push(const void *data, u32 size)
|
||||
{
|
||||
const u32 old = wr_ptr.observe();
|
||||
const u32 to_push = std::min(size, get_free_size());
|
||||
const u8 *b_data = static_cast<const u8*>(data);
|
||||
|
||||
if (!to_push || !data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old + to_push > sizeof(buf))
|
||||
{
|
||||
const u32 first_write_sz = sizeof(buf) - old;
|
||||
memcpy(&buf[old], b_data, first_write_sz);
|
||||
memcpy(&buf[0], b_data + first_write_sz, to_push - first_write_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(&buf[old], b_data, to_push);
|
||||
}
|
||||
|
||||
wr_ptr = (old + to_push) % sizeof(buf);
|
||||
|
||||
return to_push;
|
||||
}
|
||||
|
||||
u32 pop(void *data, u32 size)
|
||||
{
|
||||
const u32 old = rd_ptr.observe();
|
||||
const u32 to_pop = std::min(size, get_used_size());
|
||||
u8 *b_data = static_cast<u8*>(data);
|
||||
|
||||
if (!to_pop || !data)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (old + to_pop > sizeof(buf))
|
||||
{
|
||||
const u32 first_read_sz = sizeof(buf) - old;
|
||||
memcpy(b_data, &buf[old], first_read_sz);
|
||||
memcpy(b_data + first_read_sz, &buf[0], to_pop - first_read_sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(b_data, &buf[old], to_pop);
|
||||
}
|
||||
|
||||
rd_ptr = (old + to_pop) % sizeof(buf);
|
||||
|
||||
return to_pop;
|
||||
}
|
||||
};
|
||||
|
||||
class vuart_av_thread
|
||||
{
|
||||
public:
|
||||
|
||||
atomic_t<bool> initialized{};
|
||||
|
||||
shared_mutex rx_mutex{};
|
||||
shared_mutex tx_mutex{};
|
||||
|
||||
shared_mutex tx_wake_m{};
|
||||
cond_variable tx_wake_c{};
|
||||
shared_mutex tx_rdy_m{};
|
||||
cond_variable tx_rdy_c{};
|
||||
shared_mutex rx_wake_m{};
|
||||
cond_variable rx_wake_c{};
|
||||
|
||||
void operator()();
|
||||
void parse_tx_buffer(u32 buf_size);
|
||||
vuart_av_thread &operator=(thread_state);
|
||||
|
||||
u32 enque_tx_data(const void *data, u32 data_sz);
|
||||
u32 get_tx_bytes();
|
||||
u32 read_rx_data(void *data, u32 data_sz);
|
||||
void write_resp(u32 cid, u32 status, const void *data = nullptr, u32 data_size = 0);
|
||||
|
||||
static constexpr auto thread_name = "VUART AV Thread"sv;
|
||||
|
||||
private:
|
||||
|
||||
vuart_av_ringbuf<PS3AV_TX_BUF_SIZE> tx_buf{};
|
||||
vuart_av_ringbuf<PS3AV_RX_BUF_SIZE> rx_buf{};
|
||||
u8 temp_tx_buf[PS3AV_TX_BUF_SIZE];
|
||||
u8 temp_rx_buf[PS3AV_TX_BUF_SIZE];
|
||||
u32 temp_rx_buf_size = 0;
|
||||
|
||||
u32 read_tx_data(void *data, u32 data_sz);
|
||||
std::shared_ptr<ps3av_cmd> get_cmd(u32 cid);
|
||||
void commit_rx_buf();
|
||||
};
|
||||
|
||||
using vuart_av = named_thread<vuart_av_thread>;
|
||||
|
||||
struct vuart_params
|
||||
{
|
||||
be_t<u64, 1> rx_buf_size;
|
||||
be_t<u64, 1> tx_buf_size;
|
||||
};
|
||||
|
||||
struct ps3av_pkt_reply_hdr
|
||||
{
|
||||
be_t<u16, 1> version;
|
||||
be_t<u16, 1> length;
|
||||
be_t<u32, 1> cid;
|
||||
be_t<u32, 1> status;
|
||||
};
|
||||
|
||||
struct ps3av_header
|
||||
{
|
||||
be_t<u16, 1> version;
|
||||
be_t<u16, 1> length;
|
||||
be_t<u32, 1> cid;
|
||||
};
|
||||
|
||||
struct ps3av_info_resolution
|
||||
{
|
||||
be_t<u32, 1> res_bits;
|
||||
be_t<u32, 1> native;
|
||||
};
|
||||
|
||||
struct ps3av_info_cs
|
||||
{
|
||||
u8 rgb;
|
||||
u8 yuv444;
|
||||
u8 yuv422;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
struct ps3av_info_color
|
||||
{
|
||||
be_t<u16, 1> red_x;
|
||||
be_t<u16, 1> red_y;
|
||||
be_t<u16, 1> green_x;
|
||||
be_t<u16, 1> green_y;
|
||||
be_t<u16, 1> blue_x;
|
||||
be_t<u16, 1> blue_y;
|
||||
be_t<u16, 1> white_x;
|
||||
be_t<u16, 1> white_y;
|
||||
be_t<u32, 1> gamma;
|
||||
};
|
||||
|
||||
struct ps3av_info_audio
|
||||
{
|
||||
u8 type;
|
||||
u8 max_num_of_ch;
|
||||
u8 fs;
|
||||
u8 sbit;
|
||||
};
|
||||
|
||||
struct ps3av_get_monitor_info_reply
|
||||
{
|
||||
u8 avport;
|
||||
u8 monitor_id[10];
|
||||
u8 monitor_type;
|
||||
u8 monitor_name[16];
|
||||
ps3av_info_resolution res_60;
|
||||
ps3av_info_resolution res_50;
|
||||
ps3av_info_resolution res_other;
|
||||
ps3av_info_resolution res_vesa;
|
||||
ps3av_info_cs cs;
|
||||
ps3av_info_color color;
|
||||
u8 supported_ai;
|
||||
u8 speaker_info;
|
||||
be_t<u16, 1> num_of_audio_block;
|
||||
ps3av_info_audio audio_info[29];
|
||||
};
|
||||
|
||||
struct ps3av_get_monitor_info
|
||||
{
|
||||
ps3av_header hdr;
|
||||
be_t<u16, 1> avport;
|
||||
be_t<u16, 1> reserved;
|
||||
};
|
||||
|
||||
struct ps3av_get_hw_info_reply
|
||||
{
|
||||
be_t<u16, 1> num_of_hdmi;
|
||||
be_t<u16, 1> num_of_avmulti;
|
||||
be_t<u16, 1> num_of_spdif;
|
||||
be_t<u16, 1> resv;
|
||||
};
|
||||
|
||||
// SysCalls
|
||||
|
||||
error_code sys_uart_initialize();
|
||||
error_code sys_uart_receive(vm::ptr<void> buffer, u64 size, u32 unk);
|
||||
error_code sys_uart_send(vm::cptr<void> buffer, u64 size, u64 flags);
|
||||
error_code sys_uart_get_params(vm::ptr<char> buffer);
|
||||
error_code sys_uart_initialize(ppu_thread &ppu);
|
||||
error_code sys_uart_receive(ppu_thread &ppu, vm::ptr<void> buffer, u64 size, u32 mode);
|
||||
error_code sys_uart_send(ppu_thread &ppu, vm::cptr<void> buffer, u64 size, u32 mode);
|
||||
error_code sys_uart_get_params(vm::ptr<vuart_params> buffer);
|
||||
|
|
Loading…
Reference in New Issue