From e51d16aa37d4efa263c5e6f264fed335498fc6f9 Mon Sep 17 00:00:00 2001 From: Ilya Oleinik <16190165+Vestrel@users.noreply.github.com> Date: Sat, 3 Jul 2021 14:52:39 +0900 Subject: [PATCH] Initial implementation of sys_uart --- rpcs3/Emu/Cell/lv2/sys_uart.cpp | 573 +++++++++++++++++++++++++++++++- rpcs3/Emu/Cell/lv2/sys_uart.h | 324 +++++++++++++++++- 2 files changed, 883 insertions(+), 14 deletions(-) diff --git a/rpcs3/Emu/Cell/lv2/sys_uart.cpp b/rpcs3/Emu/Cell/lv2/sys_uart.cpp index e4c5511dca..b17b98d239 100644 --- a/rpcs3/Emu/Cell/lv2/sys_uart.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_uart.cpp @@ -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(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(); + 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(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(); + 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(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(); + + if (vuart_thread.initialized.test_and_set()) + { + return CELL_EPERM; + } return CELL_OK; } -error_code sys_uart_receive(vm::ptr buffer, u64 size, u32 unk) +error_code sys_uart_receive(ppu_thread &ppu, vm::ptr 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(); + + if (!vuart_thread.initialized) + { + return CELL_ESRCH; + } + + if (size > 0x20000U) + { + // kmalloc restriction + fmt::throw_exception("Buffer is too big"); + } + + const std::unique_ptr data = std::make_unique(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 lock(vuart_thread.rx_wake_m); + const s32 read_result = vuart_read(data.get(), static_cast(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(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 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(); + + 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(size))) + { + return CELL_EFAULT; + } + + const std::unique_ptr data = std::make_unique(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 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(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(size))); + } + else // NOT_BLOCKING_BIG_OP + { + if (!lock.try_lock()) + { + return CELL_EBUSY; + } + + u32 sent_size = 0; + u32 remaining = static_cast(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 buffer) +{ + sys_uart.trace("sys_uart_get_params(buffer=0x%x)", buffer); + + auto &vuart_thread = g_fxo->get(); + + 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 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 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 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*>(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(&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(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(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 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 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 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(); + + case PS3AV_CID_AV_GET_HW_CONF: return std::make_shared(); + case PS3AV_CID_AV_GET_MONITOR_INFO: return std::make_shared(); + default: return {}; + } +} + +void vuart_av_thread::commit_rx_buf() +{ + std::unique_lock 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(); + } } diff --git a/rpcs3/Emu/Cell/lv2/sys_uart.h b/rpcs3/Emu/Cell/lv2/sys_uart.h index 4565876a0e..2408c68f35 100644 --- a/rpcs3/Emu/Cell/lv2/sys_uart.h +++ b/rpcs3/Emu/Cell/lv2/sys_uart.h @@ -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 +class vuart_av_ringbuf +{ +private: + + atomic_t rd_ptr = 0; + atomic_t 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(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(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 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 tx_buf{}; + vuart_av_ringbuf 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 get_cmd(u32 cid); + void commit_rx_buf(); +}; + +using vuart_av = named_thread; + +struct vuart_params +{ + be_t rx_buf_size; + be_t tx_buf_size; +}; + +struct ps3av_pkt_reply_hdr +{ + be_t version; + be_t length; + be_t cid; + be_t status; +}; + +struct ps3av_header +{ + be_t version; + be_t length; + be_t cid; +}; + +struct ps3av_info_resolution +{ + be_t res_bits; + be_t native; +}; + +struct ps3av_info_cs +{ + u8 rgb; + u8 yuv444; + u8 yuv422; + u8 reserved; +}; + +struct ps3av_info_color +{ + be_t red_x; + be_t red_y; + be_t green_x; + be_t green_y; + be_t blue_x; + be_t blue_y; + be_t white_x; + be_t white_y; + be_t 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 num_of_audio_block; + ps3av_info_audio audio_info[29]; +}; + +struct ps3av_get_monitor_info +{ + ps3av_header hdr; + be_t avport; + be_t reserved; +}; + +struct ps3av_get_hw_info_reply +{ + be_t num_of_hdmi; + be_t num_of_avmulti; + be_t num_of_spdif; + be_t resv; +}; // SysCalls -error_code sys_uart_initialize(); -error_code sys_uart_receive(vm::ptr buffer, u64 size, u32 unk); -error_code sys_uart_send(vm::cptr buffer, u64 size, u64 flags); -error_code sys_uart_get_params(vm::ptr buffer); +error_code sys_uart_initialize(ppu_thread &ppu); +error_code sys_uart_receive(ppu_thread &ppu, vm::ptr buffer, u64 size, u32 mode); +error_code sys_uart_send(ppu_thread &ppu, vm::cptr buffer, u64 size, u32 mode); +error_code sys_uart_get_params(vm::ptr buffer);