Skip to main content

framework_tool_tui/
framework.rs

1use std::sync::Arc;
2
3use color_eyre::eyre::Report;
4use framework_lib::chromium_ec::CrosEc;
5use framework_lib::chromium_ec::CrosEcDriver;
6use framework_lib::chromium_ec::EcError;
7use framework_lib::smbios;
8
9use crate::framework::fingerprint::led_brightness_percentage_to_level;
10use crate::framework::fingerprint::Fingerprint;
11use crate::framework::fingerprint::FpLedBrightnessCapability;
12use crate::framework::info::FrameworkInfo;
13
14pub mod fingerprint;
15pub mod info;
16
17// Copied from framework_lib::power
18const EC_MEMMAP_FAN: u16 = 0x10; // Fan speeds 0x10 - 0x17
19const EC_FAN_SPEED_ENTRIES: usize = 4;
20/// Used on old EC firmware (before 2023)
21const EC_FAN_SPEED_NOT_PRESENT: u16 = 0xFFFF;
22
23pub struct Framework {
24    ec: CrosEc,
25    fingerprint: Arc<Fingerprint>,
26}
27
28#[derive(Debug)]
29pub struct EcErrorWrapper(pub EcError);
30
31impl std::fmt::Display for EcErrorWrapper {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        write!(f, "{:?}", self.0)
34    }
35}
36
37impl std::error::Error for EcErrorWrapper {}
38
39impl Framework {
40    pub fn new(ec: CrosEc, fingerprint: Arc<Fingerprint>) -> Self {
41        Framework { ec, fingerprint }
42    }
43
44    pub fn set_max_charge_limit(&self, value: u8) -> color_eyre::Result<()> {
45        self.ec
46            .set_charge_limit(0, value)
47            .map_err(|error| Report::from(EcErrorWrapper(error)))
48    }
49
50    pub fn set_fp_brightness(&self, percentage: u8) -> color_eyre::Result<()> {
51        let result = match self.fingerprint.led_brightness_capability {
52            FpLedBrightnessCapability::Level => {
53                let level = led_brightness_percentage_to_level(percentage);
54
55                self.ec.set_fp_led_level(level)
56            }
57            FpLedBrightnessCapability::Percentage => self.ec.set_fp_led_percentage(percentage),
58        };
59
60        result.map_err(|error| Report::from(EcErrorWrapper(error)))
61    }
62
63    // NOTE: the underlying ec API is weird
64    pub fn set_kb_brightness(&self, percentage: u8) {
65        self.ec.set_keyboard_backlight(percentage);
66    }
67
68    pub fn get_info(&mut self) -> FrameworkInfo {
69        let power = framework_lib::power::power_info(&self.ec);
70        let charge_limit = self.ec.get_charge_limit().ok();
71        let privacy = self.ec.get_privacy_info().ok();
72        let fp_brightness = self.ec.get_fp_led_level().ok();
73        let kb_brightness = self.ec.get_keyboard_backlight().ok();
74        let smbios = smbios::get_smbios();
75        let pd_ports = framework_lib::power::get_pd_info(&self.ec, 4)
76            .into_iter()
77            .map(Result::ok)
78            .collect();
79        let fan_rpm = self.get_fan_rpm().ok();
80        let platform = smbios::get_platform();
81
82        FrameworkInfo::new(
83            &power,
84            &charge_limit,
85            &privacy,
86            &fp_brightness,
87            kb_brightness,
88            &smbios,
89            pd_ports,
90            fan_rpm,
91            platform,
92        )
93    }
94
95    fn get_fan_rpm(&self) -> color_eyre::Result<Vec<u16>> {
96        let fans = self
97            .ec
98            .read_memory(EC_MEMMAP_FAN, 0x08)
99            .ok_or(Report::msg("Couldn't read fan info"))?;
100        let mut rpms = Vec::new();
101
102        for i in 0..EC_FAN_SPEED_ENTRIES {
103            let rpm = u16::from_le_bytes([fans[i * 2], fans[1 + i * 2]]);
104
105            if rpm == EC_FAN_SPEED_NOT_PRESENT {
106                continue;
107            }
108
109            rpms.push(rpm);
110        }
111
112        Ok(rpms)
113    }
114}