Files
asusctl/rog-platform/src/platform.rs
2025-10-20 00:40:54 +00:00

329 lines
8.2 KiB
Rust

use std::fmt::Display;
use std::path::PathBuf;
use std::str::FromStr;
use log::{info, warn};
use serde::{Deserialize, Serialize};
use zbus::zvariant::{OwnedValue, Type, Value};
use crate::error::{PlatformError, Result};
use crate::{attr_string, attr_string_array, to_device};
/// The "platform" device provides access to things like:
/// - `dgpu_disable`
/// - `egpu_enable`
/// - `panel_od`
/// - `gpu_mux`
/// - various CPU an GPU tunings
/// - `keyboard_mode`, set keyboard RGB mode and speed
/// - `keyboard_state`, set keyboard power states
#[derive(Debug, PartialEq, Eq, PartialOrd, Clone)]
pub struct RogPlatform {
path: PathBuf,
pp_path: PathBuf,
}
impl RogPlatform {
attr_string!(
/// The acpi platform_profile support
"platform_profile",
pp_path
);
attr_string_array!(
/// The acpi platform_profile support
"platform_profile_choices",
pp_path
);
pub fn new() -> Result<Self> {
let mut enumerator = udev::Enumerator::new().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("enumerator failed".into(), err)
})?;
enumerator.match_subsystem("platform").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
enumerator.match_sysname("asus-nb-wmi").map_err(|err| {
warn!("{}", err);
PlatformError::Udev("match_subsystem failed".into(), err)
})?;
if let Some(device) = (enumerator.scan_devices().map_err(|err| {
warn!("{}", err);
PlatformError::Udev("scan_devices failed".into(), err)
})?)
.next()
{
info!("Found platform support at {:?}", device.sysname());
return Ok(Self {
path: device.syspath().to_owned(),
pp_path: PathBuf::from_str("/sys/firmware/acpi").unwrap(),
});
}
Err(PlatformError::MissingFunction(
"asus-nb-wmi not found".into(),
))
}
}
impl Default for RogPlatform {
fn default() -> Self {
unsafe {
Self {
path: PathBuf::from_str("/this_shouldNeVErr_exisid").unwrap_unchecked(),
pp_path: PathBuf::from_str("/this_shouldNeVErr_exisid").unwrap_unchecked(),
}
}
}
}
#[repr(u8)]
#[derive(
Serialize, Deserialize, Default, Type, Value, OwnedValue, Debug, PartialEq, Eq, Clone, Copy,
)]
pub enum GpuMode {
Optimus = 0,
Integrated = 1,
Egpu = 2,
Vfio = 3,
Ultimate = 4,
#[default]
Error = 254,
NotSupported = 255,
}
impl From<u8> for GpuMode {
fn from(v: u8) -> Self {
match v {
0 => GpuMode::Optimus,
1 => GpuMode::Integrated,
2 => GpuMode::Egpu,
3 => GpuMode::Vfio,
4 => GpuMode::Ultimate,
5 => GpuMode::Error,
_ => GpuMode::NotSupported,
}
}
}
impl From<GpuMode> for u8 {
fn from(v: GpuMode) -> Self {
v as u8
}
}
impl GpuMode {
/// For writing to `gpu_mux_mode` attribute
pub fn to_mux_attr(&self) -> u8 {
if *self == Self::Ultimate {
return 0;
}
1
}
pub fn to_dgpu_attr(&self) -> u8 {
if *self == Self::Integrated {
return 1;
}
0
}
pub fn to_egpu_attr(&self) -> u8 {
if *self == Self::Egpu {
return 1;
}
0
}
pub fn from_mux(num: u8) -> Self {
if num == 0 {
return Self::Ultimate;
}
Self::Optimus
}
pub fn from_dgpu(num: u8) -> Self {
if num == 1 {
return Self::Integrated;
}
Self::Optimus
}
// `from_dgpu()` should be called also, and should take precedence if result
// are not equal.
pub fn from_egpu(num: u8) -> Self {
if num == 1 {
return Self::Egpu;
}
Self::Optimus
}
}
impl Display for GpuMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GpuMode::Optimus => write!(f, "Optimus"),
GpuMode::Integrated => write!(f, "Integrated"),
GpuMode::Egpu => write!(f, "eGPU"),
GpuMode::Vfio => write!(f, "VFIO"),
GpuMode::Ultimate => write!(f, "Ultimate"),
GpuMode::Error => write!(f, "Error"),
GpuMode::NotSupported => write!(f, "Not Supported"),
}
}
}
#[repr(u32)]
#[derive(
Deserialize,
Serialize,
Default,
Type,
Value,
OwnedValue,
Debug,
PartialEq,
Eq,
Ord,
PartialOrd,
Hash,
Clone,
Copy,
)]
#[zvariant(signature = "u")]
/// `platform_profile` in asus_wmi
pub enum PlatformProfile {
#[default]
Balanced = 0,
Performance = 1,
Quiet = 2,
LowPower = 3,
Custom = 4,
}
impl PlatformProfile {
pub fn next(current: Self, choices: &[Self]) -> Self {
match current {
Self::Balanced => Self::Performance,
Self::Performance => {
if choices.contains(&Self::LowPower) {
Self::LowPower
} else {
Self::Quiet
}
}
Self::Quiet | Self::LowPower | Self::Custom => Self::Balanced,
}
}
}
impl From<i32> for PlatformProfile {
fn from(num: i32) -> Self {
match num {
0 => Self::Balanced,
1 => Self::Performance,
2 => Self::Quiet,
3 => Self::LowPower,
4 => Self::Custom,
_ => {
warn!("Unknown number for PlatformProfile: {}", num);
Self::Balanced
}
}
}
}
impl From<PlatformProfile> for i32 {
fn from(p: PlatformProfile) -> Self {
p as i32
}
}
impl From<&PlatformProfile> for &str {
fn from(profile: &PlatformProfile) -> &'static str {
match profile {
PlatformProfile::Balanced => "balanced",
PlatformProfile::Performance => "performance",
PlatformProfile::Quiet => "quiet",
PlatformProfile::LowPower => "low-power",
PlatformProfile::Custom => "custom",
}
}
}
impl From<PlatformProfile> for &str {
fn from(profile: PlatformProfile) -> &'static str {
<&str>::from(&profile)
}
}
impl From<String> for PlatformProfile {
fn from(profile: String) -> Self {
Self::from(profile.as_str())
}
}
impl Display for PlatformProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "({})", <&str>::from(self))
}
}
impl std::str::FromStr for PlatformProfile {
type Err = PlatformError;
fn from_str(profile: &str) -> Result<Self> {
match profile
.to_ascii_lowercase()
.trim()
.replace(|c| !char::is_alphabetic(c), "")
.as_str()
{
"balanced" => Ok(PlatformProfile::Balanced),
"performance" => Ok(PlatformProfile::Performance),
"quiet" => Ok(PlatformProfile::Quiet),
"lowpower" => Ok(PlatformProfile::LowPower),
"custom" => Ok(PlatformProfile::Custom),
_ => Err(PlatformError::NotSupported),
}
}
}
impl From<&str> for PlatformProfile {
fn from(profile: &str) -> Self {
match profile
.to_ascii_lowercase()
.trim()
.replace(|c| !char::is_alphabetic(c), "")
.as_str()
{
"balanced" => PlatformProfile::Balanced,
"performance" => PlatformProfile::Performance,
"quiet" => PlatformProfile::Quiet,
"lowpower" => PlatformProfile::LowPower,
"custom" => PlatformProfile::Custom,
_ => {
warn!("{profile} is unknown, using ThrottlePolicy::Balanced");
PlatformProfile::Balanced
}
}
}
}
/// CamelCase names of the properties. Intended for use with DBUS
#[repr(u8)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Type, PartialEq, PartialOrd)]
#[zvariant(signature = "s")]
pub enum Properties {
ChargeControlEndThreshold,
DgpuDisable,
GpuMuxMode,
PostAnimationSound,
PanelOd,
MiniLedMode,
EgpuEnable,
ThrottlePolicy,
}