diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index b291643dd..65b96a623 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -66,6 +66,7 @@ KernelState::KernelState(Emulator* emulator) processor_ = emulator->processor(); file_system_ = emulator->file_system(); xam_state_ = std::make_unique(emulator, this); + smc_ = std::make_unique(); InitializeKernelGuestGlobals(); kernel_version_ = KernelVersion(cvars::kernel_build_version); @@ -894,6 +895,9 @@ void KernelState::RegisterNotifyListener(XNotifyListener* listener) { // XN_SYS_SIGNINCHANGED x2 listener->EnqueueNotification(kXNotificationSystemSignInChanged, 1); listener->EnqueueNotification(kXNotificationSystemSignInChanged, 1); + + listener->EnqueueNotification(kXNotificationDvdDriveTrayStateChanged, + X_DVD_DISC_STATE::XBOX_360_GAME_DISC); } } diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index e81b92cea..20281044d 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -23,6 +23,7 @@ #include "xenia/base/mutex.h" #include "xenia/cpu/backend/backend.h" #include "xenia/cpu/export_resolver.h" +#include "xenia/kernel/smc.h" #include "xenia/kernel/util/kernel_fwd.h" #include "xenia/kernel/util/native_list.h" #include "xenia/kernel/util/object_table.h" @@ -192,6 +193,8 @@ class KernelState { xam::XamState* xam_state() const { return xam_state_.get(); } + SystemManagementController* smc() const { return smc_.get(); } + xam::AchievementManager* achievement_manager() const { return xam_state()->achievement_manager(); } @@ -349,6 +352,7 @@ class KernelState { cpu::Processor* processor_; vfs::VirtualFileSystem* file_system_; std::unique_ptr xam_state_; + std::unique_ptr smc_; KernelVersion kernel_version_; diff --git a/src/xenia/kernel/smc.cc b/src/xenia/kernel/smc.cc new file mode 100644 index 000000000..003f70d72 --- /dev/null +++ b/src/xenia/kernel/smc.cc @@ -0,0 +1,187 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2025 Xenia Canary. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/smc.h" +#include "xenia/kernel/util/shim_utils.h" + +DECLARE_int32(avpack); + +namespace xe { +namespace kernel { + +SystemManagementController::SystemManagementController() + : dvd_tray_state_(X_DVD_TRAY_STATE::OPEN) { + smc_commands_[X_SMC_CMD::QUERY_TEMP_SENSOR] = + std::bind(&SystemManagementController::QueryTemperatureSensor, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::QUERY_TRAY] = + std::bind(&SystemManagementController::QueryDriveTraySensor, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::QUERY_AV_PACK] = + std::bind(&SystemManagementController::QueryAvPack, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::QUERY_SMC_VERSION] = + std::bind(&SystemManagementController::QuerySmcVersion, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::QUERY_IR_ADDRESS] = + std::bind(&SystemManagementController::QueryIRAddress, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::QUERY_TILT_SENSOR] = + std::bind(&SystemManagementController::QueryTiltState, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::SET_FAN_SPEED_CPU] = + std::bind(&SystemManagementController::SetFanSpeed, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::SET_FAN_SPEED_GPU] = + std::bind(&SystemManagementController::SetFanSpeed, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::SET_DVD_TRAY] = + std::bind(&SystemManagementController::SetDriveTray, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::SET_IR_ADDRESS] = + std::bind(&SystemManagementController::SetIRAddress, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::SET_POWER_LED] = + std::bind(&SystemManagementController::SetPowerLed, this, + std::placeholders::_1, std::placeholders::_2); + smc_commands_[X_SMC_CMD::SET_LEDS] = + std::bind(&SystemManagementController::SetLedState, this, + std::placeholders::_1, std::placeholders::_2); +}; +SystemManagementController::~SystemManagementController() {}; + +void SystemManagementController::SetTrayState(X_DVD_TRAY_STATE state) { + dvd_tray_state_ = state; + kernel_state()->BroadcastNotification(kXNotificationDvdDriveTrayStateChanged, + static_cast(state)); +} + +void SystemManagementController::CallCommand(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + const auto itr = smc_commands_.find(smc_message->command); + if (itr == smc_commands_.cend()) { + XELOGW("Unimplemented SMC Command: {:02X}", + static_cast(smc_message->command)); + return; + } + + itr->second(smc_message, smc_response); +} + +void SystemManagementController::QueryTemperatureSensor( + X_SMC_DATA* smc_message, X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->temps.cpu.SetTemp(69.6f); + smc_response->temps.gpu.SetTemp(69.9f); + smc_response->temps.edram.SetTemp(69.6f); + smc_response->temps.mb.SetTemp(69.9f); +} + +void SystemManagementController::QueryDriveTraySensor( + X_SMC_DATA* smc_message, X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->dvd_tray.state = dvd_tray_state_; +}; + +void SystemManagementController::QueryAvPack(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->av_pack.av_pack = 0; + + const auto entry = av_pack_to_smc_value.find(cvars::avpack); + if (entry == av_pack_to_smc_value.cend()) { + return; + } + + smc_response->av_pack.av_pack = entry->second; +} + +void SystemManagementController::QuerySmcVersion(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->smc_version.unk = smc_version[0]; + smc_response->smc_version.major = smc_version[1]; + smc_response->smc_version.minor = smc_version[2]; +} + +void SystemManagementController::QueryIRAddress(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->ir_address.ir_address = ir_address_; +} + +void SystemManagementController::QueryTiltState(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->tilt_state.tilt_state = tilt_state_; +} + +void SystemManagementController::SetIRAddress(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + if (!smc_response) { + return; + } + + smc_response->command = smc_message->command; + smc_response->ir_address.ir_address = ir_address_; +} + +void SystemManagementController::SetDriveTray(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + SetTrayState( + static_cast((smc_message->smc_data[0] & 0xF) % 5)); +} + +void SystemManagementController::SetFanSpeed(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + if (smc_message->command == X_SMC_CMD::SET_FAN_SPEED_CPU) { + cpu_fan_speed_ = (smc_message->smc_data[0] - 0x80); + } + if (smc_message->command == X_SMC_CMD::SET_FAN_SPEED_GPU) { + gpu_fan_speed_ = (smc_message->smc_data[0] - 0x80); + } +} + +void SystemManagementController::SetPowerLed(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + power_led_state_ = {smc_message->power_led_state.state, + smc_message->power_led_state.animate}; +} + +void SystemManagementController::SetLedState(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response) { + led_state_ = {smc_message->led_state.state, smc_message->led_state.region}; +} + +} // namespace kernel +} // namespace xe \ No newline at end of file diff --git a/src/xenia/kernel/smc.h b/src/xenia/kernel/smc.h new file mode 100644 index 000000000..f423f746a --- /dev/null +++ b/src/xenia/kernel/smc.h @@ -0,0 +1,218 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2025 Xenia Canary. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_SMC_H_ +#define XENIA_KERNEL_SMC_H_ + +#include +#include +#include +#include + +enum X_DVD_DISC_STATE { + NO_DISC, + XBOX_360_GAME_DISC, + XBOX_GAME_DISC, + UNKNOWN_DISC, + MOVIE_DVD, + MOVIE_DVD2, + MOVIE_DVD3, + CD, + MIXED_MEDIA_DISC, + UNKNOWN_DISC2, +}; + +enum class X_DVD_TRAY_STATE : uint8_t { + OPEN, + UNKNOWN, + CLOSED, + OPENING, + CLOSING +}; + +enum REMOTE_CONTROL : uint8_t { + MEDIA_REMOTE_BOTH = 0x01, + MEDIA_REMOTE_360 = 0x0F +}; + +enum TILT_STATE : uint8_t { VERTICAL, HORIZONTAL }; + +enum POWER_LED_STATE : uint8_t { + POWER_ON = 0x01, + POWER_DEFAULT = 0x02, + POWER_OFF = 0x03, + POWER_BLINK = 0x10 +}; + +enum LED_STATE : uint8_t { + OFF = 0x00, + RED = 0x08, + GREEN = 0x80, + ORANGE = 0x88 +}; + +// https://free60.org/Hardware/Console/SMC/ +enum X_SMC_CMD : uint8_t { + POWERON_STATE = 0x1, + QUERY_RTC = 0x4, + QUERY_TEMP_SENSOR = 0x7, + QUERY_TRAY = 0xA, + QUERY_AV_PACK = 0xF, + I2C_READ_WRITE = 0x11, + QUERY_SMC_VERSION = 0x12, + FIFO_TEST = 0x13, + QUERY_IR_ADDRESS = 0x16, + QUERY_TILT_SENSOR = 0x17, + READ_82_INTERRUPTS = 0x1E, + READ_8E_INTERRUPTS = 0x20, + SET_STANDBY = 0x82, + SET_TIME = 0x85, + SET_FAN_ALGORITHM = 0x88, + SET_FAN_SPEED_CPU = 0x89, + SET_DVD_TRAY = 0x8B, + SET_POWER_LED = 0x8C, + SET_AUDIO_MUTE = 0x8D, + ARGON_RELATED = 0x90, + SET_FAN_SPEED_GPU = + 0x94, // not present on slim, not used/respected on newer fat + SET_IR_ADDRESS = 0x95, + SET_DVD_TRAY_SECURE = 0x98, + SET_LEDS = 0x99, + SET_RTC_WAKE = 0x9A, + ANA_RELATED = 0x9B, + SET_ASYNC_OPERATION = 0x9C, + SET_82_INTERRUPTS = 0x9D, + SET_9F_INTERRUPTS = 0x9F, +}; + +struct X_TEMPERATURE_DATA { + uint8_t integer_value_; + uint8_t fractional_value_; + + void SetTemp(float celsius_temp) { + const uint16_t value = static_cast(celsius_temp * 256.0f); + + integer_value_ = value & 0xFF; + fractional_value_ = value >> 8; + } +}; + +struct X_SMC_DATA { + X_SMC_CMD command; + + union { + uint8_t smc_data[15]; + + // QUERY_TEMP_SENSOR + struct { + X_TEMPERATURE_DATA cpu; + X_TEMPERATURE_DATA gpu; + X_TEMPERATURE_DATA edram; + X_TEMPERATURE_DATA mb; + } temps; + + // QUERY_TRAY + struct { + X_DVD_TRAY_STATE state; + } dvd_tray; + + // QUERY_AV_PACK + struct { + uint8_t av_pack; + } av_pack; + + // QUERY_SMC_VERSION + struct { + uint8_t unk; + uint8_t major; + uint8_t minor; + } smc_version; + + // QUERY_IR_ADDRESS + struct { + REMOTE_CONTROL ir_address; + } ir_address; + + // QUERY_TILT_STATE + struct { + TILT_STATE tilt_state; + } tilt_state; + + // POWER_LED_STATE + struct { + POWER_LED_STATE state; + uint8_t animate; + } power_led_state; + + // LED_STATE + struct { + LED_STATE state; + uint8_t region; // top_left >> 3 | top_right >> 2 | bottom_left >> 1 | + // bottom_right; + } led_state; + }; +}; + +namespace xe { +namespace kernel { + +// SMC = System Management Controller +// https://github.com/landaire/LaunchCode/blob/master/LaunchCode/smc.cpp +// https://github.com/XboxUnity/freestyledash/blob/master/Freestyle/Tools/SMC/smc.cpp +class SystemManagementController { + public: + SystemManagementController(); + ~SystemManagementController(); + + std::array GetSMCVersion() const { return smc_version; } + X_DVD_TRAY_STATE GetTrayState() const { return dvd_tray_state_; } + + void SetTrayState(X_DVD_TRAY_STATE state); + + void CallCommand(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + + private: + const std::array smc_version = {65, 2, 1}; + const std::map av_pack_to_smc_value = { + {0, 0x43}, {3, 0x4F}, {4, 0x13}, {5, 0x0F}, {6, 0x5B}, {8, 0x1F}}; + + void QueryTemperatureSensor(X_SMC_DATA* smc_message, + X_SMC_DATA* smc_response); + void QueryDriveTraySensor(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void QueryAvPack(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void QuerySmcVersion(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void QueryIRAddress(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void QueryTiltState(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void SetDriveTray(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void SetFanSpeed(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void SetIRAddress(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void SetPowerLed(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + void SetLedState(X_SMC_DATA* smc_message, X_SMC_DATA* smc_response); + + std::map> + smc_commands_; + + X_DVD_TRAY_STATE dvd_tray_state_ = X_DVD_TRAY_STATE::OPEN; + REMOTE_CONTROL ir_address_ = REMOTE_CONTROL::MEDIA_REMOTE_360; + TILT_STATE tilt_state_ = TILT_STATE::VERTICAL; + + uint32_t cpu_fan_speed_ = 100; + uint32_t gpu_fan_speed_ = 100; + + std::pair power_led_state_ = { + POWER_LED_STATE::POWER_ON, 0x00}; + + std::pair led_state_ = {LED_STATE::GREEN, 0x01}; +}; + +} // namespace kernel +} // namespace xe + +#endif diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 63defba4a..a5ea05144 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -11,6 +11,7 @@ #include "xenia/base/math.h" #include "xenia/base/string_util.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/smc.h" #include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_content_device.h" @@ -622,13 +623,29 @@ dword_result_t XamSwapDisc_entry( } DECLARE_XAM_EXPORT1(XamSwapDisc, kContent, kSketchy); -dword_result_t XamLoaderGetMediaInfoEx_entry(dword_t unk1, dword_t unk2, - lpdword_t unk3) { - *unk3 = 0; - return 0; +dword_result_t XamLoaderGetDvdTrayState_entry() { + return static_cast(kernel_state()->smc()->GetTrayState()); } +DECLARE_XAM_EXPORT1(XamLoaderGetDvdTrayState, kNone, kImplemented); -DECLARE_XAM_EXPORT1(XamLoaderGetMediaInfoEx, kContent, kStub); +void XamLoaderGetMediaInfoEx_entry(lpdword_t media_type, lpdword_t unk2, + lpdword_t unk3) { + if (media_type) { + *media_type = X_DVD_DISC_STATE::XBOX_360_GAME_DISC; + } + if (unk2) { + *unk2 = 0; + } + if (unk3) { + *unk3 = 0; + } +} +DECLARE_XAM_EXPORT1(XamLoaderGetMediaInfoEx, kNone, kStub); + +void XamLoaderGetMediaInfo_entry(lpdword_t media_type, lpdword_t unk2) { + XamLoaderGetMediaInfoEx_entry(media_type, unk2, 0); +} +DECLARE_XAM_EXPORT1(XamLoaderGetMediaInfo, kNone, kStub); dword_result_t XamContentLaunchImageFromFileInternal_entry( lpstring_t image_location, lpstring_t xex_name, dword_t unk) { diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc index 72ed9b7f0..80a331e63 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_hal.cc @@ -7,11 +7,9 @@ ****************************************************************************** */ -#include "xenia/base/logging.h" -#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/smc.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" -#include "xenia/xbox.h" DECLARE_int32(avpack); @@ -36,6 +34,23 @@ DECLARE_XBOXKRNL_EXPORT2(HalReturnToFirmware, kNone, kStub, kImportant); dword_result_t HalGetCurrentAVPack_entry() { return cvars::avpack; } DECLARE_XBOXKRNL_EXPORT1(HalGetCurrentAVPack, kNone, kImplemented); +void HalSendSMCMessage_entry(pointer_t smc_message, + pointer_t smc_response) { + if (!smc_message) { + return; + } + + kernel_state()->smc()->CallCommand(smc_message, smc_response); +} +DECLARE_XBOXKRNL_EXPORT3(HalSendSMCMessage, kNone, kStub, kImportant, + kHighFrequency); + +void HalOpenCloseODDTray_entry(dword_t open_close) { + kernel_state()->smc()->SetTrayState(open_close ? X_DVD_TRAY_STATE::CLOSED + : X_DVD_TRAY_STATE::OPEN); +} +DECLARE_XBOXKRNL_EXPORT1(HalOpenCloseODDTray, kNone, kStub); + } // namespace xboxkrnl } // namespace kernel } // namespace xe