Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
824 changes: 811 additions & 13 deletions rust/Cargo.lock

Large diffs are not rendered by default.

10 changes: 9 additions & 1 deletion rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ license = "MIT"
description = "RUST based UI to demo EC features"
authors = ["Phil Weber <philweber@microsoft.com>"]
repository = "https://github.com/OpenDevicePartnership/ec_demo"
rust-version = "1.85"
rust-version = "1.88"

[dependencies]
# dependencies for all targets
Expand All @@ -18,9 +18,17 @@ log = "0.4"
env_logger = "0.10"
tui-input = "0.14.0"
uuid = { version = "1.17.0", default-features = false }
thermal-service-messages = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0" }
battery-service-messages = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0" }

# Serial feature specific
serialport = { version = "4.8.1", optional = true }
embedded-services = { git = "https://github.com/OpenDevicePartnership/embedded-services", branch = "v0.2.0", optional = true }

[features]
mock = []
acpi = []
serial = ["dep:serialport", "dep:embedded-services"]

[lints.clippy]
suspicious = "forbid"
Expand Down
4 changes: 2 additions & 2 deletions rust/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use std::fs;

fn main() {
// Don't do a fancy build if we're just testing our TUI
if env::var("CARGO_FEATURE_MOCK").is_ok() {
println!("cargo:warning=Skipping build.rs logic because 'mock' feature is enabled.");
if env::var("CARGO_FEATURE_ACPI").is_err() {
println!("cargo:warning=Skipping build.rs logic because 'acpi' feature is not enabled.");
return;
}

Expand Down
106 changes: 55 additions & 51 deletions rust/src/acpi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::{Source, Threshold, common};
use battery_service_messages::{
BatteryState, BixFixedStrings, BstReturn, bat_swap_try_from_u32, bat_tech_try_from_u32, power_unit_try_from_u32,
};
use color_eyre::{Result, eyre::eyre};
use std::ffi;

Expand All @@ -7,25 +10,6 @@ unsafe extern "C" {
fn EvaluateAcpi(input: *const i8, input_len: usize, buffer: *mut u8, buf_len: &mut usize) -> i32;
}

mod guid {
pub const _SENSOR_CRT_TEMP: uuid::Uuid = uuid::uuid!("218246e7-baf6-45f1-aa13-07e4845256b8");
pub const _SENSOR_PROCHOT_TEMP: uuid::Uuid = uuid::uuid!("22dc52d2-fd0b-47ab-95b8-26552f9831a5");
pub const FAN_ON_TEMP: uuid::Uuid = uuid::uuid!("ba17b567-c368-48d5-bc6f-a312a41583c1");
pub const FAN_RAMP_TEMP: uuid::Uuid = uuid::uuid!("3a62688c-d95b-4d2d-bacc-90d7a5816bcd");
pub const FAN_MAX_TEMP: uuid::Uuid = uuid::uuid!("dcb758b1-f0fd-4ec7-b2c0-ef1e2a547b76");
pub const FAN_MIN_RPM: uuid::Uuid = uuid::uuid!("db261c77-934b-45e2-9742-256c62badb7a");
pub const FAN_MAX_RPM: uuid::Uuid = uuid::uuid!("5cf839df-8be7-42b9-9ac5-3403ca2c8a6a");
pub const FAN_CURRENT_RPM: uuid::Uuid = uuid::uuid!("adf95492-0776-4ffc-84f3-b6c8b5269683");
}

fn cstr_bytes_to_string(raw: &[u8]) -> Result<String> {
Ok(ffi::CStr::from_bytes_until_nul(raw)
.map_err(|_| color_eyre::eyre::eyre!("Invalid byte slice"))?
.to_str()
.map_err(|_| color_eyre::eyre::eyre!("String contains invalid characters"))?
.to_owned())
}

// A user-friendly ACPI input method containing a name and optional arguments
struct AcpiMethodInput<'a, 'b> {
name: &'a str,
Expand Down Expand Up @@ -296,73 +280,93 @@ impl Source for Acpi {
}

fn get_rpm(&self) -> Result<f64> {
acpi_get_var(guid::FAN_CURRENT_RPM)
acpi_get_var(common::guid::FAN_CURRENT_RPM)
}

fn get_min_rpm(&self) -> Result<f64> {
acpi_get_var(guid::FAN_MIN_RPM)
acpi_get_var(common::guid::FAN_MIN_RPM)
}

fn get_max_rpm(&self) -> Result<f64> {
acpi_get_var(guid::FAN_MAX_RPM)
acpi_get_var(common::guid::FAN_MAX_RPM)
}

fn get_threshold(&self, threshold: Threshold) -> Result<f64> {
match threshold {
Threshold::On => Ok(common::dk_to_c(acpi_get_var(guid::FAN_ON_TEMP)? as u32)),
Threshold::Ramping => Ok(common::dk_to_c(acpi_get_var(guid::FAN_RAMP_TEMP)? as u32)),
Threshold::Max => Ok(common::dk_to_c(acpi_get_var(guid::FAN_MAX_TEMP)? as u32)),
Threshold::On => Ok(common::dk_to_c(acpi_get_var(common::guid::FAN_ON_TEMP)? as u32)),
Threshold::Ramping => Ok(common::dk_to_c(acpi_get_var(common::guid::FAN_RAMP_TEMP)? as u32)),
Threshold::Max => Ok(common::dk_to_c(acpi_get_var(common::guid::FAN_MAX_TEMP)? as u32)),
}
}

fn set_rpm(&self, rpm: f64) -> Result<()> {
acpi_set_var(guid::FAN_CURRENT_RPM, rpm)
acpi_set_var(common::guid::FAN_CURRENT_RPM, rpm)
}

fn get_bst(&self) -> Result<crate::battery::BstData> {
fn get_bst(&self) -> Result<BstReturn> {
let data = Acpi::evaluate("\\_SB.ECT0.TBST", None)?;

// We are expecting 4 32-bit values
if data.count != 4 {
Err(eyre!("GET_BST unrecognized output"))
} else {
Ok(crate::battery::BstData {
state: crate::battery::ChargeState::try_from(data.arguments[0].data_32)?,
rate: data.arguments[1].data_32,
capacity: data.arguments[2].data_32,
voltage: data.arguments[3].data_32,
Ok(BstReturn {
battery_state: BatteryState::from_bits(data.arguments[0].data_32)
.ok_or(eyre!("Invalid BatteryState"))?,
battery_present_rate: data.arguments[1].data_32,
battery_remaining_capacity: data.arguments[2].data_32,
battery_present_voltage: data.arguments[3].data_32,
})
}
}

fn get_bix(&self) -> Result<crate::battery::BixData> {
fn get_bix(&self) -> Result<BixFixedStrings> {
let data = Acpi::evaluate("\\_SB.ECT0.TBIX", None)?;
// We are expecting 21 arguments
if data.count != 21 {
Err(eyre!("GET_BIX unrecognized output"))
} else {
Ok(crate::battery::BixData {
Ok(BixFixedStrings {
revision: data.arguments[0].data_32,
power_unit: crate::battery::PowerUnit::try_from(data.arguments[1].data_32)?,
power_unit: power_unit_try_from_u32(data.arguments[1].data_32)
.map_err(|_| eyre!("Invalid PowerUnit"))?,
design_capacity: data.arguments[2].data_32,
last_full_capacity: data.arguments[3].data_32,
battery_technology: crate::battery::BatteryTechnology::try_from(data.arguments[4].data_32)?,
last_full_charge_capacity: data.arguments[3].data_32,
battery_technology: bat_tech_try_from_u32(data.arguments[4].data_32)
.map_err(|_| eyre!("Invalid BatteryTechnology"))?,
design_voltage: data.arguments[5].data_32,
warning_capacity: data.arguments[6].data_32,
low_capacity: data.arguments[7].data_32,
design_cap_of_warning: data.arguments[6].data_32,
design_cap_of_low: data.arguments[7].data_32,
cycle_count: data.arguments[8].data_32,
accuracy: data.arguments[9].data_32,
max_sample_time: data.arguments[10].data_32,
min_sample_time: data.arguments[11].data_32,
max_average_interval: data.arguments[12].data_32,
min_average_interval: data.arguments[13].data_32,
capacity_gran1: data.arguments[14].data_32,
capacity_gran2: data.arguments[15].data_32,
model_number: cstr_bytes_to_string(&data.arguments[16].data)?,
serial_number: cstr_bytes_to_string(&data.arguments[17].data)?,
battery_type: cstr_bytes_to_string(&data.arguments[18].data)?,
oem_info: cstr_bytes_to_string(&data.arguments[19].data)?,
swap_cap: crate::battery::SwapCap::try_from(data.arguments[20].data_32)?,
measurement_accuracy: data.arguments[9].data_32,
max_sampling_time: data.arguments[10].data_32,
min_sampling_time: data.arguments[11].data_32,
max_averaging_interval: data.arguments[12].data_32,
min_averaging_interval: data.arguments[13].data_32,
battery_capacity_granularity_1: data.arguments[14].data_32,
battery_capacity_granularity_2: data.arguments[15].data_32,
model_number: data.arguments[16]
.data
.clone()
.try_into()
.map_err(|_| eyre!("Invalid model number"))?,
serial_number: data.arguments[17]
.data
.clone()
.try_into()
.map_err(|_| eyre!("Invalid serial number"))?,
battery_type: data.arguments[18]
.data
.clone()
.try_into()
.map_err(|_| eyre!("Invalid battery type"))?,
oem_info: data.arguments[19]
.data
.clone()
.try_into()
.map_err(|_| eyre!("Invalid OEM info"))?,
battery_swapping_capability: bat_swap_try_from_u32(data.arguments[20].data_32)
.map_err(|_| eyre!("Invalid BatterySwapCapability"))?,
})
}
}
Expand Down
20 changes: 10 additions & 10 deletions rust/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,16 @@ impl<S: Source + Clone + 'static> App<S> {

fn handle_events(&mut self) -> std::io::Result<()> {
let evt = event::read()?;
if let Event::Key(key) = evt {
if key.kind == KeyEventKind::Press {
match key.code {
KeyCode::Char('l') | KeyCode::Right => self.next_tab(),
KeyCode::Char('h') | KeyCode::Left => self.previous_tab(),
KeyCode::Char('q') | KeyCode::Esc => self.quit(),

// Let the current tab handle event in this case
_ => self.handle_tab_event(&evt),
}
if let Event::Key(key) = evt
&& key.kind == KeyEventKind::Press
{
match key.code {
KeyCode::Char('l') | KeyCode::Right => self.next_tab(),
KeyCode::Char('h') | KeyCode::Left => self.previous_tab(),
KeyCode::Char('q') | KeyCode::Esc => self.quit(),

// Let the current tab handle event in this case
_ => self.handle_tab_event(&evt),
}
}
Ok(())
Expand Down
Loading