From e2b88395f03aa9cf3943884fc731b614efb673a9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 17 Apr 2025 03:29:34 +0800 Subject: [PATCH 01/39] framework_uefi: Document how to build on Windows I had only ever built on Linux before. Signed-off-by: Daniel Schaefer --- .gitignore | 4 ++++ framework_uefi/README.md | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 framework_uefi/README.md diff --git a/.gitignore b/.gitignore index 6438f1c0..a34615ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,6 @@ target/ build/ +# Windows build outputs +*.efi +*.exe +*.pdb diff --git a/framework_uefi/README.md b/framework_uefi/README.md new file mode 100644 index 00000000..19816a93 --- /dev/null +++ b/framework_uefi/README.md @@ -0,0 +1,4 @@ +## Building + +Currently recommended on Linux via: `make`. +Or (on Windows) via: `cargo rustc --target x86_64-unknown-uefi --release -- --emit link=framework_uefi/boot.efi` From 9f7ab2ec3a8e9d78e28218acd02c59be82a5311c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 20 Apr 2025 16:04:05 +0800 Subject: [PATCH 02/39] Don't show retimer as unknown if none present On platforms that don't have an updateable retimer, we don't need to show anything. Currently it would show as "Unknown". Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 50 +++++++++++++--------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 6a73d655..be5eb660 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -420,50 +420,46 @@ fn print_versions(ec: &CrosEc) { println!(" Unknown") } - println!("Retimers"); - let mut found_retimer = false; + let has_retimer = matches!( + smbios::get_platform(), + Some(Platform::IntelGen11) + | Some(Platform::IntelGen12) + | Some(Platform::IntelGen13) + | Some(Platform::IntelCoreUltra1) + ); + let mut left_retimer: Option = None; + let mut right_retimer: Option = None; if let Some(esrt) = esrt::get_esrt() { for entry in &esrt.entries { - match entry.fw_class { - esrt::TGL_RETIMER01_GUID - | esrt::TGL_RETIMER23_GUID - | esrt::ADL_RETIMER01_GUID - | esrt::ADL_RETIMER23_GUID - | esrt::RPL_RETIMER01_GUID - | esrt::RPL_RETIMER23_GUID - | esrt::MTL_RETIMER01_GUID - | esrt::MTL_RETIMER23_GUID => { - if !found_retimer { - found_retimer = true; - } - } - _ => {} - } match entry.fw_class { esrt::TGL_RETIMER01_GUID | esrt::ADL_RETIMER01_GUID | esrt::RPL_RETIMER01_GUID | esrt::MTL_RETIMER01_GUID => { - println!( - " Left: 0x{:X} ({})", - entry.fw_version, entry.fw_version - ); + left_retimer = Some(entry.fw_version); } esrt::TGL_RETIMER23_GUID | esrt::ADL_RETIMER23_GUID | esrt::RPL_RETIMER23_GUID | esrt::MTL_RETIMER23_GUID => { - println!( - " Right: 0x{:X} ({})", - entry.fw_version, entry.fw_version - ); + right_retimer = Some(entry.fw_version); } _ => {} } } } - if !found_retimer { - println!(" Unknown"); + if has_retimer { + println!("Retimers"); + if let Some(fw_version) = left_retimer { + println!(" Left: 0x{:X} ({})", fw_version, fw_version); + } + if let Some(fw_version) = right_retimer { + println!(" Right: 0x{:X} ({})", fw_version, fw_version); + } + if left_retimer.is_none() && right_retimer.is_none() { + // This means there's a bug, we should've found one but didn't + println!(" Unknown"); + } } #[cfg(feature = "linux")] From 343f0f0b3dddf073b1db7e947c3ea7a1091f5106 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 23 Apr 2025 00:01:47 -0700 Subject: [PATCH 03/39] touchscreen: Only try to get version on Framework 12 Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index be5eb660..ef3e77d4 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -481,7 +481,9 @@ fn print_versions(ec: &CrosEc) { let _ignore_err = print_touchpad_fw_ver(); #[cfg(feature = "hidapi")] - let _ignore_err = touchscreen::print_fw_ver(); + if let Some(Platform::Framework12IntelGen13) = smbios::get_platform() { + let _ignore_err = touchscreen::print_fw_ver(); + } } fn print_esrt() { From ccd8a3639e7116bbf0ff11e943436d7849223298 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 23 Apr 2025 21:18:06 +0800 Subject: [PATCH 04/39] gh-actions: Disable FreeBSD builds Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a261d25..7a8dabe2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,9 @@ on: jobs: freebsd-cross-build: + # Not building currently, container seems to have issues + if: false + name: Cross-Build for FreeBSD runs-on: 'ubuntu-24.04' env: From 9ae3349a40487ecc277d474c2281a8f16a4e940f Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 23 Apr 2025 20:55:15 +0800 Subject: [PATCH 05/39] chromium_ec: Fix reading EC console Now it can get all of the EC console buffer. I see ~100 lines. Much more than the previous ~10 lines. Follow also seems to properly work now. A good way to force messages to appear on the console is to plug in or unplug a charger, or to close and open the lid. TEST=`framework_tool --console recent` shows the same output as `ectool console` TEST=`framework_tool --console follow` keeps properly refreshing endlessly. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 63 ++++++++++++++++++---------- 1 file changed, 40 insertions(+), 23 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index acbd5c0c..687e6893 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -822,22 +822,23 @@ impl CrosEc { /// Requests recent console output from EC and constantly asks for more /// Prints the output and returns it when an error is encountered - pub fn console_read(&self) -> EcResult { - let mut console = String::new(); + pub fn console_read(&self) -> EcResult<()> { + EcRequestConsoleSnapshot {}.send_command(self)?; + let mut cmd = EcRequestConsoleRead { - subcmd: ConsoleReadSubCommand::ConsoleReadRecent as u8, + subcmd: ConsoleReadSubCommand::ConsoleReadNext as u8, }; - - EcRequestConsoleSnapshot {}.send_command(self)?; loop { match cmd.send_command_vec(self) { Ok(data) => { - // EC Buffer is empty. We can wait a bit and see if there's more - // Can't run it too quickly, otherwise the commands might fail + // EC Buffer is empty. That means we've read everything from the snapshot if data.is_empty() { - trace!("Empty EC response"); - println!("---"); - os_specific::sleep(1_000_000); // 1s + debug!("Empty EC response. Stopping console read"); + // Don't read too fast, wait a second before reading more + os_specific::sleep(1_000_000); + EcRequestConsoleSnapshot {}.send_command(self)?; + cmd.subcmd = ConsoleReadSubCommand::ConsoleReadRecent as u8; + continue; } let utf8 = std::str::from_utf8(&data).unwrap(); @@ -846,35 +847,51 @@ impl CrosEc { .replace(['\0'], ""); print!("{}", ascii); - console.push_str(ascii.as_str()); } Err(err) => { error!("Err: {:?}", err); - return Ok(console); - //return Err(err) + return Err(err); } }; - cmd.subcmd = ConsoleReadSubCommand::ConsoleReadNext as u8; // Need to explicitly handle CTRL-C termination on UEFI Shell #[cfg(feature = "uefi")] if shell_get_execution_break_flag() { - return Ok(console); + return Ok(()); } } } + /// Read all of EC console buffer and return it pub fn console_read_one(&self) -> EcResult { EcRequestConsoleSnapshot {}.send_command(self)?; - let data = EcRequestConsoleRead { - subcmd: ConsoleReadSubCommand::ConsoleReadRecent as u8, + + let mut console = String::new(); + let cmd = EcRequestConsoleRead { + subcmd: ConsoleReadSubCommand::ConsoleReadNext as u8, + }; + loop { + match cmd.send_command_vec(self) { + Ok(data) => { + // EC Buffer is empty. That means we've read everything + if data.is_empty() { + debug!("Empty EC response. Stopping console read"); + return Ok(console); + } + + let utf8 = std::str::from_utf8(&data).unwrap(); + let ascii = utf8 + .replace(|c: char| !c.is_ascii(), "") + .replace(['\0'], ""); + + console.push_str(ascii.as_str()); + } + Err(err) => { + error!("Err: {:?}", err); + return Err(err); + } + }; } - .send_command_vec(self)?; - let utf8 = std::str::from_utf8(&data).unwrap(); - let ascii = utf8 - .replace(|c: char| !c.is_ascii(), "") - .replace(['\0'], ""); - Ok(ascii) } /// Check features supported by the firmware From 4a856e8784baaf1e7f129e18262ae74ddc81c808 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 23 Apr 2025 01:01:35 -0700 Subject: [PATCH 06/39] Show version of Framework 16 Inputmodules All types of Keyboard and LED matrix ``` > framework_tool --versions [...] Laptop 16 Numpad Firmware Version: 0.2.9 Laptop 16 ANSI Keyboard Firmware Version: 0.2.9 [...] ``` ``` > framework_tool --versions [...] LED Matrix Firmware Version: 0.2.0 Laptop 16 ANSI Keyboard Firmware Version: 0.2.9 [...] ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 5 ++++ framework_lib/src/inputmodule.rs | 39 ++++++++++++++++++++++++++++ framework_lib/src/lib.rs | 2 ++ 3 files changed, 46 insertions(+) create mode 100644 framework_lib/src/inputmodule.rs diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ef3e77d4..1220e739 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -46,6 +46,8 @@ use crate::chromium_ec::{EcError, EcResult}; use crate::csme; use crate::ec_binary; use crate::esrt; +#[cfg(feature = "rusb")] +use crate::inputmodule::check_inputmodule_version; use crate::power; use crate::smbios; use crate::smbios::ConfigDigit0; @@ -477,6 +479,9 @@ fn print_versions(ec: &CrosEc) { #[cfg(feature = "rusb")] let _ignore_err = check_camera_version(); + #[cfg(feature = "rusb")] + let _ignore_err = check_inputmodule_version(); + #[cfg(feature = "hidapi")] let _ignore_err = print_touchpad_fw_ver(); diff --git a/framework_lib/src/inputmodule.rs b/framework_lib/src/inputmodule.rs new file mode 100644 index 00000000..e582d27b --- /dev/null +++ b/framework_lib/src/inputmodule.rs @@ -0,0 +1,39 @@ +pub const FRAMEWORK_VID: u16 = 0x32AC; +pub const LEDMATRIX_PID: u16 = 0x0020; +pub const FRAMEWORK16_INPUTMODULE_PIDS: [u16; 6] = [ + 0x0012, // Keyboard White Backlight ANSI + 0x0013, // Keyboard RGB Backlight Numpad + 0x0014, // Keyboard White Backlight Numpad + 0x0018, // Keyboard White Backlight ISO + 0x0019, // Keyboard White Backlight JIS + LEDMATRIX_PID, +]; + +/// Get and print the firmware version of the camera +pub fn check_inputmodule_version() -> Result<(), rusb::Error> { + for dev in rusb::devices().unwrap().iter() { + let dev_descriptor = dev.device_descriptor().unwrap(); + let vid = dev_descriptor.vendor_id(); + let pid = dev_descriptor.product_id(); + if vid != FRAMEWORK_VID || !FRAMEWORK16_INPUTMODULE_PIDS.contains(&pid) { + debug!("Skipping {:04X}:{:04X}", vid, pid); + continue; + } + + // I'm not sure why, but the LED Matrix can't be opened with this code + if pid == LEDMATRIX_PID { + println!("LED Matrix"); + } else { + debug!("Opening {:04X}:{:04X}", vid, pid); + let handle = dev.open().unwrap(); + + let dev_descriptor = dev.device_descriptor()?; + let i_product = dev_descriptor + .product_string_index() + .and_then(|x| handle.read_string_descriptor_ascii(x).ok()); + println!("{}", i_product.unwrap_or_default()); + } + println!(" Firmware Version: {}", dev_descriptor.device_version()); + } + Ok(()) +} diff --git a/framework_lib/src/lib.rs b/framework_lib/src/lib.rs index b0c92e4d..a237326a 100644 --- a/framework_lib/src/lib.rs +++ b/framework_lib/src/lib.rs @@ -16,6 +16,8 @@ extern crate log; pub mod audio_card; #[cfg(feature = "rusb")] pub mod camera; +#[cfg(feature = "rusb")] +pub mod inputmodule; #[cfg(feature = "hidapi")] pub mod touchpad; #[cfg(any(feature = "hidapi", feature = "windows"))] From 865afe038d4c4c6fb18e2f445dfe6bfbd1d71347 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 23 Apr 2025 01:45:35 -0700 Subject: [PATCH 07/39] inputmodule: Show location Otherwise if you have two LED matrices, you don't know which one's which. ``` > framework_tool --versions [...] LED Matrix Firmware Version: 0.2.0 Location: [X] [ ] [ ] [ ] [ ] LED Matrix Firmware Version: 0.2.0 Location: [ ] [ ] [ ] [ ] [X] Laptop 16 ANSI Keyboard Firmware Version: 0.1.6 Location: [ ] [X] [ ] [ ] [ ] [...] ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/inputmodule.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/inputmodule.rs b/framework_lib/src/inputmodule.rs index e582d27b..4e280065 100644 --- a/framework_lib/src/inputmodule.rs +++ b/framework_lib/src/inputmodule.rs @@ -16,7 +16,7 @@ pub fn check_inputmodule_version() -> Result<(), rusb::Error> { let vid = dev_descriptor.vendor_id(); let pid = dev_descriptor.product_id(); if vid != FRAMEWORK_VID || !FRAMEWORK16_INPUTMODULE_PIDS.contains(&pid) { - debug!("Skipping {:04X}:{:04X}", vid, pid); + trace!("Skipping {:04X}:{:04X}", vid, pid); continue; } @@ -34,6 +34,29 @@ pub fn check_inputmodule_version() -> Result<(), rusb::Error> { println!("{}", i_product.unwrap_or_default()); } println!(" Firmware Version: {}", dev_descriptor.device_version()); + + debug!("Address: {:?}", dev.address()); + debug!("Bus Number: {:?}", dev.bus_number()); + debug!("Port Number: {:?}", dev.port_number()); + debug!("Port Numbers: {:?}", dev.port_numbers()); + let port_numbers = dev.port_numbers(); + let location = if let Ok(port_numbers) = port_numbers { + if port_numbers.len() == 2 { + match (port_numbers[0], port_numbers[1]) { + (4, 2) => "[X] [ ] [ ] [ ] [ ]", + (4, 3) => "[ ] [X] [ ] [ ] [ ]", + (3, 1) => "[ ] [ ] [X] [ ] [ ]", + (3, 2) => "[ ] [ ] [ ] [X] [ ]", + (3, 3) => "[ ] [ ] [ ] [ ] [X]", + _ => "Unknown", + } + } else { + "Unknown" + } + } else { + "Unknown" + }; + println!(" Location: {}", location); } Ok(()) } From fd7d49fe696e2bd0d209d38c5db971a79e4e12d3 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 24 Apr 2025 10:31:35 +0800 Subject: [PATCH 08/39] console: Handle all NULL response from driver BUG=`framework_tool --console recent` would hang and never return because it's stuck in the loop TEST=Make sure `framework_tool --console recent` can run properly on windows and return >50 lines of log Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 687e6893..d3f08658 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -820,7 +820,7 @@ impl CrosEc { Ok(result.valid) } - /// Requests recent console output from EC and constantly asks for more + /// Requests console output from EC and constantly asks for more /// Prints the output and returns it when an error is encountered pub fn console_read(&self) -> EcResult<()> { EcRequestConsoleSnapshot {}.send_command(self)?; @@ -832,7 +832,8 @@ impl CrosEc { match cmd.send_command_vec(self) { Ok(data) => { // EC Buffer is empty. That means we've read everything from the snapshot - if data.is_empty() { + // The windows crosecbus driver returns all NULL instead of empty response + if data.is_empty() || data.iter().all(|x| *x == 0) { debug!("Empty EC response. Stopping console read"); // Don't read too fast, wait a second before reading more os_specific::sleep(1_000_000); @@ -874,7 +875,8 @@ impl CrosEc { match cmd.send_command_vec(self) { Ok(data) => { // EC Buffer is empty. That means we've read everything - if data.is_empty() { + // The windows crosecbus driver returns all NULL instead of empty response + if data.is_empty() || data.iter().all(|x| *x == 0) { debug!("Empty EC response. Stopping console read"); return Ok(console); } From 03c750a094a667686fbbe03f04f6e7a21463c606 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 27 Mar 2025 17:43:51 -0700 Subject: [PATCH 09/39] chromium_ec: Add adc_read Support EC host command EC_CMD_ADC_READ Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 16 ++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 5 +++++ 3 files changed, 22 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 0eaa9395..29cac41a 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -46,6 +46,7 @@ pub enum EcCommands { RebootEc = 0x00D2, /// Get information about PD controller power UsbPdPowerInfo = 0x0103, + AdcRead = 0x0123, RgbKbdSetColor = 0x013A, RgbKbd = 0x013B, diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 6bde4170..0f90fef2 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -518,6 +518,22 @@ impl EcRequest for EcRequestUsbPdPowerInfo { } } +#[repr(C, packed)] +pub struct EcRequestAdcRead { + /// ADC Channel, specific to each mainboard schematic + pub adc_channel: u8, +} + +pub struct EcResponseAdcRead { + pub adc_value: i32, +} + +impl EcRequest for EcRequestAdcRead { + fn command_id() -> EcCommands { + EcCommands::AdcRead + } +} + // TODO: Actually 128, but if we go above ~80 EC returns REQUEST_TRUNCATED // At least when I use the portio driver pub const EC_RGBKBD_MAX_KEY_COUNT: usize = 64; diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index acbd5c0c..3cd62f91 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -953,6 +953,11 @@ impl CrosEc { Ok(res.val == 1) } + pub fn adc_read(&self, adc_channel: u8) -> EcResult { + let res = EcRequestAdcRead { adc_channel }.send_command(self)?; + Ok(res.adc_value) + } + pub fn rgbkbd_set_color(&self, start_key: u8, colors: Vec) -> EcResult<()> { for (chunk, colors) in colors.chunks(EC_RGBKBD_MAX_KEY_COUNT).enumerate() { let mut request = EcRequestRgbKbdSetColor { From 4831aef3b97fdd0c3ea4964f56b032387fef1025 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 14 Apr 2025 11:33:43 +0800 Subject: [PATCH 10/39] freebsd: Remove warning Unused variable because we immediately override it. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/hid.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework_lib/src/ccgx/hid.rs b/framework_lib/src/ccgx/hid.rs index e3e14dc0..64dcc748 100644 --- a/framework_lib/src/ccgx/hid.rs +++ b/framework_lib/src/ccgx/hid.rs @@ -256,6 +256,7 @@ pub fn find_devices(api: &HidApi, filter_devs: &[u16], sn: Option<&str>) -> Vec< let usage_page = dev_info.usage_page(); debug!("Found {:X}:{:X} Usage Page: {}", vid, pid, usage_page); + #[cfg(not(target_os = "freebsd"))] let usage_page_filter = usage_page == CCG_USAGE_PAGE; // On FreeBSD it seems we don't get different usage pages // There's just one entry overall From 00defa96588f58cf3c7c1585a8c4e9334a30fa8c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 26 Apr 2025 23:01:06 +0800 Subject: [PATCH 11/39] Fix windows clippy lint Same if body twice. Should combine it into a single one. Signed-off-by: Daniel Schaefer --- framework_lib/src/touchpad.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/framework_lib/src/touchpad.rs b/framework_lib/src/touchpad.rs index 72bc30c8..e767c7e6 100644 --- a/framework_lib/src/touchpad.rs +++ b/framework_lib/src/touchpad.rs @@ -101,9 +101,7 @@ pub fn print_touchpad_fw_ver() -> Result<(), HidError> { #[cfg(target_os = "linux")] debug!(" HID Version {:04X}", hid_ver); #[cfg(not(target_os = "linux"))] - if ver != format!("{:04X}", hid_ver) { - println!(" HID Version v{:04X}", hid_ver); - } else if log_enabled!(Level::Debug) { + if ver != format!("{:04X}", hid_ver) || log_enabled!(Level::Debug) { println!(" HID Version v{:04X}", hid_ver); } From a18e28299da4d533bb25e7f4a5dfbc64a17cbc30 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 14 Jun 2024 11:11:59 +0800 Subject: [PATCH 12/39] power: Detect number of fans and report all of them Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index d69dda24..b97ec0e2 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -71,6 +71,11 @@ const EC_BATT_FLAG_DISCHARGING: u8 = 0x04; const EC_BATT_FLAG_CHARGING: u8 = 0x08; const EC_BATT_FLAG_LEVEL_CRITICAL: u8 = 0x10; +const EC_FAN_SPEED_ENTRIES: usize = 4; +/// Used on old EC firmware (before 2023) +const EC_FAN_SPEED_STALLED_DEPRECATED: u16 = 0xFFFE; +const EC_FAN_SPEED_NOT_PRESENT: u16 = 0xFFFF; + #[derive(Debug)] enum TempSensor { Ok(u8), @@ -373,8 +378,30 @@ pub fn print_thermal(ec: &CrosEc) { } } - let fan0 = u16::from_le_bytes([fans[0], fans[1]]); - println!(" Fan Speed: {:>4} RPM", fan0); + for i in 0..EC_FAN_SPEED_ENTRIES { + let fan = u16::from_le_bytes([fans[i * 2], fans[1 + i * 2]]); + if fan == EC_FAN_SPEED_STALLED_DEPRECATED { + println!(" Fan Speed: {:>4} RPM (Stalled)", fan); + } else if fan == EC_FAN_SPEED_NOT_PRESENT { + info!(" Fan Speed: Not present"); + } else { + println!(" Fan Speed: {:>4} RPM", fan); + } + } +} + +pub fn get_fan_num(ec: &CrosEc) -> EcResult { + let fans = ec.read_memory(EC_MEMMAP_FAN, 0x08).unwrap(); + + let mut count = 0; + for i in 0..EC_FAN_SPEED_ENTRIES { + let fan = u16::from_le_bytes([fans[i * 2], fans[1 + i * 2]]); + if fan == EC_FAN_SPEED_NOT_PRESENT { + continue; + } + count += 1; + } + Ok(count) } // TODO: Use Result From b121a2ad21926b1d68123cc5fe2a264aef1f6ddb Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 11:59:52 +0800 Subject: [PATCH 13/39] Add EXAMPLES.md Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 EXAMPLES.md diff --git a/EXAMPLES.md b/EXAMPLES.md new file mode 100644 index 00000000..e31650a8 --- /dev/null +++ b/EXAMPLES.md @@ -0,0 +1,33 @@ +# Example usage + + +## Check temperatures and fan speed + +``` +> sudo ./target/debug/framework_tool --thermal + F75303_Local: 43 C + F75303_CPU: 44 C + F75303_DDR: 39 C + APU: 62 C + Fan Speed: 0 RPM +``` + +## Check sensors (ALS and G-Sensor) + +``` +> sudo ./target/debug/framework_tool --sensors +ALS: 76 Lux +``` + +## Check power (AC and battery) status + +``` +> sudo ./target/debug/framework_tool --power + AC is: not connected + Battery is: connected + Battery LFCC: 3949 mAh (Last Full Charge Capacity) + Battery Capacity: 2770 mAh + 44.729 Wh + Charge level: 70% + Battery discharging +``` From 0016e6fae729bcc5eb17f0819acb52a8e4bc1908 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 15:52:10 +0800 Subject: [PATCH 14/39] Revert "gh-actions: Disable FreeBSD builds" This reverts commit ccd8a3639e7116bbf0ff11e943436d7849223298. --- .github/workflows/ci.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a8dabe2..2a261d25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,9 +4,6 @@ on: jobs: freebsd-cross-build: - # Not building currently, container seems to have issues - if: false - name: Cross-Build for FreeBSD runs-on: 'ubuntu-24.04' env: From 7585bf1062e8a0c37e93c9c4ba0b17dd86e3dde9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 15:25:47 +0800 Subject: [PATCH 15/39] Add --fansetduty --fansetrpm --autofanctrl Allow manual fancontrol from the OS. See EXAMPLES.md for details. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 33 +++++++++ framework_lib/src/chromium_ec/command.rs | 2 + framework_lib/src/chromium_ec/commands.rs | 81 +++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 27 ++++++++ framework_lib/src/commandline/clap_std.rs | 27 ++++++++ framework_lib/src/commandline/mod.rs | 12 ++++ framework_lib/src/commandline/uefi.rs | 64 ++++++++++++++++++ 7 files changed, 246 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index e31650a8..084c88aa 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -31,3 +31,36 @@ ALS: 76 Lux Charge level: 70% Battery discharging ``` + +## Set custom fan duty/RPM + +``` +# Set a target fanduty of 100% (all or just fan ID=0) +> sudo framework_tool --fansetduty 100 +> sudo framework_tool --fansetduty 0 100 +> sudo framework_tool --thermal + F75303_Local: 40 C + F75303_CPU: 41 C + F75303_DDR: 37 C + APU: 42 C + Fan Speed: 7281 RPM + +# Set a target RPM (all or just fan ID=0) +> sudo framework_tool --fansetrpm 3141 +> sudo framework_tool --fansetrpm 0 3141 +> sudo framework_tool --thermal + F75303_Local: 41 C + F75303_CPU: 42 C + F75303_DDR: 37 C + APU: 44 C + Fan Speed: 3171 RPM + +# And back to normal +> sudo framework_tool --autofanctrl +> sudo framework_tool --thermal + F75303_Local: 40 C + F75303_CPU: 40 C + F75303_DDR: 38 C + APU: 42 C + Fan Speed: 0 RPM +``` diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 29cac41a..21fb5f57 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -28,12 +28,14 @@ pub enum EcCommands { /// Erase section of EC flash FlashErase = 0x13, FlashProtect = 0x15, + PwmSetFanTargetRpm = 0x0021, PwmGetKeyboardBacklight = 0x0022, PwmSetKeyboardBacklight = 0x0023, PwmSetFanDuty = 0x0024, PwmSetDuty = 0x0025, PwmGetDuty = 0x0026, SetTabletMode = 0x0031, + AutoFanCtrl = 0x0052, GpioGet = 0x0093, I2cPassthrough = 0x009e, ConsoleSnapshot = 0x0097, diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 0f90fef2..e84f7687 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -187,6 +187,63 @@ impl EcRequest for EcRequestPwmGetKeyboardBac } } +#[repr(C, packed)] +pub struct EcRequestPwmSetFanTargetRpmV0 { + /// Duty cycle in percent + pub rpm: u32, +} + +impl EcRequest<()> for EcRequestPwmSetFanTargetRpmV0 { + fn command_id() -> EcCommands { + EcCommands::PwmSetFanTargetRpm + } +} + +pub struct EcRequestPwmSetFanTargetRpmV1 { + /// Fan RPM + pub rpm: u32, + /// Fan index + pub fan_idx: u32, +} + +impl EcRequest<()> for EcRequestPwmSetFanTargetRpmV1 { + fn command_id() -> EcCommands { + EcCommands::PwmSetFanTargetRpm + } + fn command_version() -> u8 { + 1 + } +} + +#[repr(C, packed)] +pub struct EcRequestPwmSetFanDutyV0 { + /// Duty cycle in percent + pub percent: u32, +} + +impl EcRequest<()> for EcRequestPwmSetFanDutyV0 { + fn command_id() -> EcCommands { + EcCommands::PwmSetFanDuty + } +} + +#[repr(C, packed)] +pub struct EcRequestPwmSetFanDutyV1 { + /// Duty cycle in percent + pub percent: u32, + /// Fan index + pub fan_idx: u32, +} + +impl EcRequest<()> for EcRequestPwmSetFanDutyV1 { + fn command_id() -> EcCommands { + EcCommands::PwmSetFanDuty + } + fn command_version() -> u8 { + 1 + } +} + pub const PWM_MAX_DUTY: u16 = 0xFFFF; #[repr(C, packed)] @@ -243,6 +300,30 @@ impl EcRequest<()> for EcRequestSetTabletMode { } } +#[repr(C, packed)] +pub struct EcRequestAutoFanCtrlV0 {} + +impl EcRequest<()> for EcRequestAutoFanCtrlV0 { + fn command_id() -> EcCommands { + EcCommands::AutoFanCtrl + } +} + +#[repr(C, packed)] +pub struct EcRequestAutoFanCtrlV1 { + /// Fan id + pub fan_idx: u8, +} + +impl EcRequest<()> for EcRequestAutoFanCtrlV1 { + fn command_id() -> EcCommands { + EcCommands::AutoFanCtrl + } + fn command_version() -> u8 { + 1 + } +} + #[repr(C, packed)] pub struct EcRequestGpioGetV0 { pub name: [u8; 32], diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 631c70a2..f35595ba 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -426,6 +426,33 @@ impl CrosEc { Ok((kblight.duty / (PWM_MAX_DUTY / 100)) as u8) } + pub fn fan_set_rpm(&self, fan: Option, rpm: u32) -> EcResult<()> { + if let Some(fan_idx) = fan { + EcRequestPwmSetFanTargetRpmV1 { rpm, fan_idx }.send_command(self) + } else { + EcRequestPwmSetFanTargetRpmV0 { rpm }.send_command(self) + } + } + + pub fn fan_set_duty(&self, fan: Option, percent: u32) -> EcResult<()> { + if percent > 100 { + return Err(EcError::DeviceError("Fan duty must be <= 100".to_string())); + } + if let Some(fan_idx) = fan { + EcRequestPwmSetFanDutyV1 { fan_idx, percent }.send_command(self) + } else { + EcRequestPwmSetFanDutyV0 { percent }.send_command(self) + } + } + + pub fn autofanctrl(&self, fan: Option) -> EcResult<()> { + if let Some(fan_idx) = fan { + EcRequestAutoFanCtrlV1 { fan_idx }.send_command(self) + } else { + EcRequestAutoFanCtrlV0 {}.send_command(self) + } + } + /// Set tablet mode pub fn set_tablet_mode(&self, mode: TabletModeOverride) { let mode = mode as u8; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index f75f23ef..1d62e9e6 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -57,6 +57,20 @@ struct ClapCli { #[arg(long)] sensors: bool, + /// Set fan duty cycle (0-100%) + #[clap(num_args=..=2)] + #[arg(long)] + fansetduty: Vec, + + /// Set fan RPM (limited by EC fan table max RPM) + #[clap(num_args=..=2)] + #[arg(long)] + fansetrpm: Vec, + + /// Turn on automatic fan speed control + #[arg(long)] + autofanctrl: bool, + /// Show information about USB-C PD ports #[arg(long)] pdports: bool, @@ -277,6 +291,16 @@ pub fn parse(args: &[String]) -> Cli { // Checked by clap _ => unreachable!(), }; + let fansetduty = match args.fansetduty.len() { + 2 => Some((Some(args.fansetduty[0]), args.fansetduty[1])), + 1 => Some((None, args.fansetduty[0])), + _ => None, + }; + let fansetrpm = match args.fansetrpm.len() { + 2 => Some((Some(args.fansetrpm[0]), args.fansetrpm[1])), + 1 => Some((None, args.fansetrpm[0])), + _ => None, + }; Cli { verbosity: args.verbosity.log_level_filter(), @@ -289,6 +313,9 @@ pub fn parse(args: &[String]) -> Cli { power: args.power, thermal: args.thermal, sensors: args.sensors, + fansetduty, + fansetrpm, + autofanctrl: args.autofanctrl, pdports: args.pdports, pd_info: args.pd_info, dp_hdmi_info: args.dp_hdmi_info, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ef3e77d4..f991d55f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -148,6 +148,9 @@ pub struct Cli { pub power: bool, pub thermal: bool, pub sensors: bool, + pub fansetduty: Option<(Option, u32)>, + pub fansetrpm: Option<(Option, u32)>, + pub autofanctrl: bool, pub pdports: bool, pub privacy: bool, pub pd_info: bool, @@ -850,6 +853,12 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { power::print_thermal(&ec); } else if args.sensors { power::print_sensors(&ec); + } else if let Some((fan, percent)) = args.fansetduty { + print_err(ec.fan_set_duty(fan, percent)); + } else if let Some((fan, rpm)) = args.fansetrpm { + print_err(ec.fan_set_rpm(fan, rpm)); + } else if args.autofanctrl { + print_err(ec.autofanctrl(None)); } else if args.pdports { power::get_and_print_pd_info(&ec); } else if args.info { @@ -1046,6 +1055,9 @@ Options: --power Show current power status (battery and AC) --thermal Print thermal information (Temperatures and Fan speed) --sensors Print sensor information (ALS, G-Sensor) + --fansetduty Set fan duty cycle (0-100%) + --fansetrpm Set fan RPM (limited by EC fan table max RPM) + --autofanctrl Turn on automatic fan speed control --pdports Show information about USB-C PD ports --info Show info from SMBIOS (Only on UEFI) --pd-info Show details about the PD controllers diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 54a79fc2..8e612eef 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -65,6 +65,9 @@ pub fn parse(args: &[String]) -> Cli { power: false, thermal: false, sensors: false, + fansetduty: None, + fansetrpm: None, + autofanctrl: false, pdports: false, pd_info: false, dp_hdmi_info: false, @@ -148,6 +151,67 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "--sensors" { cli.sensors = true; found_an_option = true; + } else if arg == "--fansetduty" { + cli.fansetduty = if args.len() > i + 2 { + let fan_idx = args[i + 1].parse::(); + let duty = args[i + 2].parse::(); + if let (Ok(fan_idx), Ok(duty)) = (fan_idx, duty) { + Some((Some(fan_idx), duty)) + } else { + println!( + "Invalid values for --fansetduty: '{} {}'. Must be u32 integers.", + args[i + 1], + args[i + 2] + ); + None + } + } else if args.len() > i + 1 { + if let Ok(duty) = args[i + 1].parse::() { + Some((None, duty)) + } else { + println!( + "Invalid values for --fansetduty: '{}'. Must be 0-100.", + args[i + 1], + ); + None + } + } else { + println!("--fansetduty requires one or two. [fan id] [duty] or [duty]"); + None + }; + found_an_option = true; + } else if arg == "--fansetrpm" { + cli.fansetrpm = if args.len() > i + 2 { + let fan_idx = args[i + 1].parse::(); + let rpm = args[i + 2].parse::(); + if let (Ok(fan_idx), Ok(rpm)) = (fan_idx, rpm) { + Some((Some(fan_idx), rpm)) + } else { + println!( + "Invalid values for --fansetrpm: '{} {}'. Must be u32 integers.", + args[i + 1], + args[i + 2] + ); + None + } + } else if args.len() > i + 1 { + if let Ok(rpm) = args[i + 1].parse::() { + Some((None, rpm)) + } else { + println!( + "Invalid values for --fansetrpm: '{}'. Must be an integer.", + args[i + 1], + ); + None + } + } else { + println!("--fansetrpm requires one or two. [fan id] [rpm] or [rpm]"); + None + }; + found_an_option = true; + } else if arg == "--autofanctrol" { + cli.autofanctrl = true; + found_an_option = true; } else if arg == "--pdports" { cli.pdports = true; found_an_option = true; From 096a232dc1abee93eee538b962942da8ce6dd2a1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 17:06:11 +0800 Subject: [PATCH 16/39] Add more EXAMPLES Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 084c88aa..55b451b5 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,5 +1,79 @@ # Example usage +## Check firmware versions + +### Camera (Framework 12, Framework 13, Framework 16) + +Example on Framework 12: + +``` +> framework_tool --versions +[...] +Framework Laptop 12 Webcam Module + Firmware Version: 0.1.6 +``` + +Example on Framework 13: + +``` +> framework_tool --versions +[...] +Laptop Webcam Module (2nd Gen) + Firmware Version: 1.1.1 +``` + +### Touchscreen (Framework 12) + +``` +> framework_tool --versions +[...] +Touchscreen + Firmware Version: v7.0.0.5.0.0.0.0 + USI Protocol: false + MPP Protocol: true +``` + +### Touchpad (Framework 12, Framework 13, Framework 16) + +``` +> framework_tool --versions +[...] +Touchpad + IC Type: 0239 + Firmware Version: v0E07 +``` + +### Input modules (Framework 16) + +Shows firmware version and location of the modules. + +``` +> framework_tool --versions +[...] +Laptop 16 Numpad + Firmware Version: 0.2.9 + Location: [X] [ ] [ ] [ ] [ ] +Laptop 16 ANSI Keyboard + Firmware Version: 0.2.9 + Location: [ ] [ ] [X] [ ] [ ] +[...] +``` + +``` +> framework_tool --versions +[...] +LED Matrix + Firmware Version: 0.2.0 + Location: [X] [ ] [ ] [ ] [ ] +Laptop 16 ANSI Keyboard + Firmware Version: 0.2.9 + Location: [ ] [x] [ ] [ ] [ ] +LED Matrix + Firmware Version: 0.2.0 + Location: [ ] [ ] [ ] [ ] [x] +[...] +``` + ## Check temperatures and fan speed From 25a5518aa27f0123e1b49ed7de3ce8a895767fe5 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Apr 2025 09:58:38 +0800 Subject: [PATCH 17/39] Add --expansion-bay command to print info ``` > framework_tool.exe --expansion-bay Expansion Bay Serial Number: FRAXXXXXXXXXXXXXXX ``` TODO: - [x] Try with dGPU - [ ] Try with SSD Holder - [ ] Try with fan module - [ ] Try with bad interposer Signed-off-by: Daniel Schaefer --- README.md | 1 + framework_lib/src/commandline/clap_std.rs | 5 +++++ framework_lib/src/commandline/mod.rs | 9 +++++++++ framework_lib/src/commandline/uefi.rs | 4 ++++ 4 files changed, 19 insertions(+) diff --git a/README.md b/README.md index ded23458..db57686c 100644 --- a/README.md +++ b/README.md @@ -181,6 +181,7 @@ Options: --inputmodules Show status of the input modules (Framework 16 only) --input-deck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) [possible values: auto, off, on] + --expansion-bay Show status of the expansion bay (Framework 16 only) --charge-limit [] Get or set max charge limit --get-gpio diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 1d62e9e6..38ef8f5f 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -147,6 +147,10 @@ struct ClapCli { #[arg(long)] input_deck_mode: Option, + /// Show status of the expansion bay (Framework 16 only) + #[arg(long)] + expansion_bay: bool, + /// Get or set max charge limit #[arg(long)] charge_limit: Option>, @@ -352,6 +356,7 @@ pub fn parse(args: &[String]) -> Cli { intrusion: args.intrusion, inputmodules: args.inputmodules, input_deck_mode: args.input_deck_mode, + expansion_bay: args.expansion_bay, charge_limit: args.charge_limit, get_gpio: args.get_gpio, fp_led_level: args.fp_led_level, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index dd3f7cd6..5bbdbce3 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -173,6 +173,7 @@ pub struct Cli { pub intrusion: bool, pub inputmodules: bool, pub input_deck_mode: Option, + pub expansion_bay: bool, pub charge_limit: Option>, pub get_gpio: Option, pub fp_led_level: Option>, @@ -761,6 +762,13 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(mode) = &args.input_deck_mode { println!("Set mode to: {:?}", mode); ec.set_input_deck_mode((*mode).into()).unwrap(); + } else if args.expansion_bay { + println!("Expansion Bay"); + if let Ok(sn) = ec.get_gpu_serial() { + println!(" Serial Number: {}", sn); + } else { + println!(" Serial Number: Unknown"); + } } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); } else if let Some(gpio_name) = &args.get_gpio { @@ -1080,6 +1088,7 @@ Options: --intrusion Show status of intrusion switch --inputmodules Show status of the input modules (Framework 16 only) --input-deck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) + --expansion-bay Show status of the expansion bay (Framework 16 only) --charge-limit [] Get or set battery charge limit (Percentage number as arg, e.g. '100') --get-gpio Get GPIO value by name --fp-led-level [] Get or set fingerprint LED brightness level [possible values: high, medium, low] diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 8e612eef..9d3239a5 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -86,6 +86,7 @@ pub fn parse(args: &[String]) -> Cli { intrusion: false, inputmodules: false, input_deck_mode: None, + expansion_bay: false, charge_limit: None, get_gpio: None, fp_led_level: None, @@ -247,6 +248,9 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--expansion-bay" { + cli.expansion_bay = true; + found_an_option = true; } else if arg == "--charge-limit" { cli.charge_limit = if args.len() > i + 1 { if let Ok(percent) = args[i + 1].parse::() { From defcaa3d2177e9e6d3e00e44413ea397a92128a4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Apr 2025 10:12:45 +0800 Subject: [PATCH 18/39] expansion-bay: Add information about current status ``` framework_tool.exe --expansion-bay Expansion Bay Enabled: true Has fault: false Hatch closed: true Board: DualInterposer Serial Number: FRAXXXXXXXXXXXXXXX ``` Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 12 ++++++++++++ framework_lib/src/chromium_ec/mod.rs | 21 +++++++++++++++++++++ framework_lib/src/commandline/mod.rs | 7 ++----- 3 files changed, 35 insertions(+), 5 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 084c88aa..f59576fd 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -64,3 +64,15 @@ ALS: 76 Lux APU: 42 C Fan Speed: 0 RPM ``` + +## Check expansion bay (Framework 16) + +``` +> sudo framework_tool --expansion-bay +Expansion Bay + Enabled: true + Has fault: false + Hatch closed: true + Board: DualInterposer + Serial Number: FRAXXXXXXXXXXXXXXX +``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index f35595ba..e1a53c20 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -817,6 +817,27 @@ impl CrosEc { res } + pub fn check_bay_status(&self) -> EcResult<()> { + println!("Expansion Bay"); + + let info = EcRequestExpansionBayStatus {}.send_command(self)?; + println!(" Enabled: {}", info.module_enabled()); + println!(" Has fault: {}", info.module_fault()); + println!(" Hatch closed: {}", info.hatch_switch_closed()); + match info.expansion_bay_board() { + Ok(board) => println!(" Board: {:?}", board), + Err(err) => println!(" Board: {:?}", err), + } + + if let Ok(sn) = self.get_gpu_serial() { + println!(" Serial Number: {}", sn); + } else { + println!(" Serial Number: Unknown"); + } + + Ok(()) + } + /// Get the GPU Serial /// pub fn get_gpu_serial(&self) -> EcResult { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 5bbdbce3..2a88db09 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -763,11 +763,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!("Set mode to: {:?}", mode); ec.set_input_deck_mode((*mode).into()).unwrap(); } else if args.expansion_bay { - println!("Expansion Bay"); - if let Ok(sn) = ec.get_gpu_serial() { - println!(" Serial Number: {}", sn); - } else { - println!(" Serial Number: Unknown"); + if let Err(err) = ec.check_bay_status() { + error!("{:?}", err); } } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); From af354d02c46084a1787fc7bae9a18886889c4d7c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Apr 2025 20:36:16 +0800 Subject: [PATCH 19/39] Rename --inputmodules to --inputdeck Prepare for compatibility with Framework 12 and Framework 13 Signed-off-by: Daniel Schaefer --- README.md | 2 +- completions/bash/framework_tool | 2 +- completions/zsh/_framework_tool | 2 +- framework_lib/src/commandline/clap_std.rs | 4 ++-- framework_lib/src/commandline/mod.rs | 6 +++--- framework_lib/src/commandline/uefi.rs | 6 +++--- support-matrices.md | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index db57686c..0e89f34b 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ Options: --dump Dump extracted UX capsule bitmap image to a file --h2o-capsule Parse UEFI Capsule information from binary file --intrusion Show status of intrusion switch - --inputmodules Show status of the input modules (Framework 16 only) + --inputdeck Show status of the input deck --input-deck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) [possible values: auto, off, on] --expansion-bay Show status of the expansion bay (Framework 16 only) diff --git a/completions/bash/framework_tool b/completions/bash/framework_tool index 24f41d46..7278dab5 100755 --- a/completions/bash/framework_tool +++ b/completions/bash/framework_tool @@ -34,7 +34,7 @@ _framework_tool() { "--flash-ro-ec" "--flash-rw-ec" "--intrusion" - "--inputmodules" + "--inputdeck" "--input-deck-mode" "--charge-limit" "--get-gpio" diff --git a/completions/zsh/_framework_tool b/completions/zsh/_framework_tool index 70ea7516..89ca4350 100644 --- a/completions/zsh/_framework_tool +++ b/completions/zsh/_framework_tool @@ -32,7 +32,7 @@ options=( '--flash-ro-ec[Flash EC with new RO firmware from file]:flash_ro_ec' '--flash-rw-ec[Flash EC with new RW firmware from file]:flash_rw_ec' '--intrusion[Show status of intrusion switch]' - '--inputmodules[Show status of the input modules (Framework 16 only)]' + '--inputdeck[Show status of the input deck]' '--input-deck-mode[Set input deck power mode]:input_deck_mode:(auto off on)' '--charge-limit[Get or set max charge limit]:charge_limit' '--get-gpio[Get GPIO value by name]:get_gpio' diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 38ef8f5f..b3bcaeb8 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -141,7 +141,7 @@ struct ClapCli { /// Show status of the input modules (Framework 16 only) #[arg(long)] - inputmodules: bool, + inputdeck: bool, /// Set input deck power mode [possible values: auto, off, on] (Framework 16 only) #[arg(long)] @@ -354,7 +354,7 @@ pub fn parse(args: &[String]) -> Cli { .flash_rw_ec .map(|x| x.into_os_string().into_string().unwrap()), intrusion: args.intrusion, - inputmodules: args.inputmodules, + inputdeck: args.inputdeck, input_deck_mode: args.input_deck_mode, expansion_bay: args.expansion_bay, charge_limit: args.charge_limit, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 2a88db09..649000b2 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -171,7 +171,7 @@ pub struct Cli { pub driver: Option, pub test: bool, pub intrusion: bool, - pub inputmodules: bool, + pub inputdeck: bool, pub input_deck_mode: Option, pub expansion_bay: bool, pub charge_limit: Option>, @@ -745,7 +745,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!(" Unable to tell"); } - } else if args.inputmodules { + } else if args.inputdeck { println!("Input Module Status:"); if let Some(status) = print_err(ec.get_input_deck_status()) { println!("Input Deck State: {:?}", status.state); @@ -1083,7 +1083,7 @@ Options: --flash-rw-ec Flash EC with new firmware from file --reboot-ec Control EC RO/RW jump [possible values: reboot, jump-ro, jump-rw, cancel-jump, disable-jump] --intrusion Show status of intrusion switch - --inputmodules Show status of the input modules (Framework 16 only) + --inputdeck Show status of the input deck --input-deck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) --expansion-bay Show status of the expansion bay (Framework 16 only) --charge-limit [] Get or set battery charge limit (Percentage number as arg, e.g. '100') diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 9d3239a5..83f76773 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -84,7 +84,7 @@ pub fn parse(args: &[String]) -> Cli { dump: None, h2o_capsule: None, intrusion: false, - inputmodules: false, + inputdeck: false, input_deck_mode: None, expansion_bay: false, charge_limit: None, @@ -225,8 +225,8 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "--intrusion" { cli.intrusion = true; found_an_option = true; - } else if arg == "--inputmodules" { - cli.inputmodules = true; + } else if arg == "--inputdeck" { + cli.inputdeck = true; found_an_option = true; } else if arg == "--input-deck-mode" { cli.input_deck_mode = if args.len() > i + 1 { diff --git a/support-matrices.md b/support-matrices.md index 4a4396ed..6c468160 100644 --- a/support-matrices.md +++ b/support-matrices.md @@ -37,6 +37,6 @@ | `--pd-info` | PD Communication | All | | `--privacy` | EC Communication | All | | `--intrusion` | EC Communication | All | -| `--inputmodules` | EC Communication | Framework 16 | +| `--inputdeck` | EC Communication | Framework 16 | | `--console` | EC Communication | All | -| `--kblight` | EC Communication | All, except FL16 | \ No newline at end of file +| `--kblight` | EC Communication | All, except FL16 | From 42e4d00e1a9479612dae80f53d1959850d94fc13 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Apr 2025 20:39:26 +0800 Subject: [PATCH 20/39] Rename --input-deck-mode to --inputdeck-mode Align it with --inputdeck Signed-off-by: Daniel Schaefer --- completions/bash/framework_tool | 8 ++++---- completions/zsh/_framework_tool | 2 +- framework_lib/src/commandline/clap_std.rs | 4 ++-- framework_lib/src/commandline/mod.rs | 6 +++--- framework_lib/src/commandline/uefi.rs | 18 +++++++++--------- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/completions/bash/framework_tool b/completions/bash/framework_tool index 7278dab5..f96fcc23 100755 --- a/completions/bash/framework_tool +++ b/completions/bash/framework_tool @@ -35,7 +35,7 @@ _framework_tool() { "--flash-rw-ec" "--intrusion" "--inputdeck" - "--input-deck-mode" + "--inputdeck-mode" "--charge-limit" "--get-gpio" "--fp-led-level" @@ -56,7 +56,7 @@ _framework_tool() { ) local devices=("bios" "ec" "pd0" "pd1" "rtm01" "rtm23" "ac-left" "ac-right") - local input_deck_modes=("auto" "off" "on") + local inputdeck_modes=("auto" "off" "on") local console_modes=("recent" "follow") local drivers=("portio" "cros-ec" "windows") local has_mec_options=("true" "false") @@ -71,8 +71,8 @@ _framework_tool() { COMPREPLY=( $(compgen -W "${options[*]}" -- "$current_word") ) elif [[ $prev_word == "--device" ]]; then COMPREPLY=( $(compgen -W "${devices[*]}" -- "$current_word") ) - elif [[ $prev_word == "--input-deck-mode" ]]; then - COMPREPLY=( $(compgen -W "${input_deck_modes[*]}" -- "$current_word") ) + elif [[ $prev_word == "--inputdeck-mode" ]]; then + COMPREPLY=( $(compgen -W "${inputdeck_modes[*]}" -- "$current_word") ) elif [[ $prev_word == "--console" ]]; then COMPREPLY=( $(compgen -W "${console_modes[*]}" -- "$current_word") ) elif [[ $prev_word == "--driver" ]]; then diff --git a/completions/zsh/_framework_tool b/completions/zsh/_framework_tool index 89ca4350..ad6aa668 100644 --- a/completions/zsh/_framework_tool +++ b/completions/zsh/_framework_tool @@ -33,7 +33,7 @@ options=( '--flash-rw-ec[Flash EC with new RW firmware from file]:flash_rw_ec' '--intrusion[Show status of intrusion switch]' '--inputdeck[Show status of the input deck]' - '--input-deck-mode[Set input deck power mode]:input_deck_mode:(auto off on)' + '--inputdeck-mode[Set input deck power mode]:inputdeck_mode:(auto off on)' '--charge-limit[Get or set max charge limit]:charge_limit' '--get-gpio[Get GPIO value by name]:get_gpio' '--fp-led-level-gpio[Get or set fingerprint LED brightness level]:fp_led_level:(high medium low ultra-low auto)' diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index b3bcaeb8..e94a330c 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -145,7 +145,7 @@ struct ClapCli { /// Set input deck power mode [possible values: auto, off, on] (Framework 16 only) #[arg(long)] - input_deck_mode: Option, + inputdeck_mode: Option, /// Show status of the expansion bay (Framework 16 only) #[arg(long)] @@ -355,7 +355,7 @@ pub fn parse(args: &[String]) -> Cli { .map(|x| x.into_os_string().into_string().unwrap()), intrusion: args.intrusion, inputdeck: args.inputdeck, - input_deck_mode: args.input_deck_mode, + inputdeck_mode: args.inputdeck_mode, expansion_bay: args.expansion_bay, charge_limit: args.charge_limit, get_gpio: args.get_gpio, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 649000b2..b5f28f32 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -172,7 +172,7 @@ pub struct Cli { pub test: bool, pub intrusion: bool, pub inputdeck: bool, - pub input_deck_mode: Option, + pub inputdeck_mode: Option, pub expansion_bay: bool, pub charge_limit: Option>, pub get_gpio: Option, @@ -759,7 +759,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!(" Unable to tell"); } - } else if let Some(mode) = &args.input_deck_mode { + } else if let Some(mode) = &args.inputdeck_mode { println!("Set mode to: {:?}", mode); ec.set_input_deck_mode((*mode).into()).unwrap(); } else if args.expansion_bay { @@ -1084,7 +1084,7 @@ Options: --reboot-ec Control EC RO/RW jump [possible values: reboot, jump-ro, jump-rw, cancel-jump, disable-jump] --intrusion Show status of intrusion switch --inputdeck Show status of the input deck - --input-deck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) + --inputdeck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) --expansion-bay Show status of the expansion bay (Framework 16 only) --charge-limit [] Get or set battery charge limit (Percentage number as arg, e.g. '100') --get-gpio Get GPIO value by name diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 83f76773..3c097344 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -85,7 +85,7 @@ pub fn parse(args: &[String]) -> Cli { h2o_capsule: None, intrusion: false, inputdeck: false, - input_deck_mode: None, + inputdeck_mode: None, expansion_bay: false, charge_limit: None, get_gpio: None, @@ -228,22 +228,22 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "--inputdeck" { cli.inputdeck = true; found_an_option = true; - } else if arg == "--input-deck-mode" { - cli.input_deck_mode = if args.len() > i + 1 { - let input_deck_mode = &args[i + 1]; - if input_deck_mode == "auto" { + } else if arg == "--inputdeck-mode" { + cli.inputdeck_mode = if args.len() > i + 1 { + let inputdeck_mode = &args[i + 1]; + if inputdeck_mode == "auto" { Some(InputDeckModeArg::Auto) - } else if input_deck_mode == "off" { + } else if inputdeck_mode == "off" { Some(InputDeckModeArg::Off) - } else if input_deck_mode == "on" { + } else if inputdeck_mode == "on" { Some(InputDeckModeArg::On) } else { - println!("Invalid value for --input-deck-mode: {}", input_deck_mode); + println!("Invalid value for --inputdeck-mode: {}", inputdeck_mode); None } } else { println!( - "Need to provide a value for --input-deck-mode. Either `auto`, `off`, or `on`" + "Need to provide a value for --inputdeck-mode. Either `auto`, `off`, or `on`" ); None }; From c0cfcd922021c48bd7fc0a66e0db98abf890c3f4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Apr 2025 21:02:23 +0800 Subject: [PATCH 21/39] Move input deck status into chromium_ec Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 13 +++++++++++++ framework_lib/src/commandline/mod.rs | 14 +------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e1a53c20..04d3ee3d 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -394,6 +394,19 @@ impl CrosEc { Ok(InputDeckStatus::from(status)) } + pub fn print_fw16_inputdeck_status(&self) -> EcResult<()> { + let status = self.get_input_deck_status()?; + println!("Input Deck State: {:?}", status.state); + println!("Touchpad present: {:?}", status.touchpad_present); + println!("Positions:"); + println!(" Pos 0: {:?}", status.top_row.pos0); + println!(" Pos 1: {:?}", status.top_row.pos1); + println!(" Pos 2: {:?}", status.top_row.pos2); + println!(" Pos 3: {:?}", status.top_row.pos3); + println!(" Pos 4: {:?}", status.top_row.pos4); + Ok(()) + } + pub fn set_input_deck_mode(&self, mode: DeckStateMode) -> EcResult { let status = EcRequestDeckState { mode }.send_command(self)?; diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b5f28f32..1396f9a0 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -746,19 +746,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!(" Unable to tell"); } } else if args.inputdeck { - println!("Input Module Status:"); - if let Some(status) = print_err(ec.get_input_deck_status()) { - println!("Input Deck State: {:?}", status.state); - println!("Touchpad present: {:?}", status.touchpad_present); - println!("Positions:"); - println!(" Pos 0: {:?}", status.top_row.pos0); - println!(" Pos 1: {:?}", status.top_row.pos1); - println!(" Pos 2: {:?}", status.top_row.pos2); - println!(" Pos 3: {:?}", status.top_row.pos3); - println!(" Pos 4: {:?}", status.top_row.pos4); - } else { - println!(" Unable to tell"); - } + let _ = print_err(ec.print_fw16_inputdeck_status()); } else if let Some(mode) = &args.inputdeck_mode { println!("Set mode to: {:?}", mode); ec.set_input_deck_mode((*mode).into()).unwrap(); From 22cc7da6d370e0035cf12c047bc3e9146c40f851 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 25 Apr 2025 21:08:53 +0800 Subject: [PATCH 22/39] util: Add Platform::which_family Determine which family a platform belongs to Signed-off-by: Daniel Schaefer --- framework_lib/src/util.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 3fa8f505..cfb97368 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -40,6 +40,31 @@ pub enum Platform { GenericFramework((u16, u16), (u8, u8), bool), } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum PlatformFamily { + Framework12, + Framework13, + Framework16, + FrameworkDesktop, +} + +impl Platform { + pub fn which_family(self) -> Option { + match self { + Platform::Framework12IntelGen13 => Some(PlatformFamily::Framework12), + Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1 + | Platform::Framework13Amd7080 + | Platform::Framework13AmdAi300 => Some(PlatformFamily::Framework13), + Platform::Framework16Amd7080 => Some(PlatformFamily::Framework16), + Platform::FrameworkDesktopAmdAiMax300 => Some(PlatformFamily::FrameworkDesktop), + Platform::GenericFramework(..) => None, + } + } +} + #[derive(Debug)] pub struct Config { // TODO: Actually set and read this From ddf04d854315a925567b63c3f6b639a1754bbf4e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 26 Apr 2025 00:07:02 +0800 Subject: [PATCH 23/39] chromium_ec: Add function to read Framework12 boardid Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 66 ++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 04d3ee3d..564d0942 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -120,6 +120,47 @@ pub enum EcResponseStatus { Busy = 16, } +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum Framework12Adc { + MainboardBoard, + PowerButtonBoard, + Psys, + AdapterCurrent, + Touchpad, + AudioBoard, +} + +/* + * PLATFORM_EC_ADC_RESOLUTION default 10 bit + * + * +------------------+-----------+--------------+---------+----------------------+ + * | BOARD VERSION | voltage | main board | GPU | Input module | + * +------------------+-----------+--------------+---------+----------------------+ + * | BOARD_VERSION_0 | 0 mv | Unused | | Reserved | + * | BOARD_VERSION_1 | 173 mv | Unused | | Reserved | + * | BOARD_VERSION_2 | 300 mv | Unused | | Reserved | + * | BOARD_VERSION_3 | 430 mv | Unused | | Reserved | + * | BOARD_VERSION_4 | 588 mv | EVT1 | | Reserved | + * | BOARD_VERSION_5 | 783 mv | Unused | | Reserved | + * | BOARD_VERSION_6 | 905 mv | Unused | | Reserved | + * | BOARD_VERSION_7 | 1033 mv | DVT1 | | Reserved | + * | BOARD_VERSION_8 | 1320 mv | DVT2 | | Generic A size | + * | BOARD_VERSION_9 | 1500 mv | PVT | | Generic B size | + * | BOARD_VERSION_10 | 1650 mv | MP | | Generic C size | + * | BOARD_VERSION_11 | 1980 mv | Unused | RID_0 | 10 Key B size | + * | BOARD_VERSION_12 | 2135 mv | Unused | RID_0,1 | Keyboard | + * | BOARD_VERSION_13 | 2500 mv | Unused | RID_0 | Touchpad | + * | BOARD_VERSION_14 | 2706 mv | Unused | | Reserved | + * | BOARD_VERSION_15 | 2813 mv | Unused | | Not installed | + * +------------------+-----------+--------------+---------+----------------------+ + */ + +const BOARD_VERSION_COUNT: usize = 16; +const BOARD_VERSION: [i32; BOARD_VERSION_COUNT] = [ + 85, 233, 360, 492, 649, 844, 965, 1094, 1380, 1562, 1710, 2040, 2197, 2557, 2766, 2814, +]; + pub fn has_mec() -> bool { let platform = smbios::get_platform().unwrap(); if let Platform::GenericFramework(_, _, has_mec) = platform { @@ -1038,6 +1079,31 @@ impl CrosEc { Ok(res.adc_value) } + pub fn read_board_id(&self, channel: Framework12Adc) -> EcResult> { + let mv = self.adc_read(channel as u8)?; + if mv < 0 { + return Err(EcError::DeviceError(format!( + "Failed to read ADC channel {}", + channel as u8 + ))); + } + + for (board_id, board_id_res) in BOARD_VERSION.iter().enumerate() { + if mv < *board_id_res { + return Ok(if board_id == 15 { + None + } else { + Some(board_id as u8) + }); + } + } + + Err(EcError::DeviceError(format!( + "Unknown board id. ADC mv: {}", + mv + ))) + } + pub fn rgbkbd_set_color(&self, start_key: u8, colors: Vec) -> EcResult<()> { for (chunk, colors) in colors.chunks(EC_RGBKBD_MAX_KEY_COUNT).enumerate() { let mut request = EcRequestRgbKbdSetColor { From d1eefd72c3e7ed4302233f2537dfd8d1b02c9081 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 26 Apr 2025 00:08:12 +0800 Subject: [PATCH 24/39] --input-deck: Print Framework12 input deck status ``` > sudo framework_tool --inputdeck Input Deck Chassis Open: false Power Button Board: Present Audio Daughterboard: Present Touchpad: Present ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 21 +++++++++++++++++++++ framework_lib/src/commandline/mod.rs | 10 ++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 564d0942..21b954ff 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -435,6 +435,27 @@ impl CrosEc { Ok(InputDeckStatus::from(status)) } + pub fn print_fw12_inputdeck_status(&self) -> EcResult<()> { + let intrusion = self.get_intrusion_status()?; + let pwrbtn = self.read_board_id(Framework12Adc::PowerButtonBoard)?; + let audio = self.read_board_id(Framework12Adc::AudioBoard)?; + let tp = self.read_board_id(Framework12Adc::Touchpad)?; + + let is_present = |p| if p { "Present" } else { "Missing" }; + + println!("Input Deck"); + println!(" Chassis Open: {}", intrusion.currently_open); + println!(" Power Button Board: {}", is_present(pwrbtn.is_some())); + println!(" Audio Daughterboard: {}", is_present(audio.is_some())); + println!(" Touchpad: {}", is_present(tp.is_some())); + + Ok(()) + } + + pub fn print_fw13_inputdeck_status(&self) -> EcResult<()> { + Ok(()) + } + pub fn print_fw16_inputdeck_status(&self) -> EcResult<()> { let status = self.get_input_deck_status()?; println!("Input Deck State: {:?}", status.state); diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 1396f9a0..598a6d64 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -59,7 +59,7 @@ use crate::touchscreen; #[cfg(feature = "uefi")] use crate::uefi::enable_page_break; use crate::util; -use crate::util::{Config, Platform}; +use crate::util::{Config, Platform, PlatformFamily}; #[cfg(feature = "hidapi")] use hidapi::HidApi; use sha2::{Digest, Sha256, Sha384, Sha512}; @@ -746,7 +746,13 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!(" Unable to tell"); } } else if args.inputdeck { - let _ = print_err(ec.print_fw16_inputdeck_status()); + let res = match smbios::get_platform().and_then(Platform::which_family) { + Some(PlatformFamily::Framework12) => ec.print_fw12_inputdeck_status(), + Some(PlatformFamily::Framework13) => Ok(()), + Some(PlatformFamily::Framework16) => ec.print_fw16_inputdeck_status(), + _ => Ok(()), + }; + print_err(res); } else if let Some(mode) = &args.inputdeck_mode { println!("Set mode to: {:?}", mode); ec.set_input_deck_mode((*mode).into()).unwrap(); From af984dd2b127e7c3b8501115a1fdb367823b8edf Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 26 Apr 2025 22:56:33 +0800 Subject: [PATCH 25/39] chromium_ec: Add board id for 13 and 16 Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 55 +++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 21b954ff..bd410db2 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -123,12 +123,47 @@ pub enum EcResponseStatus { #[repr(u8)] #[derive(Copy, Clone, Debug)] pub enum Framework12Adc { - MainboardBoard, - PowerButtonBoard, + MainboardBoardId, + PowerButtonBoardId, Psys, AdapterCurrent, - Touchpad, - AudioBoard, + TouchpadBoardId, + AudioBoardId, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum FrameworkHx20Hx30Adc { + AdapterCurrent, + Psys, + BattTemp, + TouchpadBoardId, + MainboardBoardId, + AudioBoardId, +} + +/// So far on all Nuvoton/Zephyr EC based platforms +/// Until at least Framework 13 AMD Ryzen AI 300 +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum Framework13Adc { + MainboardBoardId, + Psys, + AdapterCurrent, + TouchpadBoardId, + AudioBoardId, + BattTemp, +} + +#[repr(u8)] +#[derive(Copy, Clone, Debug)] +pub enum Framework16Adc { + MainboardBoardId, + HubBoardId, + GpuBoardId0, + GpuBoardId1, + AdapterCurrent, + Psys, } /* @@ -437,9 +472,9 @@ impl CrosEc { pub fn print_fw12_inputdeck_status(&self) -> EcResult<()> { let intrusion = self.get_intrusion_status()?; - let pwrbtn = self.read_board_id(Framework12Adc::PowerButtonBoard)?; - let audio = self.read_board_id(Framework12Adc::AudioBoard)?; - let tp = self.read_board_id(Framework12Adc::Touchpad)?; + let pwrbtn = self.read_board_id(Framework12Adc::PowerButtonBoardId as u8)?; + let audio = self.read_board_id(Framework12Adc::AudioBoardId as u8)?; + let tp = self.read_board_id(Framework12Adc::TouchpadBoardId as u8)?; let is_present = |p| if p { "Present" } else { "Missing" }; @@ -1100,12 +1135,12 @@ impl CrosEc { Ok(res.adc_value) } - pub fn read_board_id(&self, channel: Framework12Adc) -> EcResult> { - let mv = self.adc_read(channel as u8)?; + pub fn read_board_id(&self, channel: u8) -> EcResult> { + let mv = self.adc_read(channel)?; if mv < 0 { return Err(EcError::DeviceError(format!( "Failed to read ADC channel {}", - channel as u8 + channel ))); } From ce17b061748afd0b2fec5a1592c946b6af0561df Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 26 Apr 2025 22:56:55 +0800 Subject: [PATCH 26/39] inputdeck: Print Framework 13 state ``` framework_tool.exe --inputdeck Input Deck Chassis Open: false Audio Daughterboard: Present Touchpad: Present ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 87 ++++++++++++++++++++-------- framework_lib/src/commandline/mod.rs | 2 +- 2 files changed, 65 insertions(+), 24 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index bd410db2..cb041605 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -169,26 +169,26 @@ pub enum Framework16Adc { /* * PLATFORM_EC_ADC_RESOLUTION default 10 bit * - * +------------------+-----------+--------------+---------+----------------------+ - * | BOARD VERSION | voltage | main board | GPU | Input module | - * +------------------+-----------+--------------+---------+----------------------+ - * | BOARD_VERSION_0 | 0 mv | Unused | | Reserved | - * | BOARD_VERSION_1 | 173 mv | Unused | | Reserved | - * | BOARD_VERSION_2 | 300 mv | Unused | | Reserved | - * | BOARD_VERSION_3 | 430 mv | Unused | | Reserved | - * | BOARD_VERSION_4 | 588 mv | EVT1 | | Reserved | - * | BOARD_VERSION_5 | 783 mv | Unused | | Reserved | - * | BOARD_VERSION_6 | 905 mv | Unused | | Reserved | - * | BOARD_VERSION_7 | 1033 mv | DVT1 | | Reserved | - * | BOARD_VERSION_8 | 1320 mv | DVT2 | | Generic A size | - * | BOARD_VERSION_9 | 1500 mv | PVT | | Generic B size | - * | BOARD_VERSION_10 | 1650 mv | MP | | Generic C size | - * | BOARD_VERSION_11 | 1980 mv | Unused | RID_0 | 10 Key B size | - * | BOARD_VERSION_12 | 2135 mv | Unused | RID_0,1 | Keyboard | - * | BOARD_VERSION_13 | 2500 mv | Unused | RID_0 | Touchpad | - * | BOARD_VERSION_14 | 2706 mv | Unused | | Reserved | - * | BOARD_VERSION_15 | 2813 mv | Unused | | Not installed | - * +------------------+-----------+--------------+---------+----------------------+ + * +------------------+-----------+----------+-------------+---------+----------------------+ + * | BOARD VERSION | voltage | NPC DB V | main board | GPU | Input module | + * +------------------+-----------+----------|-------------+---------+----------------------+ + * | BOARD_VERSION_0 | 0 mV | 100 mV | Unused | | Reserved | + * | BOARD_VERSION_1 | 173 mV | 310 mV | Unused | | Reserved | + * | BOARD_VERSION_2 | 300 mV | 520 mV | Unused | | Reserved | + * | BOARD_VERSION_3 | 430 mV | 720 mV | Unused | | Reserved | + * | BOARD_VERSION_4 | 588 mV | 930 mV | EVT1 | | Reserved | + * | BOARD_VERSION_5 | 783 mV | 1130 mV | Unused | | Reserved | + * | BOARD_VERSION_6 | 905 mV | 1340 mV | Unused | | Reserved | + * | BOARD_VERSION_7 | 1033 mV | 1550 mV | DVT1 | | Reserved | + * | BOARD_VERSION_8 | 1320 mV | 1750 mV | DVT2 | | Generic A size | + * | BOARD_VERSION_9 | 1500 mV | 1960 mV | PVT | | Generic B size | + * | BOARD_VERSION_10 | 1650 mV | 2170 mV | MP | | Generic C size | + * | BOARD_VERSION_11 | 1980 mV | 2370 mV | Unused | RID_0 | 10 Key B size | + * | BOARD_VERSION_12 | 2135 mV | 2580 mV | Unused | RID_0,1 | Keyboard | + * | BOARD_VERSION_13 | 2500 mV | 2780 mV | Unused | RID_0 | Touchpad | + * | BOARD_VERSION_14 | 2706 mV | 2990 mV | Unused | | Reserved | + * | BOARD_VERSION_15 | 2813 mV | 3200 mV | Unused | | Not installed | + * +------------------+-----------+----------+-------------+---------+----------------------+ */ const BOARD_VERSION_COUNT: usize = 16; @@ -196,6 +196,10 @@ const BOARD_VERSION: [i32; BOARD_VERSION_COUNT] = [ 85, 233, 360, 492, 649, 844, 965, 1094, 1380, 1562, 1710, 2040, 2197, 2557, 2766, 2814, ]; +const BOARD_VERSION_NPC_DB: [i32; BOARD_VERSION_COUNT] = [ + 100, 311, 521, 721, 931, 1131, 1341, 1551, 1751, 1961, 2171, 2370, 2580, 2780, 2990, 3200, +]; + pub fn has_mec() -> bool { let platform = smbios::get_platform().unwrap(); if let Platform::GenericFramework(_, _, has_mec) = platform { @@ -488,6 +492,29 @@ impl CrosEc { } pub fn print_fw13_inputdeck_status(&self) -> EcResult<()> { + let intrusion = self.get_intrusion_status()?; + + let (audio, tp) = match smbios::get_platform() { + Some(Platform::IntelGen11) + | Some(Platform::IntelGen12) + | Some(Platform::IntelGen13) => ( + self.read_board_id(FrameworkHx20Hx30Adc::AudioBoardId as u8)?, + self.read_board_id(FrameworkHx20Hx30Adc::TouchpadBoardId as u8)?, + ), + + _ => ( + self.read_board_id_npc_db(Framework13Adc::AudioBoardId as u8)?, + self.read_board_id_npc_db(Framework13Adc::TouchpadBoardId as u8)?, + ), + }; + + let is_present = |p| if p { "Present" } else { "Missing" }; + + println!("Input Deck"); + println!(" Chassis Open: {}", intrusion.currently_open); + println!(" Audio Daughterboard: {}", is_present(audio.is_some())); + println!(" Touchpad: {}", is_present(tp.is_some())); + Ok(()) } @@ -1135,7 +1162,18 @@ impl CrosEc { Ok(res.adc_value) } - pub fn read_board_id(&self, channel: u8) -> EcResult> { + fn read_board_id(&self, channel: u8) -> EcResult> { + self.read_board_id_raw(channel, BOARD_VERSION) + } + fn read_board_id_npc_db(&self, channel: u8) -> EcResult> { + self.read_board_id_raw(channel, BOARD_VERSION_NPC_DB) + } + + fn read_board_id_raw( + &self, + channel: u8, + table: [i32; BOARD_VERSION_COUNT], + ) -> EcResult> { let mv = self.adc_read(channel)?; if mv < 0 { return Err(EcError::DeviceError(format!( @@ -1144,9 +1182,12 @@ impl CrosEc { ))); } - for (board_id, board_id_res) in BOARD_VERSION.iter().enumerate() { + debug!("ADC Channel {} - Measured {}mv", channel, mv); + for (board_id, board_id_res) in table.iter().enumerate() { if mv < *board_id_res { - return Ok(if board_id == 15 { + debug!("ADC Channel {} - Board ID {}", channel, board_id); + // 15 is not present, less than 2 is undefined + return Ok(if board_id == 15 || board_id < 2 { None } else { Some(board_id as u8) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 598a6d64..75c8300a 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -748,7 +748,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if args.inputdeck { let res = match smbios::get_platform().and_then(Platform::which_family) { Some(PlatformFamily::Framework12) => ec.print_fw12_inputdeck_status(), - Some(PlatformFamily::Framework13) => Ok(()), + Some(PlatformFamily::Framework13) => ec.print_fw13_inputdeck_status(), Some(PlatformFamily::Framework16) => ec.print_fw16_inputdeck_status(), _ => Ok(()), }; From 8cd99d95b2e16da0ea15ddf95cdf419d447db587 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 26 Apr 2025 22:57:40 +0800 Subject: [PATCH 27/39] inputdeck: Add chassis open state to Framework 16 --- framework_lib/src/chromium_ec/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index cb041605..ff0327d4 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -519,9 +519,11 @@ impl CrosEc { } pub fn print_fw16_inputdeck_status(&self) -> EcResult<()> { + let intrusion = self.get_intrusion_status()?; let status = self.get_input_deck_status()?; + println!("Chassis Open: {}", intrusion.currently_open); println!("Input Deck State: {:?}", status.state); - println!("Touchpad present: {:?}", status.touchpad_present); + println!("Touchpad present: {}", status.touchpad_present); println!("Positions:"); println!(" Pos 0: {:?}", status.top_row.pos0); println!(" Pos 1: {:?}", status.top_row.pos1); From 52343c0d987b1e378bcb839d7dfc33e3d4a1c097 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 30 Apr 2025 15:06:21 +0800 Subject: [PATCH 28/39] Bump version to 0.4.0 Signed-off-by: Daniel Schaefer --- Cargo.lock | 6 +++--- framework_lib/Cargo.toml | 2 +- framework_tool/Cargo.toml | 2 +- framework_uefi/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 228dd7f2..465dc6e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,7 +388,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.3.0" +version = "0.4.0" dependencies = [ "built", "clap", @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.3.0" +version = "0.4.0" dependencies = [ "framework_lib", "static_vcruntime", @@ -428,7 +428,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.3.0" +version = "0.4.0" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 27819125..b9a7cc91 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.3.0" +version = "0.4.0" edition = "2021" # Minimum Supported Rust Version # Ubuntu 24.04 LTS ships 1.75 diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index d5a8958d..ecef1fb0 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.3.0" +version = "0.4.0" edition = "2021" [features] diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index 8b7755c0..13d6e0b5 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.3.0" +version = "0.4.0" edition = "2021" # Minimum Supported Rust Version rust-version = "1.74" From 4fa875c59b1d5a6672aa2b25b0c209fc725ffb40 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 30 Apr 2025 16:26:32 +0800 Subject: [PATCH 29/39] touchscreen_win: Find the right path with hidapi Signed-off-by: Daniel Schaefer --- framework_lib/src/touchscreen_win.rs | 89 ++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/framework_lib/src/touchscreen_win.rs b/framework_lib/src/touchscreen_win.rs index 2a1be801..6e73bacd 100644 --- a/framework_lib/src/touchscreen_win.rs +++ b/framework_lib/src/touchscreen_win.rs @@ -1,4 +1,7 @@ -use crate::touchscreen::TouchScreen; +use hidapi::HidApi; +use std::path::Path; + +use crate::touchscreen::{TouchScreen, ILI_PID, ILI_VID}; #[allow(unused_imports)] use windows::{ core::*, @@ -19,35 +22,69 @@ pub struct NativeWinTouchScreen { impl TouchScreen for NativeWinTouchScreen { fn open_device() -> Option { - // TODO: I don't know if this might be different on other systems - // Should enumerate and find the right one - // See: https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/finding-and-opening-a-hid-collection - let path = - w!(r"\\?\HID#ILIT2901&Col03#5&357cbf85&0&0002#{4d1e55b2-f16f-11cf-88cb-001111000030}"); + debug!("Looking for touchscreen HID device"); + match HidApi::new() { + Ok(api) => { + for dev_info in api.device_list() { + let vid = dev_info.vendor_id(); + let pid = dev_info.product_id(); + let usage_page = dev_info.usage_page(); + if vid != ILI_VID { + trace!(" Skipping VID:PID. Expected {:04X}:*", ILI_VID); + continue; + } + debug!( + " Found {:04X}:{:04X} (Usage Page {:04X})", + vid, pid, usage_page + ); + if usage_page != 0xFF00 { + debug!(" Skipping usage page. Expected {:04X}", 0xFF00); + continue; + } + if pid != ILI_PID { + debug!(" Warning: PID is {:04X}, expected {:04X}", pid, ILI_PID); + } - let res = unsafe { - CreateFileW( - path, - FILE_GENERIC_WRITE.0 | FILE_GENERIC_READ.0, - FILE_SHARE_READ | FILE_SHARE_WRITE, - None, - OPEN_EXISTING, - // hidapi-rs is using FILE_FLAG_OVERLAPPED but it doesn't look like we need that - FILE_FLAGS_AND_ATTRIBUTES(0), - None, - ) - }; - let handle = match res { - Ok(h) => h, - Err(err) => { - error!("Failed to open device {:?}", err); - return None; + debug!(" Found matching touchscreen HID device"); + debug!(" Path: {:?}", dev_info.path()); + debug!(" IC Type: {:04X}", pid); + + // TODO: Enumerate with windows + // Should enumerate and find the right one + // See: https://learn.microsoft.com/en-us/windows-hardware/drivers/hid/finding-and-opening-a-hid-collection + let path = dev_info.path().to_str().unwrap(); + + let res = unsafe { + CreateFileW( + &HSTRING::from(Path::new(path)), + FILE_GENERIC_WRITE.0 | FILE_GENERIC_READ.0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + // hidapi-rs is using FILE_FLAG_OVERLAPPED but it doesn't look like we need that + FILE_FLAGS_AND_ATTRIBUTES(0), + None, + ) + }; + let handle = match res { + Ok(h) => h, + Err(err) => { + error!("Failed to open device {:?}", err); + return None; + } + }; + + debug!("Opened {:?}", path); + + return Some(NativeWinTouchScreen { handle }); + } + } + Err(e) => { + error!("Failed to open hidapi. Error: {e}"); } }; - debug!("Opened {:?}", path); - - Some(NativeWinTouchScreen { handle }) + None } fn send_message(&self, message_id: u8, read_len: usize, data: Vec) -> Option> { From 660e2bc6cb05b60c58daa3cbda8da0503f1b8f4b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 20:53:45 +0800 Subject: [PATCH 30/39] --power: Add information about charger ``` > sudo framework_tool --power Place your right index finger on the fingerprint reader Charger Status AC is: not connected Charger Voltage: 17048mV Charger Current: 0mA Chg Input Current:384mA Battery SoC: 93% Battery Status AC is: not connected Battery is: connected Battery LFCC: 3693 mAh (Last Full Charge Capacity) Battery Capacity: 3409 mAh 58.96 Wh Charge level: 92% Battery discharging ``` Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 20 ++++++++++++++ framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 32 +++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 23 ++++++++++++++++ framework_lib/src/power.rs | 2 ++ 5 files changed, 78 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 76d6755a..d03d32d0 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -139,6 +139,7 @@ ALS: 76 Lux Fan Speed: 0 RPM ``` + ## Check expansion bay (Framework 16) ``` @@ -149,4 +150,23 @@ Expansion Bay Hatch closed: true Board: DualInterposer Serial Number: FRAXXXXXXXXXXXXXXX + +## Check charger and battery status (Framework 12/13/16) + +``` +> sudo framework_tool --power +Charger Status + AC is: not connected + Charger Voltage: 17048mV + Charger Current: 0mA + Chg Input Current:384mA + Battery SoC: 93% +Battery Status + AC is: not connected + Battery is: connected + Battery LFCC: 3693 mAh (Last Full Charge Capacity) + Battery Capacity: 3409 mAh + 58.96 Wh + Charge level: 92% + Battery discharging ``` diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 21fb5f57..b08fd17a 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -40,6 +40,7 @@ pub enum EcCommands { I2cPassthrough = 0x009e, ConsoleSnapshot = 0x0097, ConsoleRead = 0x0098, + ChargeState = 0x00A0, /// List the features supported by the firmware GetFeatures = 0x000D, /// Force reboot, causes host reboot as well diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index e84f7687..d00587b5 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -377,6 +377,38 @@ impl EcRequest<()> for EcRequestConsoleRead { } } +#[repr(u8)] +pub enum ChargeStateCmd { + GetState = 0, + GetParam, + SetParam, + NumCmds, +} + +#[repr(C, packed)] +pub struct EcRequestChargeStateGetV0 { + pub cmd: u8, + pub param: u32, +} + +#[repr(C, packed)] +pub struct EcResponseChargeStateGetV0 { + pub ac: u32, + pub chg_voltage: u32, + pub chg_current: u32, + pub chg_input_current: u32, + pub batt_state_of_charge: u32, +} + +impl EcRequest for EcRequestChargeStateGetV0 { + fn command_id() -> EcCommands { + EcCommands::ChargeState + } + fn command_version() -> u8 { + 0 + } +} + /// Supported features #[derive(Debug, FromPrimitive)] pub enum EcFeatureCode { diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index ff0327d4..f0f42c08 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1083,6 +1083,29 @@ impl CrosEc { } } + pub fn get_charge_state(&self) -> EcResult<()> { + let res = EcRequestChargeStateGetV0 { + cmd: ChargeStateCmd::GetState as u8, + param: 0, + } + .send_command(self)?; + println!("Charger Status"); + println!( + " AC is: {}", + if res.ac == 1 { + "connected" + } else { + "not connected" + } + ); + println!(" Charger Voltage: {}mV", { res.chg_voltage }); + println!(" Charger Current: {}mA", { res.chg_current }); + println!(" Chg Input Current:{}mA", { res.chg_input_current }); + println!(" Battery SoC: {}%", { res.batt_state_of_charge }); + + Ok(()) + } + /// Check features supported by the firmware pub fn get_features(&self) -> EcResult<()> { let data = EcRequestGetFeatures {}.send_command(self)?; diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index b97ec0e2..92c0ed6e 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -471,6 +471,7 @@ pub fn is_standalone(ec: &CrosEc) -> bool { } pub fn get_and_print_power_info(ec: &CrosEc) -> i32 { + print_err_ref(&ec.get_charge_state()); if let Some(power_info) = power_info(ec) { print_battery_information(&power_info); if let Some(_battery) = &power_info.battery { @@ -481,6 +482,7 @@ pub fn get_and_print_power_info(ec: &CrosEc) -> i32 { } fn print_battery_information(power_info: &PowerInfo) { + println!("Battery Status"); print!(" AC is: "); if power_info.ac_present { println!("connected"); From 1c1c59d7d8e33057f3ea5ebd5e9625a0c5508279 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 23:23:37 +0800 Subject: [PATCH 31/39] Add commandline option to set charger current limit Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 43 +++++++++++++++++++++++ framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 29 +++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 13 +++++++ framework_lib/src/commandline/clap_std.rs | 14 ++++++++ framework_lib/src/commandline/mod.rs | 4 +++ framework_lib/src/commandline/uefi.rs | 30 ++++++++++++++++ 7 files changed, 134 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index d03d32d0..b150cd5e 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -170,3 +170,46 @@ Battery Status Charge level: 92% Battery discharging ``` + +### Setting a custom charger current limit + +``` +# Set limit to 2A +> sudo framework_tool --charge-current-limit 2000 + +# And then plug in a power adapter +> sudo framework_tool --power +Charger Status + AC is: connected + Charger Voltage: 17800mV + Charger Current: 2000mA + Chg Input Current:3084mA + Battery SoC: 87% +Battery Status + AC is: connected + Battery is: connected + Battery LFCC: 3713 mAh (Last Full Charge Capacity) + Battery Capacity: 3215 mAh + 56.953 Wh + Charge level: 86% + Battery charging + +# Remove limit (highest USB-PD current is 5A) +> sudo framework_tool --charge-current-limit 5000 + +> sudo framework_tool --power +Charger Status + AC is: connected + Charger Voltage: 17800mV + Charger Current: 2740mA + Chg Input Current:3084mA + Battery SoC: 92% +Battery Status + AC is: connected + Battery is: connected + Battery LFCC: 3713 mAh (Last Full Charge Capacity) + Battery Capacity: 3387 mAh + 60.146 Wh + Charge level: 91% + Battery charging +``` diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index b08fd17a..057681e5 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -41,6 +41,7 @@ pub enum EcCommands { ConsoleSnapshot = 0x0097, ConsoleRead = 0x0098, ChargeState = 0x00A0, + ChargeCurrentLimit = 0x00A1, /// List the features supported by the firmware GetFeatures = 0x000D, /// Force reboot, causes host reboot as well diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index d00587b5..5b909663 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -409,6 +409,35 @@ impl EcRequest for EcRequestChargeStateGetV0 { } } +pub struct EcRequestCurrentLimitV0 { + /// Current limit in mA + pub current: u32, +} + +impl EcRequest<()> for EcRequestCurrentLimitV0 { + fn command_id() -> EcCommands { + EcCommands::ChargeCurrentLimit + } +} + +pub struct EcRequestCurrentLimitV1 { + /// Current limit in mA + pub current: u32, + /// Battery state of charge is the minimum charge percentage at which + /// the battery charge current limit will apply. + /// When not set, the limit will apply regardless of state of charge. + pub battery_soc: u8, +} + +impl EcRequest<()> for EcRequestCurrentLimitV1 { + fn command_id() -> EcCommands { + EcCommands::ChargeCurrentLimit + } + fn command_version() -> u8 { + 1 + } +} + /// Supported features #[derive(Debug, FromPrimitive)] pub enum EcFeatureCode { diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index f0f42c08..f14b8da2 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -394,6 +394,19 @@ impl CrosEc { Ok((limits.min_percentage, limits.max_percentage)) } + pub fn set_charge_current_limit(&self, current: u32, battery_soc: Option) -> EcResult<()> { + if let Some(battery_soc) = battery_soc { + let battery_soc = battery_soc as u8; + EcRequestCurrentLimitV1 { + current, + battery_soc, + } + .send_command(self) + } else { + EcRequestCurrentLimitV0 { current }.send_command(self) + } + } + pub fn set_fp_led_percentage(&self, percentage: u8) -> EcResult<()> { // Sending bytes manually because the Set command, as opposed to the Get command, // does not return any data diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index e94a330c..3adf8113 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -155,6 +155,11 @@ struct ClapCli { #[arg(long)] charge_limit: Option>, + /// Get or set max charge current limit + #[arg(long)] + #[clap(num_args = ..2)] + charge_current_limit: Vec, + /// Get GPIO value by name #[arg(long)] get_gpio: Option, @@ -305,6 +310,14 @@ pub fn parse(args: &[String]) -> Cli { 1 => Some((None, args.fansetrpm[0])), _ => None, }; + let charge_current_limit = match args.charge_current_limit.len() { + 2 => Some(( + args.charge_current_limit[0], + Some(args.charge_current_limit[1]), + )), + 1 => Some((args.charge_current_limit[0], None)), + _ => None, + }; Cli { verbosity: args.verbosity.log_level_filter(), @@ -358,6 +371,7 @@ pub fn parse(args: &[String]) -> Cli { inputdeck_mode: args.inputdeck_mode, expansion_bay: args.expansion_bay, charge_limit: args.charge_limit, + charge_current_limit, get_gpio: args.get_gpio, fp_led_level: args.fp_led_level, fp_brightness: args.fp_brightness, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 75c8300a..5712cf81 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -175,6 +175,7 @@ pub struct Cli { pub inputdeck_mode: Option, pub expansion_bay: bool, pub charge_limit: Option>, + pub charge_current_limit: Option<(u32, Option)>, pub get_gpio: Option, pub fp_led_level: Option>, pub fp_brightness: Option>, @@ -762,6 +763,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); + } else if let Some((limit, soc)) = args.charge_current_limit { + print_err(ec.set_charge_current_limit(limit, soc)); } else if let Some(gpio_name) = &args.get_gpio { print!("Getting GPIO value {}: ", gpio_name); if let Ok(value) = ec.get_gpio(gpio_name) { @@ -1081,6 +1084,7 @@ Options: --inputdeck-mode Set input deck power mode [possible values: auto, off, on] (Framework 16 only) --expansion-bay Show status of the expansion bay (Framework 16 only) --charge-limit [] Get or set battery charge limit (Percentage number as arg, e.g. '100') + --charge-current-limit [] Get or set battery current charge limit (Percentage number as arg, e.g. '100') --get-gpio Get GPIO value by name --fp-led-level [] Get or set fingerprint LED brightness level [possible values: high, medium, low] --fp-brightness []Get or set fingerprint LED brightness percentage diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 3c097344..70bba487 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -88,6 +88,7 @@ pub fn parse(args: &[String]) -> Cli { inputdeck_mode: None, expansion_bay: false, charge_limit: None, + charge_current_limit: None, get_gpio: None, fp_led_level: None, fp_brightness: None, @@ -266,6 +267,35 @@ pub fn parse(args: &[String]) -> Cli { Some(None) }; found_an_option = true; + } else if arg == "--charge-current-limit" { + cli.charge_current_limit = if args.len() > i + 2 { + let limit = args[i + 1].parse::(); + let soc = args[i + 2].parse::(); + if let (Ok(limit), Ok(soc)) = (limit, soc) { + Some((limit, Some(soc))) + } else { + println!( + "Invalid values for --charge-current-limit: '{} {}'. Must be u32 integers.", + args[i + 1], + args[i + 2] + ); + None + } + } else if args.len() > i + 1 { + if let Ok(limit) = args[i + 1].parse::() { + Some((limit, None)) + } else { + println!( + "Invalid values for --charge-current-limit: '{}'. Must be an integer.", + args[i + 1], + ); + None + } + } else { + println!("--charge-current-limit requires one or two. [limit] [soc] or [limit]"); + None + }; + found_an_option = true; } else if arg == "--get-gpio" { cli.get_gpio = if args.len() > i + 1 { Some(args[i + 1].clone()) From e7ce0d0d4faea37cddd33538844c592fe4917cac Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 1 May 2025 18:23:22 -0700 Subject: [PATCH 32/39] --expansion-bay: Improved wording In the case everything is good, everything should say true. That makes it easier at a glance to understand. Also we call it "Interposer Door", not hatch. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 4 ++-- framework_lib/src/chromium_ec/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 76d6755a..97a2c844 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -145,8 +145,8 @@ ALS: 76 Lux > sudo framework_tool --expansion-bay Expansion Bay Enabled: true - Has fault: false - Hatch closed: true + No fault: true + Door closed: true Board: DualInterposer Serial Number: FRAXXXXXXXXXXXXXXX ``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index ff0327d4..67a47f67 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -961,8 +961,8 @@ impl CrosEc { let info = EcRequestExpansionBayStatus {}.send_command(self)?; println!(" Enabled: {}", info.module_enabled()); - println!(" Has fault: {}", info.module_fault()); - println!(" Hatch closed: {}", info.hatch_switch_closed()); + println!(" No fault: {}", !info.module_fault()); + println!(" Door closed: {}", info.hatch_switch_closed()); match info.expansion_bay_board() { Ok(board) => println!(" Board: {:?}", board), Err(err) => println!(" Board: {:?}", err), From 18ab45ca36af23908d51e7590dbb45c2021e7e5b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 30 Apr 2025 14:38:34 +0800 Subject: [PATCH 33/39] Add --charge-rate-limit Usually the user won't want to specify a mA value to limit the charge current. Setting a charge rate 0.0-1.0 is more useful. ``` PS C:\Users\Daniel\clone\framework-system-private> sudo .\target\debug\framework_tool.exe --power Charger Status AC is: connected Charger Voltage: 17800mV Charger Current: 2740mA 0.6998723C Chg Input Current:2848mA Battery SoC: 94% Battery Status AC is: connected Battery is: connected Battery LFCC: 4021 mAh (Last Full Charge Capacity) Battery Capacity: 3751 mAh 66.512 Wh Charge level: 93% Battery charging PS C:\Users\Daniel\clone\framework-system-private> sudo .\target\debug\framework_tool.exe --charge-rate-limit 0.5 Requested Rate: 0.5C Design Current: 3915mA Limiting Current to: 1957mA PS C:\Users\Daniel\clone\framework-system-private> sudo .\target\debug\framework_tool.exe --power Charger Status AC is: connected Charger Voltage: 17800mV Charger Current: 1956mA 0.49961686C Chg Input Current:2848mA Battery SoC: 94% Battery Status AC is: connected Battery is: connected Battery LFCC: 4021 mAh (Last Full Charge Capacity) Battery Capacity: 3754 mAh 66.584 Wh Charge level: 93% Battery charging ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 30 ++++++++++++++++++++++- framework_lib/src/commandline/clap_std.rs | 11 +++++++++ framework_lib/src/commandline/mod.rs | 3 +++ framework_lib/src/commandline/uefi.rs | 30 +++++++++++++++++++++++ framework_lib/src/power.rs | 2 +- 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index f14b8da2..a37b5a63 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -10,6 +10,7 @@ use crate::ec_binary; use crate::os_specific; +use crate::power; use crate::smbios; #[cfg(feature = "uefi")] use crate::uefi::shell_get_execution_break_flag; @@ -407,6 +408,29 @@ impl CrosEc { } } + pub fn set_charge_rate_limit(&self, rate: f32, battery_soc: Option) -> EcResult<()> { + let power_info = power::power_info(self).ok_or(EcError::DeviceError( + "Failed to get battery info".to_string(), + ))?; + let battery = power_info + .battery + .ok_or(EcError::DeviceError("No battery present".to_string()))?; + println!("Requested Rate: {}C", rate); + println!("Design Current: {}mA", battery.design_capacity); + let current = (rate * (battery.design_capacity as f32)) as u32; + println!("Limiting Current to: {}mA", current); + if let Some(battery_soc) = battery_soc { + let battery_soc = battery_soc as u8; + EcRequestCurrentLimitV1 { + current, + battery_soc, + } + .send_command(self) + } else { + EcRequestCurrentLimitV0 { current }.send_command(self) + } + } + pub fn set_fp_led_percentage(&self, percentage: u8) -> EcResult<()> { // Sending bytes manually because the Set command, as opposed to the Get command, // does not return any data @@ -1096,7 +1120,7 @@ impl CrosEc { } } - pub fn get_charge_state(&self) -> EcResult<()> { + pub fn get_charge_state(&self, power_info: &power::PowerInfo) -> EcResult<()> { let res = EcRequestChargeStateGetV0 { cmd: ChargeStateCmd::GetState as u8, param: 0, @@ -1113,6 +1137,10 @@ impl CrosEc { ); println!(" Charger Voltage: {}mV", { res.chg_voltage }); println!(" Charger Current: {}mA", { res.chg_current }); + if let Some(battery) = &power_info.battery { + let charge_rate = (res.chg_current as f32) / (battery.design_capacity as f32); + println!(" {:.2}C", charge_rate); + } println!(" Chg Input Current:{}mA", { res.chg_input_current }); println!(" Battery SoC: {}%", { res.batt_state_of_charge }); diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 3adf8113..591b9b4b 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -160,6 +160,11 @@ struct ClapCli { #[clap(num_args = ..2)] charge_current_limit: Vec, + /// Get or set max charge current limit + #[arg(long)] + #[clap(num_args = ..2)] + charge_rate_limit: Vec, + /// Get GPIO value by name #[arg(long)] get_gpio: Option, @@ -318,6 +323,11 @@ pub fn parse(args: &[String]) -> Cli { 1 => Some((args.charge_current_limit[0], None)), _ => None, }; + let charge_rate_limit = match args.charge_rate_limit.len() { + 2 => Some((args.charge_rate_limit[0], Some(args.charge_rate_limit[1]))), + 1 => Some((args.charge_rate_limit[0], None)), + _ => None, + }; Cli { verbosity: args.verbosity.log_level_filter(), @@ -372,6 +382,7 @@ pub fn parse(args: &[String]) -> Cli { expansion_bay: args.expansion_bay, charge_limit: args.charge_limit, charge_current_limit, + charge_rate_limit, get_gpio: args.get_gpio, fp_led_level: args.fp_led_level, fp_brightness: args.fp_brightness, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 5712cf81..f16b5489 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -176,6 +176,7 @@ pub struct Cli { pub expansion_bay: bool, pub charge_limit: Option>, pub charge_current_limit: Option<(u32, Option)>, + pub charge_rate_limit: Option<(f32, Option)>, pub get_gpio: Option, pub fp_led_level: Option>, pub fp_brightness: Option>, @@ -765,6 +766,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { print_err(handle_charge_limit(&ec, maybe_limit)); } else if let Some((limit, soc)) = args.charge_current_limit { print_err(ec.set_charge_current_limit(limit, soc)); + } else if let Some((limit, soc)) = args.charge_rate_limit { + print_err(ec.set_charge_rate_limit(limit, soc)); } else if let Some(gpio_name) = &args.get_gpio { print!("Getting GPIO value {}: ", gpio_name); if let Ok(value) = ec.get_gpio(gpio_name) { diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 70bba487..4b32d765 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -89,6 +89,7 @@ pub fn parse(args: &[String]) -> Cli { expansion_bay: false, charge_limit: None, charge_current_limit: None, + charge_rate_limit: None, get_gpio: None, fp_led_level: None, fp_brightness: None, @@ -296,6 +297,35 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--charge-rate-limit" { + cli.charge_rate_limit = if args.len() > i + 2 { + let limit = args[i + 1].parse::(); + let soc = args[i + 2].parse::(); + if let (Ok(limit), Ok(soc)) = (limit, soc) { + Some((limit, Some(soc))) + } else { + println!( + "Invalid values for --charge-rate-limit: '{} {}'. Must be u32 integers.", + args[i + 1], + args[i + 2] + ); + None + } + } else if args.len() > i + 1 { + if let Ok(limit) = args[i + 1].parse::() { + Some((limit, None)) + } else { + println!( + "Invalid values for --charge-rate-limit: '{}'. Must be an integer.", + args[i + 1], + ); + None + } + } else { + println!("--charge-rate-limit requires one or two. [limit] [soc] or [limit]"); + None + }; + found_an_option = true; } else if arg == "--get-gpio" { cli.get_gpio = if args.len() > i + 1 { Some(args[i + 1].clone()) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index 92c0ed6e..bb3e9ea4 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -471,8 +471,8 @@ pub fn is_standalone(ec: &CrosEc) -> bool { } pub fn get_and_print_power_info(ec: &CrosEc) -> i32 { - print_err_ref(&ec.get_charge_state()); if let Some(power_info) = power_info(ec) { + print_err_ref(&ec.get_charge_state(&power_info)); print_battery_information(&power_info); if let Some(_battery) = &power_info.battery { return 0; From cf81dbfcf9c81ef1b82fe7fe1fda258823f7ddb2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 30 Apr 2025 11:48:24 +0800 Subject: [PATCH 34/39] More examples Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 64 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index b150cd5e..2cccc7a4 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -93,19 +93,6 @@ LED Matrix ALS: 76 Lux ``` -## Check power (AC and battery) status - -``` -> sudo ./target/debug/framework_tool --power - AC is: not connected - Battery is: connected - Battery LFCC: 3949 mAh (Last Full Charge Capacity) - Battery Capacity: 2770 mAh - 44.729 Wh - Charge level: 70% - Battery discharging -``` - ## Set custom fan duty/RPM ``` @@ -171,10 +158,48 @@ Battery Status Battery discharging ``` +Get more information + +``` +> sudo framework_tool --power -vv +Charger Status + AC is: not connected + Charger Voltage: 14824mV + Charger Current: 0mA + Chg Input Current:384mA + Battery SoC: 33% +Battery Status + AC is: not connected + Battery is: connected + Battery LFCC: 4021 mAh (Last Full Charge Capacity) + Battery Capacity: 1300 mAh + 19.267 Wh + Charge level: 32% + Manufacturer: NVT + Model Number: FRANGWA + Serial Number: 038F + Battery Type: LION + Present Voltage: 14.821 V + Present Rate: 943 mA + Design Capacity: 3915 mAh + 60.604 Wh + Design Voltage: 15.480 V + Cycle Count: 64 + Battery discharging +``` + ### Setting a custom charger current limit ``` -# Set limit to 2A +# 1C = normal charging rate +# This means charging from 0 to 100% takes 1 hour +# Set charging rate to 0.8C +> sudo framework_tool --charge-rate-limit 0.8 + +# Limit charge current to the battery to to 2A +# In the output of `framework_tool --power -vv` above you can se "Design Capacity" +# Dividing that by 1h gives you the maximum charging current (1C) +# For example Design Capacity: 3915 mAh => 3915mA > sudo framework_tool --charge-current-limit 2000 # And then plug in a power adapter @@ -183,6 +208,7 @@ Charger Status AC is: connected Charger Voltage: 17800mV Charger Current: 2000mA + 0.51C Chg Input Current:3084mA Battery SoC: 87% Battery Status @@ -194,14 +220,16 @@ Battery Status Charge level: 86% Battery charging -# Remove limit (highest USB-PD current is 5A) -> sudo framework_tool --charge-current-limit 5000 +# Remove limit (set rate to 1C) +> sudo framework_tool --charge-rate-limit 1 +# Back to normal > sudo framework_tool --power Charger Status AC is: connected Charger Voltage: 17800mV Charger Current: 2740mA + 0.70C Chg Input Current:3084mA Battery SoC: 92% Battery Status @@ -212,4 +240,8 @@ Battery Status 60.146 Wh Charge level: 91% Battery charging + +# Set charge rate/current limit only if battery is >80% charged +> sudo framework_tool --charge-rate-limit 80 0.8 +> sudo framework_tool --charge-current-limit 80 2000 ``` From 49eb0b914ee2f11124614ba6d3f3259adf199fac Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 1 May 2025 19:08:51 -0700 Subject: [PATCH 35/39] --inputdeck: Positive wording Normal case should use all-positive wording. If everything's good it should say "true" and "present" Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 37 ++++++++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 6 ++--- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 97a2c844..8f61c6bd 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -74,6 +74,43 @@ LED Matrix [...] ``` +## Check input deck status + +### On Framework 12 + +``` +> framework_tool --inputdeck +Input Deck + Chassis Closed: true + Power Button Board: Present + Audio Daughterboard: Present + Touchpad: Present +``` + +### On Framework 13 + +``` +> framework_tool --inputdeck +Input Deck + Chassis Closed: true + Audio Daughterboard: Present + Touchpad: Present +``` + +### On Framework 16 + +``` +> framework_tool --inputdeck +Chassis Closed: true +Input Deck State: On +Touchpad present: true +Positions: + Pos 0: GenericC + Pos 1: KeyboardA + Pos 2: Disconnected + Pos 3: Disconnected + Pos 4: GenericC +``` ## Check temperatures and fan speed diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 67a47f67..a01bb7b7 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -483,7 +483,7 @@ impl CrosEc { let is_present = |p| if p { "Present" } else { "Missing" }; println!("Input Deck"); - println!(" Chassis Open: {}", intrusion.currently_open); + println!(" Chassis Closed: {}", intrusion.currently_open); println!(" Power Button Board: {}", is_present(pwrbtn.is_some())); println!(" Audio Daughterboard: {}", is_present(audio.is_some())); println!(" Touchpad: {}", is_present(tp.is_some())); @@ -511,7 +511,7 @@ impl CrosEc { let is_present = |p| if p { "Present" } else { "Missing" }; println!("Input Deck"); - println!(" Chassis Open: {}", intrusion.currently_open); + println!(" Chassis Closed: {}", intrusion.currently_open); println!(" Audio Daughterboard: {}", is_present(audio.is_some())); println!(" Touchpad: {}", is_present(tp.is_some())); @@ -521,7 +521,7 @@ impl CrosEc { pub fn print_fw16_inputdeck_status(&self) -> EcResult<()> { let intrusion = self.get_intrusion_status()?; let status = self.get_input_deck_status()?; - println!("Chassis Open: {}", intrusion.currently_open); + println!("Chassis Closed: {}", intrusion.currently_open); println!("Input Deck State: {:?}", status.state); println!("Touchpad present: {}", status.touchpad_present); println!("Positions:"); From d3a9f76acaa67f0bd1725b90002fb96e3427dbf4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 1 May 2025 19:22:02 -0700 Subject: [PATCH 36/39] --features: Make the output prettier ``` framework_tool.exe --features ID | Name | Enabled? -- | --------------------------- | -------- 0 | Limited | false 1 | Flash | true 2 | PwmFan | true 3 | PwmKeyboardBacklight | false 4 | Lightbar | false 5 | Led | true [...] ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a01bb7b7..06041791 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1086,6 +1086,8 @@ impl CrosEc { /// Check features supported by the firmware pub fn get_features(&self) -> EcResult<()> { let data = EcRequestGetFeatures {}.send_command(self)?; + println!(" ID | Name | Enabled?"); + println!(" -- | --------------------------- | --------"); for i in 0..64 { let byte = i / 32; let bit = i % 32; @@ -1093,7 +1095,8 @@ impl CrosEc { let feat: Option = FromPrimitive::from_usize(i); if let Some(feat) = feat { - println!("{:>2}: {:>5} {:?}", i, val, feat); + let name = format!("{:?}", feat); + println!(" {:>2} | {:<27} | {:>5}", i, name, val); } } From c86b6036cc1c3ddf777f789ebc4d41d447edb46d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 18 Apr 2025 17:21:22 +0800 Subject: [PATCH 37/39] --versions: Get stylus information through USI Only works on our touchscreen because report IDs are hardcoded instead of derived from report descriptor. ``` > sudo framework_tool --versions [...] Stylus Serial Number: 28C1A00-12E71DAE Vendor ID: 32AC (Framework Computer) Product ID: 002B (Framework Stylus) Firmware Version: FF.FF [...] ``` Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 13 +++++ framework_lib/src/touchscreen.rs | 79 +++++++++++++++++++++++++--- framework_lib/src/touchscreen_win.rs | 37 +++++++++++-- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 2cccc7a4..75cf17c9 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -33,6 +33,19 @@ Touchscreen MPP Protocol: true ``` +### Stylus (Framework 12) + +``` +> sudo framework_tool --versions +[...] +Stylus + Serial Number: 28C1A00-12E71DAE + Vendor ID: 32AC (Framework Computer) + Product ID: 002B (Framework Stylus) + Firmware Version: FF.FF +[...] +``` + ### Touchpad (Framework 12, Framework 13, Framework 16) ``` diff --git a/framework_lib/src/touchscreen.rs b/framework_lib/src/touchscreen.rs index c155c173..a3246226 100644 --- a/framework_lib/src/touchscreen.rs +++ b/framework_lib/src/touchscreen.rs @@ -5,16 +5,24 @@ use crate::touchscreen_win; pub const ILI_VID: u16 = 0x222A; pub const ILI_PID: u16 = 0x5539; +const VENDOR_USAGE_PAGE: u16 = 0xFF00; pub const USI_BITMAP: u8 = 1 << 1; pub const MPP_BITMAP: u8 = 1 << 2; +const REPORT_ID_FIRMWARE: u8 = 0x27; +const REPORT_ID_USI_VER: u8 = 0x28; + struct HidapiTouchScreen { device: HidDevice, } impl TouchScreen for HidapiTouchScreen { - fn open_device() -> Option { - debug!("Looking for touchscreen HID device"); + fn open_device(target_up: u16, skip: u8) -> Option { + debug!( + "Looking for touchscreen HID device {:X} {}", + target_up, skip + ); + let mut skip = skip; match HidApi::new() { Ok(api) => { for dev_info in api.device_list() { @@ -29,7 +37,7 @@ impl TouchScreen for HidapiTouchScreen { " Found {:04X}:{:04X} (Usage Page {:04X})", vid, pid, usage_page ); - if usage_page != 0xFF00 { + if usage_page != target_up { debug!(" Skipping usage page. Expected {:04X}", 0xFF00); continue; } @@ -40,6 +48,10 @@ impl TouchScreen for HidapiTouchScreen { debug!(" Found matching touchscreen HID device"); debug!(" Path: {:?}", dev_info.path()); debug!(" IC Type: {:04X}", pid); + if skip > 0 { + skip -= 1; + continue; + } // Unwrapping because if we can enumerate it, we should be able to open it let device = dev_info.open_device(&api).unwrap(); @@ -97,16 +109,57 @@ impl TouchScreen for HidapiTouchScreen { debug!(" Read buf: {:X?}", buf); Some(buf[msg_len..msg_len + read_len].to_vec()) } + + fn get_stylus_fw(&self) -> Option<()> { + let mut msg = [0u8; 0x40]; + msg[0] = REPORT_ID_USI_VER; + self.device.get_feature_report(&mut msg).ok()?; + let usi_major = msg[2]; + let usi_minor = msg[3]; + debug!("USI version (Major.Minor): {}.{}", usi_major, usi_minor); + + if usi_major != 2 || usi_minor != 0 { + // Probably not USI mode + return None; + } + + let mut msg = [0u8; 0x40]; + msg[0] = REPORT_ID_FIRMWARE; + self.device.get_feature_report(&mut msg).ok()?; + let sn_low = u32::from_le_bytes([msg[2], msg[3], msg[4], msg[5]]); + let sn_high = u32::from_le_bytes([msg[6], msg[7], msg[8], msg[9]]); + let vid = u16::from_le_bytes([msg[14], msg[15]]); + let vendor = if vid == 0x32AC { + " (Framework Computer)" + } else { + "" + }; + let pid = u16::from_le_bytes([msg[16], msg[17]]); + let product = if pid == 0x002B { + " (Framework Stylus)" + } else { + "" + }; + println!("Stylus"); + println!(" Serial Number: {:X}-{:X}", sn_high, sn_low); + debug!(" Redundant SN {:X?}", &msg[10..14]); + println!(" Vendor ID: {:04X}{}", vid, vendor); + println!(" Product ID: {:04X}{}", pid, product); + println!(" Firmware Version: {:02X}.{:02X}", &msg[18], msg[19]); + + Some(()) + } } pub trait TouchScreen { - fn open_device() -> Option + fn open_device(usage_page: u16, skip: u8) -> Option where Self: std::marker::Sized; fn send_message(&self, message_id: u8, read_len: usize, data: Vec) -> Option>; fn check_fw_version(&self) -> Option<()> { println!("Touchscreen"); + let res = self.send_message(0x42, 3, vec![0])?; let ver = res .iter() @@ -135,22 +188,32 @@ pub trait TouchScreen { self.send_message(0x38, 0, vec![!enable as u8, 0x00])?; Some(()) } + + fn get_stylus_fw(&self) -> Option<()>; } pub fn print_fw_ver() -> Option<()> { + for skip in 0..5 { + if let Some(device) = HidapiTouchScreen::open_device(0x000D, skip) { + if device.get_stylus_fw().is_some() { + break; + } + } + } + #[cfg(target_os = "windows")] - let device = touchscreen_win::NativeWinTouchScreen::open_device()?; + let device = touchscreen_win::NativeWinTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?; #[cfg(not(target_os = "windows"))] - let device = HidapiTouchScreen::open_device()?; + let device = HidapiTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?; device.check_fw_version() } pub fn enable_touch(enable: bool) -> Option<()> { #[cfg(target_os = "windows")] - let device = touchscreen_win::NativeWinTouchScreen::open_device()?; + let device = touchscreen_win::NativeWinTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?; #[cfg(not(target_os = "windows"))] - let device = HidapiTouchScreen::open_device()?; + let device = HidapiTouchScreen::open_device(VENDOR_USAGE_PAGE, 0)?; device.enable_touch(enable) } diff --git a/framework_lib/src/touchscreen_win.rs b/framework_lib/src/touchscreen_win.rs index 6e73bacd..bcc6be8d 100644 --- a/framework_lib/src/touchscreen_win.rs +++ b/framework_lib/src/touchscreen_win.rs @@ -16,13 +16,20 @@ use windows::{ }, }; +const REPORT_ID_FIRMWARE: u8 = 0x27; +const REPORT_ID_USI_VER: u8 = 0x28; + pub struct NativeWinTouchScreen { handle: HANDLE, } impl TouchScreen for NativeWinTouchScreen { - fn open_device() -> Option { - debug!("Looking for touchscreen HID device"); + fn open_device(target_up: u16, skip: u8) -> Option { + debug!( + "Looking for touchscreen HID device {:X} {}", + target_up, skip + ); + let mut skip = skip; match HidApi::new() { Ok(api) => { for dev_info in api.device_list() { @@ -37,7 +44,7 @@ impl TouchScreen for NativeWinTouchScreen { " Found {:04X}:{:04X} (Usage Page {:04X})", vid, pid, usage_page ); - if usage_page != 0xFF00 { + if usage_page != target_up { debug!(" Skipping usage page. Expected {:04X}", 0xFF00); continue; } @@ -48,6 +55,10 @@ impl TouchScreen for NativeWinTouchScreen { debug!(" Found matching touchscreen HID device"); debug!(" Path: {:?}", dev_info.path()); debug!(" IC Type: {:04X}", pid); + if skip > 0 { + skip -= 1; + continue; + } // TODO: Enumerate with windows // Should enumerate and find the right one @@ -135,4 +146,24 @@ impl TouchScreen for NativeWinTouchScreen { Some(buf[msg_len..msg_len + read_len].to_vec()) } + + fn get_stylus_fw(&self) -> Option<()> { + let mut msg = [0u8; 0x40]; + msg[0] = REPORT_ID_FIRMWARE; + unsafe { + let success = HidD_GetFeature(self.handle, msg.as_mut_ptr() as _, msg.len() as u32); + debug!(" Success: {}", success); + } + println!("Stylus firmware: {:X?}", msg); + + let mut msg = [0u8; 0x40]; + msg[0] = REPORT_ID_USI_VER; + unsafe { + let success = HidD_GetFeature(self.handle, msg.as_mut_ptr() as _, msg.len() as u32); + debug!(" Success: {}", success); + } + println!("USI Version: {:X?}", msg); + + None + } } From 7205d8f366ae20e36f544c63c6a5c7734a9b4b6e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 20 Apr 2025 17:28:37 +0800 Subject: [PATCH 38/39] Read stylus battery level Tested on Linux with USI stylus. Hardcoded to our firmware with report ID. Guaranteed won't work on other systems. I assume it will work on Windows with USI. It won't work with MPP styluses. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 8 ++++- framework_lib/src/commandline/clap_std.rs | 6 ++++ framework_lib/src/commandline/mod.rs | 18 ++++++++++ framework_lib/src/commandline/uefi.rs | 1 + framework_lib/src/touchscreen.rs | 41 +++++++++++++++++++++++ framework_lib/src/touchscreen_win.rs | 4 +++ 6 files changed, 77 insertions(+), 1 deletion(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 75cf17c9..e5190c26 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -139,7 +139,6 @@ ALS: 76 Lux Fan Speed: 0 RPM ``` - ## Check expansion bay (Framework 16) ``` @@ -258,3 +257,10 @@ Battery Status > sudo framework_tool --charge-rate-limit 80 0.8 > sudo framework_tool --charge-current-limit 80 2000 ``` + +## Stylus (Framework 12) + +``` +> sudo framework_tool --stylus-battery +Stylus Battery Strength: 77% +``` diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 591b9b4b..11ef2b72 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -198,6 +198,11 @@ struct ClapCli { #[arg(long)] touchscreen_enable: Option, + /// Check stylus battery level (USI 2.0 stylus only) + #[clap(value_enum)] + #[arg(long)] + stylus_battery: bool, + /// Get EC console, choose whether recent or to follow the output #[clap(value_enum)] #[arg(long)] @@ -390,6 +395,7 @@ pub fn parse(args: &[String]) -> Cli { rgbkbd: args.rgbkbd, tablet_mode: args.tablet_mode, touchscreen_enable: args.touchscreen_enable, + stylus_battery: args.stylus_battery, console: args.console, reboot_ec: args.reboot_ec, hash: args.hash.map(|x| x.into_os_string().into_string().unwrap()), diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index f16b5489..fb9cb491 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -184,6 +184,7 @@ pub struct Cli { pub rgbkbd: Vec, pub tablet_mode: Option, pub touchscreen_enable: Option, + pub stylus_battery: bool, pub console: Option, pub reboot_ec: Option, pub hash: Option, @@ -336,6 +337,18 @@ fn active_mode(mode: &FwMode, reference: FwMode) -> &'static str { } } +#[cfg(feature = "hidapi")] +fn print_stylus_battery_level() { + loop { + if let Some(level) = touchscreen::get_battery_level() { + println!("Stylus Battery Strength: {}%", level); + return; + } else { + debug!("Stylus Battery Strength: Unknown"); + } + } +} + fn print_versions(ec: &CrosEc) { println!("UEFI BIOS"); if let Some(smbios) = get_smbios() { @@ -816,6 +829,11 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { if touchscreen::enable_touch(*_enable).is_none() { error!("Failed to enable/disable touch"); } + } else if args.stylus_battery { + #[cfg(feature = "hidapi")] + print_stylus_battery_level(); + #[cfg(not(feature = "hidapi"))] + error!("Not build with hidapi feature"); } else if let Some(console_arg) = &args.console { match console_arg { ConsoleArg::Follow => { diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 4b32d765..bd362142 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -97,6 +97,7 @@ pub fn parse(args: &[String]) -> Cli { rgbkbd: vec![], tablet_mode: None, touchscreen_enable: None, + stylus_battery: false, console: None, reboot_ec: None, hash: None, diff --git a/framework_lib/src/touchscreen.rs b/framework_lib/src/touchscreen.rs index a3246226..b06965a7 100644 --- a/framework_lib/src/touchscreen.rs +++ b/framework_lib/src/touchscreen.rs @@ -110,6 +110,35 @@ impl TouchScreen for HidapiTouchScreen { Some(buf[msg_len..msg_len + read_len].to_vec()) } + fn get_battery_status(&self) -> Option { + let mut msg = [0u8; 0x40]; + msg[0] = 0x0D; + self.device.read(&mut msg).ok()?; + // println!(" Tip Switch {}%", msg[12]); + // println!(" Barrell Switch: {}%", msg[12]); + // println!(" Eraser: {}%", msg[12]); + // println!(" Invert: {}%", msg[12]); + // println!(" In Range: {}%", msg[12]); + // println!(" 2nd Barrel Switch:{}%", msg[12]); + // println!(" X {}%", msg[12]); + // println!(" Y {}%", msg[12]); + // println!(" Tip Pressure: {}%", msg[12]); + // println!(" X Tilt: {}%", msg[12]); + // println!(" Y Tilt: {}%", msg[12]); + debug!(" Battery Strength: {}%", msg[12]); + debug!( + " Barrel Pressure: {}", + u16::from_le_bytes([msg[13], msg[14]]) + ); + debug!(" Transducer Index: {}", msg[15]); + + if msg[12] == 0 { + None + } else { + Some(msg[12]) + } + } + fn get_stylus_fw(&self) -> Option<()> { let mut msg = [0u8; 0x40]; msg[0] = REPORT_ID_USI_VER; @@ -190,6 +219,18 @@ pub trait TouchScreen { } fn get_stylus_fw(&self) -> Option<()>; + fn get_battery_status(&self) -> Option; +} + +pub fn get_battery_level() -> Option { + for skip in 0..5 { + if let Some(device) = HidapiTouchScreen::open_device(0x000D, skip) { + if let Some(level) = device.get_battery_status() { + return Some(level); + } + } + } + None } pub fn print_fw_ver() -> Option<()> { diff --git a/framework_lib/src/touchscreen_win.rs b/framework_lib/src/touchscreen_win.rs index bcc6be8d..f5fd42a0 100644 --- a/framework_lib/src/touchscreen_win.rs +++ b/framework_lib/src/touchscreen_win.rs @@ -166,4 +166,8 @@ impl TouchScreen for NativeWinTouchScreen { None } + fn get_battery_status(&self) -> Option { + error!("Get stylus battery status not supported on Windows"); + None + } } From 09380bebc4843beaf85bef69b863557509d4fe03 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 2 May 2025 11:16:07 +0800 Subject: [PATCH 39/39] Add command to set EC hibernation delay After the system turns off and no AC power is present, the EC waits a few seconds and then turns off completely. Using this command we can find out what that delay is and change it. ``` > framework_tool.exe --ec-hib-delay 30 EC Hibernation Delay: 30s ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 20 ++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 13 +++++++++++++ framework_lib/src/commandline/clap_std.rs | 6 ++++++ framework_lib/src/commandline/mod.rs | 6 ++++++ framework_lib/src/commandline/uefi.rs | 18 ++++++++++++++++++ 6 files changed, 64 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 057681e5..416fd3e0 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -42,6 +42,7 @@ pub enum EcCommands { ConsoleRead = 0x0098, ChargeState = 0x00A0, ChargeCurrentLimit = 0x00A1, + HibernationDelay = 0x00A8, /// List the features supported by the firmware GetFeatures = 0x000D, /// Force reboot, causes host reboot as well diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 5b909663..38cafeb6 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -438,6 +438,26 @@ impl EcRequest<()> for EcRequestCurrentLimitV1 { } } +#[repr(C, packed)] +pub struct EcRequesetHibernationDelay { + /// Seconds in G3 after EC turns off, 0 to read current + pub seconds: u32, +} + +#[repr(C, packed)] +pub struct EcResponseHibernationDelay { + pub time_g3: u32, + pub time_remaining: u32, + /// How long to wait in G3 until turn off + pub hibernation_delay: u32, +} + +impl EcRequest for EcRequesetHibernationDelay { + fn command_id() -> EcCommands { + EcCommands::HibernationDelay + } +} + /// Supported features #[derive(Debug, FromPrimitive)] pub enum EcFeatureCode { diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index bf169cb5..e0a3094a 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1147,6 +1147,19 @@ impl CrosEc { Ok(()) } + pub fn set_ec_hib_delay(&self, seconds: u32) -> EcResult<()> { + EcRequesetHibernationDelay { seconds }.send_command(self)?; + Ok(()) + } + + pub fn get_ec_hib_delay(&self) -> EcResult { + let res = EcRequesetHibernationDelay { seconds: 0 }.send_command(self)?; + debug!("Time in G3: {:?}", { res.time_g3 }); + debug!("Time remaining: {:?}", { res.time_remaining }); + println!("EC Hibernation Delay: {:?}s", { res.hibernation_delay }); + Ok(res.hibernation_delay) + } + /// Check features supported by the firmware pub fn get_features(&self) -> EcResult<()> { let data = EcRequestGetFeatures {}.send_command(self)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 11ef2b72..3c81c755 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -213,6 +213,11 @@ struct ClapCli { #[arg(long)] reboot_ec: Option, + /// Get or set EC hibernate delay (S5 to G3) + #[clap(value_enum)] + #[arg(long)] + ec_hib_delay: Option>, + /// Hash a file of arbitrary data #[arg(long)] hash: Option, @@ -398,6 +403,7 @@ pub fn parse(args: &[String]) -> Cli { stylus_battery: args.stylus_battery, console: args.console, reboot_ec: args.reboot_ec, + ec_hib_delay: args.ec_hib_delay, hash: args.hash.map(|x| x.into_os_string().into_string().unwrap()), driver: args.driver, pd_addrs, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index fb9cb491..87b08e3f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -187,6 +187,7 @@ pub struct Cli { pub stylus_battery: bool, pub console: Option, pub reboot_ec: Option, + pub ec_hib_delay: Option>, pub hash: Option, pub pd_addrs: Option<(u16, u16)>, pub pd_ports: Option<(u8, u8)>, @@ -868,6 +869,11 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Err(err) => println!("Failed: {:?}", err), }, } + } else if let Some(delay) = &args.ec_hib_delay { + if let Some(delay) = delay { + print_err(ec.set_ec_hib_delay(*delay)); + } + print_err(ec.get_ec_hib_delay()); } else if args.test { println!("Self-Test"); let result = selftest(&ec); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index bd362142..626f0bd0 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -100,6 +100,7 @@ pub fn parse(args: &[String]) -> Cli { stylus_battery: false, console: None, reboot_ec: None, + ec_hib_delay: None, hash: None, // This is the only driver that works on UEFI driver: Some(CrosEcDriverType::Portio), @@ -462,6 +463,23 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--reboot-ec" { + cli.ec_hib_delay = if args.len() > i + 1 { + if let Ok(delay) = args[i + 1].parse::() { + if delay == 0 { + println!("Invalid value for --ec-hib-delay: {}. Must be >0", delay); + None + } else { + Some(Some(delay)) + } + } else { + println!("Invalid value for --fp-brightness. Must be amount in seconds >0"); + None + } + } else { + Some(None) + }; + found_an_option = true; } else if arg == "-t" || arg == "--test" { cli.test = true; found_an_option = true; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy