Skip to content

Commit 3ea5850

Browse files
committed
Add support for platform profile
1 parent 53641c2 commit 3ea5850

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

src/cli/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod dgpu;
22
pub mod dtx;
33
pub mod perf;
4+
pub mod profile;
45
pub mod status;
56

67
use anyhow::Result;
@@ -24,6 +25,7 @@ impl Registry {
2425
let list: Vec<Box<dyn Command>> = vec![
2526
Box::new(status::Command),
2627
Box::new(perf::Command),
28+
Box::new(profile::Command),
2729
Box::new(dtx::Command),
2830
Box::new(dgpu::Command),
2931
];

src/cli/profile.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
use crate::cli::Command as DynCommand;
2+
use crate::sys;
3+
4+
use anyhow::{Context, Result};
5+
6+
7+
pub struct Command;
8+
9+
impl DynCommand for Command {
10+
fn name(&self) -> &'static str {
11+
"profile"
12+
}
13+
14+
fn build(&self) -> clap::App<'static, 'static> {
15+
use clap::{AppSettings, Arg, SubCommand};
16+
17+
SubCommand::with_name(self.name())
18+
.about("Control or query the current platform profile")
19+
.setting(AppSettings::SubcommandRequiredElseHelp)
20+
.subcommand(SubCommand::with_name("set")
21+
.about("Set the current platform profile")
22+
.arg(Arg::with_name("profile")
23+
.required(true)
24+
.index(1)))
25+
.subcommand(SubCommand::with_name("get")
26+
.about("Get the current platform profile"))
27+
.subcommand(SubCommand::with_name("list")
28+
.about("List all available platform profiles"))
29+
}
30+
31+
fn execute(&self, m: &clap::ArgMatches) -> Result<()> {
32+
match m.subcommand() {
33+
("set", Some(m)) => self.profile_set(m),
34+
("get", Some(m)) => self.profile_get(m),
35+
("list", Some(m)) => self.profile_list(m),
36+
_ => unreachable!(),
37+
}
38+
}
39+
}
40+
41+
impl Command {
42+
fn profile_set(&self, m: &clap::ArgMatches) -> Result<()> {
43+
let profile = m.value_of("profile").unwrap();
44+
45+
let dev = sys::profile::Device::open()
46+
.context("Failed to open platform profile device")?;
47+
48+
let supported = dev.get_supported()
49+
.context("Failed to get supported platform profiles")?;
50+
51+
if !supported.iter().any(|p| p == profile) {
52+
anyhow::bail!("Platform profile '{}' is not supported", profile);
53+
}
54+
55+
let current_profile = dev.get()
56+
.context("Failed to get current platform profile")?;
57+
58+
if profile != current_profile {
59+
dev.set(profile)
60+
.context("Failed to set platform profile")?;
61+
62+
if !m.is_present("quiet") {
63+
println!("Platform profile set to '{}'", profile);
64+
}
65+
66+
} else if !m.is_present("quiet") {
67+
println!("Platform profile already set to '{}', not changing", profile);
68+
}
69+
70+
Ok(())
71+
}
72+
73+
fn profile_get(&self, _m: &clap::ArgMatches) -> Result<()> {
74+
let dev = sys::profile::Device::open()
75+
.context("Failed to open platform profile device")?;
76+
77+
let profile = dev.get()
78+
.context("Failed to get current platform profile")?;
79+
80+
println!("{}", profile);
81+
Ok(())
82+
}
83+
84+
fn profile_list(&self, m: &clap::ArgMatches) -> Result<()> {
85+
let dev = sys::profile::Device::open()
86+
.context("Failed to open platform profile device")?;
87+
88+
let supported = dev.get_supported()
89+
.context("Failed to get supported platform profiles")?;
90+
91+
if !m.is_present("quiet") {
92+
for profile in supported {
93+
println!("{}", profile);
94+
}
95+
96+
} else {
97+
let text = serde_json::to_string(&supported)
98+
.context("Failed to serialize data")?;
99+
100+
println!("{}", text);
101+
}
102+
103+
Ok(())
104+
}
105+
}

src/sys/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
pub mod pci;
22
pub mod perf;
3+
pub mod profile;
34

45
use thiserror::Error;
56

src/sys/profile.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use std::{fs::OpenOptions, io::{Read, Write}};
2+
use std::path::{Path, PathBuf};
3+
4+
use crate::sys::{Error, Result};
5+
6+
7+
pub struct Device {
8+
path: PathBuf,
9+
}
10+
11+
impl Device {
12+
pub fn open() -> Result<Self> {
13+
Device::open_path("/sys/firmware/acpi/")
14+
}
15+
16+
pub fn open_path<P: AsRef<Path>>(path: P) -> Result<Self> {
17+
use std::io;
18+
19+
if path.as_ref().is_dir() && path.as_ref().join("platform_profile").is_file()
20+
&& path.as_ref().join("platform_profile_choices").is_file()
21+
{
22+
Ok(Device {
23+
path: path.as_ref().to_owned(),
24+
})
25+
} else {
26+
Err(Error::DeviceAccess {
27+
source: io::Error::new(io::ErrorKind::NotFound, "No platform profile support found"),
28+
device: path.as_ref().to_owned(),
29+
})
30+
}
31+
}
32+
33+
pub fn get(&self) -> Result<String> {
34+
let attribute = "platform_profile";
35+
36+
let mut file = OpenOptions::new()
37+
.read(true)
38+
.open(self.path.as_path().join(attribute))
39+
.map_err(|source| Error::DeviceAccess {
40+
source,
41+
device: self.path.as_path().join(attribute),
42+
})?;
43+
44+
let mut profile = String::new();
45+
file.read_to_string(&mut profile)
46+
.map_err(|e| Error::IoError { source: e })?;
47+
48+
Ok(profile.trim().to_owned())
49+
}
50+
51+
pub fn set(&self, profile: &str) -> Result<()> {
52+
let attribute = "platform_profile";
53+
54+
let mut file = OpenOptions::new()
55+
.write(true)
56+
.open(self.path.as_path().join(attribute))
57+
.map_err(|source| Error::DeviceAccess {
58+
source,
59+
device: self.path.as_path().join(attribute),
60+
})?;
61+
62+
file.write(profile.as_bytes())
63+
.map_err(|e| Error::IoError { source: e })?;
64+
65+
Ok(())
66+
}
67+
68+
pub fn get_supported(&self) -> Result<Vec<String>> {
69+
let attribute = "platform_profile_choices";
70+
71+
let mut file = OpenOptions::new()
72+
.read(true)
73+
.open(self.path.as_path().join(attribute))
74+
.map_err(|source| Error::DeviceAccess {
75+
source,
76+
device: self.path.as_path().join(attribute),
77+
})?;
78+
79+
let mut supported = String::new();
80+
file.read_to_string(&mut supported)
81+
.map_err(|e| Error::IoError { source: e })?;
82+
83+
let supported = supported.split_ascii_whitespace()
84+
.map(|s| s.trim().to_owned())
85+
.collect();
86+
87+
Ok(supported)
88+
}
89+
}

0 commit comments

Comments
 (0)