#include "stdafx.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Cell/PPUThread.h" #include "Emu/System.h" #include "Emu/system_config.h" #include "cellos/sys_process.h" #include "cellos/sys_rsxaudio.h" #include "cellos/sys_sync.h" #include "sys_uart.h" LOG_CHANNEL(sys_uart); template <> void fmt_class_string::format(std::string &out, u64 arg) { format_enum(out, arg, [](UartAudioCtrlID value) { switch (value) { STR_CASE(UartAudioCtrlID::DAC_RESET); STR_CASE(UartAudioCtrlID::DAC_DE_EMPHASIS); STR_CASE(UartAudioCtrlID::AVCLK); } return unknown; }); } struct av_init_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_av_init); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.av_cmd_ver = pkt->hdr.version; vuart.hdmi_events_bitmask |= pkt->event_bit; if (pkt->event_bit & PS3AV_EVENT_BIT_UNK) { // 0 or 255, probably ps2 backwards compatibility (inverted) const ps3av_pkt_av_init_reply reply = {0}; vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS, &reply, sizeof(ps3av_pkt_av_init_reply)); return; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_fini_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.hdmi_events_bitmask = 0; vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS); } }; struct av_get_monitor_info_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_get_monitor_info); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } ps3av_get_monitor_info_reply cfg{}; if (pkt->avport == static_cast(UartAudioAvport::AVMULTI_0)) { cfg.avport = static_cast(UartAudioAvport::AVMULTI_0); cfg.monitor_type = PS3AV_MONITOR_TYPE_AVMULTI; cfg.res_60.res_bits = UINT32_MAX; cfg.res_50.res_bits = UINT32_MAX; cfg.res_vesa.res_bits = UINT32_MAX; cfg.cs.rgb = PS3AV_CS_SUPPORTED; cfg.cs.yuv444 = PS3AV_CS_SUPPORTED; cfg.cs.yuv422 = PS3AV_CS_SUPPORTED; cfg.speaker_info = 1; cfg.num_of_audio_block = 1; cfg.audio_info[0].sbit = 7; cfg.audio_info[0].max_num_of_ch = 2; cfg.audio_info[0].type = PS3AV_MON_INFO_AUDIO_TYPE_LPCM; cfg.audio_info[0].fs = 127; vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS, &cfg, sizeof(ps3av_get_monitor_info_reply)); } else if (pkt->avport <= static_cast(UartAudioAvport::HDMI_1)) { if (pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL); return; } set_hdmi_display_cfg(vuart, cfg, static_cast(pkt->avport)); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS, &cfg, sizeof(ps3av_get_monitor_info_reply) - 4); // Length is different for some reason } else { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); } } static void set_hdmi_display_cfg(vuart_av_thread &vuart, ps3av_get_monitor_info_reply &cfg, u8 avport) { if (vuart.hdmi_behavior_mode != PS3AV_HDMI_BEHAVIOR_NORMAL && (vuart.hdmi_behavior_mode & PS3AV_HDMI_BEHAVIOR_EDID_PASS)) { cfg.monitor_type = vuart.hdmi_behavior_mode & PS3AV_HDMI_BEHAVIOR_DVI ? PS3AV_MONITOR_TYPE_DVI : PS3AV_MONITOR_TYPE_HDMI; return; } // Report maximum support static constexpr u8 mon_id[sizeof(cfg.monitor_id)] = { 0x4A, 0x13, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15}; static constexpr u8 mon_name[sizeof(cfg.monitor_name)] = { 'R', 'P', 'C', 'S', '3', ' ', 'V', 'i', 'r', 't', 'M', 'o', 'n', '\0', '\0', '\0'}; static constexpr ps3av_info_audio audio_info[PS3AV_MON_INFO_AUDIO_BLK_MAX] = { {PS3AV_MON_INFO_AUDIO_TYPE_LPCM, 8, 0x7F, 0x07}, {PS3AV_MON_INFO_AUDIO_TYPE_AC3, 8, 0x7F, 0xFF}, {PS3AV_MON_INFO_AUDIO_TYPE_AAC, 8, 0x7F, 0xFF}, {PS3AV_MON_INFO_AUDIO_TYPE_DTS, 8, 0x7F, 0xFF}, {PS3AV_MON_INFO_AUDIO_TYPE_DDP, 8, 0x7F, 0xFF}, {PS3AV_MON_INFO_AUDIO_TYPE_DTS_HD, 8, 0x7F, 0xFF}, {PS3AV_MON_INFO_AUDIO_TYPE_DOLBY_THD, 8, 0x7F, 0xFF}, }; cfg.avport = avport; memcpy(cfg.monitor_id, mon_id, sizeof(cfg.monitor_id)); cfg.monitor_type = PS3AV_MONITOR_TYPE_HDMI; memcpy(cfg.monitor_name, mon_name, sizeof(cfg.monitor_name)); const u32 native_res = [&]() { switch (g_cfg.video.resolution) { case video_resolution::_1080p: return PS3AV_RESBIT_1920x1080P; case video_resolution::_1080i: return PS3AV_RESBIT_1920x1080I; case video_resolution::_1600x1080p: case video_resolution::_1440x1080p: case video_resolution::_1280x1080p: case video_resolution::_720p: return PS3AV_RESBIT_1280x720P; case video_resolution::_576p: return PS3AV_RESBIT_720x576P; default: return PS3AV_RESBIT_720x480P; } }(); cfg.res_60.res_bits = UINT32_MAX; cfg.res_60.native = native_res; cfg.res_50.res_bits = UINT32_MAX; cfg.res_50.native = native_res; cfg.res_other.res_bits = UINT32_MAX; cfg.res_vesa.res_bits = 1; // Always one mode at a time cfg.cs.rgb = PS3AV_CS_SUPPORTED | PS3AV_RGB_SELECTABLE_QAUNTIZATION_RANGE | PS3AV_12BIT_COLOR; cfg.cs.yuv444 = PS3AV_CS_SUPPORTED | PS3AV_12BIT_COLOR; cfg.cs.yuv422 = PS3AV_CS_SUPPORTED; cfg.cs.colorimetry_data = PS3AV_COLORIMETRY_xvYCC_601 | PS3AV_COLORIMETRY_xvYCC_709 | PS3AV_COLORIMETRY_MD0 | PS3AV_COLORIMETRY_MD1 | PS3AV_COLORIMETRY_MD2; cfg.color.red_x = 1023; cfg.color.red_y = 0; cfg.color.green_x = 0; cfg.color.green_y = 1023; cfg.color.blue_x = 0; cfg.color.blue_y = 0; cfg.color.white_x = 341; cfg.color.white_y = 341; cfg.color.gamma = 100; cfg.supported_ai = 1; cfg.speaker_info = 0x4F; // Audio formats cfg.num_of_audio_block = 7; memcpy(cfg.audio_info, audio_info, sizeof(cfg.audio_info)); // 16:9 27-inch (as a default) cfg.hor_screen_size = 60; cfg.ver_screen_size = 34; cfg.supported_content_types = 0b1111; // Graphics, cinema, photo, game // 3D modes, no native formats cfg.res_60_packed_3D.res_bits = UINT32_MAX; cfg.res_50_packed_3D.res_bits = UINT32_MAX; cfg.res_other_3D.res_bits = UINT32_MAX; cfg.res_60_sbs_3D.res_bits = UINT32_MAX; cfg.res_50_sbs_3D.res_bits = UINT32_MAX; cfg.vendor_specific_flags = 0; // values from 0-3 (unk) } }; struct av_get_bksv_list_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_get_bksv); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->avport > static_cast(UartAudioAvport::HDMI_1)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); return; } if (pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL); return; } ps3av_pkt_get_bksv_reply reply{}; reply.avport = pkt->avport; u16 pkt_size = offsetof(ps3av_pkt_get_bksv_reply, ksv_arr); if (vuart.hdmi_behavior_mode == PS3AV_HDMI_BEHAVIOR_NORMAL || !(vuart.hdmi_behavior_mode & PS3AV_HDMI_BEHAVIOR_HDCP_OFF)) { reply.ksv_cnt = 1; memcpy(reply.ksv_arr[0], PS3AV_BKSV_VALUE, sizeof(PS3AV_BKSV_VALUE)); pkt_size = (pkt_size + 5 * reply.ksv_cnt + 3) & 0xFFFC; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS, &reply, pkt_size); } }; struct av_enable_event_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_enable_event); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.hdmi_events_bitmask |= pkt->event_bit; vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_disable_event_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_enable_event); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.hdmi_events_bitmask &= ~pkt->event_bit; vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_tv_mute_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_av_audio_mute); } // Behavior is unknown, but it seems that this pkt could be ignored void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->avport < (g_cfg.core.debug_console_mode ? 2 : 1)) { sys_uart.notice("[av_tv_mute_cmd] tv mute set to %u", pkt->mute > 0); } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_null_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return 12; } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS); } }; struct av_get_aksv_list_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr) + sizeof(ps3av_pkt_get_aksv_reply)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } ps3av_pkt_get_aksv_reply reply{}; memcpy(reply.ksv_arr[0], PS3AV_AKSV_VALUE, sizeof(PS3AV_AKSV_VALUE)); if (g_cfg.core.debug_console_mode) { memcpy(reply.ksv_arr[1], PS3AV_AKSV_VALUE, sizeof(PS3AV_AKSV_VALUE)); reply.ksv_size = 2 * sizeof(PS3AV_AKSV_VALUE); } else { reply.ksv_size = sizeof(PS3AV_AKSV_VALUE); } vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS, &reply, sizeof(ps3av_pkt_get_aksv_reply)); } }; struct video_disable_signal_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_video_disable_sig); } // Cross color reduction filter setting in vsh. (AVMULTI) void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->avport <= static_cast(UartAudioAvport::HDMI_1)) { g_fxo->get().update_av_mute_state( vuart.avport_to_idx(static_cast(pkt->avport.get())), false, true); if (pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL); return; } vuart.hdmi_res_set[pkt->avport == static_cast(UartAudioAvport::HDMI_1)] = false; vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } else if (pkt->avport == static_cast(UartAudioAvport::AVMULTI_0)) { if (vuart.head_b_initialized) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } } else { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } } }; struct av_video_ytrapcontrol_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_av_video_ytrapcontrol); } // Cross color reduction filter setting in vsh. (AVMULTI) void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_get_hw_info_reply) + sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->unk1 && pkt->unk1 != 5U) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); return; } sys_uart.notice("[av_video_ytrapcontrol_cmd] unk1=0x%04x unk2=0x%04x", pkt->unk1, pkt->unk2); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_audio_mute_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_av_audio_mute); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (pkt->avport == static_cast(UartAudioAvport::AVMULTI_1)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); return; } if ((pkt->avport > static_cast(UartAudioAvport::HDMI_1) && pkt->avport != static_cast(UartAudioAvport::AVMULTI_0)) || (pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL); return; } g_fxo->get().update_av_mute_state( vuart.avport_to_idx(static_cast(pkt->avport.get())), true, false, pkt->mute); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_acp_ctrl_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_acp_ctrl); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (pkt->avport > static_cast(UartAudioAvport::HDMI_1) || (pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); return; } sys_uart.notice( "[av_acp_ctrl_cmd] HDMI_%u data island ctrl pkt ctrl=0x%02x", pkt->avport, pkt->packetctl); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_set_acp_packet_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_set_acp_packet); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (pkt->avport > static_cast(UartAudioAvport::HDMI_1) || (pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode) || (pkt->pkt_type > 0x0A && pkt->pkt_type < 0x81) || pkt->pkt_type > 0x85) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); return; } sys_uart.notice( "[av_set_acp_packet_cmd] HDMI_%u data island pkt type=0x%02x", pkt->avport, pkt->pkt_type); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_add_signal_ctl_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_add_signal_ctl); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->avport != static_cast(UartAudioAvport::AVMULTI_0)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); return; } sys_uart.notice("[av_add_signal_ctl_cmd] signal_ctl=0x%04x", pkt->signal_ctl); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_set_cgms_wss_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_av_set_cgms_wss); } // Something related to copy control on AVMULTI. void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->avport != static_cast(UartAudioAvport::AVMULTI_0)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_PORT); return; } sys_uart.notice("[av_set_cgms_wss_cmd] cgms_wss=0x%08x", pkt->cgms_wss); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_get_hw_conf_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_get_hw_info_reply) + sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } ps3av_get_hw_info_reply out{}; out.num_of_hdmi = g_cfg.core.debug_console_mode ? 2 : 1; out.num_of_avmulti = 1; out.num_of_spdif = 1; out.extra_bistream_support = 1; vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS, &out, sizeof(ps3av_get_hw_info_reply)); } }; struct av_set_hdmi_mode_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_set_hdmi_mode); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (pkt->mode != PS3AV_HDMI_BEHAVIOR_NORMAL) { if ((pkt->mode & PS3AV_HDMI_BEHAVIOR_HDCP_OFF) && !g_cfg.core.debug_console_mode) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_UNSUPPORTED_HDMI_MODE); return; } if (pkt->mode & ~(PS3AV_HDMI_BEHAVIOR_HDCP_OFF | PS3AV_HDMI_BEHAVIOR_EDID_PASS | PS3AV_HDMI_BEHAVIOR_DVI)) { sys_uart.warning( "[av_set_hdmi_mode_cmd] Unknown bits in hdmi mode: 0x%02x", pkt->mode); } } vuart.hdmi_behavior_mode = pkt->mode; vuart.add_hdmi_events(UartHdmiEvent::UNPLUGGED, vuart.hdmi_res_set[0] ? UartHdmiEvent::HDCP_DONE : UartHdmiEvent::PLUGGED, true, false); vuart.add_hdmi_events(UartHdmiEvent::UNPLUGGED, vuart.hdmi_res_set[1] ? UartHdmiEvent::HDCP_DONE : UartHdmiEvent::PLUGGED, false, true); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct av_get_cec_status_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_av_get_cec_config_reply) + sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } const ps3av_pkt_av_get_cec_config_reply reply{1}; vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS, &reply, sizeof(ps3av_pkt_av_get_cec_config_reply)); } }; struct video_init_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS); } }; struct video_set_format_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_video_format); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->video_head > PS3AV_HEAD_B_ANALOG || pkt->video_order > 1 || pkt->video_format > 16) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_VIDEO_PARAM); return; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct video_set_route_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return 24; } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } vuart.write_resp(pkt->cid, PS3AV_STATUS_NO_SEL); // Only available in PS2_GX_LPAR } }; struct video_set_pitch_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_video_set_pitch); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (pkt->video_head > PS3AV_HEAD_B_ANALOG || (pkt->pitch & 7) != 0U || pkt->pitch > UINT16_MAX) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_VIDEO_PARAM); return; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct video_get_hw_cfg_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_video_get_hw_cfg_reply) + sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } ps3av_pkt_video_get_hw_cfg_reply reply{}; reply.gx_available = 0; // Set to 1 only in PS2_GX_LPAR vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS, &reply, sizeof(ps3av_pkt_video_get_hw_cfg_reply)); } }; struct audio_init_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_header); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } g_fxo->get().reset_hw(); vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS); } }; struct audio_set_mode_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_audio_mode); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } if (!set_mode(*pkt)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_INVALID_AUDIO_PARAM); } else { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } } private: bool set_mode(const ps3av_pkt_audio_mode &pkt) { bool spdif_use_serial_buf = false; RsxaudioPort avport_src, rsxaudio_port; RsxaudioAvportIdx avport_idx; switch (pkt.avport) { case UartAudioAvport::HDMI_0: { avport_idx = RsxaudioAvportIdx::HDMI_0; if (pkt.audio_source == UartAudioSource::SPDIF) { avport_src = rsxaudio_port = RsxaudioPort::SPDIF_1; } else { avport_src = rsxaudio_port = RsxaudioPort::SERIAL; } break; } case UartAudioAvport::HDMI_1: { avport_idx = RsxaudioAvportIdx::HDMI_1; if (pkt.audio_source == UartAudioSource::SPDIF) { avport_src = rsxaudio_port = RsxaudioPort::SPDIF_1; } else { avport_src = rsxaudio_port = RsxaudioPort::SERIAL; } break; } case UartAudioAvport::AVMULTI_0: { avport_idx = RsxaudioAvportIdx::AVMULTI; avport_src = rsxaudio_port = RsxaudioPort::SERIAL; break; } case UartAudioAvport::SPDIF_0: { avport_idx = RsxaudioAvportIdx::SPDIF_0; rsxaudio_port = RsxaudioPort::SPDIF_0; if (pkt.audio_source == UartAudioSource::SERIAL) { spdif_use_serial_buf = true; avport_src = RsxaudioPort::SERIAL; } else { avport_src = RsxaudioPort::SPDIF_0; } break; } case UartAudioAvport::SPDIF_1: { avport_idx = RsxaudioAvportIdx::SPDIF_1; rsxaudio_port = RsxaudioPort::SPDIF_1; if (pkt.audio_source == UartAudioSource::SERIAL) { spdif_use_serial_buf = true; avport_src = RsxaudioPort::SERIAL; } else { avport_src = RsxaudioPort::SPDIF_1; } break; } default: { return false; } } if (static_cast(pkt.audio_fs.value()) > static_cast(UartAudioFreq::_192K)) return false; const auto bit_cnt = [&]() { if ((rsxaudio_port != RsxaudioPort::SERIAL && pkt.audio_format != UartAudioFormat::PCM) || pkt.audio_word_bits == UartAudioSampleSize::_16BIT) { return UartAudioSampleSize::_16BIT; } else { return UartAudioSampleSize::_24BIT; } }(); return commit_param(rsxaudio_port, avport_idx, avport_src, pkt.audio_fs, bit_cnt, spdif_use_serial_buf, pkt.audio_cs_info); } bool commit_param(RsxaudioPort rsxaudio_port, RsxaudioAvportIdx avport, RsxaudioPort avport_src, UartAudioFreq freq, UartAudioSampleSize bit_cnt, bool spdif_use_serial_buf, const u8 *cs_data) { auto &rsxaudio_thread = g_fxo->get(); const auto avport_idx = static_cast>(avport); const auto rsxaudio_word_depth = bit_cnt == UartAudioSampleSize::_16BIT ? RsxaudioSampleSize::_16BIT : RsxaudioSampleSize::_32BIT; const auto freq_param = [&]() { switch (freq) { case UartAudioFreq::_44K: return std::make_pair(8, SYS_RSXAUDIO_FREQ_BASE_352K); default: case UartAudioFreq::_48K: return std::make_pair(8, SYS_RSXAUDIO_FREQ_BASE_384K); case UartAudioFreq::_88K: return std::make_pair(4, SYS_RSXAUDIO_FREQ_BASE_352K); case UartAudioFreq::_96K: return std::make_pair(4, SYS_RSXAUDIO_FREQ_BASE_384K); case UartAudioFreq::_176K: return std::make_pair(2, SYS_RSXAUDIO_FREQ_BASE_352K); case UartAudioFreq::_192K: return std::make_pair(2, SYS_RSXAUDIO_FREQ_BASE_384K); } }(); switch (rsxaudio_port) { case RsxaudioPort::SERIAL: { rsxaudio_thread.update_hw_param([&](auto &obj) { obj.serial_freq_base = freq_param.second; obj.serial.freq_div = freq_param.first; obj.serial.depth = rsxaudio_word_depth; obj.serial.buf_empty_en = true; obj.avport_src[avport_idx] = avport_src; }); break; } case RsxaudioPort::SPDIF_0: case RsxaudioPort::SPDIF_1: { const u8 spdif_idx = rsxaudio_port == RsxaudioPort::SPDIF_1; rsxaudio_thread.update_hw_param([&](auto &obj) { obj.spdif_freq_base = freq_param.second; obj.spdif[spdif_idx].freq_div = freq_param.first; obj.spdif[spdif_idx].depth = rsxaudio_word_depth; obj.spdif[spdif_idx].use_serial_buf = spdif_use_serial_buf; obj.spdif[spdif_idx].buf_empty_en = true; obj.avport_src[avport_idx] = avport_src; memcpy(obj.spdif[spdif_idx].cs_data.data(), cs_data, sizeof(obj.spdif[spdif_idx].cs_data)); }); break; } default: { return false; } } return true; } }; struct audio_mute_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { // From RE return 0; } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } switch (pkt->avport) { case UartAudioAvport::HDMI_0: case UartAudioAvport::HDMI_1: case UartAudioAvport::AVMULTI_0: case UartAudioAvport::AVMULTI_1: g_fxo->get().update_mute_state(RsxaudioPort::SERIAL, pkt->mute); break; case UartAudioAvport::SPDIF_0: g_fxo->get().update_mute_state(RsxaudioPort::SPDIF_0, pkt->mute); break; case UartAudioAvport::SPDIF_1: g_fxo->get().update_mute_state(RsxaudioPort::SPDIF_1, pkt->mute); break; default: break; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct audio_set_active_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_audio_set_active); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } const bool requested_avports[SYS_RSXAUDIO_AVPORT_CNT] = { (pkt->audio_port & PS3AV_AUDIO_PORT_HDMI_0) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_HDMI_1) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_AVMULTI) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_SPDIF_0) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_SPDIF_1) != 0U}; g_fxo->get().update_hw_param([&](auto &obj) { for (u8 avport_idx = 0; avport_idx < SYS_RSXAUDIO_AVPORT_CNT; avport_idx++) { if (requested_avports[avport_idx]) { switch (obj.avport_src[avport_idx]) { case RsxaudioPort::SERIAL: obj.serial.en = true; break; case RsxaudioPort::SPDIF_0: case RsxaudioPort::SPDIF_1: { const u8 spdif_idx = obj.avport_src[avport_idx] == RsxaudioPort::SPDIF_1; if (!obj.spdif[spdif_idx].use_serial_buf) { obj.spdif[spdif_idx].en = true; } break; } default: break; } } } obj.serial.muted = false; obj.spdif[1].muted = false; }); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct audio_set_inactive_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_audio_set_active); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } g_fxo->get().update_hw_param([&](auto &obj) { if ((pkt->audio_port & 0x8000'0000) == 0U) { obj.avport_src.fill(RsxaudioPort::INVALID); } obj.serial.en = false; obj.serial.muted = true; obj.spdif[1].muted = true; for (u8 spdif_idx = 0; spdif_idx < SYS_RSXAUDIO_SPDIF_CNT; spdif_idx++) { if (!obj.spdif[spdif_idx].use_serial_buf) { obj.spdif[spdif_idx].en = false; } } }); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct audio_spdif_bit_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_audio_spdif_bit); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } const bool requested_avports[SYS_RSXAUDIO_AVPORT_CNT] = { (pkt->audio_port & PS3AV_AUDIO_PORT_HDMI_0) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_HDMI_1) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_AVMULTI) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_SPDIF_0) != 0U, (pkt->audio_port & PS3AV_AUDIO_PORT_SPDIF_1) != 0U}; g_fxo->get().update_hw_param([&](auto &obj) { for (u8 avport_idx = 0; avport_idx < SYS_RSXAUDIO_AVPORT_CNT; avport_idx++) { if (requested_avports[avport_idx] && obj.avport_src[avport_idx] == RsxaudioPort::SPDIF_0) { auto &b_data = pkt->spdif_bit_data; sys_uart.notice("[audio_spdif_bit_cmd] Data 0x%x 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x", b_data[0], b_data[1], b_data[2], b_data[3], b_data[4], b_data[5], b_data[6], b_data[7], b_data[8], b_data[9], b_data[10], b_data[11]); break; } } }); vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct audio_ctrl_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return sizeof(ps3av_pkt_audio_ctrl); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } switch (pkt->audio_ctrl_id) { case UartAudioCtrlID::DAC_RESET: case UartAudioCtrlID::DAC_DE_EMPHASIS: case UartAudioCtrlID::AVCLK: sys_uart.notice("[audio_ctrl_cmd] Option 0x%x", pkt->audio_ctrl_id); break; default: break; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } }; struct inc_avset_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (pkt->num_of_video_pkt > 2 || pkt->num_of_av_video_pkt > 4 || pkt->num_of_av_audio_pkt > 4) { return -1; } const auto data_start = static_cast(pkt_buf) + sizeof(ps3av_pkt_inc_avset); u64 video_pkt_sec_size = 0; u64 av_video_pkt_sec_size = 0; u64 av_audio_pkt_sec_size = 0; for (u16 pkt_idx = 0; pkt_idx < pkt->num_of_video_pkt; pkt_idx++) { video_pkt_sec_size += reinterpret_cast( &data_start[video_pkt_sec_size]) ->length + 4ULL; } for (u16 pkt_idx = 0; pkt_idx < pkt->num_of_av_video_pkt; pkt_idx++) { av_video_pkt_sec_size += reinterpret_cast( &data_start[video_pkt_sec_size + av_video_pkt_sec_size]) ->length + 4ULL; } for (u16 pkt_idx = 0; pkt_idx < pkt->num_of_av_audio_pkt; pkt_idx++) { av_audio_pkt_sec_size += reinterpret_cast( &data_start[video_pkt_sec_size + av_video_pkt_sec_size + av_audio_pkt_sec_size]) ->length + 4ULL; } return static_cast(sizeof(ps3av_pkt_inc_avset) + video_pkt_sec_size + av_video_pkt_sec_size + av_audio_pkt_sec_size); } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); auto pkt_data_addr = static_cast(pkt_buf) + sizeof(ps3av_pkt_inc_avset); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } bool syscon_check_passed = true; // Video u32 video_cmd_status = PS3AV_STATUS_SUCCESS; for (u32 video_pkt_idx = 0; video_pkt_idx < pkt->num_of_video_pkt; ++video_pkt_idx) { const auto video_pkt = reinterpret_cast(pkt_data_addr); const u32 subcmd_status = video_pkt_parse(*video_pkt); if (video_pkt->video_head == PS3AV_HEAD_B_ANALOG) { vuart.head_b_initialized = true; } if (subcmd_status != PS3AV_STATUS_SUCCESS) { video_cmd_status = subcmd_status; } pkt_data_addr += video_pkt->hdr.length + 4ULL; } if (pkt->num_of_av_video_pkt == 0U && pkt->num_of_av_audio_pkt == 0U) { vuart.write_resp(pkt->hdr.cid, video_cmd_status); return; } bool hdcp_done[2]{}; // AV Video for (u32 video_av_pkt_idx = 0; video_av_pkt_idx < pkt->num_of_av_video_pkt; video_av_pkt_idx++) { const auto av_video_pkt = reinterpret_cast(pkt_data_addr); const av_video_resp subcmd_resp = av_video_pkt_parse(*av_video_pkt, syscon_check_passed); if (subcmd_resp.status != PS3AV_STATUS_SUCCESS) { vuart.write_resp(pkt->hdr.cid, subcmd_resp.status); return; } if (syscon_check_passed) { hdcp_done[0] |= subcmd_resp.hdcp_done_event[0]; hdcp_done[1] |= subcmd_resp.hdcp_done_event[1]; } pkt_data_addr += av_video_pkt->hdr.length + 4ULL; } vuart.hdmi_res_set[0] = hdcp_done[0]; vuart.hdmi_res_set[1] = hdcp_done[1]; vuart.add_hdmi_events(UartHdmiEvent::HDCP_DONE, vuart.hdmi_res_set[0], vuart.hdmi_res_set[1]); if (vuart.hdmi_res_set[0]) { g_fxo->get().update_av_mute_state( RsxaudioAvportIdx::HDMI_0, false, true); } if (vuart.hdmi_res_set[1]) { g_fxo->get().update_av_mute_state( RsxaudioAvportIdx::HDMI_1, false, true); } bool valid_av_audio_pkt = false; // AV Audio for (u32 audio_av_pkt_idx = 0; audio_av_pkt_idx < pkt->num_of_av_audio_pkt; audio_av_pkt_idx++) { const auto av_audio_pkt = reinterpret_cast(pkt_data_addr); if (av_audio_pkt->avport <= static_cast(UartAudioAvport::HDMI_1)) { valid_av_audio_pkt = true; if (!syscon_check_passed || (av_audio_pkt->avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode)) { syscon_check_passed = false; break; } const u8 hdmi_idx = av_audio_pkt->avport == static_cast(UartAudioAvport::HDMI_1); g_fxo->get().update_hw_param([&](auto &obj) { auto &hdmi = obj.hdmi[hdmi_idx]; hdmi.init = true; const std::array fifomap = { static_cast((av_audio_pkt->fifomap >> 0) & 3U), static_cast((av_audio_pkt->fifomap >> 2) & 3U), static_cast((av_audio_pkt->fifomap >> 4) & 3U), static_cast((av_audio_pkt->fifomap >> 6) & 3U)}; const std::array en_streams = { static_cast(av_audio_pkt->enable & 0x10), static_cast(av_audio_pkt->enable & 0x20), static_cast(av_audio_pkt->enable & 0x40), static_cast(av_audio_pkt->enable & 0x80)}; // Might be wrong const std::array swap_lr = { static_cast(av_audio_pkt->swaplr & 0x10), static_cast(av_audio_pkt->swaplr & 0x20), static_cast(av_audio_pkt->swaplr & 0x40), static_cast(av_audio_pkt->swaplr & 0x80)}; memcpy(hdmi.info_frame.data(), av_audio_pkt->info, sizeof(av_audio_pkt->info)); memcpy(hdmi.chstat.data(), av_audio_pkt->chstat, sizeof(av_audio_pkt->chstat)); hdmi.ch_cfg = hdmi_param_conv(fifomap, en_streams, swap_lr); }); } pkt_data_addr += av_audio_pkt->hdr.length + 4ULL; } if (pkt->num_of_av_video_pkt || valid_av_audio_pkt) { if (!syscon_check_passed) { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SYSCON_COMMUNICATE_FAIL); return; } vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } else { vuart.write_resp(pkt->hdr.cid, PS3AV_STATUS_SUCCESS); } } private: struct video_sce_param { u32 width_div; u32 width; u32 height; }; struct av_video_resp { u32 status = PS3AV_STATUS_SUCCESS; bool hdcp_done_event[2]{}; }; u32 video_pkt_parse(const ps3av_pkt_video_mode &video_head_cfg) { static constexpr video_sce_param sce_param_arr[28] = { {0, 0, 0}, {4, 2880, 480}, {4, 2880, 480}, {4, 2880, 576}, {4, 2880, 576}, {2, 1440, 480}, {2, 1440, 576}, {1, 1920, 1080}, {1, 1920, 1080}, {1, 1920, 1080}, {1, 1280, 720}, {1, 1280, 720}, {1, 1920, 1080}, {1, 1920, 1080}, {1, 1920, 1080}, {1, 1920, 1080}, {1, 1280, 768}, {1, 1280, 1024}, {1, 1920, 1200}, {1, 1360, 768}, {1, 1280, 1470}, {1, 1280, 1470}, {1, 1920, 1080}, {1, 1920, 2205}, {1, 1920, 2205}, {1, 1280, 721}, {1, 720, 481}, {1, 720, 577}}; const auto sce_idx = [&]() -> u8 { switch (video_head_cfg.video_vid) { case 16: return 1; case 1: return 2; case 3: return 4; case 17: return 4; case 5: return 5; case 6: return 6; case 18: return 7; case 7: return 8; case 33: return 8; case 8: return 9; case 34: return 9; case 9: return 10; case 31: return 10; case 10: return 11; case 32: return 11; case 11: return 12; case 35: return 12; case 37: return 12; case 12: return 13; case 36: return 13; case 38: return 13; case 19: return 14; case 20: return 15; case 13: return 16; case 14: return 17; case 15: return 18; case 21: return 19; case 22: return 20; case 27: return 20; case 23: return 21; case 28: return 21; case 24: return 22; case 25: return 23; case 29: return 23; case 26: return 24; case 30: return 24; case 39: return 25; case 40: return 26; case 41: return 27; default: return umax; } }(); const video_sce_param &sce_param = sce_param_arr[sce_idx]; if (sce_idx == umax || video_head_cfg.video_head > PS3AV_HEAD_B_ANALOG || video_head_cfg.video_order > 1 || video_head_cfg.video_format > 16 || video_head_cfg.video_out_format > 16 || ((1ULL << video_head_cfg.video_out_format) & 0x1CE07) == 0U || video_head_cfg.unk2 > 3 || video_head_cfg.pitch & 7 || video_head_cfg.pitch > UINT16_MAX || (video_head_cfg.width != 1280U && ((video_head_cfg.width & 7) != 0U || video_head_cfg.width > UINT16_MAX)) || (sce_param.width != 720 && video_head_cfg.width > sce_param.width / sce_param.width_div) || !((video_head_cfg.height == 1470U && (sce_param.height == 721 || sce_param.height == 481 || sce_param.height == 577)) || (video_head_cfg.height <= sce_param.height && video_head_cfg.height <= UINT16_MAX))) { return PS3AV_STATUS_INVALID_VIDEO_PARAM; } sys_uart.notice( "[inc_avset_cmd] new resolution on HEAD_%c width=%u height=%u", video_head_cfg.video_head == PS3AV_HEAD_A_HDMI ? 'A' : 'B', video_head_cfg.width, video_head_cfg.height); return PS3AV_STATUS_SUCCESS; } av_video_resp av_video_pkt_parse(const ps3av_pkt_av_video_cs &pkt, bool &syscon_pkt_valid) { if (pkt.avport <= static_cast(UartAudioAvport::HDMI_1)) { if (pkt.av_vid > 23) { return {PS3AV_STATUS_INVALID_AV_PARAM}; } if (pkt.avport == static_cast(UartAudioAvport::HDMI_1) && !g_cfg.core.debug_console_mode) { syscon_pkt_valid = false; } else if (syscon_pkt_valid) { // HDMI setup, code 0x80 av_video_resp resp{}; resp.hdcp_done_event[pkt.avport] = true; return resp; } } else { if ((pkt.avport != static_cast(UartAudioAvport::AVMULTI_0) && pkt.avport != static_cast(UartAudioAvport::AVMULTI_1)) || pkt.av_vid > 23 || (pkt.av_vid > 12 && pkt.av_vid != 18U)) { return {PS3AV_STATUS_INVALID_AV_PARAM}; } if (pkt.avport == static_cast(UartAudioAvport::AVMULTI_1)) { syscon_pkt_valid = false; } else if (syscon_pkt_valid) { // AVMULTI setup } } return {}; } static rsxaudio_hw_param_t::hdmi_param_t::hdmi_ch_cfg_t hdmi_param_conv( const std::array &map, const std::array &en, const std::array &swap) { std::array result{}; u8 ch_cnt = 0; for (usz stream_idx = 0; stream_idx < SYS_RSXAUDIO_SERIAL_STREAM_CNT; stream_idx++) { const u8 stream_pos = map[stream_idx]; if (en[stream_pos]) { result[stream_idx * 2 + 0] = stream_pos * 2 + swap[stream_pos]; result[stream_idx * 2 + 1] = stream_pos * 2 + !swap[stream_pos]; ch_cnt = static_cast((stream_idx + 1) * 2); } else { result[stream_idx * 2 + 0] = rsxaudio_hw_param_t::hdmi_param_t::MAP_SILENT_CH; result[stream_idx * 2 + 1] = rsxaudio_hw_param_t::hdmi_param_t::MAP_SILENT_CH; } } const AudioChannelCnt ch_cnt_conv = [&]() { switch (ch_cnt) { default: case 0: case 2: return AudioChannelCnt::STEREO; case 4: case 6: return AudioChannelCnt::SURROUND_5_1; case 8: return AudioChannelCnt::SURROUND_7_1; } }(); return {result, ch_cnt_conv}; } }; struct generic_reply_cmd : public ps3av_cmd { u16 get_size(vuart_av_thread & /*vuart*/, const void * /*pkt_buf*/) override { return 0; } void execute(vuart_av_thread &vuart, const void *pkt_buf) override { const auto pkt = static_cast(pkt_buf); if (vuart.get_reply_buf_free_size() < sizeof(ps3av_pkt_reply_hdr)) { vuart.write_resp(pkt->cid, PS3AV_STATUS_BUFFER_OVERFLOW); return; } sys_uart.todo("Unimplemented cid=0x%08x", pkt->cid); vuart.write_resp(pkt->cid, PS3AV_STATUS_SUCCESS); } }; error_code sys_uart_initialize(ppu_thread &ppu) { ppu.state += cpu_flag::wait; sys_uart.trace("sys_uart_initialize()"); if (!g_ps3_process_info.has_root_perm()) { return CELL_ENOSYS; } auto &vuart_thread = g_fxo->get(); if (vuart_thread.initialized.test_and_set()) { return CELL_EPERM; } return CELL_OK; } error_code sys_uart_receive(ppu_thread &ppu, vm::ptr buffer, u64 size, u32 mode) { sys_uart.trace("sys_uart_receive(buffer=*0x%x, size=0x%llx, mode=0x%x)", buffer, size, mode); if (!g_ps3_process_info.has_root_perm()) { return CELL_ENOSYS; } if (!size) { return CELL_OK; } if (mode & ~(BLOCKING_BIG_OP | NOT_BLOCKING_BIG_OP)) { 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 { constexpr 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 (!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 (!g_ps3_process_info.has_root_perm()) { return CELL_ENOSYS; } if (!size) { return CELL_OK; } if (mode & ~(BLOCKING_BIG_OP | NOT_BLOCKING_OP | NOT_BLOCKING_BIG_OP)) { 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); constexpr 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); if (!g_ps3_process_info.has_root_perm()) { return CELL_ENOSYS; } auto &vuart_thread = g_fxo->get(); if (!vuart_thread.initialized) { return CELL_ESRCH; } if (!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; } void vuart_av_thread::operator()() { while (thread_ctrl::state() != thread_state::aborting) { if (Emu.IsPausedOrReady()) { thread_ctrl::wait_for(5000); continue; } const u64 hdmi_event_dist[2] = {hdmi_event_handler[0].time_until_next(), hdmi_event_handler[1].time_until_next()}; bool update_dist = false; if (hdmi_event_dist[0] == 0) { dispatch_hdmi_event(hdmi_event_handler[0].get_occured_event(), UartAudioAvport::HDMI_0); update_dist |= hdmi_event_handler[0].events_available(); } if (hdmi_event_dist[1] == 0) { dispatch_hdmi_event(hdmi_event_handler[1].get_occured_event(), UartAudioAvport::HDMI_1); update_dist |= hdmi_event_handler[1].events_available(); } if (update_dist) { continue; } const u64 wait_time = [&]() { if (hdmi_event_dist[0] != 0 && hdmi_event_dist[1] != 0) return std::min(hdmi_event_dist[0], hdmi_event_dist[1]); else return std::max(hdmi_event_dist[0], hdmi_event_dist[1]); }(); std::unique_lock lock(tx_wake_m); if (!tx_buf.get_used_size()) { tx_wake_c.wait_unlock(wait_time ? wait_time : -1, lock); } else { lock.unlock(); } if (u32 read_size = read_tx_data(temp_tx_buf, PS3AV_TX_BUF_SIZE)) { parse_tx_buffer(read_size); // Give vsh some time thread_ctrl::wait_for(1000 * 100 / g_cfg.core.clocks_scale); commit_rx_buf(false); commit_rx_buf(true); } } } void vuart_av_thread::parse_tx_buffer(u32 buf_size) { if (buf_size >= PS3AV_TX_BUF_SIZE) { while (read_tx_data(temp_tx_buf, PS3AV_TX_BUF_SIZE) >= PS3AV_TX_BUF_SIZE) ; write_resp(reinterpret_cast *>(temp_tx_buf)[3], PS3AV_STATUS_BUFFER_OVERFLOW); return; } 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.todo("Unimplemented AV version: 0x%04x", 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%08x", hdr->cid); continue; } const auto cmd_size = cmd->get_size(*this, pkt_storage); if (cmd_size != pkt_size && cmd_size) { sys_uart.error("Invalid size for cid=0x%x, expected=0x%x, got=0x%x", static_cast(pkt_storage)->cid, cmd_size, pkt_size); write_resp(static_cast(hdr->cid.get()), PS3AV_STATUS_INVALID_SAMPLE_SIZE); return; } cmd->execute(*this, pkt_storage); } } vuart_av_thread &vuart_av_thread::operator=(thread_state) { { std::lock_guard lock(tx_wake_m); } 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 (u32 size = static_cast(tx_buf.push(data, data_sz, true))) { lock.unlock(); tx_wake_c.notify_all(); return size; } return 0; } u32 vuart_av_thread::get_tx_bytes() { return static_cast(tx_buf.get_used_size()); } u32 vuart_av_thread::read_rx_data(void *data, u32 data_sz) { return static_cast(rx_buf.pop(data, data_sz, true)); } u32 vuart_av_thread::read_tx_data(void *data, u32 data_sz) { std::unique_lock lock(tx_rdy_m); if (u32 size = static_cast(tx_buf.pop(data, data_sz, true))) { lock.unlock(); tx_rdy_c.notify_all(); return size; } return 0; } u32 vuart_av_thread::get_reply_buf_free_size() { return sizeof(temp_rx_buf.buf) - temp_rx_buf.crnt_size; } template void vuart_av_thread::write_resp(u32 cid, u32 status, const void *data, u16 data_size) { const ps3av_pkt_reply_hdr pkt_hdr = {PS3AV_VERSION, data_size + 8U, cid | PS3AV_REPLY_BIT, status}; if (status != PS3AV_STATUS_SUCCESS) { sys_uart.error("Packet failed cid=0x%08x status=0x%02x", cid, status); } temp_buf &buf = UseScBuffer ? temp_rx_sc_buf : temp_rx_buf; const u32 total_size = sizeof(pkt_hdr) + data_size; if (buf.crnt_size + total_size <= sizeof(buf.buf)) { memcpy(&buf.buf[buf.crnt_size], &pkt_hdr, sizeof(pkt_hdr)); memcpy(&buf.buf[buf.crnt_size + sizeof(pkt_hdr)], data, data_size); buf.crnt_size += total_size; } } std::shared_ptr vuart_av_thread::get_cmd(u32 cid) { switch (cid) { case PS3AV_CID_AV_CEC_MESSAGE: case PS3AV_CID_AV_UNK11: case PS3AV_CID_AV_UNK12: return std::make_shared(); // AV commands case PS3AV_CID_AV_INIT: return std::make_shared(); case PS3AV_CID_AV_FIN: 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(); case PS3AV_CID_AV_GET_BKSV_LIST: return std::make_shared(); case PS3AV_CID_AV_ENABLE_EVENT: return std::make_shared(); case PS3AV_CID_AV_DISABLE_EVENT: return std::make_shared(); case PS3AV_CID_AV_TV_MUTE: return std::make_shared(); case PS3AV_CID_AV_NULL_CMD: return std::make_shared(); case PS3AV_CID_AV_GET_AKSV: return std::make_shared(); case PS3AV_CID_AV_VIDEO_DISABLE_SIG: return std::make_shared(); case PS3AV_CID_AV_VIDEO_YTRAPCONTROL: return std::make_shared(); case PS3AV_CID_AV_AUDIO_MUTE: return std::make_shared(); case PS3AV_CID_AV_ACP_CTRL: return std::make_shared(); case PS3AV_CID_AV_SET_ACP_PACKET: return std::make_shared(); case PS3AV_CID_AV_ADD_SIGNAL_CTL: return std::make_shared(); case PS3AV_CID_AV_SET_CGMS_WSS: return std::make_shared(); case PS3AV_CID_AV_HDMI_MODE: return std::make_shared(); case PS3AV_CID_AV_GET_CEC_CONFIG: return std::make_shared(); // Video commands case PS3AV_CID_VIDEO_INIT: return std::make_shared(); case PS3AV_CID_VIDEO_ROUTE: return std::make_shared(); case PS3AV_CID_VIDEO_FORMAT: return std::make_shared(); case PS3AV_CID_VIDEO_PITCH: return std::make_shared(); case PS3AV_CID_VIDEO_GET_HW_CONF: return std::make_shared(); // Audio commands case PS3AV_CID_AUDIO_INIT: return std::make_shared(); case PS3AV_CID_AUDIO_MODE: return std::make_shared(); case PS3AV_CID_AUDIO_MUTE: return std::make_shared(); case PS3AV_CID_AUDIO_ACTIVE: return std::make_shared(); case PS3AV_CID_AUDIO_INACTIVE: return std::make_shared(); case PS3AV_CID_AUDIO_SPDIF_BIT: return std::make_shared(); case PS3AV_CID_AUDIO_CTRL: return std::make_shared(); // Multipacket case PS3AV_CID_AVB_PARAM: return std::make_shared(); default: return {}; } } void vuart_av_thread::commit_rx_buf(bool syscon_buf) { temp_buf &buf = syscon_buf ? temp_rx_sc_buf : temp_rx_buf; std::unique_lock lock(rx_wake_m); rx_buf.push(buf.buf, buf.crnt_size, true); buf.crnt_size = 0; if (rx_buf.get_used_size()) { lock.unlock(); rx_wake_c.notify_all(); } } void vuart_av_thread::add_hdmi_events(UartHdmiEvent first_event, UartHdmiEvent last_event, bool hdmi_0, bool hdmi_1) { if (hdmi_0) { hdmi_event_handler[0].set_target_state(first_event, last_event); } if (hdmi_1 && g_cfg.core.debug_console_mode) { hdmi_event_handler[1].set_target_state(first_event, last_event); } } void vuart_av_thread::add_hdmi_events(UartHdmiEvent last_event, bool hdmi_0, bool hdmi_1) { add_hdmi_events(last_event, last_event, hdmi_0, hdmi_1); } void vuart_av_thread::dispatch_hdmi_event(UartHdmiEvent event, UartAudioAvport hdmi) { const bool hdmi_0 = hdmi == UartAudioAvport::HDMI_0; const bool hdmi_1 = hdmi == UartAudioAvport::HDMI_1; switch (event) { case UartHdmiEvent::UNPLUGGED: { add_unplug_event(hdmi_0, hdmi_1); break; } case UartHdmiEvent::PLUGGED: { add_plug_event(hdmi_0, hdmi_1); break; } case UartHdmiEvent::HDCP_DONE: { add_hdcp_done_event(hdmi_0, hdmi_1); break; } default: break; } } RsxaudioAvportIdx vuart_av_thread::avport_to_idx(UartAudioAvport avport) { switch (avport) { case UartAudioAvport::HDMI_0: return RsxaudioAvportIdx::HDMI_0; case UartAudioAvport::HDMI_1: return RsxaudioAvportIdx::HDMI_1; case UartAudioAvport::AVMULTI_0: return RsxaudioAvportIdx::AVMULTI; case UartAudioAvport::SPDIF_0: return RsxaudioAvportIdx::SPDIF_0; case UartAudioAvport::SPDIF_1: return RsxaudioAvportIdx::SPDIF_1; default: ensure(false); return RsxaudioAvportIdx::HDMI_0; } } void vuart_av_thread::add_unplug_event(bool hdmi_0, bool hdmi_1) { if ((hdmi_events_bitmask & PS3AV_EVENT_BIT_UNPLUGGED) == 0) return; ps3av_header pkt{}; pkt.version = av_cmd_ver; pkt.length = sizeof(ps3av_header) - 4; pkt.cid = PS3AV_CID_EVENT_UNPLUGGED; if (hdmi_0) { g_fxo->get().update_av_mute_state(RsxaudioAvportIdx::HDMI_0, false, true); hdcp_first_auth[0] = true; commit_event_data(&pkt, sizeof(pkt)); } if (hdmi_1) { g_fxo->get().update_av_mute_state(RsxaudioAvportIdx::HDMI_1, false, true); hdcp_first_auth[1] = true; pkt.cid |= 0x10000; commit_event_data(&pkt, sizeof(pkt)); } } void vuart_av_thread::add_plug_event(bool hdmi_0, bool hdmi_1) { if ((hdmi_events_bitmask & PS3AV_EVENT_BIT_PLUGGED) == 0) return; ps3av_pkt_hdmi_plugged_event pkt{}; pkt.hdr.version = av_cmd_ver; pkt.hdr.length = sizeof(ps3av_pkt_hdmi_plugged_event) - 8; pkt.hdr.cid = PS3AV_CID_EVENT_PLUGGED; if (hdmi_0) { g_fxo->get().update_av_mute_state(RsxaudioAvportIdx::HDMI_0, false, true); av_get_monitor_info_cmd::set_hdmi_display_cfg( *this, pkt.minfo, static_cast(UartAudioAvport::HDMI_0)); commit_event_data(&pkt, sizeof(pkt) - 4); } if (hdmi_1) { g_fxo->get().update_av_mute_state(RsxaudioAvportIdx::HDMI_1, false, true); memset(&pkt.minfo, 0, sizeof(pkt.minfo)); pkt.hdr.cid |= 0x10000; av_get_monitor_info_cmd::set_hdmi_display_cfg( *this, pkt.minfo, static_cast(UartAudioAvport::HDMI_1)); commit_event_data(&pkt, sizeof(pkt) - 4); } } void vuart_av_thread::add_hdcp_done_event(bool hdmi_0, bool hdmi_1) { u16 pkt_size = offsetof(ps3av_pkt_hdmi_hdcp_done_event, ksv_arr); ps3av_pkt_hdmi_hdcp_done_event pkt{}; pkt.hdr.version = av_cmd_ver; if (hdmi_behavior_mode == PS3AV_HDMI_BEHAVIOR_NORMAL || !(hdmi_behavior_mode & PS3AV_HDMI_BEHAVIOR_HDCP_OFF)) { pkt.ksv_cnt = 1; memcpy(&pkt.ksv_arr[0], PS3AV_BKSV_VALUE, sizeof(PS3AV_BKSV_VALUE)); pkt_size = (pkt_size + 5 * pkt.ksv_cnt + 3) & 0xFFFC; } pkt.hdr.length = pkt_size - 4; if (hdmi_0) { g_fxo->get().update_av_mute_state(RsxaudioAvportIdx::HDMI_0, false, true, false); if (hdcp_first_auth[0]) { if (hdmi_events_bitmask & PS3AV_EVENT_BIT_HDCP_DONE) { hdcp_first_auth[0] = false; pkt.hdr.cid = PS3AV_CID_EVENT_HDCP_DONE; commit_event_data(&pkt, pkt_size); } } else if (hdmi_events_bitmask & PS3AV_EVENT_BIT_HDCP_REAUTH) { pkt.hdr.cid = PS3AV_CID_EVENT_HDCP_REAUTH; commit_event_data(&pkt, pkt_size); } } if (hdmi_1) { g_fxo->get().update_av_mute_state(RsxaudioAvportIdx::HDMI_1, false, true, false); if (hdcp_first_auth[1]) { if (hdmi_events_bitmask & PS3AV_EVENT_BIT_HDCP_DONE) { hdcp_first_auth[1] = false; pkt.hdr.cid = PS3AV_CID_EVENT_HDCP_DONE | 0x10000; commit_event_data(&pkt, pkt_size); } } else if (hdmi_events_bitmask & PS3AV_EVENT_BIT_HDCP_REAUTH) { pkt.hdr.cid = PS3AV_CID_EVENT_HDCP_REAUTH | 0x10000; commit_event_data(&pkt, pkt_size); } } } void vuart_av_thread::commit_event_data(const void *data, u16 data_size) { std::unique_lock lock(rx_wake_m); rx_buf.push(data, data_size, true); if (rx_buf.get_used_size()) { lock.unlock(); rx_wake_c.notify_all(); } } vuart_hdmi_event_handler::vuart_hdmi_event_handler(u64 time_offset) : time_offset(time_offset) {} void vuart_hdmi_event_handler::set_target_state(UartHdmiEvent start_state, UartHdmiEvent end_state) { ensure(start_state != UartHdmiEvent::NONE && static_cast(start_state) <= static_cast(end_state)); base_state = static_cast(std::min( static_cast(start_state) - 1, static_cast(current_to_state))); target_state = end_state; if (!events_available()) { advance_state(); } } bool vuart_hdmi_event_handler::events_available() { return time_of_next_event != 0; } u64 vuart_hdmi_event_handler::time_until_next() { const u64 current_time = get_system_time(); if (!events_available() || current_time + EVENT_TIME_THRESHOLD >= time_of_next_event) { return 0; } return time_of_next_event - current_time; } UartHdmiEvent vuart_hdmi_event_handler::get_occured_event() { if (events_available() && time_until_next() == 0) { const UartHdmiEvent occured = current_to_state; advance_state(); return occured; } return UartHdmiEvent::NONE; } void vuart_hdmi_event_handler::schedule_next() { time_of_next_event = get_system_time() + (EVENT_TIME_DURATION + time_offset) * 100 / g_cfg.core.clocks_scale; } void vuart_hdmi_event_handler::advance_state() { current_state = current_to_state; while (base_state != target_state) { base_state = static_cast(static_cast(base_state) + 1); if (current_state == UartHdmiEvent::UNPLUGGED && base_state == UartHdmiEvent::UNPLUGGED) { continue; } if (current_state == UartHdmiEvent::PLUGGED && base_state == UartHdmiEvent::PLUGGED) { continue; } if (current_state == UartHdmiEvent::HDCP_DONE && base_state == UartHdmiEvent::PLUGGED) { continue; } current_to_state = base_state; schedule_next(); return; } time_of_next_event = 0; }