[Kernel] - Implemented SystemManagementController Class

- Added handling (partial or dummy) for SMC actions:
 - QUERY_TEMP_SENSOR, QUERY_TRAY, QUERY_AV_PACK, QUERY_SMC_VERSION, QUERY_IR_ADDRESS, QUERY_TILT_SENSOR
 - SET_FAN_SPEED_CPU, SET_FAN_SPEED_GPU, SET_DVD_TRAY, SET_IR_ADDRESS, SET_POWER_LED, SET_LEDS

- Stub HalOpenCloseODDTray, XamLoaderGetMediaInfo, XamLoaderGetMediaInfoEx
- Partially Implement HalSendSMCMessage
- Implement XamLoaderGetDvdTrayState
- HalOpenCloseODDTray is used by dash and HalSendSMCMessage can be used by custom dashboards
This commit is contained in:
The-Little-Wolf
2025-04-01 05:34:54 -07:00
committed by Radosław Gliński
parent 4b24f128f6
commit 0771938db6
6 changed files with 453 additions and 8 deletions

View File

@@ -66,6 +66,7 @@ KernelState::KernelState(Emulator* emulator)
processor_ = emulator->processor();
file_system_ = emulator->file_system();
xam_state_ = std::make_unique<xam::XamState>(emulator, this);
smc_ = std::make_unique<SystemManagementController>();
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);
}
}

View File

@@ -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::XamState> xam_state_;
std::unique_ptr<SystemManagementController> smc_;
KernelVersion kernel_version_;

187
src/xenia/kernel/smc.cc Normal file
View File

@@ -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<uint8_t>(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<uint8_t>(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<X_DVD_TRAY_STATE>((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

218
src/xenia/kernel/smc.h Normal file
View File

@@ -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 <array>
#include <cstdint>
#include <functional>
#include <map>
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<uint16_t>(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<uint8_t, 3> 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<uint8_t, 3> smc_version = {65, 2, 1};
const std::map<uint8_t, uint8_t> 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<X_SMC_CMD, std::function<void(X_SMC_DATA* smc_message,
X_SMC_DATA* smc_response)>>
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, uint8_t> power_led_state_ = {
POWER_LED_STATE::POWER_ON, 0x00};
std::pair<LED_STATE, uint8_t> led_state_ = {LED_STATE::GREEN, 0x01};
};
} // namespace kernel
} // namespace xe
#endif

View File

@@ -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<uint8_t>(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) {

View File

@@ -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<X_SMC_DATA> smc_message,
pointer_t<X_SMC_DATA> 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