From e2b88395f03aa9cf3943884fc731b614efb673a9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 17 Apr 2025 03:29:34 +0800 Subject: [PATCH 001/174] 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 002/174] 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 003/174] 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 004/174] 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 005/174] 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 006/174] 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 007/174] 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 008/174] 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 009/174] 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 010/174] 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 011/174] 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 012/174] 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 013/174] 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 014/174] 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 015/174] 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 016/174] 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 017/174] 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 018/174] 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 019/174] 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 020/174] 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 021/174] 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 022/174] 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 023/174] 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 024/174] --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 025/174] 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 026/174] 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 027/174] 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 028/174] 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 029/174] 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 8ad5c4fe8d714c9fc2672a321fb0f6dcc6cfa413 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 27 Apr 2025 16:14:02 +0800 Subject: [PATCH 030/174] chromium_ec: Extract i2c_passthrough to separate module TODO: Split out port and address code also Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 121 ++---------------- .../src/chromium_ec/i2c_passthrough.rs | 118 +++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 1 + 3 files changed, 129 insertions(+), 111 deletions(-) create mode 100644 framework_lib/src/chromium_ec/i2c_passthrough.rs diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index aa1af5d0..ef7ac889 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -3,23 +3,17 @@ //! The current implementation talks to them by tunneling I2C through EC host commands. use alloc::format; -use alloc::string::ToString; -use alloc::vec; use alloc::vec::Vec; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; use crate::ccgx::{AppVersion, BaseVersion, ControllerVersion}; -use crate::chromium_ec::command::EcCommands; -use crate::chromium_ec::{CrosEc, CrosEcDriver, EcError, EcResult}; -use crate::util::{self, assert_win_len, Config, Platform}; -use std::mem::size_of; +use crate::chromium_ec::i2c_passthrough::*; +use crate::chromium_ec::{CrosEc, EcError, EcResult}; +use crate::util::{assert_win_len, Config, Platform}; use super::*; -/// Maximum transfer size for one I2C transaction supported by the chip -const MAX_I2C_CHUNK: usize = 128; - enum ControlRegisters { DeviceMode = 0, SiliconId = 2, // Two bytes long, First LSB, then MSB @@ -106,58 +100,6 @@ pub struct PdController { ec: CrosEc, } -fn passthrough_offset(dev_index: u16) -> u16 { - dev_index * 0x4000 -} - -#[repr(C, packed)] -struct EcParamsI2cPassthruMsg { - /// Slave address and flags - addr_and_flags: u16, - transfer_len: u16, -} - -#[repr(C, packed)] -struct EcParamsI2cPassthru { - port: u8, - /// How many messages - messages: u8, - msg: [EcParamsI2cPassthruMsg; 0], -} - -#[repr(C, packed)] -struct _EcI2cPassthruResponse { - i2c_status: u8, - /// How many messages - messages: u8, - data: [u8; 0], -} - -struct EcI2cPassthruResponse { - i2c_status: u8, // TODO: Can probably use enum - data: Vec, -} - -impl EcI2cPassthruResponse { - fn is_successful(&self) -> EcResult<()> { - if self.i2c_status & 1 > 0 { - return Err(EcError::DeviceError( - "I2C Transfer not acknowledged".to_string(), - )); - } - if self.i2c_status & (1 << 1) > 0 { - return Err(EcError::DeviceError("I2C Transfer timeout".to_string())); - } - // I'm not aware of any other errors, but there might be. - // But I don't think multiple errors can be indicated at the same time - assert_eq!(self.i2c_status, 0); - Ok(()) - } -} - -/// Indicate that it's a read, not a write -const I2C_READ_FLAG: u16 = 1 << 15; - #[derive(Debug, PartialEq)] pub enum FwMode { BootLoader = 0, @@ -194,13 +136,6 @@ impl PdController { pub fn new(port: PdPort, ec: CrosEc) -> Self { PdController { port, ec } } - /// Wrapped with support for dev id - /// TODO: Should move into chromium_ec module - /// TODO: Must not call CrosEc::new() otherwise the driver isn't configurable! - fn send_ec_command(&self, code: u16, dev_index: u16, data: &[u8]) -> EcResult> { - let command_id = code + passthrough_offset(dev_index); - self.ec.send_command(command_id, 0, data) - } fn i2c_read(&self, addr: u16, len: u16) -> EcResult { trace!( @@ -208,49 +143,13 @@ impl PdController { self.port.i2c_port().unwrap(), self.port.i2c_address() ); - trace!("i2c_read(addr: {}, len: {})", addr, len); - if usize::from(len) > MAX_I2C_CHUNK { - return EcResult::Err(EcError::DeviceError(format!( - "i2c_read too long. Must be <128, is: {}", - len - ))); - } - let addr_bytes = u16::to_le_bytes(addr); - let messages = vec![ - EcParamsI2cPassthruMsg { - addr_and_flags: self.port.i2c_address(), - transfer_len: addr_bytes.len() as u16, - }, - EcParamsI2cPassthruMsg { - addr_and_flags: self.port.i2c_address() + I2C_READ_FLAG, - transfer_len: len, // How much to read - }, - ]; - let msgs_len = size_of::() * messages.len(); - let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) }; - - let params = EcParamsI2cPassthru { - port: self.port.i2c_port()?, - messages: messages.len() as u8, - msg: [], // Messages are copied right after this struct - }; - let params_len = size_of::(); - let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(¶ms) }; - - let mut buffer: Vec = vec![0; params_len + msgs_len + addr_bytes.len()]; - buffer[0..params_len].copy_from_slice(params_buffer); - buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer); - buffer[params_len + msgs_len..].copy_from_slice(&addr_bytes); - - let data = self.send_ec_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?; - let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) }; - let res_data = &data[size_of::<_EcI2cPassthruResponse>()..]; - // TODO: Seems to be either one, non-deterministically - debug_assert!(res.messages as usize == messages.len() || res.messages == 0); - Ok(EcI2cPassthruResponse { - i2c_status: res.i2c_status, - data: res_data.to_vec(), - }) + i2c_read( + &self.ec, + self.port.i2c_port().unwrap(), + self.port.i2c_address(), + addr, + len, + ) } fn ccgx_read(&self, reg: ControlRegisters, len: u16) -> EcResult> { diff --git a/framework_lib/src/chromium_ec/i2c_passthrough.rs b/framework_lib/src/chromium_ec/i2c_passthrough.rs new file mode 100644 index 00000000..9a1fac97 --- /dev/null +++ b/framework_lib/src/chromium_ec/i2c_passthrough.rs @@ -0,0 +1,118 @@ +use crate::chromium_ec::command::EcCommands; +use crate::chromium_ec::{CrosEc, CrosEcDriver, EcError, EcResult}; +use crate::util; +use alloc::format; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use std::mem::size_of; + +/// Maximum transfer size for one I2C transaction supported by the chip +pub const MAX_I2C_CHUNK: usize = 128; + +#[repr(C, packed)] +pub struct EcParamsI2cPassthruMsg { + /// Slave address and flags + addr_and_flags: u16, + transfer_len: u16, +} + +#[repr(C, packed)] +pub struct EcParamsI2cPassthru { + port: u8, + /// How many messages + messages: u8, + msg: [EcParamsI2cPassthruMsg; 0], +} + +#[repr(C, packed)] +struct _EcI2cPassthruResponse { + i2c_status: u8, + /// How many messages + messages: u8, + data: [u8; 0], +} + +#[derive(Debug)] +pub struct EcI2cPassthruResponse { + pub i2c_status: u8, // TODO: Can probably use enum + pub data: Vec, +} + +impl EcI2cPassthruResponse { + pub fn is_successful(&self) -> EcResult<()> { + if self.i2c_status & 1 > 0 { + return Err(EcError::DeviceError( + "I2C Transfer not acknowledged".to_string(), + )); + } + if self.i2c_status & (1 << 1) > 0 { + return Err(EcError::DeviceError("I2C Transfer timeout".to_string())); + } + // I'm not aware of any other errors, but there might be. + // But I don't think multiple errors can be indicated at the same time + assert_eq!(self.i2c_status, 0); + Ok(()) + } +} + +/// Indicate that it's a read, not a write +const I2C_READ_FLAG: u16 = 1 << 15; + +pub fn i2c_read( + ec: &CrosEc, + i2c_port: u8, + i2c_addr: u16, + addr: u16, + len: u16, +) -> EcResult { + trace!( + "i2c_read(i2c_port: 0x{:X}, i2c_addr: 0x{:X}, addr: 0x{:X}, len: 0x{:X})", + i2c_port, + i2c_addr, + addr, + len + ); + if usize::from(len) > MAX_I2C_CHUNK { + return EcResult::Err(EcError::DeviceError(format!( + "i2c_read too long. Must be <128, is: {}", + len + ))); + } + let addr_bytes = u16::to_le_bytes(addr); + let messages = vec![ + EcParamsI2cPassthruMsg { + addr_and_flags: i2c_addr, + transfer_len: addr_bytes.len() as u16, + }, + EcParamsI2cPassthruMsg { + addr_and_flags: i2c_addr + I2C_READ_FLAG, + transfer_len: len, // How much to read + }, + ]; + let msgs_len = size_of::() * messages.len(); + let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) }; + + let params = EcParamsI2cPassthru { + port: i2c_port, + messages: messages.len() as u8, + msg: [], // Messages are copied right after this struct + }; + let params_len = size_of::(); + let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(¶ms) }; + + let mut buffer: Vec = vec![0; params_len + msgs_len + addr_bytes.len()]; + buffer[0..params_len].copy_from_slice(params_buffer); + buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer); + buffer[params_len + msgs_len..].copy_from_slice(&addr_bytes); + + let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?; + let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) }; + let res_data = &data[size_of::<_EcI2cPassthruResponse>()..]; + // TODO: Seems to be either one, non-deterministically + debug_assert!(res.messages as usize == messages.len() || res.messages == 0); + Ok(EcI2cPassthruResponse { + i2c_status: res.i2c_status, + data: res_data.to_vec(), + }) +} diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e1a53c20..7ee21b72 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -22,6 +22,7 @@ pub mod command; pub mod commands; #[cfg(feature = "cros_ec_driver")] mod cros_ec; +pub mod i2c_passthrough; pub mod input_deck; mod portio; mod portio_mec; From 426090e9faf9d9a37975fb701d217c427936a041 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 28 Apr 2025 18:29:23 +0800 Subject: [PATCH 031/174] i2c_passthrough: Add i2c_write function Signed-off-by: Daniel Schaefer --- .../src/chromium_ec/i2c_passthrough.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/framework_lib/src/chromium_ec/i2c_passthrough.rs b/framework_lib/src/chromium_ec/i2c_passthrough.rs index 9a1fac97..dfebaafc 100644 --- a/framework_lib/src/chromium_ec/i2c_passthrough.rs +++ b/framework_lib/src/chromium_ec/i2c_passthrough.rs @@ -116,3 +116,49 @@ pub fn i2c_read( data: res_data.to_vec(), }) } + +pub fn i2c_write( + ec: &CrosEc, + i2c_port: u8, + i2c_addr: u16, + addr: u16, + data: &[u8], +) -> EcResult { + trace!( + " i2c_write(addr: {}, len: {}, data: {:?})", + addr, + data.len(), + data + ); + let addr_bytes = [addr as u8, (addr >> 8) as u8]; + let messages = vec![EcParamsI2cPassthruMsg { + addr_and_flags: i2c_addr, + transfer_len: (addr_bytes.len() + data.len()) as u16, + }]; + let msgs_len = size_of::() * messages.len(); + let msgs_buffer: &[u8] = unsafe { util::any_vec_as_u8_slice(&messages) }; + + let params = EcParamsI2cPassthru { + port: i2c_port, + messages: messages.len() as u8, + msg: [], // Messages are copied right after this struct + }; + let params_len = size_of::(); + let params_buffer: &[u8] = unsafe { util::any_as_u8_slice(¶ms) }; + + let mut buffer: Vec = vec![0; params_len + msgs_len + addr_bytes.len() + data.len()]; + buffer[0..params_len].copy_from_slice(params_buffer); + buffer[params_len..params_len + msgs_len].copy_from_slice(msgs_buffer); + buffer[params_len + msgs_len..params_len + msgs_len + addr_bytes.len()] + .copy_from_slice(&addr_bytes); + buffer[params_len + msgs_len + addr_bytes.len()..].copy_from_slice(data); + + let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?; + let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) }; + assert_eq!(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header + debug_assert_eq!(res.messages as usize, messages.len()); + Ok(EcI2cPassthruResponse { + i2c_status: res.i2c_status, + data: vec![], // Writing doesn't return any data + }) +} From 660e2bc6cb05b60c58daa3cbda8da0503f1b8f4b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 29 Apr 2025 20:53:45 +0800 Subject: [PATCH 032/174] --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 033/174] 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 034/174] --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 035/174] 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 036/174] 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 037/174] --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 038/174] --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 039/174] --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 040/174] 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 041/174] 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; From 13e9c67bcf2f73b2b37df0b3c77da2e42568e9a3 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 2 May 2025 11:40:13 +0800 Subject: [PATCH 042/174] EXAMPLES.md: Fix layout --- EXAMPLES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 4f1ae29c..50c611b7 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -186,6 +186,7 @@ Expansion Bay Door closed: true Board: DualInterposer Serial Number: FRAXXXXXXXXXXXXXXX +``` ## Check charger and battery status (Framework 12/13/16) From 208425401f07fb2b8b41ef785d9d2b44d7962d40 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 2 May 2025 17:09:20 +0800 Subject: [PATCH 043/174] Fix charge limit based on battery charge Following are fixed now ``` > sudo framework_tool --charge-rate-limit 80 0.8 > sudo framework_tool --charge-current-limit 80 2000 ``` Reported by: github.com/tjkirch Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 11ef2b72..e9e6a9ba 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -157,12 +157,12 @@ struct ClapCli { /// Get or set max charge current limit #[arg(long)] - #[clap(num_args = ..2)] + #[clap(num_args = ..=2)] charge_current_limit: Vec, /// Get or set max charge current limit #[arg(long)] - #[clap(num_args = ..2)] + #[clap(num_args = ..=2)] charge_rate_limit: Vec, /// Get GPIO value by name From 8f347ae64b42cc165bac710e32b5440bc78f7866 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 2 May 2025 17:47:01 +0800 Subject: [PATCH 044/174] charge-limit: Fix documentation Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 4f1ae29c..52cb3e69 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -291,8 +291,8 @@ Battery Status 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 +> sudo framework_tool --charge-rate-limit 0.8 80 +> sudo framework_tool --charge-current-limit 2000 80 ``` ## Stylus (Framework 12) From 98e8788ba6f9a00e71e585e4625b4549e0d08fc8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 30 Oct 2023 15:31:54 +0800 Subject: [PATCH 045/174] chromium_ec: Use portio if /dev/cros_ec does not exist Then users don't manually have to specify `--driver portio` if the cros_ec kernel driver is not loaded. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/cros_ec.rs | 2 +- framework_lib/src/chromium_ec/mod.rs | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/framework_lib/src/chromium_ec/cros_ec.rs b/framework_lib/src/chromium_ec/cros_ec.rs index a3b4928a..372c45ff 100644 --- a/framework_lib/src/chromium_ec/cros_ec.rs +++ b/framework_lib/src/chromium_ec/cros_ec.rs @@ -55,7 +55,7 @@ struct CrosEcCommandV2 { data: [u8; IN_SIZE], } -const DEV_PATH: &str = "/dev/cros_ec"; +pub const DEV_PATH: &str = "/dev/cros_ec"; lazy_static! { static ref CROS_EC_FD: Arc>> = Arc::new(Mutex::new(None)); diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e0a3094a..4ca04926 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -239,14 +239,20 @@ impl Default for CrosEc { /// /// Depending on the availability we choose the first one as default fn available_drivers() -> Vec { - vec![ - #[cfg(feature = "win_driver")] - CrosEcDriverType::Windows, - #[cfg(feature = "cros_ec_driver")] - CrosEcDriverType::CrosEc, - #[cfg(not(feature = "windows"))] - CrosEcDriverType::Portio, - ] + let mut drivers = vec![]; + + #[cfg(feature = "win_driver")] + drivers.push(CrosEcDriverType::Windows); + + #[cfg(feature = "cros_ec_driver")] + if std::path::Path::new(cros_ec::DEV_PATH).exists() { + drivers.push(CrosEcDriverType::CrosEc); + } + + #[cfg(not(feature = "windows"))] + drivers.push(CrosEcDriverType::Portio); + + drivers } impl CrosEc { From aad94339fc3ea449e4a3d9e9036d15e4820e8572 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 2 May 2025 19:21:32 +0800 Subject: [PATCH 046/174] chromioum_ec: Autodetect Microchip EC when using portio Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 1 + framework_lib/src/chromium_ec/portio.rs | 72 +++++++++++---------- framework_lib/src/chromium_ec/portio_mec.rs | 10 +-- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 4ca04926..30621225 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -268,6 +268,7 @@ impl CrosEc { return None; } debug!("Chromium EC Driver: {:?}", driver); + Some(CrosEc { driver }) } diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index b453c41c..1f480187 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -12,10 +12,9 @@ use log::Level; #[cfg(feature = "linux_pio")] use nix::unistd::Uid; use num::FromPrimitive; -#[cfg(feature = "linux_pio")] -use std::sync::{Arc, Mutex}; +use spin::Mutex; -use crate::chromium_ec::{has_mec, portio_mec}; +use crate::chromium_ec::{portio_mec, EC_MEMMAP_ID}; use crate::os_specific; use crate::util; @@ -172,65 +171,68 @@ fn transfer_read(port: u16, address: u16, size: u16) -> Vec { buffer } -#[cfg(feature = "linux_pio")] +#[derive(PartialEq)] +#[allow(dead_code)] enum Initialized { NotYet, + SucceededMec, Succeeded, Failed, } -#[cfg(feature = "linux_pio")] lazy_static! { - static ref INITIALIZED: Arc> = Arc::new(Mutex::new(Initialized::NotYet)); + static ref INITIALIZED: Mutex = Mutex::new(Initialized::NotYet); } -#[cfg(not(feature = "linux_pio"))] -fn init() -> bool { - // Nothing to do for bare-metal (UEFI) port I/O - true +fn has_mec() -> bool { + let init = INITIALIZED.lock(); + *init != Initialized::Succeeded } -// In Linux userspace has to first request access to ioports -// TODO: Close these again after we're done -#[cfg(feature = "linux_pio")] fn init() -> bool { - let mut init = INITIALIZED.lock().unwrap(); + let mut init = INITIALIZED.lock(); match *init { // Can directly give up, trying again won't help Initialized::Failed => return false, // Already initialized, no need to do anything. - Initialized::Succeeded => return true, + Initialized::Succeeded | Initialized::SucceededMec => return true, Initialized::NotYet => {} } + // First try on MEC + portio_mec::init(); + let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); + if ec_id[0] == b'E' && ec_id[1] == b'C' { + *init = Initialized::SucceededMec; + return true; + } + + // In Linux userspace has to first request access to ioports + // TODO: Close these again after we're done + #[cfg(feature = "linux_pio")] if !Uid::effective().is_root() { error!("Must be root to use port based I/O for EC communication."); *init = Initialized::Failed; return false; } - + #[cfg(feature = "linux_pio")] unsafe { - if has_mec() { - portio_mec::mec_init(); - } else { - // 8 for request/response header, 0xFF for response - let res = ioperm(EC_LPC_ADDR_HOST_ARGS as u64, 8 + 0xFF, 1); - if res != 0 { - error!( - "ioperm failed. portio driver is likely block by Linux kernel lockdown mode" - ); - return false; - } - - let res = ioperm(EC_LPC_ADDR_HOST_CMD as u64, 1, 1); - assert_eq!(res, 0); - let res = ioperm(EC_LPC_ADDR_HOST_DATA as u64, 1, 1); - assert_eq!(res, 0); - - let res = ioperm(NPC_MEMMAP_OFFSET as u64, super::EC_MEMMAP_SIZE as u64, 1); - assert_eq!(res, 0); + // 8 for request/response header, 0xFF for response + let res = ioperm(EC_LPC_ADDR_HOST_ARGS as u64, 8 + 0xFF, 1); + if res != 0 { + error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); + return false; } + + let res = ioperm(EC_LPC_ADDR_HOST_CMD as u64, 1, 1); + assert_eq!(res, 0); + let res = ioperm(EC_LPC_ADDR_HOST_DATA as u64, 1, 1); + assert_eq!(res, 0); + + let res = ioperm(NPC_MEMMAP_OFFSET as u64, super::EC_MEMMAP_SIZE as u64, 1); + assert_eq!(res, 0); } + *init = Initialized::Succeeded; true } diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 974b8686..471a3219 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -22,10 +22,12 @@ const _MEC_LPC_DATA_REGISTER1: u16 = 0x0805; const MEC_LPC_DATA_REGISTER2: u16 = 0x0806; const _MEC_LPC_DATA_REGISTER3: u16 = 0x0807; -#[cfg(feature = "linux_pio")] -pub unsafe fn mec_init() { - ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); - ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); +pub fn init() { + #[cfg(feature = "linux_pio")] + unsafe { + ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); + ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); + } } // TODO: Create a wrapper From 8799d7b4161bcdfa1cd1a00fd6140a6faa707652 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 3 May 2025 20:19:37 +0800 Subject: [PATCH 047/174] commandline: Remove --has-mec Now we can autodetect, we don't need it anymore, yay! Signed-off-by: Daniel Schaefer --- README.md | 2 - completions/bash/framework_tool | 4 -- completions/zsh/_framework_tool | 1 - framework_lib/src/ccgx/device.rs | 8 ++-- framework_lib/src/chromium_ec/mod.rs | 53 +++++++---------------- framework_lib/src/commandline/clap_std.rs | 10 +---- framework_lib/src/commandline/mod.rs | 11 ++--- framework_lib/src/commandline/uefi.rs | 24 ++-------- framework_lib/src/smbios.rs | 4 +- framework_lib/src/util.rs | 4 +- rgbkbd.py | 2 +- 11 files changed, 32 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index 0e89f34b..0784ba54 100644 --- a/README.md +++ b/README.md @@ -202,8 +202,6 @@ Options: Specify I2C addresses of the PD chips (Advanced) --pd-ports Specify I2C ports of the PD chips (Advanced) - --has-mec - Specify the type of EC chip (MEC/MCHP or other) [possible values: true, false] -t, --test Run self-test to check if interaction with EC is possible -h, --help Print help information ``` diff --git a/completions/bash/framework_tool b/completions/bash/framework_tool index f96fcc23..7031b8d5 100755 --- a/completions/bash/framework_tool +++ b/completions/bash/framework_tool @@ -50,7 +50,6 @@ _framework_tool() { "--driver" "--pd-addrs" "--pd-ports" - "--has-mec" "-t" "--test" "-h" "--help" ) @@ -59,7 +58,6 @@ _framework_tool() { local inputdeck_modes=("auto" "off" "on") local console_modes=("recent" "follow") local drivers=("portio" "cros-ec" "windows") - local has_mec_options=("true" "false") local brightness_options=("high" "medium" "low" "ultra-low") local current_word prev_word @@ -77,8 +75,6 @@ _framework_tool() { COMPREPLY=( $(compgen -W "${console_modes[*]}" -- "$current_word") ) elif [[ $prev_word == "--driver" ]]; then COMPREPLY=( $(compgen -W "${drivers[*]}" -- "$current_word") ) - elif [[ $prev_word == "--has-mec" ]]; then - COMPREPLY=( $(compgen -W "${has_mec_options[*]}" -- "$current_word") ) elif [[ $prev_word == "--fp-brightness" ]]; then COMPREPLY=( $(compgen -W "${brightness_options[*]}" -- "$current_word") ) fi diff --git a/completions/zsh/_framework_tool b/completions/zsh/_framework_tool index ad6aa668..2d473c8f 100644 --- a/completions/zsh/_framework_tool +++ b/completions/zsh/_framework_tool @@ -48,7 +48,6 @@ options=( '--driver[Select which driver is used]:driver:(portio cros-ec windows)' '--pd-addrs[Specify I2C addresses of the PD chips (Advanced)]:pd_addrs' '--pd-ports[Specify I2C ports of the PD chips (Advanced)]:pd_ports' - '--has-mec[Specify the type of EC chip (MEC/MCHP or other)]:has_mec:(true false)' '-t[Run self-test to check if interaction with EC is possible]' '-h[Print help]' ) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index aa1af5d0..b928d24d 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -41,8 +41,8 @@ impl PdPort { let platform = &(*config).as_ref().unwrap().platform; match (platform, self) { - (Platform::GenericFramework((left, _), _, _), PdPort::Left01) => *left, - (Platform::GenericFramework((_, right), _, _), PdPort::Right23) => *right, + (Platform::GenericFramework((left, _), _), PdPort::Left01) => *left, + (Platform::GenericFramework((_, right), _), PdPort::Right23) => *right, // Framework AMD Platforms (CCG8) ( Platform::Framework13Amd7080 @@ -70,8 +70,8 @@ impl PdPort { let platform = &(*config).as_ref().unwrap().platform; Ok(match (platform, self) { - (Platform::GenericFramework(_, (left, _), _), PdPort::Left01) => *left, - (Platform::GenericFramework(_, (_, right), _), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (left, _)), PdPort::Left01) => *left, + (Platform::GenericFramework(_, (_, right)), PdPort::Right23) => *right, (Platform::IntelGen11, _) => 6, (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left01) => 6, (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right23) => 7, diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 30621225..82c1e0a8 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -201,24 +201,6 @@ 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 { - return has_mec; - } - - // TODO: Should turn this around - !matches!( - smbios::get_platform().unwrap(), - Platform::Framework13Amd7080 - | Platform::Framework16Amd7080 - | Platform::IntelCoreUltra1 - | Platform::Framework13AmdAi300 - | Platform::Framework12IntelGen13 - | Platform::FrameworkDesktopAmdAiMax300 - ) -} - pub trait CrosEcDriver { fn read_memory(&self, offset: u16, length: u16) -> Option>; fn send_command(&self, command: u16, command_version: u8, data: &[u8]) -> EcResult>; @@ -953,30 +935,25 @@ impl CrosEc { // Everything before is probably a header. // TODO: I don't think there are magic bytes on zephyr firmware // - if has_mec() { - println!(" Check MCHP magic byte at start of firmware code."); - // Make sure we can read at an offset and with arbitrary length - let data = self.read_ec_flash(FLASH_PROGRAM_OFFSET, 16).unwrap(); - debug!("Expecting beginning with 50 48 43 4D ('PHCM' in ASCII)"); - debug!("{:02X?}", data); - println!( - " {:02X?} ASCII:{:?}", - &data[..4], - core::str::from_utf8(&data[..4]) - ); - - if data[0..4] != [0x50, 0x48, 0x43, 0x4D] { - println!(" INVALID: {:02X?}", &data[0..3]); - res = Err(EcError::DeviceError(format!( - "INVALID: {:02X?}", - &data[0..3] - ))); - } + debug!(" Check MCHP magic bytes at start of firmware code."); + // Make sure we can read at an offset and with arbitrary length + let data = self.read_ec_flash(FLASH_PROGRAM_OFFSET, 16).unwrap(); + debug!("Expecting beginning with 50 48 43 4D ('PHCM' in ASCII)"); + debug!("{:02X?}", data); + debug!( + " {:02X?} ASCII:{:?}", + &data[..4], + core::str::from_utf8(&data[..4]) + ); + + let has_mec = data[0..4] == [0x50, 0x48, 0x43, 0x4D]; + if has_mec { + debug!(" Found MCHP magic bytes at start of firmware code."); } // ===== Test 4 ===== println!(" Read flash flags"); - let data = if has_mec() { + let data = if has_mec { self.read_ec_flash(MEC_FLASH_FLAGS, 0x80).unwrap() } else { self.read_ec_flash(NPC_FLASH_FLAGS, 0x80).unwrap() diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 3c81c755..b4b6e5b6 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -228,20 +228,15 @@ struct ClapCli { driver: Option, /// Specify I2C addresses of the PD chips (Advanced) - #[clap(number_of_values = 2, requires("pd_ports"), requires("has_mec"))] + #[clap(number_of_values = 2, requires("pd_ports"))] #[arg(long)] pd_addrs: Vec, /// Specify I2C ports of the PD chips (Advanced) - #[clap(number_of_values = 2, requires("pd_addrs"), requires("has_mec"))] + #[clap(number_of_values = 2, requires("pd_addrs"))] #[arg(long)] pd_ports: Vec, - /// Specify the type of EC chip (MEC/MCHP or other) - #[clap(requires("pd_addrs"), requires("pd_ports"))] - #[arg(long)] - has_mec: Option, - /// Run self-test to check if interaction with EC is possible #[arg(long, short)] test: bool, @@ -408,7 +403,6 @@ pub fn parse(args: &[String]) -> Cli { driver: args.driver, pd_addrs, pd_ports, - has_mec: args.has_mec, test: args.test, // TODO: Set help. Not very important because Clap handles this by itself help: false, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 87b08e3f..88fd1e61 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -191,7 +191,6 @@ pub struct Cli { pub hash: Option, pub pd_addrs: Option<(u16, u16)>, pub pd_ports: Option<(u8, u8)>, - pub has_mec: Option, pub help: bool, pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, @@ -701,12 +700,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } // Must be run before any application code to set the config - if args.pd_addrs.is_some() && args.pd_ports.is_some() && args.has_mec.is_some() { - let platform = Platform::GenericFramework( - args.pd_addrs.unwrap(), - args.pd_ports.unwrap(), - args.has_mec.unwrap(), - ); + if args.pd_addrs.is_some() && args.pd_ports.is_some() { + let platform = Platform::GenericFramework(args.pd_addrs.unwrap(), args.pd_ports.unwrap()); Config::set(platform); } @@ -1169,7 +1164,7 @@ fn selftest(ec: &CrosEc) -> Option<()> { } else { println!(" SMBIOS Platform: Unknown"); println!(); - println!("Specify custom platform parameters with --pd-ports --pd-addrs --has-mec"); + println!("Specify custom platform parameters with --pd-ports --pd-addrs"); return None; }; diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 626f0bd0..1fd9e5d8 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -106,7 +106,6 @@ pub fn parse(args: &[String]) -> Cli { driver: Some(CrosEcDriverType::Portio), pd_addrs: None, pd_ports: None, - has_mec: None, test: false, help: false, flash_gpu_descriptor: None, @@ -610,22 +609,6 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; - } else if arg == "--has-mec" { - cli.has_mec = if args.len() > i + 1 { - if let Ok(b) = args[i + 1].parse::() { - Some(b) - } else { - println!( - "Invalid value for --has-mec: '{}'. Must be 'true' or 'false'.", - args[i + 1] - ); - None - } - } else { - println!("--has-mec requires extra boolean argument."); - None - }; - found_an_option = true; } else if arg == "--raw-command" { cli.raw_command = args[1..].to_vec(); } else if arg == "--compare-version" { @@ -699,11 +682,10 @@ pub fn parse(args: &[String]) -> Cli { } } - let custom_platform = cli.pd_addrs.is_some() && cli.pd_ports.is_some() && cli.has_mec.is_some(); - let no_customization = - cli.pd_addrs.is_none() && cli.pd_ports.is_none() && cli.has_mec.is_none(); + let custom_platform = cli.pd_addrs.is_some() && cli.pd_ports.is_some(); + let no_customization = cli.pd_addrs.is_none() && cli.pd_ports.is_none(); if !(custom_platform || no_customization) { - println!("To customize the platform you need to provide all of --pd-addrs, --pd-ports and --has-mec"); + println!("To customize the platform you need to provide all of --pd-addrs, and --pd-ports"); } if args.len() == 1 && cli.paginate { diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index b25ed153..1345b396 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -46,7 +46,7 @@ pub enum ConfigDigit0 { pub fn is_framework() -> bool { if matches!( get_platform(), - Some(Platform::GenericFramework((_, _), (_, _), _)) + Some(Platform::GenericFramework((_, _), (_, _))) ) { return true; } @@ -252,7 +252,7 @@ pub fn get_platform() -> Option { // Except if it's a GenericFramework platform let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; - if matches!(platform, Platform::GenericFramework((_, _), (_, _), _)) { + if matches!(platform, Platform::GenericFramework((_, _), (_, _))) { return Some(*platform); } } diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index cfb97368..972d82a3 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -36,8 +36,8 @@ pub enum Platform { /// Framework Desktop - AMD Ryzen AI Max 300 FrameworkDesktopAmdAiMax300, /// Generic Framework device - /// pd_addrs, pd_ports, has_mec - GenericFramework((u16, u16), (u8, u8), bool), + /// pd_addrs, pd_ports + GenericFramework((u16, u16), (u8, u8)), } #[derive(Debug, PartialEq, Clone, Copy)] diff --git a/rgbkbd.py b/rgbkbd.py index 9088b85e..c997d897 100755 --- a/rgbkbd.py +++ b/rgbkbd.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Example invocation in fish shell # cargo build && sudo ./target/debug/framework_tool \ -# --driver portio --has-mec false --pd-ports 1 1 --pd-addrs 64 64 \ +# --driver portio --pd-ports 1 1 --pd-addrs 64 64 \ # (./rgbkbd.py | string split ' ') BRIGHTNESS = 1 From db46c4bbd77d3a01079d30edea6655be594b1a9b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 4 May 2025 20:33:10 +0800 Subject: [PATCH 048/174] Simplify features and detect based on OS Now we can build on Windows just with `cargo build`! Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 10 +- README.md | 21 +--- framework_lib/Cargo.toml | 80 +++++-------- framework_lib/src/chromium_ec/command.rs | 4 +- framework_lib/src/chromium_ec/mod.rs | 24 ++-- framework_lib/src/chromium_ec/portio.rs | 121 +------------------- framework_lib/src/chromium_ec/portio_mec.rs | 6 +- framework_lib/src/chromium_ec/protocol.rs | 108 +++++++++++++++++ framework_lib/src/chromium_ec/windows.rs | 2 +- framework_lib/src/commandline/mod.rs | 14 +-- framework_lib/src/csme.rs | 8 +- framework_lib/src/esrt/mod.rs | 12 +- framework_lib/src/lib.rs | 4 +- framework_lib/src/touchscreen.rs | 2 +- framework_lib/src/util.rs | 13 +-- framework_tool/Cargo.toml | 7 -- 16 files changed, 198 insertions(+), 238 deletions(-) create mode 100644 framework_lib/src/chromium_ec/protocol.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a261d25..92f5299d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: run: cargo install cross - name: Build FreeBSD tool - run: cross build --target=x86_64-unknown-freebsd --no-default-features --features cross_freebsd + run: cross build --target=x86_64-unknown-freebsd --no-default-features -p framework_lib - name: Upload FreeBSD App uses: actions/upload-artifact@v4 @@ -93,15 +93,15 @@ jobs: # Build debug library first to fail fast - name: Build library (Windows) - run: cargo build -p framework_lib --no-default-features --features "windows" + run: cargo build -p framework_lib - name: Build Windows tool run: | - cargo build -p framework_tool --no-default-features --features "windows" - cargo build -p framework_tool --no-default-features --features "windows" --release + cargo build -p framework_tool + cargo build -p framework_tool --release - name: Check if Windows tool can start - run: cargo run --no-default-features --features "windows" -- --help --release + run: cargo run -- --help --release # Upload release build so that vcruntime is statically linked - name: Upload Windows App diff --git a/README.md b/README.md index 0784ba54..7c41c4ca 100644 --- a/README.md +++ b/README.md @@ -130,23 +130,10 @@ Building on Windows or in general with fewer features: ```ps1 # Build the library and tool -cargo build --no-default-features --features "windows" +cargo build # Running the tool -cargo run --no-default-features --features "windows" -``` - -Cross compile from Linux to FreeBSD: - -```sh -# One time, install cross tool -cargo install cross - -# Make sure docker is started as well -sudo systemctl start docker - -# Build -cross build --target=x86_64-unknown-freebsd --no-default-features --features unix +cargo run ``` ## Running @@ -375,8 +362,8 @@ Keyboard backlight: 0% sudo pkg install hidapi # Build the library and tool -cargo build --no-default-features --features freebsd +cargo build # Running the tool -cargo run --no-default-features --features freebsd +cargo run ``` diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index b9a7cc91..21d57035 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -8,39 +8,18 @@ rust-version = "1.74" build = "build.rs" [features] -default = ["linux"] -# Linux/FreeBSD -unix = ["std", "raw_pio", "smbios", "dep:nix", "dep:libc"] -linux = ["unix", "linux_pio", "cros_ec_driver", "hidapi", "rusb"] -freebsd = ["unix", "freebsd_pio", "hidapi", "rusb"] -# hidapi and rusb don't seem to build in the cross container at the moment -cross_freebsd = ["unix", "freebsd_pio"] -# Windows does not have the cros_ec driver nor raw port I/O access to userspace -windows = ["std", "smbios", "dep:windows", "win_driver", "raw_pio", "hidapi", "rusb", "dep:wmi"] -smbios = ["dep:smbios-lib"] -std = ["dep:clap", "dep:clap-num", "dep:clap-verbosity-flag", "dep:env_logger", "smbios-lib?/std"] +default = ["std"] +std = ["hidapi", "rusb"] rusb = ["dep:rusb"] hidapi = ["dep:hidapi"] uefi = [ - "dep:plain", "raw_pio", "smbios", "lazy_static/spin_no_std", "dep:uefi", "dep:uefi-services", + "lazy_static/spin_no_std", # Otherwise I get: `LLVM ERROR: Do not know how to split the result of this operator!` # Seems to be a Ruset/LLVM bug when SSE is enabled. # See: https://github.com/rust-lang/rust/issues/61721 "sha2/force-soft" ] -# EC communication via Port I/O on FreeBSD -freebsd_pio = ["redox_hwio/std"] -# EC communication via Port I/O on Linux -linux_pio = ["dep:libc", "redox_hwio/std"] -# EC communication via raw Port I/O (e.g. UEFI or other ring 0 code) -raw_pio = [] -# EC communication via cros_ec driver on Linux -cros_ec_driver = [] - -# Chromium EC driver by DHowett -win_driver = [] - [build-dependencies] built = { version = "0.5", features = ["chrono", "git2"] } @@ -48,39 +27,42 @@ built = { version = "0.5", features = ["chrono", "git2"] } lazy_static = "1.4.0" sha2 = { version = "0.10.8", default-features = false, features = [ "force-soft" ] } regex = { version = "1.11.1", default-features = false } -redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd", default-features = false } -libc = { version = "0.2.155", optional = true } -clap = { version = "4.5", features = ["derive", "cargo"], optional = true } -clap-num = { version = "1.2.0", optional = true } -clap-verbosity-flag = { version = "2.2.1", optional = true } -nix = { version = "0.29.0", features = ["ioctl", "user"], optional = true } num = { version = "0.4", default-features = false } num-derive = { version = "0.4", default-features = false } num-traits = { version = "0.2", default-features = false } -env_logger = { version = "0.11", optional = true } log = { version = "0.4", default-features = true } -uefi = { version = "0.20", features = ["alloc"], optional = true } -uefi-services = { version = "0.17", optional = true } -plain = { version = "0.2.3", optional = true } -spin = { version = "0.9.8", optional = false } -hidapi = { version = "2.6.3", optional = true, features = [ "windows-native" ] } -rusb = { version = "0.9.4", optional = true } +spin = { version = "0.9.8" } no-std-compat = { version = "0.4.1", features = [ "alloc" ] } guid_macros = { path = "../guid_macros" } -wmi = { version = "0.15.0", optional = true } +hidapi = { version = "2.6.3", features = [ "windows-native" ], optional = true } +rusb = { version = "0.9.4", optional = true } + +[target.'cfg(target_os = "uefi")'.dependencies] +uefi = { version = "0.20", features = ["alloc"] } +uefi-services = "0.17" +plain = "0.2.3" +redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd", default-features = false } +smbios-lib = { git = "https://github.com/FrameworkComputer/smbios-lib.git", branch = "no-std", default-features = false } + +[target.'cfg(windows)'.dependencies] +wmi = "0.15.0" +smbios-lib = { git = "https://github.com/FrameworkComputer/smbios-lib.git", branch = "no-std" } +env_logger = "0.11" +clap = { version = "4.5", features = ["derive", "cargo"] } +clap-num = { version = "1.2.0" } +clap-verbosity-flag = { version = "2.2.1" } -[dependencies.smbios-lib] -git = "https://github.com/FrameworkComputer/smbios-lib.git" -branch = "no-std" -optional = true -default-features = false -# Local development -#path = "../../smbios-lib" -# After my changes are upstreamed -#version = "0.9.0" +[target.'cfg(unix)'.dependencies] +libc = "0.2.155" +nix = { version = "0.29.0", features = ["ioctl", "user"] } +redox_hwio = { git = "https://github.com/FrameworkComputer/rust-hwio", branch = "freebsd" } +smbios-lib = { git = "https://github.com/FrameworkComputer/smbios-lib.git", branch = "no-std" } +env_logger = "0.11" +clap = { version = "4.5", features = ["derive", "cargo"] } +clap-num = { version = "1.2.0" } +clap-verbosity-flag = { version = "2.2.1" } -[dependencies.windows] -optional = true +[target.'cfg(windows)'.dependencies.windows] version = "0.59.0" features = [ "Win32_Foundation", diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 416fd3e0..54b8db52 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -173,9 +173,9 @@ pub trait EcRequestRaw { { let response = self.send_command_vec_extra(ec, extra_data)?; // TODO: The Windows driver seems to return 20 more bytes than expected - #[cfg(feature = "win_driver")] + #[cfg(windows)] let expected = response.len() != std::mem::size_of::() + 20; - #[cfg(not(feature = "win_driver"))] + #[cfg(not(windows))] let expected = response.len() != std::mem::size_of::(); if expected { return Err(EcError::DeviceError(format!( diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 5e60b148..b1791240 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -21,13 +21,17 @@ use num_derive::FromPrimitive; pub mod command; pub mod commands; -#[cfg(feature = "cros_ec_driver")] +#[cfg(target_os = "linux")] mod cros_ec; pub mod i2c_passthrough; pub mod input_deck; +#[cfg(not(windows))] mod portio; +#[cfg(not(windows))] mod portio_mec; -#[cfg(feature = "win_driver")] +#[allow(dead_code)] +mod protocol; +#[cfg(windows)] mod windows; use alloc::format; @@ -224,15 +228,15 @@ impl Default for CrosEc { fn available_drivers() -> Vec { let mut drivers = vec![]; - #[cfg(feature = "win_driver")] + #[cfg(windows)] drivers.push(CrosEcDriverType::Windows); - #[cfg(feature = "cros_ec_driver")] + #[cfg(target_os = "linux")] if std::path::Path::new(cros_ec::DEV_PATH).exists() { drivers.push(CrosEcDriverType::CrosEc); } - #[cfg(not(feature = "windows"))] + #[cfg(not(windows))] drivers.push(CrosEcDriverType::Portio); drivers @@ -1320,10 +1324,11 @@ impl CrosEcDriver for CrosEc { // TODO: Change this function to return EcResult instead and print the error only in UI code print_err(match self.driver { + #[cfg(not(windows))] CrosEcDriverType::Portio => portio::read_memory(offset, length), - #[cfg(feature = "win_driver")] + #[cfg(windows)] CrosEcDriverType::Windows => windows::read_memory(offset, length), - #[cfg(feature = "cros_ec_driver")] + #[cfg(target_os = "linux")] CrosEcDriverType::CrosEc => cros_ec::read_memory(offset, length), _ => Err(EcError::DeviceError("No EC driver available".to_string())), }) @@ -1341,10 +1346,11 @@ impl CrosEcDriver for CrosEc { } match self.driver { + #[cfg(not(windows))] CrosEcDriverType::Portio => portio::send_command(command, command_version, data), - #[cfg(feature = "win_driver")] + #[cfg(windows)] CrosEcDriverType::Windows => windows::send_command(command, command_version, data), - #[cfg(feature = "cros_ec_driver")] + #[cfg(target_os = "linux")] CrosEcDriverType::CrosEc => cros_ec::send_command(command, command_version, data), _ => Err(EcError::DeviceError("No EC driver available".to_string())), } diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index 1f480187..152d5f40 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -4,130 +4,21 @@ use alloc::string::ToString; use alloc::vec; use alloc::vec::Vec; use core::convert::TryInto; -#[cfg(any(feature = "linux_pio", feature = "freebsd_pio", feature = "raw_pio"))] +#[cfg(not(windows))] use hwio::{Io, Pio}; -#[cfg(all(feature = "linux_pio", target_os = "linux"))] +#[cfg(target_os = "linux")] use libc::ioperm; use log::Level; -#[cfg(feature = "linux_pio")] +#[cfg(target_os = "linux")] use nix::unistd::Uid; use num::FromPrimitive; use spin::Mutex; +use crate::chromium_ec::protocol::*; use crate::chromium_ec::{portio_mec, EC_MEMMAP_ID}; use crate::os_specific; use crate::util; -/* - * Value written to legacy command port / prefix byte to indicate protocol - * 3+ structs are being used. Usage is bus-dependent. - */ -const EC_COMMAND_PROTOCOL_3: u8 = 0xda; - -// LPC command status byte masks -/// EC has written data but host hasn't consumed it yet -const _EC_LPC_STATUS_TO_HOST: u8 = 0x01; -/// Host has written data/command but EC hasn't consumed it yet -const EC_LPC_STATUS_FROM_HOST: u8 = 0x02; -/// EC is still processing a command -const EC_LPC_STATUS_PROCESSING: u8 = 0x04; -/// Previous command wasn't data but command -const _EC_LPC_STATUS_LAST_CMD: u8 = 0x08; -/// EC is in burst mode -const _EC_LPC_STATUS_BURST_MODE: u8 = 0x10; -/// SCI event is pending (requesting SCI query) -const _EC_LPC_STATUS_SCI_PENDING: u8 = 0x20; -/// SMI event is pending (requesting SMI query) -const _EC_LPC_STATUS_SMI_PENDING: u8 = 0x40; -/// Reserved -const _EC_LPC_STATUS_RESERVED: u8 = 0x80; - -/// EC is busy -const EC_LPC_STATUS_BUSY_MASK: u8 = EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING; - -// I/O addresses for ACPI commands -const _EC_LPC_ADDR_ACPI_DATA: u16 = 0x62; -const _EC_LPC_ADDR_ACPI_CMD: u16 = 0x66; - -// I/O addresses for host command -const EC_LPC_ADDR_HOST_DATA: u16 = 0x200; -const EC_LPC_ADDR_HOST_CMD: u16 = 0x204; - -// I/O addresses for host command args and params -// Protocol version 2 -const EC_LPC_ADDR_HOST_ARGS: u16 = 0x800; /* And 0x801, 0x802, 0x803 */ -const _EC_LPC_ADDR_HOST_PARAM: u16 = 0x804; /* For version 2 params; size is - * EC_PROTO2_MAX_PARAM_SIZE */ -// Protocol version 3 -const _EC_LPC_ADDR_HOST_PACKET: u16 = 0x800; /* Offset of version 3 packet */ -const EC_LPC_HOST_PACKET_SIZE: u16 = 0x100; /* Max size of version 3 packet */ - -const MEC_MEMMAP_OFFSET: u16 = 0x100; -const NPC_MEMMAP_OFFSET: u16 = 0xE00; - -// The actual block is 0x800-0x8ff, but some BIOSes think it's 0x880-0x8ff -// and they tell the kernel that so we have to think of it as two parts. -const _EC_HOST_CMD_REGION0: u16 = 0x800; -const _EC_HOST_CMD_REGION1: u16 = 0x8800; -const _EC_HOST_CMD_REGION_SIZE: u16 = 0x80; - -// EC command register bit functions -const _EC_LPC_CMDR_DATA: u16 = 1 << 0; // Data ready for host to read -const _EC_LPC_CMDR_PENDING: u16 = 1 << 1; // Write pending to EC -const _EC_LPC_CMDR_BUSY: u16 = 1 << 2; // EC is busy processing a command -const _EC_LPC_CMDR_CMD: u16 = 1 << 3; // Last host write was a command -const _EC_LPC_CMDR_ACPI_BRST: u16 = 1 << 4; // Burst mode (not used) -const _EC_LPC_CMDR_SCI: u16 = 1 << 5; // SCI event is pending -const _EC_LPC_CMDR_SMI: u16 = 1 << 6; // SMI event is pending - -const EC_HOST_REQUEST_VERSION: u8 = 3; - -/// Request header of version 3 -#[repr(C, packed)] -struct EcHostRequest { - /// Version of this request structure (must be 3) - pub struct_version: u8, - - /// Checksum of entire request (header and data) - /// Everything added together adds up to 0 (wrapping around u8 limit) - pub checksum: u8, - - /// Command number - pub command: u16, - - /// Command version, usually 0 - pub command_version: u8, - - /// Reserved byte in protocol v3. Must be 0 - pub reserved: u8, - - /// Data length. Data is immediately after the header - pub data_len: u16, -} - -const EC_HOST_RESPONSE_VERSION: u8 = 3; - -/// Response header of version 3 -#[repr(C, packed)] -struct EcHostResponse { - /// Version of this request structure (must be 3) - pub struct_version: u8, - - /// Checksum of entire request (header and data) - pub checksum: u8, - - /// Status code of response. See enum _EcStatus - pub result: u16, - - /// Data length. Data is immediately after the header - pub data_len: u16, - - /// Reserved byte in protocol v3. Must be 0 - pub reserved: u16, -} -#[allow(dead_code)] -pub const HEADER_LEN: usize = std::mem::size_of::(); - fn transfer_write(buffer: &[u8]) { if has_mec() { return portio_mec::transfer_write(buffer); @@ -209,13 +100,13 @@ fn init() -> bool { // In Linux userspace has to first request access to ioports // TODO: Close these again after we're done - #[cfg(feature = "linux_pio")] + #[cfg(target_os = "linux")] if !Uid::effective().is_root() { error!("Must be root to use port based I/O for EC communication."); *init = Initialized::Failed; return false; } - #[cfg(feature = "linux_pio")] + #[cfg(target_os = "linux")] unsafe { // 8 for request/response header, 0xFF for response let res = ioperm(EC_LPC_ADDR_HOST_ARGS as u64, 8 + 0xFF, 1); diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 471a3219..9d3664e2 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -5,11 +5,11 @@ use alloc::vec::Vec; use log::Level; use hwio::{Io, Pio}; -#[cfg(feature = "linux_pio")] +#[cfg(target_os = "linux")] use libc::ioperm; // I/O addresses for host command -#[cfg(feature = "linux_pio")] +#[cfg(target_os = "linux")] const EC_LPC_ADDR_HOST_DATA: u16 = 0x200; const MEC_EC_BYTE_ACCESS: u16 = 0x00; @@ -23,7 +23,7 @@ const MEC_LPC_DATA_REGISTER2: u16 = 0x0806; const _MEC_LPC_DATA_REGISTER3: u16 = 0x0807; pub fn init() { - #[cfg(feature = "linux_pio")] + #[cfg(target_os = "linux")] unsafe { ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); diff --git a/framework_lib/src/chromium_ec/protocol.rs b/framework_lib/src/chromium_ec/protocol.rs new file mode 100644 index 00000000..80152842 --- /dev/null +++ b/framework_lib/src/chromium_ec/protocol.rs @@ -0,0 +1,108 @@ +/* + * Value written to legacy command port / prefix byte to indicate protocol + * 3+ structs are being used. Usage is bus-dependent. + */ +pub const EC_COMMAND_PROTOCOL_3: u8 = 0xda; + +// LPC command status byte masks +/// EC has written data but host hasn't consumed it yet +const _EC_LPC_STATUS_TO_HOST: u8 = 0x01; +/// Host has written data/command but EC hasn't consumed it yet +pub const EC_LPC_STATUS_FROM_HOST: u8 = 0x02; +/// EC is still processing a command +pub const EC_LPC_STATUS_PROCESSING: u8 = 0x04; +/// Previous command wasn't data but command +const _EC_LPC_STATUS_LAST_CMD: u8 = 0x08; +/// EC is in burst mode +const _EC_LPC_STATUS_BURST_MODE: u8 = 0x10; +/// SCI event is pending (requesting SCI query) +const _EC_LPC_STATUS_SCI_PENDING: u8 = 0x20; +/// SMI event is pending (requesting SMI query) +const _EC_LPC_STATUS_SMI_PENDING: u8 = 0x40; +/// Reserved +const _EC_LPC_STATUS_RESERVED: u8 = 0x80; + +/// EC is busy +pub const EC_LPC_STATUS_BUSY_MASK: u8 = EC_LPC_STATUS_FROM_HOST | EC_LPC_STATUS_PROCESSING; + +// I/O addresses for ACPI commands +const _EC_LPC_ADDR_ACPI_DATA: u16 = 0x62; +const _EC_LPC_ADDR_ACPI_CMD: u16 = 0x66; + +// I/O addresses for host command +pub const EC_LPC_ADDR_HOST_DATA: u16 = 0x200; +pub const EC_LPC_ADDR_HOST_CMD: u16 = 0x204; + +// I/O addresses for host command args and params +// Protocol version 2 +pub const EC_LPC_ADDR_HOST_ARGS: u16 = 0x800; /* And 0x801, 0x802, 0x803 */ +const _EC_LPC_ADDR_HOST_PARAM: u16 = 0x804; /* For version 2 params; size is + * EC_PROTO2_MAX_PARAM_SIZE */ +// Protocol version 3 +const _EC_LPC_ADDR_HOST_PACKET: u16 = 0x800; /* Offset of version 3 packet */ +pub const EC_LPC_HOST_PACKET_SIZE: u16 = 0x100; /* Max size of version 3 packet */ + +pub const MEC_MEMMAP_OFFSET: u16 = 0x100; +pub const NPC_MEMMAP_OFFSET: u16 = 0xE00; + +// The actual block is 0x800-0x8ff, but some BIOSes think it's 0x880-0x8ff +// and they tell the kernel that so we have to think of it as two parts. +const _EC_HOST_CMD_REGION0: u16 = 0x800; +const _EC_HOST_CMD_REGION1: u16 = 0x8800; +const _EC_HOST_CMD_REGION_SIZE: u16 = 0x80; + +// EC command register bit functions +const _EC_LPC_CMDR_DATA: u16 = 1 << 0; // Data ready for host to read +const _EC_LPC_CMDR_PENDING: u16 = 1 << 1; // Write pending to EC +const _EC_LPC_CMDR_BUSY: u16 = 1 << 2; // EC is busy processing a command +const _EC_LPC_CMDR_CMD: u16 = 1 << 3; // Last host write was a command +const _EC_LPC_CMDR_ACPI_BRST: u16 = 1 << 4; // Burst mode (not used) +const _EC_LPC_CMDR_SCI: u16 = 1 << 5; // SCI event is pending +const _EC_LPC_CMDR_SMI: u16 = 1 << 6; // SMI event is pending + +pub const EC_HOST_REQUEST_VERSION: u8 = 3; + +/// Request header of version 3 +#[repr(C, packed)] +pub struct EcHostRequest { + /// Version of this request structure (must be 3) + pub struct_version: u8, + + /// Checksum of entire request (header and data) + /// Everything added together adds up to 0 (wrapping around u8 limit) + pub checksum: u8, + + /// Command number + pub command: u16, + + /// Command version, usually 0 + pub command_version: u8, + + /// Reserved byte in protocol v3. Must be 0 + pub reserved: u8, + + /// Data length. Data is immediately after the header + pub data_len: u16, +} + +pub const EC_HOST_RESPONSE_VERSION: u8 = 3; + +/// Response header of version 3 +#[repr(C, packed)] +pub struct EcHostResponse { + /// Version of this request structure (must be 3) + pub struct_version: u8, + + /// Checksum of entire request (header and data) + pub checksum: u8, + + /// Status code of response. See enum _EcStatus + pub result: u16, + + /// Data length. Data is immediately after the header + pub data_len: u16, + + /// Reserved byte in protocol v3. Must be 0 + pub reserved: u16, +} +pub const HEADER_LEN: usize = std::mem::size_of::(); diff --git a/framework_lib/src/chromium_ec/windows.rs b/framework_lib/src/chromium_ec/windows.rs index 6cd2db33..5b0cca68 100644 --- a/framework_lib/src/chromium_ec/windows.rs +++ b/framework_lib/src/chromium_ec/windows.rs @@ -11,7 +11,7 @@ use windows::{ }, }; -use crate::chromium_ec::portio::HEADER_LEN; +use crate::chromium_ec::protocol::HEADER_LEN; use crate::chromium_ec::EC_MEMMAP_SIZE; use crate::chromium_ec::{EcError, EcResponseStatus, EcResult}; diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 88fd1e61..786e4ed6 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -42,7 +42,7 @@ use crate::chromium_ec::commands::TabletModeOverride; use crate::chromium_ec::EcResponseStatus; use crate::chromium_ec::{print_err, EcFlashType}; use crate::chromium_ec::{EcError, EcResult}; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use crate::csme; use crate::ec_binary; use crate::esrt; @@ -54,7 +54,7 @@ use crate::smbios::ConfigDigit0; use crate::smbios::{dmidecode_string_val, get_smbios, is_framework}; #[cfg(feature = "hidapi")] use crate::touchpad::print_touchpad_fw_ver; -#[cfg(any(feature = "hidapi", feature = "windows"))] +#[cfg(feature = "hidapi")] use crate::touchscreen; #[cfg(feature = "uefi")] use crate::uefi::enable_page_break; @@ -483,7 +483,7 @@ fn print_versions(ec: &CrosEc) { } } - #[cfg(feature = "linux")] + #[cfg(target_os = "linux")] { println!("CSME"); if let Ok(csme) = csme::csme_from_sysfs() { @@ -821,7 +821,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { }; ec.set_tablet_mode(mode); } else if let Some(_enable) = &args.touchscreen_enable { - #[cfg(any(feature = "hidapi", feature = "windows"))] + #[cfg(feature = "hidapi")] if touchscreen::enable_touch(*_enable).is_none() { error!("Failed to enable/disable touch"); } @@ -1151,11 +1151,11 @@ fn hash(data: &[u8]) { println!("Hashes"); print!(" SHA256: "); - util::print_buffer_short(sha256); + util::print_buffer(sha256); print!(" SHA384: "); - util::print_buffer_short(sha384); + util::print_buffer(sha384); print!(" SHA512: "); - util::print_buffer_short(sha512); + util::print_buffer(sha512); } fn selftest(ec: &CrosEc) -> Option<()> { diff --git a/framework_lib/src/csme.rs b/framework_lib/src/csme.rs index 610560a2..5bd6c9eb 100644 --- a/framework_lib/src/csme.rs +++ b/framework_lib/src/csme.rs @@ -3,11 +3,11 @@ //! Currently only works on Linux (from sysfs). use core::fmt; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use std::fs; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use std::io; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use std::path::Path; pub struct CsmeInfo { @@ -85,7 +85,7 @@ impl fmt::Display for CsmeVersion { } } -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] pub fn csme_from_sysfs() -> io::Result { let dir = Path::new("/sys/class/mei"); let mut csme_info: Option = None; diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index feb98a2a..16770df5 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -22,11 +22,11 @@ use guid_macros::guid; #[cfg(feature = "uefi")] use uefi::{guid, Guid}; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use std::fs; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use std::io; -#[cfg(feature = "linux")] +#[cfg(target_os = "linux")] use std::path::Path; #[cfg(target_os = "freebsd")] @@ -262,7 +262,7 @@ pub fn print_esrt(esrt: &Esrt) { } } -#[cfg(all(not(feature = "uefi"), feature = "std", feature = "linux"))] +#[cfg(target_os = "linux")] /// On Linux read the ESRT table from the sysfs /// resource_version and resource_count_max are reported by sysfs, so they're defaulted to reaesonable values /// capsule_flags in sysfs seems to be 0 always. Not sure why. @@ -323,7 +323,7 @@ fn esrt_from_sysfs(dir: &Path) -> io::Result { Ok(esrt_table) } -#[cfg(all(not(feature = "uefi"), feature = "linux", target_os = "linux"))] +#[cfg(target_os = "linux")] pub fn get_esrt() -> Option { let res = esrt_from_sysfs(Path::new("/sys/firmware/efi/esrt/entries")).ok(); if res.is_none() { @@ -332,7 +332,7 @@ pub fn get_esrt() -> Option { res } -#[cfg(all(not(feature = "uefi"), feature = "windows"))] +#[cfg(all(not(feature = "uefi"), windows))] pub fn get_esrt() -> Option { let mut esrt_table = Esrt { resource_count: 0, diff --git a/framework_lib/src/lib.rs b/framework_lib/src/lib.rs index a237326a..661f9e14 100644 --- a/framework_lib/src/lib.rs +++ b/framework_lib/src/lib.rs @@ -20,9 +20,9 @@ pub mod camera; pub mod inputmodule; #[cfg(feature = "hidapi")] pub mod touchpad; -#[cfg(any(feature = "hidapi", feature = "windows"))] +#[cfg(feature = "hidapi")] pub mod touchscreen; -#[cfg(feature = "windows")] +#[cfg(all(feature = "hidapi", windows))] pub mod touchscreen_win; #[cfg(feature = "uefi")] diff --git a/framework_lib/src/touchscreen.rs b/framework_lib/src/touchscreen.rs index b06965a7..256df139 100644 --- a/framework_lib/src/touchscreen.rs +++ b/framework_lib/src/touchscreen.rs @@ -1,6 +1,6 @@ use hidapi::{HidApi, HidDevice}; -#[cfg(target_os = "windows")] +#[cfg(windows)] use crate::touchscreen_win; pub const ILI_VID: u16 = 0x222A; diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 972d82a3..4f34a18e 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -157,7 +157,7 @@ pub unsafe fn any_vec_as_u8_slice(p: &[T]) -> &[u8] { /// Print a byte buffer as a series of hex bytes pub fn print_buffer(buffer: &[u8]) { for byte in buffer { - print!("{:#X} ", byte); + print!("{:02x}", byte); } println!(); } @@ -232,15 +232,8 @@ pub fn find_sequence(haystack: &[u8], needle: &[u8]) -> Option { /// Assert length of an EC response from the windows driver /// It's always 20 more than expected. TODO: Figure out why pub fn assert_win_len(left: N, right: N) { - #[cfg(feature = "win_driver")] + #[cfg(windows)] assert_eq!(left, right + NumCast::from(20).unwrap()); - #[cfg(not(feature = "win_driver"))] + #[cfg(not(windows))] assert_eq!(left, right); } - -pub fn print_buffer_short(buffer: &[u8]) { - for byte in buffer { - print!("{:02x}", byte); - } - println!(); -} diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index ecef1fb0..da69ded0 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -3,15 +3,8 @@ name = "framework_tool" version = "0.4.0" edition = "2021" -[features] -default = ["linux"] -linux = ["framework_lib/linux"] -freebsd = ["framework_lib/freebsd"] -windows = ["framework_lib/windows"] - [dependencies.framework_lib] path = "../framework_lib" -default-features = false [build-dependencies] # Note: Only takes effect in release builds From 1117009119ac3205810f9f3752fd2a381a37e3b3 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 May 2025 09:47:45 +0800 Subject: [PATCH 049/174] --version: Add revision of mainboard ``` > framework_tool.exe --versions Mainboard Hardware Type: Laptop 13 (AMD Ryzen AI 300 Series) Revision: MassProduction [...] ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 13 ++++++++++ framework_lib/src/smbios.rs | 36 +++++++++++++++++++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 88fd1e61..97bf7f7d 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -350,12 +350,25 @@ fn print_stylus_battery_level() { } fn print_versions(ec: &CrosEc) { + println!("Mainboard Hardware"); + if let Some(ver) = smbios::get_product_name() { + println!(" Type: {}", ver); + } else { + println!(" Type: Unknown"); + } + if let Some(ver) = smbios::get_baseboard_version() { + println!(" Revision: {:?}", ver); + } else { + println!(" Revision: Unknown"); + } println!("UEFI BIOS"); if let Some(smbios) = get_smbios() { let bios_entries = smbios.collect::(); let bios = bios_entries.first().unwrap(); println!(" Version: {}", bios.version()); println!(" Release Date: {}", bios.release_date()); + } else { + println!(" Version: Unknown"); } println!("EC Firmware"); diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index 1345b396..5e713513 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -8,6 +8,7 @@ use std::io::ErrorKind; use crate::util::Config; pub use crate::util::Platform; use num_derive::FromPrimitive; +use num_traits::FromPrimitive; use smbioslib::*; #[cfg(feature = "uefi")] use spin::Mutex; @@ -215,7 +216,7 @@ pub fn get_smbios() -> Option { } } -fn get_product_name() -> Option { +pub fn get_product_name() -> Option { // On FreeBSD we can short-circuit and avoid parsing SMBIOS #[cfg(target_os = "freebsd")] if let Ok(product) = kenv_get("smbios.system.product") { @@ -225,6 +226,7 @@ fn get_product_name() -> Option { let smbios = get_smbios(); if smbios.is_none() { println!("Failed to find SMBIOS"); + return None; } let mut smbios = smbios.into_iter().flatten(); smbios.find_map(|undefined_struct| { @@ -237,6 +239,38 @@ fn get_product_name() -> Option { }) } +pub fn get_baseboard_version() -> Option { + // TODO: On FreeBSD we can short-circuit and avoid parsing SMBIOS + // #[cfg(target_os = "freebsd")] + // if let Ok(product) = kenv_get("smbios.system.product") { + // return Some(product); + // } + + let smbios = get_smbios(); + if smbios.is_none() { + error!("Failed to find SMBIOS"); + return None; + } + let mut smbios = smbios.into_iter().flatten(); + smbios.find_map(|undefined_struct| { + if let DefinedStruct::BaseBoardInformation(data) = undefined_struct.defined_struct() { + if let Some(version) = dmidecode_string_val(&data.version()) { + // Assumes it's ASCII, which is guaranteed by SMBIOS + let config_digit0 = &version[0..1]; + let config_digit0 = u8::from_str_radix(config_digit0, 16); + if let Ok(version_config) = + config_digit0.map(::from_u8) + { + return version_config; + } else { + error!(" Invalid BaseBoard Version: {}'", version); + } + } + } + None + }) +} + pub fn get_platform() -> Option { #[cfg(feature = "uefi")] let mut cached_platform = CACHED_PLATFORM.lock(); From 6531f1c1fea08419307cb63b1d56f9995e880d90 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 May 2025 10:46:01 +0800 Subject: [PATCH 050/174] uefi: Don't need sha2/force-soft anymore Linking doesn't fail anymore for UEFI builds. Signed-off-by: Daniel Schaefer --- framework_lib/Cargo.toml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 21d57035..849ef663 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -12,13 +12,7 @@ default = ["std"] std = ["hidapi", "rusb"] rusb = ["dep:rusb"] hidapi = ["dep:hidapi"] -uefi = [ - "lazy_static/spin_no_std", - # Otherwise I get: `LLVM ERROR: Do not know how to split the result of this operator!` - # Seems to be a Ruset/LLVM bug when SSE is enabled. - # See: https://github.com/rust-lang/rust/issues/61721 - "sha2/force-soft" -] +uefi = [ "lazy_static/spin_no_std" ] [build-dependencies] built = { version = "0.5", features = ["chrono", "git2"] } From f73981d9c1716a6cd5d2cf332cbc87ed177e3308 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 May 2025 10:55:12 +0800 Subject: [PATCH 051/174] Remove std feature There's no need for it, the only no_std environment is UEFI and it's unlikely there'll be another one. Maybe we could rename uefi to no_std in the future, but right now it makes no sense to duplicate it. Signed-off-by: Daniel Schaefer --- framework_lib/Cargo.toml | 3 +-- framework_lib/src/capsule.rs | 6 +++--- framework_lib/src/commandline/mod.rs | 4 ++-- framework_lib/src/util.rs | 22 +++++++++++----------- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 849ef663..84355995 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -8,8 +8,7 @@ rust-version = "1.74" build = "build.rs" [features] -default = ["std"] -std = ["hidapi", "rusb"] +default = ["hidapi", "rusb"] rusb = ["dep:rusb"] hidapi = ["dep:hidapi"] uefi = [ "lazy_static/spin_no_std" ] diff --git a/framework_lib/src/capsule.rs b/framework_lib/src/capsule.rs index 9e6a35bc..971db631 100644 --- a/framework_lib/src/capsule.rs +++ b/framework_lib/src/capsule.rs @@ -11,9 +11,9 @@ use std::prelude::v1::*; use core::prelude::rust_2021::derive; -#[cfg(all(not(feature = "uefi"), feature = "std"))] +#[cfg(not(feature = "uefi"))] use std::fs::File; -#[cfg(all(not(feature = "uefi"), feature = "std"))] +#[cfg(not(feature = "uefi"))] use std::io::prelude::*; #[cfg(not(feature = "uefi"))] @@ -180,7 +180,7 @@ pub fn dump_winux_image(data: &[u8], header: &DisplayCapsule, filename: &str) { let image = &data[header_len..image_size]; - #[cfg(all(not(feature = "uefi"), feature = "std"))] + #[cfg(not(feature = "uefi"))] { let mut file = File::create(filename).unwrap(); file.write_all(image).unwrap(); diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 786e4ed6..f3c56eba 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -17,7 +17,7 @@ pub mod uefi; #[cfg(not(feature = "uefi"))] use std::fs; -#[cfg(all(not(feature = "uefi"), feature = "std"))] +#[cfg(not(feature = "uefi"))] use std::io::prelude::*; #[cfg(feature = "rusb")] @@ -553,7 +553,7 @@ fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) { fn dump_ec_flash(ec: &CrosEc, dump_path: &str) { let flash_bin = ec.get_entire_ec_flash().unwrap(); - #[cfg(all(not(feature = "uefi"), feature = "std"))] + #[cfg(not(feature = "uefi"))] { let mut file = fs::File::create(dump_path).unwrap(); file.write_all(&flash_bin).unwrap(); diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 4f34a18e..15444913 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -6,11 +6,11 @@ use std::prelude::v1::*; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; -#[cfg(not(feature = "std"))] +#[cfg(feature = "uefi")] use alloc::sync::Arc; -#[cfg(not(feature = "std"))] +#[cfg(feature = "uefi")] use spin::{Mutex, MutexGuard}; -#[cfg(feature = "std")] +#[cfg(not(feature = "uefi"))] use std::sync::{Arc, Mutex, MutexGuard}; use crate::smbios; @@ -74,9 +74,9 @@ pub struct Config { impl Config { pub fn set(platform: Platform) { - #[cfg(feature = "std")] + #[cfg(not(feature = "uefi"))] let mut config = CONFIG.lock().unwrap(); - #[cfg(not(feature = "std"))] + #[cfg(feature = "uefi")] let mut config = CONFIG.lock(); if (*config).is_none() { @@ -87,9 +87,9 @@ impl Config { } } pub fn is_set() -> bool { - #[cfg(feature = "std")] + #[cfg(not(feature = "uefi"))] let config = CONFIG.lock().unwrap(); - #[cfg(not(feature = "std"))] + #[cfg(feature = "uefi")] let config = CONFIG.lock(); (*config).is_some() @@ -98,9 +98,9 @@ impl Config { pub fn get() -> MutexGuard<'static, Option> { trace!("Config::get() entry"); let unset = { - #[cfg(feature = "std")] + #[cfg(not(feature = "uefi"))] let config = CONFIG.lock().unwrap(); - #[cfg(not(feature = "std"))] + #[cfg(feature = "uefi")] let config = CONFIG.lock(); (*config).is_none() }; @@ -115,9 +115,9 @@ impl Config { None }; - #[cfg(feature = "std")] + #[cfg(not(feature = "uefi"))] let mut config = CONFIG.lock().unwrap(); - #[cfg(not(feature = "std"))] + #[cfg(feature = "uefi")] let mut config = CONFIG.lock(); if new_config.is_some() { From 96b7fcce39b4dfefbf401139bc3bf13568cf1ab4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 May 2025 11:42:54 +0800 Subject: [PATCH 052/174] Don't require PD config if platform isn't detected Now even if the platform isn't detected, we can run the commands just fine. For example both of these work (if I comment out the smbios entry): ``` framework_tool --versions framework_tool --versions --pd-ports 1 2 --pd-addrs 66 64 ``` The first one just fails accessing I2C passthrough for the PD ports. This is the final change to allow just running the tool on any system in as many cases as possible - even if the linux kernel or framework_tool dose not recognize the system. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 24 +++++++++++++----------- framework_lib/src/smbios.rs | 9 ++++++--- framework_lib/src/util.rs | 2 ++ 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 3163f082..532ad10e 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -30,11 +30,11 @@ pub enum PdPort { impl PdPort { /// SMBUS/I2C Address - fn i2c_address(&self) -> u16 { + fn i2c_address(&self) -> EcResult { let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; - match (platform, self) { + Ok(match (platform, self) { (Platform::GenericFramework((left, _), _), PdPort::Left01) => *left, (Platform::GenericFramework((_, right), _), PdPort::Right23) => *right, // Framework AMD Platforms (CCG8) @@ -52,10 +52,13 @@ impl PdPort { ) => 0x40, // TODO: It only has a single PD controller (Platform::FrameworkDesktopAmdAiMax300, _) => 0x08, + (Platform::UnknownSystem, _) => { + Err(EcError::DeviceError("Unsupported platform".to_string()))? + } // Framework Intel Platforms (CCG5 and CCG6) (_, PdPort::Left01) => 0x08, (_, PdPort::Right23) => 0x40, - } + }) } /// I2C port on the EC @@ -87,10 +90,9 @@ impl PdPort { ) => 2, // TODO: It only has a single PD controller (Platform::FrameworkDesktopAmdAiMax300, _) => 1, - // (_, _) => Err(EcError::DeviceError(format!( - // "Unsupported platform: {:?} {:?}", - // platform, self - // )))?, + (Platform::UnknownSystem, _) => { + Err(EcError::DeviceError("Unsupported platform".to_string()))? + } }) } } @@ -140,13 +142,13 @@ impl PdController { fn i2c_read(&self, addr: u16, len: u16) -> EcResult { trace!( "I2C passthrough from I2C Port {} to I2C Addr {}", - self.port.i2c_port().unwrap(), - self.port.i2c_address() + self.port.i2c_port()?, + self.port.i2c_address()? ); i2c_read( &self.ec, - self.port.i2c_port().unwrap(), - self.port.i2c_address(), + self.port.i2c_port()?, + self.port.i2c_address()?, addr, len, ) diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index 1345b396..83190bd4 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -46,7 +46,7 @@ pub enum ConfigDigit0 { pub fn is_framework() -> bool { if matches!( get_platform(), - Some(Platform::GenericFramework((_, _), (_, _))) + Some(Platform::GenericFramework((_, _), (_, _))) | Some(Platform::UnknownSystem) ) { return true; } @@ -252,7 +252,10 @@ pub fn get_platform() -> Option { // Except if it's a GenericFramework platform let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; - if matches!(platform, Platform::GenericFramework((_, _), (_, _))) { + if matches!( + platform, + Platform::GenericFramework((_, _), (_, _)) | Platform::UnknownSystem + ) { return Some(*platform); } } @@ -270,7 +273,7 @@ pub fn get_platform() -> Option { "Laptop 13 (Intel Core Ultra Series 1)" => Some(Platform::IntelCoreUltra1), "Laptop 16 (AMD Ryzen 7040 Series)" => Some(Platform::Framework16Amd7080), "Desktop (AMD Ryzen AI Max 300 Series)" => Some(Platform::FrameworkDesktopAmdAiMax300), - _ => None, + _ => Some(Platform::UnknownSystem), }; if let Some(platform) = platform { diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 972d82a3..1aeb765e 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -38,6 +38,7 @@ pub enum Platform { /// Generic Framework device /// pd_addrs, pd_ports GenericFramework((u16, u16), (u8, u8)), + UnknownSystem, } #[derive(Debug, PartialEq, Clone, Copy)] @@ -61,6 +62,7 @@ impl Platform { Platform::Framework16Amd7080 => Some(PlatformFamily::Framework16), Platform::FrameworkDesktopAmdAiMax300 => Some(PlatformFamily::FrameworkDesktop), Platform::GenericFramework(..) => None, + Platform::UnknownSystem => None, } } } From 6d7afb404d2e79b626b7be162446d4f9c5938e4a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 5 May 2025 13:49:43 +0800 Subject: [PATCH 053/174] --inputdeck: Fix Chassis Closed status Fixes: 49eb0b914ee2f11124614ba6d3f3259adf199fac That commit reversed the wording but not the variable. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 5e60b148..87d45b13 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -510,7 +510,7 @@ impl CrosEc { let is_present = |p| if p { "Present" } else { "Missing" }; println!("Input Deck"); - println!(" Chassis Closed: {}", 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())); @@ -538,7 +538,7 @@ impl CrosEc { let is_present = |p| if p { "Present" } else { "Missing" }; println!("Input Deck"); - println!(" Chassis Closed: {}", intrusion.currently_open); + println!(" Chassis Closed: {}", !intrusion.currently_open); println!(" Audio Daughterboard: {}", is_present(audio.is_some())); println!(" Touchpad: {}", is_present(tp.is_some())); @@ -548,7 +548,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 Closed: {}", intrusion.currently_open); + println!("Chassis Closed: {}", !intrusion.currently_open); println!("Input Deck State: {:?}", status.state); println!("Touchpad present: {}", status.touchpad_present); println!("Positions:"); From d111ee5aa170906448b24c83cb682e9bc338b2e6 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 6 May 2025 17:50:08 +0800 Subject: [PATCH 054/174] windows: Run --versions command if double-clicked Signed-off-by: Daniel Schaefer --- Cargo.lock | 1 + framework_tool/Cargo.toml | 6 ++++++ framework_tool/src/main.rs | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 465dc6e0..3ee80253 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -424,6 +424,7 @@ version = "0.4.0" dependencies = [ "framework_lib", "static_vcruntime", + "winapi", ] [[package]] diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index da69ded0..bc30ab61 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -9,3 +9,9 @@ path = "../framework_lib" [build-dependencies] # Note: Only takes effect in release builds static_vcruntime = "2.0" + +[target.'cfg(windows)'.dependencies.winapi] +version = "0.3.9" +features = [ + "wincon" +] diff --git a/framework_tool/src/main.rs b/framework_tool/src/main.rs index 3a4d54a4..251f85f4 100644 --- a/framework_tool/src/main.rs +++ b/framework_tool/src/main.rs @@ -6,9 +6,42 @@ fn get_args() -> Vec { } fn main() -> Result<(), &'static str> { - let args = commandline::parse(&get_args()); + let args = get_args(); + + // If the user double-clicks (opens from explorer/desktop), + // then we want to have the default behavior of showing a report of + // all firmware versions. + #[cfg(windows)] + let (args, double_clicked) = { + let double_clicked = unsafe { + // See https://devblogs.microsoft.com/oldnewthing/20160125-00/?p=92922 + let mut plist: winapi::shared::minwindef::DWORD = 0; + let processes = winapi::um::wincon::GetConsoleProcessList(&mut plist, 1); + processes == 1 + }; + if double_clicked { + ( + vec![args[0].clone(), "--versions".to_string()], + double_clicked, + ) + } else { + (args, double_clicked) + } + }; + + let args = commandline::parse(&args); if (commandline::run_with_args(&args, false)) != 0 { return Err("Fail"); } + + // Prevent command prompt from auto closing + #[cfg(windows)] + if double_clicked { + println!(); + println!("Press ENTER to exit..."); + let mut line = String::new(); + let _ = std::io::stdin().read_line(&mut line).unwrap(); + } + Ok(()) } From 7336362ac1b22f945226168ad903c83bee279b82 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 6 May 2025 05:47:04 -0700 Subject: [PATCH 055/174] windows: Embed resource to always launch as admin Only in release builds, because running `cargo run` from non-admin terminal won't work with the resources. Signed-off-by: Daniel Schaefer --- Cargo.lock | 128 ++++++++++++++++++++- framework_tool/Cargo.toml | 1 + framework_tool/build.rs | 10 +- framework_tool/framework_tool-manifest.rc | 2 + framework_tool/framework_tool.exe.manifest | 10 ++ 5 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 framework_tool/framework_tool-manifest.rc create mode 100644 framework_tool/framework_tool.exe.manifest diff --git a/Cargo.lock b/Cargo.lock index 3ee80253..fb0f76b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,7 +127,7 @@ checksum = "031718ddb8f78aa5def78a09e90defe30151d1f6c672f937af4dd916429ed996" dependencies = [ "semver", "serde", - "toml", + "toml 0.5.11", "url", ] @@ -354,6 +354,20 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "embed-resource" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 0.8.22", + "vswhom", + "winreg", +] + [[package]] name = "env_filter" version = "0.1.2" @@ -377,6 +391,12 @@ dependencies = [ "log", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -422,6 +442,7 @@ dependencies = [ name = "framework_tool" version = "0.4.0" dependencies = [ + "embed-resource", "framework_lib", "static_vcruntime", "winapi", @@ -567,6 +588,12 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" + [[package]] name = "heck" version = "0.5.0" @@ -626,6 +653,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "io-kit-sys" version = "0.1.0" @@ -1070,6 +1107,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1208,6 +1254,47 @@ dependencies = [ "serde", ] +[[package]] +name = "toml" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "typenum" version = "1.16.0" @@ -1313,6 +1400,26 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -1673,6 +1780,25 @@ version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +[[package]] +name = "winnow" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9fb597c990f03753e08d3c29efbfcf2019a003b4bf4ba19225c158e1549f0f3" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wmi" version = "0.15.0" diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index bc30ab61..598d00c0 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -9,6 +9,7 @@ path = "../framework_lib" [build-dependencies] # Note: Only takes effect in release builds static_vcruntime = "2.0" +embed-resource = "3.0" [target.'cfg(windows)'.dependencies.winapi] version = "0.3.9" diff --git a/framework_tool/build.rs b/framework_tool/build.rs index 20e1c8e9..de98f1f6 100644 --- a/framework_tool/build.rs +++ b/framework_tool/build.rs @@ -1,3 +1,11 @@ fn main() { - static_vcruntime::metabuild(); + if !cfg!(debug_assertions) { + // Statically link vcruntime to allow running on clean install + static_vcruntime::metabuild(); + + // Embed resources file to force running as admin + embed_resource::compile("framework_tool-manifest.rc", embed_resource::NONE) + .manifest_optional() + .unwrap(); + } } diff --git a/framework_tool/framework_tool-manifest.rc b/framework_tool/framework_tool-manifest.rc new file mode 100644 index 00000000..68429585 --- /dev/null +++ b/framework_tool/framework_tool-manifest.rc @@ -0,0 +1,2 @@ +#define RT_MANIFEST 24 +1 RT_MANIFEST "framework_tool.exe.manifest" diff --git a/framework_tool/framework_tool.exe.manifest b/framework_tool/framework_tool.exe.manifest new file mode 100644 index 00000000..287bba26 --- /dev/null +++ b/framework_tool/framework_tool.exe.manifest @@ -0,0 +1,10 @@ + + + + + + + + + + From e1e57c922d830f84bc23938d5ba8c2419312316b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 01:11:24 +0800 Subject: [PATCH 056/174] Bump version to 0.4.1 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 465dc6e0..01316989 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,7 +388,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.4.0" +version = "0.4.1" dependencies = [ "built", "clap", @@ -420,7 +420,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.4.0" +version = "0.4.1" dependencies = [ "framework_lib", "static_vcruntime", @@ -428,7 +428,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.4.0" +version = "0.4.1" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 84355995..f0477678 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.4.0" +version = "0.4.1" 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 da69ded0..e9b3422b 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.4.0" +version = "0.4.1" edition = "2021" [dependencies.framework_lib] diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index 13d6e0b5..d0ad0cc4 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.4.0" +version = "0.4.1" edition = "2021" # Minimum Supported Rust Version rust-version = "1.74" From 079af34d4b4d995ac9e0d78921f5f6201fdd3421 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 10:59:37 +0800 Subject: [PATCH 057/174] windows: Don't run --versions if any arguments provided Should always try to run as admin in release builds. But only autorun --versions if no arguments provided. Tested: - [x] Release Build with args - [x] Run from powershell - [x] Run from powershell with sudo "In new window" - [x] Run from powershell with sudo "With input disabled" - [x] Doubleclick from Explorer - This is impossible - [x] Release Build without args - [x] Run from powershell - This case autoruns --versions when it shouldn't - [x] Run from powershell with sudo "In new window" - This case autoruns --versions when it shouldn't - [x] Run from powershell with sudo "With input disabled" - [x] Doubleclick from Explorer - [x] Debug Build with args - [x] Run from powershell - [x] Run from powershell with sudo "In new window" - [x] Run from powershell with sudo "With input disabled" - [x] Doubleclick from Explorer - This is impossible - [x] Debug Build without args - [x] Run from powershell - [x] Run from powershell with sudo "In new window" - This case autoruns --versions when it shouldn't - [x] Run from powershell with sudo "With input disabled" - [x] Doubleclick from Explorer Signed-off-by: Daniel Schaefer --- framework_tool/src/main.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/framework_tool/src/main.rs b/framework_tool/src/main.rs index 251f85f4..d755aa77 100644 --- a/framework_tool/src/main.rs +++ b/framework_tool/src/main.rs @@ -17,9 +17,16 @@ fn main() -> Result<(), &'static str> { // See https://devblogs.microsoft.com/oldnewthing/20160125-00/?p=92922 let mut plist: winapi::shared::minwindef::DWORD = 0; let processes = winapi::um::wincon::GetConsoleProcessList(&mut plist, 1); + + // If we're the only process that means we're in a fresh terminal + // without CMD or powershell. This happens in some cases, for example + // if the user double-clicks the app from Explorer. processes == 1 }; - if double_clicked { + // But it also happens if launched from the commandline and a UAC prompt is necessary, + // for example with sudo set to open "In a new windows", therefore we also have to + // check that no commandline arguments were provided. + if double_clicked && args.len() == 1 { ( vec![args[0].clone(), "--versions".to_string()], double_clicked, From c49ad0c15a3f4757ba7ffecec763e66b63479696 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 13:49:50 +0800 Subject: [PATCH 058/174] --thermal: Add Framework 12 charger IC temp ``` > sudo framework_tool --thermal F75303_CPU: 35 C F75303_Skin: 33 C F75303_Local: 33 C Battery: 33 C PECI: 51 C Charger IC 32 C Fan Speed: 0 RPM ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index bb3e9ea4..266d4f07 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -340,6 +340,7 @@ pub fn print_thermal(ec: &CrosEc) { println!(" F75303_Local: {:>4}", TempSensor::from(temps[2])); println!(" Battery: {:>4}", TempSensor::from(temps[3])); println!(" PECI: {:>4}", TempSensor::from(temps[4])); + println!(" Charger IC {:>4}", TempSensor::from(temps[5])); } Some( From 8a6249fa2e7a2f38406af27738cb5f1b683f06c9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 13:59:20 +0800 Subject: [PATCH 059/174] --thermal: Print sensors we don't know about, if present Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index 266d4f07..4868ba24 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -76,7 +76,7 @@ const EC_FAN_SPEED_ENTRIES: usize = 4; const EC_FAN_SPEED_STALLED_DEPRECATED: u16 = 0xFFFE; const EC_FAN_SPEED_NOT_PRESENT: u16 = 0xFFFF; -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum TempSensor { Ok(u8), NotPresent, @@ -311,7 +311,7 @@ pub fn print_thermal(ec: &CrosEc) { let fans = ec.read_memory(EC_MEMMAP_FAN, 0x08).unwrap(); let platform = smbios::get_platform(); - match platform { + let remaining_sensors = match platform { Some(Platform::IntelGen11) | Some(Platform::IntelGen12) | Some(Platform::IntelGen13) => { println!(" F75303_Local: {:>4}", TempSensor::from(temps[0])); println!(" F75303_CPU: {:>4}", TempSensor::from(temps[1])); @@ -324,6 +324,7 @@ pub fn print_thermal(ec: &CrosEc) { ) { println!(" F57397_VCCGT: {:>4}", TempSensor::from(temps[5])); } + 2 } Some(Platform::IntelCoreUltra1) => { @@ -332,6 +333,7 @@ pub fn print_thermal(ec: &CrosEc) { println!(" Battery: {:>4}", TempSensor::from(temps[2])); println!(" F75303_DDR: {:>4}", TempSensor::from(temps[3])); println!(" PECI: {:>4}", TempSensor::from(temps[4])); + 3 } Some(Platform::Framework12IntelGen13) => { @@ -341,6 +343,7 @@ pub fn print_thermal(ec: &CrosEc) { println!(" Battery: {:>4}", TempSensor::from(temps[3])); println!(" PECI: {:>4}", TempSensor::from(temps[4])); println!(" Charger IC {:>4}", TempSensor::from(temps[5])); + 2 } Some( @@ -357,6 +360,9 @@ pub fn print_thermal(ec: &CrosEc) { println!(" dGPU VRAM: {:>4}", TempSensor::from(temps[5])); println!(" dGPU AMB: {:>4}", TempSensor::from(temps[6])); println!(" dGPU temp: {:>4}", TempSensor::from(temps[7])); + 0 + } else { + 4 } } @@ -365,6 +371,7 @@ pub fn print_thermal(ec: &CrosEc) { println!(" F75303_DDR: {:>4}", TempSensor::from(temps[1])); println!(" F75303_AMB: {:>4}", TempSensor::from(temps[2])); println!(" APU: {:>4}", TempSensor::from(temps[3])); + 4 } _ => { @@ -376,6 +383,15 @@ pub fn print_thermal(ec: &CrosEc) { println!(" Temp 5: {:>4}", TempSensor::from(temps[5])); println!(" Temp 6: {:>4}", TempSensor::from(temps[6])); println!(" Temp 7: {:>4}", TempSensor::from(temps[7])); + 0 + } + }; + + // Just in case EC has more sensors than we know about, print them + for (i, temp) in temps.iter().enumerate().take(8).skip(8 - remaining_sensors) { + let temp = TempSensor::from(*temp); + if temp != TempSensor::NotPresent { + println!(" Temp {}: {:>4}", i, temp); } } From b80e2cba6e783550d342670a4aa51eed12232da1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 14:15:17 +0800 Subject: [PATCH 060/174] --thermal: Align RPM with C ``` sudo framework_tool --thermal F75303_CPU: 36 C F75303_Skin: 34 C F75303_Local: 33 C Battery: 31 C PECI: 53 C Charger IC 30 C Fan Speed: 0 RPM ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index 4868ba24..df0d8031 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -398,11 +398,11 @@ pub fn print_thermal(ec: &CrosEc) { 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); + 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); + println!(" Fan Speed: {:>4} RPM", fan); } } } From 4056948f290885ae577f651b0d850973a6dcfd51 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 17 Jul 2024 17:18:09 +0800 Subject: [PATCH 061/174] windows: Add app icon Signed-off-by: Daniel Schaefer --- Cargo.lock | 11 +++++++++++ framework_tool/Cargo.toml | 1 + framework_tool/build.rs | 8 ++++++++ res/framework_startmenuicon.ico | Bin 0 -> 89962 bytes 4 files changed, 20 insertions(+) create mode 100644 res/framework_startmenuicon.ico diff --git a/Cargo.lock b/Cargo.lock index fb0f76b6..86d1a77c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -446,6 +446,7 @@ dependencies = [ "framework_lib", "static_vcruntime", "winapi", + "winresource", ] [[package]] @@ -1799,6 +1800,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winresource" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba4a67c78ee5782c0c1cb41bebc7e12c6e79644daa1650ebbc1de5d5b08593f7" +dependencies = [ + "toml 0.8.22", + "version_check", +] + [[package]] name = "wmi" version = "0.15.0" diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 598d00c0..0469dab3 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -10,6 +10,7 @@ path = "../framework_lib" # Note: Only takes effect in release builds static_vcruntime = "2.0" embed-resource = "3.0" +winresource = "0.1.17" [target.'cfg(windows)'.dependencies.winapi] version = "0.3.9" diff --git a/framework_tool/build.rs b/framework_tool/build.rs index de98f1f6..87d2140e 100644 --- a/framework_tool/build.rs +++ b/framework_tool/build.rs @@ -1,4 +1,12 @@ fn main() { + // Add app icon + if std::env::var_os("CARGO_CFG_WINDOWS").is_some() { + winresource::WindowsResource::new() + .set_icon("..\\res\\framework_startmenuicon.ico") + .compile() + .unwrap(); + } + if !cfg!(debug_assertions) { // Statically link vcruntime to allow running on clean install static_vcruntime::metabuild(); diff --git a/res/framework_startmenuicon.ico b/res/framework_startmenuicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8c6be29a6b1462e13005c3c113d81888f51b73ab GIT binary patch literal 89962 zcmeI5d5|1c9mi)$AV2{zsNAqgRE|F^K?DV&%MlI{L=g~B5>N;z$XS94f!9#4@0~C#9lWa?~UB1aj)t-Fz zWG4daAa)jb6r|-NzD@c0HP{-srp++mO7Jd-+nM(JDfT}NtOq=qiNIQjIUJ0DwAi=W z_6En-fa)`EnutQx#WK*!&a~}IIere906cXc0bK(g0co-Bber1#0o|L_@sWFS#ABdS zJJa_68}iiFJaraz)wk{y+9mC){@T4D4;#~TD27)7#k8MkcbtD7 zq{Yg`Hr;n!2EGKQg6RF1zE1;(fqCE`z=e%zI-kR)-9W#Q#`#VM?|`)U6lt5r!-XQ* zlxB-!tA1X;kouQ5g0z_UZRXtC^A zFtrB?Ykyx?g=<%uCsKBrjRXFCRje2&CuOIvyg<{q9sC@e1?B*)d(nK*89?hv^_a;3Eye*u?(eZXXpl_X-ZgP`3yFuk9ztL|d2cY_=Xl*6KuY&!%z0!-IUD|x+8SI6fVx7X2iCYT7y zLOPdf-ArEVqg8XtN!jTP9B9>kOl()*c(BC5+8o=j$thN<(}co#z^azK3dkmq}Z)BS&sn|HIyVn=f$$q6F89MTUMUl z0i-pqy-FM4*bN|SoRW^$SVx&jV>X?>^Drq6ao**~E%Lpdshr8jAA`96S$}JMcvDkG zI*@aUg_vCGzz^3d&#@546J0x%LBF2AWo=v0<8^P@uXHkw>peu#cx3sm_bQcWekF0o6j{I)4dGaXItD_D7epH3>MfnxJ2?bhbP|Vy8W$lmU2}V(zsiQ7>>6VkZptsp6e-SGZ}EalQQX`byBz~s2f+sG?g zyKx=oH2oi9<9|Unb(D0Ba;coUcqggaiRNLx2$F2e!n*{So;r)bh3uXs=1Ir?2bsFB z&+EGJadh2d!4LWNPvGzW2ttRXzaKXOlIkNX?;+&w)}cL}?EV3E{spk{969t|=Ri;0 zMc^n73*%_|Q#n`eRRg9-=RxKBWnkLgsJtb}_v4=gp-a+u>C+$@Q`6sHMZT_$(v^iY zex&u4rtM3Tr{}7kvPEDHhbP6>G*4?iKHY_>n{&)H;5}g4rX+c~zf~W>Q?>|9;&4;r z(n)!W^R%X5>o!f+b$U67%dq*o+NN!Ox7HE(PR_Ygt*BYoW{dxs zJp#ugB8+dIpUcp@am0L`!}8Q0>e4$_Opb{}T zda@INyAYcedt2KUHQdoPFt721xG(*Nj-i6-KT?2jxn`wcpY{c{2o_s zdi`d*xj&V-+E}OEj()vg!IRwxT!;8}a$$O(AEHU}cFnNGUx`kCzRD-RuC%y1U-|x(tBl*%>#~3KJnq># zAEwFO!hTr$UW3c~!KWP6l^?&dU-nZS<@QPRKI};M9_aOCHv*qPeA+d`);5i||Jjl5 zW6|r$ZUl}(ysg?vlY2LMZ*-(vYZX1&jX(|YY5BLcO*Qo+N4jr9uP3_^xE}GgY9~#u zYU*2#bU$i0q+U7^c+gR9HDB})N4hn?eIww>P6XCR>Tt6tUT#aEpU?-$rDiLrseiZFD z*RaDz?Y5GsKGr6{li3L9-5V~RH!I(BBd+LL=wd#F>?q6M{fUID@L%U(VXHQ6WBcpq z{a`FQ$2zjbzxyMW2m?Rwbd<~Eo7nE6ucv2K{xr135Ff8sK2{MPd%PuU`%cWTem;Bx z&MyY>8Zh}=V+N-I=`uwc6PXXR*21K%QCac#>6)^=ALM-1VqVlAx)RuY7wnG6zuS@z z`nz+Jfv3(Qus*wwf@m8x^<96@>r)^P8bHb6K-Vr)_J?vbcgIs_5x9ii!}yu{)V-9h zc`nuuYo5!`fbI=U`ST94b^xB*i@- zG5RAg75A5s_1S_&U>f$AeCN^k3g%xC%{d#ROx4sVvbHOtli0KcFxBVMT!-BX>Sqzm z()-cXXE0G$8cW@#2u`Kg@G-XOxwZ*^N03v5>wJp4PBy7u`wTDvi=>Jdkx2-pYIXRG91f=t{X`P{U;fYh0a@=zteBm#B(gf z@pNRCm9!?p#&c>t163cc*GWF=d4{R}R47Md>fOwnDpI^3vk(`>NB2a%JO-h$gx^?- z-QSU0cDe=!!ggZ%^I2r?TJ``X*stfKj|0>33gz9DptEeO?^}$I;-q&gpHQ}-q}Y2L z+uj0U4cPenL5lveY&z9)jKa9zfu5p1-)bjUTe6d_>s}b6@bkOv7*z0mj>W#vAKRa5 z=ZaaG<}=IIvw@zc*p5#q{}gnUoo3@e=ucjs`nx|zgCdV5>t656z-B+HS-gE+WvfZu z_sjtkK~YHcbTD`rG|RhfzOO4Yu^%CaRWKE}BF#fN515*Vkfru~U74w?FbD6F*W18s zuqm)jo1owrpgEn=9opgg)7O=~TxiT$?`rxfI0$S8vXW}`bKoan5s(hoHI$^^*HylG zd6hc2A6yM|&C(idjVB!r_60K=-^J55?Q<2l54-}Ba#%#3udDLps)+T|%qCw~zOI_p zMi#!WD_>Vx>ZjSUzOH;-HLHy*d|y|-uCmlmvtxZ-`MPRW8(H|hP*-0m(fxOpT$SnA z6Uj;0X&)RYQ_TCp*3Xx{sIXtp#rh#vW%6|7hQ}dVJE>V5GhOI)8&r4C;^e(?MzyRns(tE+5 z1JQUC;rk2NbUDy`zUd&^*VP(~Y2Yw$8CVL6U~|+4*|Z1fXPScZ3qTR(>}uZUfuNK1 zVY=7d58Mr+xpnpZm)Nl}@Kle$Og@$X7x`F>Uah^#N(Ug|N#MeVQS|N)Jgpgl^$(g%$J>Yl<=_YqLPSTS4zvgGCsj<119 zAa9Znv%t$Biw{dVzG&|Sz@$>sI#{pzdH{5i8(oLG{d=(-YLs-JHV<^V_F3mx_1V<_ z^kgprnk#rSXqBIb*k^`4GrE?nKGFRkt$nCH+zR#u{W!LX+s*=pVjA|yGy)&*u=-YCC@RxRWI!7<)&@N1O+rKG+eKM+{| zzrj;mu2go^e^L_cClw)uJP8kJsIlK@$l!CZ!5fb7P#nmsMR@qw5ifG6Vi6o$THYWj zhoXKE^dJRMo)R`3+mDzB36Aaui-3%i$7X0eFDNnzj2t@@>_<`~ylTKB`4L{gl9C1w zTZ38*tmd2Y0Z)}Hs%dM3S63@f8a$*7279UUgBn(|sJ9FF7*h?7P2vR`>jwv$r6Ukh z=LMq03zXG)m^C?&7Z^3hnmyJx7J_<;XH?ib#@nhg46iyS4`VAE1UEV3c@vxR#ty~T z@O5M3d9a~2d5tR2-UPEw1?2@NJXyFpkmwqJx-NZ;Dc(Hi^JaCv-wo7YQq zY)94F#Kig=Wy{dmeyZfH66;wK2FLc(QRgMLkEC{#*xr)bB}&G%Rkh!wc3wY8)vaNB za6U*pZ(wI=^VsI3U z!sr@2F`%ftI;T9mT4*qzlwy#hc6q~*V~JyM>}Z5H*x)JVA(4Y%KjkK*YJ(R9J=U)a zPH?gQUy!hRK9s&dUXY!kAibz<@TeyWL8?&E;58~WsK}6q5;en`GC9T@Cgr+d2PIe6 P!S%KZZ+yQv)_?v#QgVSl literal 0 HcmV?d00001 From 3ac0088fa8fa39c6e0f1274df2b7c41aed893d2e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 17:05:07 +0800 Subject: [PATCH 062/174] windows: Fix --console follow Now you can get a live view of the EC console on Windows, too. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 4c99bcc9..afdaef0c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1044,21 +1044,21 @@ impl CrosEc { loop { match cmd.send_command_vec(self) { Ok(data) => { - // EC Buffer is empty. That means we've read everything from the snapshot - // The windows crosecbus driver returns all NULL instead of empty response - if data.is_empty() || data.iter().all(|x| *x == 0) { + // EC Buffer is empty. That means we've read everything from the snapshot. + // The windows crosecbus driver returns all NULL with a leading 0x01 instead of + // an empty response. + if data.is_empty() || data.iter().all(|x| *x == 0 || *x == 1) { debug!("Empty EC response. Stopping console read"); - // Don't read too fast, wait a second before reading more - os_specific::sleep(1_000_000); + // Don't read too fast, wait 100ms before reading more + os_specific::sleep(100_000); EcRequestConsoleSnapshot {}.send_command(self)?; cmd.subcmd = ConsoleReadSubCommand::ConsoleReadRecent as u8; continue; } let utf8 = std::str::from_utf8(&data).unwrap(); - let ascii = utf8 - .replace(|c: char| !c.is_ascii(), "") - .replace(['\0'], ""); + let full_ascii = utf8.replace(|c: char| !c.is_ascii(), ""); + let ascii = full_ascii.replace(['\0'], ""); print!("{}", ascii); } From 53bbee0204d7dd477c709e2ab18bbcaea825e417 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 18:02:10 +0800 Subject: [PATCH 063/174] --sensors: Hide accel if not present Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index bb3e9ea4..6fbee681 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -288,22 +288,25 @@ pub fn print_sensors(ec: &CrosEc) { let accel_1 = ec.read_memory(EC_MEMMAP_ACC_DATA + 2, 0x06).unwrap(); let accel_2 = ec.read_memory(EC_MEMMAP_ACC_DATA + 8, 0x06).unwrap(); - println!("Accelerometers:"); - println!(" Status Bit: {} 0x{:X}", acc_status, acc_status); - println!(" Present: {}", (acc_status & 0x80) > 0); - println!(" Busy: {}", (acc_status & 0x8) > 0); - print!(" Lid Angle: "); - if lid_angle == LID_ANGLE_UNRELIABLE { - println!("Unreliable"); - } else { - println!("{} Deg", lid_angle); + let present = (acc_status & 0x80) > 0; + if present { + println!("Accelerometers:"); + println!(" Status Bit: {} 0x{:X}", acc_status, acc_status); + println!(" Present: {}", present); + println!(" Busy: {}", (acc_status & 0x8) > 0); + print!(" Lid Angle: "); + if lid_angle == LID_ANGLE_UNRELIABLE { + println!("Unreliable"); + } else { + println!("{} Deg", lid_angle); + } + println!(" Sensor 1: {}", AccelData::from(accel_1)); + println!(" Sensor 2: {}", AccelData::from(accel_2)); + // Accelerometers + // Lid Angle: 26 Deg + // Sensor 1: 00.00 X 00.00 Y 00.00 Z + // Sensor 2: 00.00 X 00.00 Y 00.00 Z } - println!(" Sensor 1: {}", AccelData::from(accel_1)); - println!(" Sensor 2: {}", AccelData::from(accel_2)); - // Accelerometers - // Lid Angle: 26 Deg - // Sensor 1: 00.00 X 00.00 Y 00.00 Z - // Sensor 2: 00.00 X 00.00 Y 00.00 Z } pub fn print_thermal(ec: &CrosEc) { From 7c1b9886da471d20140c0c2cab50aba3279d5acb Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 18:05:18 +0800 Subject: [PATCH 064/174] EXAMPLES: Add more Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index b00f20d4..5616dad1 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -128,7 +128,7 @@ Positions: ## Check temperatures and fan speed ``` -> sudo ./target/debug/framework_tool --thermal +> sudo framework_tool --thermal F75303_Local: 43 C F75303_CPU: 44 C F75303_DDR: 39 C @@ -136,13 +136,28 @@ Positions: Fan Speed: 0 RPM ``` -## Check sensors (ALS and G-Sensor) +## Check sensors + +### Ambient Light (Framework 13, Framework 16) ``` -> sudo ./target/debug/framework_tool --sensors +> sudo framework_tool --sensors ALS: 76 Lux ``` +### Accelerometer (Framework 12) +``` +> sudo framework_tool --sensors +ALS: 0 Lux +Accelerometers: + Status Bit: 140 0x8C + Present: true + Busy: true + Lid Angle: 122 Deg + Sensor 1: X: 0.00G Y: 0.84G, Z: 0.52G + Sensor 2: X: -0.03G Y: 0.00G, Z: 1.01G +``` + ## Set custom fan duty/RPM ``` @@ -296,6 +311,45 @@ Battery Status > sudo framework_tool --charge-current-limit 2000 80 ``` +## EC Console + +``` +# Get recent EC console logs and watch for more +> framework_tool.exe --console follow +[53694.741000 Battery 62% (Display 61.1 %) / 3h:18 to empty] +[53715.010000 Battery 62% (Display 61.0 %) / 3h:21 to empty] +[53734.281200 Battery 62% (Display 60.9 %) / 3h:18 to empty] +[53738.037200 Battery 61% (Display 60.9 %) / 3h:6 to empty] +[53752.301500 Battery 61% (Display 60.8 %) / 3h:15 to empty] +``` + +## Keyboard backlight + +``` +# Check current keyboard backlight brightness +> framework_tool.exe --kblight +Keyboard backlight: 5% + +# Set keyboard backlight brightness +# Off +> framework_tool.exe --kblight 0 +# 20% +> framework_tool.exe --kblight 20 +``` + +## RGB LED (Framework Desktop) + +``` +# To set three LEDs to red, green, blue +sudo framework_tool --rgbkbd 0 0xFF0000 0x00FF00 0x0000FF + +# To clear 8 LEDs +sudo framework_tool --rgbkbd 0 0 0 0 0 0 0 0 0 + +# Just turn the 3rd LED red +sudo framework_tool --rgbkbd 2 0xFF0000 +``` + ## Stylus (Framework 12) ``` From d488a1f6b5334ba0c69b40ff6a1192b2dbe875a5 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 18:12:52 +0800 Subject: [PATCH 065/174] --sensors: Prettier formatting of Accel Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 9 +++------ framework_lib/src/power.rs | 14 +++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 5616dad1..fe734abd 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -150,12 +150,9 @@ ALS: 76 Lux > sudo framework_tool --sensors ALS: 0 Lux Accelerometers: - Status Bit: 140 0x8C - Present: true - Busy: true - Lid Angle: 122 Deg - Sensor 1: X: 0.00G Y: 0.84G, Z: 0.52G - Sensor 2: X: -0.03G Y: 0.00G, Z: 1.01G + Lid Angle: 122 Deg + Sensor 1: X=+0.00G Y=+0.84G, Z=+0.52G + Sensor 2: X=-0.03G Y=+0.00G, Z=+1.01G ``` ## Set custom fan duty/RPM diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index 6fbee681..9cc15cd7 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -190,7 +190,7 @@ impl fmt::Display for AccelData { let x = (self.x as f32) / quarter; let y = (self.y as f32) / quarter; let z = (self.z as f32) / quarter; - write!(f, "X: {:.2}G Y: {:.2}G, Z: {:.2}G", x, y, z) + write!(f, "X={:+.2}G Y={:+.2}G, Z={:+.2}G", x, y, z) } } @@ -291,17 +291,17 @@ pub fn print_sensors(ec: &CrosEc) { let present = (acc_status & 0x80) > 0; if present { println!("Accelerometers:"); - println!(" Status Bit: {} 0x{:X}", acc_status, acc_status); - println!(" Present: {}", present); - println!(" Busy: {}", (acc_status & 0x8) > 0); - print!(" Lid Angle: "); + debug!(" Status Bit: {} 0x{:X}", acc_status, acc_status); + debug!(" Present: {}", present); + debug!(" Busy: {}", (acc_status & 0x8) > 0); + print!(" Lid Angle: "); if lid_angle == LID_ANGLE_UNRELIABLE { println!("Unreliable"); } else { println!("{} Deg", lid_angle); } - println!(" Sensor 1: {}", AccelData::from(accel_1)); - println!(" Sensor 2: {}", AccelData::from(accel_2)); + println!(" Sensor 1: {}", AccelData::from(accel_1)); + println!(" Sensor 2: {}", AccelData::from(accel_2)); // Accelerometers // Lid Angle: 26 Deg // Sensor 1: 00.00 X 00.00 Y 00.00 Z From 74281f3a9a2d5773ff049e5e8025902db912296b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 7 May 2025 18:19:48 +0800 Subject: [PATCH 066/174] EXAMPLES: Add system firmware example Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index fe734abd..7b915984 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -2,6 +2,33 @@ ## Check firmware versions +### BIOS (Mainboard, UEFI, EC, PD) + +Example on Framework 13 AMD Ryzen AI 300 Series: + +``` +> framework_tool --versions +Mainboard Hardware + Type: Laptop 13 (AMD Ryzen AI 300 Series) + Revision: MassProduction +UEFI BIOS + Version: 03.00 + Release Date: 03/10/2025 +EC Firmware + Build version: "lilac-3.0.0-1541dc6 2025-05-05 11:31:24 zoid@localhost" + RO Version: "lilac-3.0.0-1541dc6" + RW Version: "lilac-3.0.0-1541dc6" + Current image: RO +PD Controllers + Right (01) + Main: 0.0.0E (Active) + Backup: 0.0.0E + Left (23) + Main: 0.0.0E (Active) + Backup: 0.0.0E +[...] +``` + ### Camera (Framework 12, Framework 13, Framework 16) Example on Framework 12: From 0b2658b8c90e13fbd1b809f59d0d9a08b6076a89 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 8 May 2025 20:06:06 +0800 Subject: [PATCH 067/174] --expansion-bay: Print PCIe config Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 2 ++ framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 37 +++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 14 +++++++++ 4 files changed, 54 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 7b915984..08c5cf8d 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -225,6 +225,8 @@ Expansion Bay Door closed: true Board: DualInterposer Serial Number: FRAXXXXXXXXXXXXXXX + Config: Pcie4x2 + Vendor: SsdHolder ``` ## Check charger and battery status (Framework 12/13/16) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 54b8db52..f1f47fec 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -96,6 +96,7 @@ pub enum EcCommands { GetHwDiag = 0x3E1C, /// Get gpu bay serial GetGpuSerial = 0x3E1D, + GetGpuPcie = 0x3E1E, /// Set gpu bay serial and program structure ProgramGpuEeprom = 0x3E1F, } diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 38cafeb6..1eb588ed 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -1129,6 +1129,43 @@ impl EcRequest for EcRequestGetGpuSerial { } } +#[repr(C, packed)] +pub struct EcRequestGetGpuPcie {} + +#[repr(u8)] +#[derive(Debug, FromPrimitive)] +pub enum GpuPcieConfig { + /// PCIe 8x1 + Pcie8x1 = 0, + /// PCIe 4x1 + Pcie4x1 = 1, + /// PCIe 4x2 + Pcie4x2 = 2, +} + +#[repr(u8)] +#[derive(Debug, FromPrimitive)] +pub enum GpuVendor { + Initializing = 0x00, + FanOnly = 0x01, + GpuAmdR23M = 0x02, + SsdHolder = 0x03, + PcieAccessory = 0x4, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct EcResponseGetGpuPcie { + pub gpu_pcie_config: u8, + pub gpu_vendor: u8, +} + +impl EcRequest for EcRequestGetGpuPcie { + fn command_id() -> EcCommands { + EcCommands::GetGpuPcie + } +} + #[repr(u8)] pub enum SetGpuSerialMagic { /// 7700S config magic value diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index afdaef0c..d77994c5 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1000,6 +1000,20 @@ impl CrosEc { println!(" Serial Number: Unknown"); } + let res = EcRequestGetGpuPcie {}.send_command(self)?; + let config: Option = FromPrimitive::from_u8(res.gpu_pcie_config); + let vendor: Option = FromPrimitive::from_u8(res.gpu_vendor); + if let Some(config) = config { + println!(" Config: {:?}", config); + } else { + println!(" Config: Unknown ({})", res.gpu_pcie_config); + } + if let Some(vendor) = vendor { + println!(" Vendor: {:?}", vendor); + } else { + println!(" Vendor: Unknown ({})", res.gpu_vendor); + } + Ok(()) } From 417ea9e1a29793c6e6c951a2f16aed1cd23cc841 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 8 May 2025 20:05:25 +0800 Subject: [PATCH 068/174] Add --ps2-enable to control ps2 emulation Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 11 +++++++++++ framework_lib/src/chromium_ec/mod.rs | 7 +++++++ framework_lib/src/commandline/clap_std.rs | 6 ++++++ framework_lib/src/commandline/mod.rs | 3 +++ framework_lib/src/commandline/uefi.rs | 21 +++++++++++++++++++++ 6 files changed, 49 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 54b8db52..86b50c92 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -60,6 +60,7 @@ pub enum EcCommands { FlashNotified = 0x3E01, /// Change charge limit ChargeLimitControl = 0x3E03, + DisablePs2Emulation = 0x3E08, /// Get/Set Fingerprint LED brightness FpLedLevelControl = 0x3E0E, /// Get information about the current chassis open/close status diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 38cafeb6..d3bc4d8f 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -1048,6 +1048,17 @@ impl EcRequest for EcRequestChargeLimitControl { /// TODO: Use this pub const EC_CHARGE_LIMIT_RESTORE: u8 = 0x7F; +#[repr(C, packed)] +pub struct EcRequestDisablePs2Emulation { + pub disable: u8, +} + +impl EcRequest<()> for EcRequestDisablePs2Emulation { + fn command_id() -> EcCommands { + EcCommands::DisablePs2Emulation + } +} + #[repr(u8)] #[derive(Debug, FromPrimitive)] pub enum FpLedBrightnessLevel { diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index afdaef0c..2ea843f3 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -596,6 +596,13 @@ impl CrosEc { Ok((kblight.duty / (PWM_MAX_DUTY / 100)) as u8) } + pub fn ps2_emulation_enable(&self, enable: bool) -> EcResult<()> { + EcRequestDisablePs2Emulation { + disable: !enable as u8, + } + .send_command(self) + } + pub fn fan_set_rpm(&self, fan: Option, rpm: u32) -> EcResult<()> { if let Some(fan_idx) = fan { EcRequestPwmSetFanTargetRpmV1 { rpm, fan_idx }.send_command(self) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 76784a5f..9ace517f 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -188,6 +188,11 @@ struct ClapCli { #[arg(long, value_parser=maybe_hex::)] rgbkbd: Vec, + /// Enable/disable PS2 touchpad emulation + #[clap(value_enum)] + #[arg(long)] + ps2_enable: Option, + /// Set tablet mode override #[clap(value_enum)] #[arg(long)] @@ -393,6 +398,7 @@ pub fn parse(args: &[String]) -> Cli { fp_brightness: args.fp_brightness, kblight: args.kblight, rgbkbd: args.rgbkbd, + ps2_enable: args.ps2_enable, tablet_mode: args.tablet_mode, touchscreen_enable: args.touchscreen_enable, stylus_battery: args.stylus_battery, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b2a324f1..5df3e642 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -182,6 +182,7 @@ pub struct Cli { pub fp_brightness: Option>, pub kblight: Option>, pub rgbkbd: Vec, + pub ps2_enable: Option, pub tablet_mode: Option, pub touchscreen_enable: Option, pub stylus_battery: bool, @@ -819,6 +820,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { }); ec.rgbkbd_set_color(start_key, colors.collect()).unwrap(); } + } else if let Some(enable) = args.ps2_enable { + print_err(ec.ps2_emulation_enable(enable)); } else if let Some(None) = args.kblight { print!("Keyboard backlight: "); if let Some(percentage) = print_err(ec.get_keyboard_backlight()) { diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 1fd9e5d8..b02667dd 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -95,6 +95,7 @@ pub fn parse(args: &[String]) -> Cli { fp_brightness: None, kblight: None, rgbkbd: vec![], + ps2_enable: None, tablet_mode: None, touchscreen_enable: None, stylus_battery: false, @@ -361,6 +362,26 @@ pub fn parse(args: &[String]) -> Cli { println!("--rgbkbd requires at least 2 arguments, the start key and an RGB value"); vec![] } + } else if arg == "--ps2-enable" { + cli.ps2_enable = if args.len() > i + 1 { + let enable_arg = &args[i + 1]; + if enable_arg == "true" { + Some(true) + } else if enable_arg == "false" { + Some(false) + } else { + println!( + "Need to provide a value for --ps2-enable: '{}'. {}", + args[i + 1], + "Must be `true` or `false`", + ); + None + } + } else { + println!("Need to provide a value for --tablet-mode. One of: `auto`, `tablet` or `laptop`"); + None + }; + found_an_option = true; } else if arg == "--tablet-mode" { cli.tablet_mode = if args.len() > i + 1 { let tablet_mode_arg = &args[i + 1]; From e4832fe054805b6869147c2b595b766e67bc3569 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 12 May 2025 14:10:00 +0800 Subject: [PATCH 069/174] --get-gpio: Return all GPIOs if no name was passed Another benefit is that you can find out the names of all GPIOs. ``` > sudo framework_tool --get-gpio ec_espi_rst_l 0 runpwrok 1 otg_en 0 ec_i2c0_clk0 0 ec_i2c0_sda0 0 ec_i2c1_clk0 0 ec_i2c1_sda0 0 ec_i2c2_clk0 0 [...] ``` Signed-off-by: Daniel Schaefer --- README.md | 4 +- framework_lib/src/chromium_ec/commands.rs | 55 +++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 35 +++++++++++++++ framework_lib/src/commandline/clap_std.rs | 4 +- framework_lib/src/commandline/mod.rs | 16 ++++--- framework_lib/src/commandline/uefi.rs | 4 +- 6 files changed, 106 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 7c41c4ca..2841b579 100644 --- a/README.md +++ b/README.md @@ -171,8 +171,8 @@ Options: --expansion-bay Show status of the expansion bay (Framework 16 only) --charge-limit [] Get or set max charge limit - --get-gpio - Get GPIO value by name + --get-gpio [] + Get GPIO value by name or all, if no name provided --fp-led-level [] Get or set fingerprint LED brightness level [possible values: high, medium, low, ultra-low, auto] --fp-brightness [] diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 38cafeb6..0c8a6ef2 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -343,6 +343,61 @@ impl EcRequest for EcRequestGpioGetV0 { } } +pub enum GpioGetSubCommand { + ByName = 0, + Count = 1, + Info = 2, +} + +#[repr(C, packed)] +pub struct EcRequestGpioGetV1Count { + pub subcmd: u8, +} + +#[repr(C, packed)] +pub struct EcRequestGpioGetV1ByName { + pub subcmd: u8, + pub name: [u8; 32], +} + +#[repr(C, packed)] +pub struct EcRequestGpioGetV1Info { + pub subcmd: u8, + pub index: u8, +} + +#[repr(C)] +pub struct EcResponseGpioGetV1Info { + pub val: u8, + pub name: [u8; 32], + pub flags: u32, +} + +impl EcRequest for EcRequestGpioGetV1Count { + fn command_id() -> EcCommands { + EcCommands::GpioGet + } + fn command_version() -> u8 { + 1 + } +} +impl EcRequest for EcRequestGpioGetV1ByName { + fn command_id() -> EcCommands { + EcCommands::GpioGet + } + fn command_version() -> u8 { + 1 + } +} +impl EcRequest for EcRequestGpioGetV1Info { + fn command_id() -> EcCommands { + EcCommands::GpioGet + } + fn command_version() -> u8 { + 1 + } +} + #[repr(C, packed)] pub struct EcRequestReboot {} diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index afdaef0c..460e7985 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1228,6 +1228,41 @@ impl CrosEc { Ok(res.val == 1) } + pub fn get_all_gpios(&self) -> EcResult { + let res = EcRequestGpioGetV1Count { + subcmd: GpioGetSubCommand::Count as u8, + } + .send_command(self)?; + let gpio_count = res.val; + + debug!("Found {} GPIOs", gpio_count); + + for index in 0..res.val { + let res = EcRequestGpioGetV1Info { + subcmd: GpioGetSubCommand::Info as u8, + index, + } + .send_command(self)?; + + let name = std::str::from_utf8(&res.name) + .map_err(|utf8_err| { + EcError::DeviceError(format!("Failed to decode GPIO name: {:?}", utf8_err)) + })? + .trim_end_matches(char::from(0)) + .to_string(); + + if log_enabled!(Level::Info) { + // Same output as ectool + println!("{:>32}: {:>2} 0x{:04X}", res.val, name, { res.flags }); + } else { + // Simple output, just name and level high/low + println!("{:<32} {}", name, res.val); + } + } + + Ok(gpio_count) + } + pub fn adc_read(&self, adc_channel: u8) -> EcResult { let res = EcRequestAdcRead { adc_channel }.send_command(self)?; Ok(res.adc_value) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 76784a5f..14147f82 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -165,9 +165,9 @@ struct ClapCli { #[clap(num_args = ..=2)] charge_rate_limit: Vec, - /// Get GPIO value by name + /// Get GPIO value by name or all, if no name provided #[arg(long)] - get_gpio: Option, + get_gpio: Option>, /// Get or set fingerprint LED brightness level #[arg(long)] diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b2a324f1..6676ca07 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -177,7 +177,7 @@ pub struct Cli { pub charge_limit: Option>, pub charge_current_limit: Option<(u32, Option)>, pub charge_rate_limit: Option<(f32, Option)>, - pub get_gpio: Option, + pub get_gpio: Option>, pub fp_led_level: Option>, pub fp_brightness: Option>, pub kblight: Option>, @@ -791,11 +791,15 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } 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) { - println!("{:?}", value); + if let Some(gpio_name) = gpio_name { + print!("GPIO {}: ", gpio_name); + if let Ok(value) = ec.get_gpio(gpio_name) { + println!("{:?}", value); + } else { + println!("Not found"); + } } else { - println!("Not found"); + print_err(ec.get_all_gpios()); } } else if let Some(maybe_led_level) = &args.fp_led_level { print_err(handle_fp_led_level(&ec, *maybe_led_level)); @@ -1120,7 +1124,7 @@ Options: --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 + --get-gpio Get GPIO value by name or all, if no name provided --fp-led-level [] Get or set fingerprint LED brightness level [possible values: high, medium, low] --fp-brightness []Get or set fingerprint LED brightness percentage --kblight [] Set keyboard backlight percentage or get, if no value provided diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 1fd9e5d8..1675c5be 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -329,9 +329,9 @@ pub fn parse(args: &[String]) -> Cli { found_an_option = true; } else if arg == "--get-gpio" { cli.get_gpio = if args.len() > i + 1 { - Some(args[i + 1].clone()) + Some(Some(args[i + 1].clone())) } else { - None + Some(None) }; found_an_option = true; } else if arg == "--kblight" { From 36cd61e1581c05f608f2714fe40a8b69f81c861b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 12 May 2025 15:01:24 +0800 Subject: [PATCH 070/174] --sensors: Get information about sensors On Framework 12: ALS is not shown anymore and now shows which accel is which. ``` > framework_tool --sensors -vv [INFO ] Sensors: 2 [INFO ] Type: Accel [INFO ] Location: Lid [INFO ] Chip: Bma422 [INFO ] Type: Accel [INFO ] Location: Base [INFO ] Chip: Bma422 Accelerometers: Lid Angle: 118 Deg Lid Sensor: X=+0.00G Y=+0.86G, Z=+0.53G Base Sensor: X=-0.03G Y=-0.07G, Z=+1.02G ``` Framework 13 ``` > framework_tool --sensors ALS: 130 Lux ``` Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 8 +- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 128 ++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 37 +++++++ framework_lib/src/power.rs | 71 +++++++++--- framework_lib/src/smbios.rs | 6 +- 6 files changed, 232 insertions(+), 19 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 7b915984..8ed7d1c0 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -173,13 +173,13 @@ ALS: 76 Lux ``` ### Accelerometer (Framework 12) + ``` > sudo framework_tool --sensors -ALS: 0 Lux Accelerometers: - Lid Angle: 122 Deg - Sensor 1: X=+0.00G Y=+0.84G, Z=+0.52G - Sensor 2: X=-0.03G Y=+0.00G, Z=+1.01G + Lid Angle: 118 Deg + Lid Sensor: X=+0.00G Y=+0.86G, Z=+0.53G + Base Sensor: X=-0.03G Y=-0.07G, Z=+1.02G ``` ## Set custom fan duty/RPM diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 54b8db52..e1048ea9 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -33,6 +33,7 @@ pub enum EcCommands { PwmSetKeyboardBacklight = 0x0023, PwmSetFanDuty = 0x0024, PwmSetDuty = 0x0025, + MotionSense = 0x002B, PwmGetDuty = 0x0026, SetTabletMode = 0x0031, AutoFanCtrl = 0x0052, diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 38cafeb6..1dec8b19 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -282,6 +282,134 @@ impl EcRequest for EcRequestPwmGetDuty { } } +#[repr(u8)] +pub enum MotionSenseCmd { + Dump = 0, + Info = 1, +} + +#[repr(C, packed)] +pub struct EcRequestMotionSenseDump { + /// MotionSenseCmd::Dump + pub cmd: u8, + /// Maximal number of sensor the host is expecting. + /// 0 means the host is only interested in the number + /// of sensors controlled by the EC. + pub max_sensor_count: u8, +} + +#[repr(C, packed)] +pub struct EcResponseMotionSenseDump { + /// Flags representing the motion sensor module + pub module_flags: u8, + + /// Number of sensors managed directly by the EC + pub sensor_count: u8, + + /// Sensor data is truncated if response_max is too small + /// for holding all the data. + pub sensor: [u8; 0], +} + +impl EcRequest for EcRequestMotionSenseDump { + fn command_id() -> EcCommands { + EcCommands::MotionSense + } + fn command_version() -> u8 { + 1 + } +} + +#[derive(Debug, FromPrimitive, PartialEq)] +pub enum MotionSenseType { + Accel = 0, + Gyro = 1, + Mag = 2, + Prox = 3, + Light = 4, + Activity = 5, + Baro = 6, + Sync = 7, + LightRgb = 8, +} + +#[derive(Debug, FromPrimitive)] +pub enum MotionSenseLocation { + Base = 0, + Lid = 1, + Camera = 2, +} + +#[derive(Debug, FromPrimitive)] +pub enum MotionSenseChip { + Kxcj9 = 0, + Lsm6ds0 = 1, + Bmi160 = 2, + Si1141 = 3, + Si1142 = 4, + Si1143 = 5, + Kx022 = 6, + L3gd20h = 7, + Bma255 = 8, + Bmp280 = 9, + Opt3001 = 10, + Bh1730 = 11, + Gpio = 12, + Lis2dh = 13, + Lsm6dsm = 14, + Lis2de = 15, + Lis2mdl = 16, + Lsm6ds3 = 17, + Lsm6dso = 18, + Lng2dm = 19, + Tcs3400 = 20, + Lis2dw12 = 21, + Lis2dwl = 22, + Lis2ds = 23, + Bmi260 = 24, + Icm426xx = 25, + Icm42607 = 26, + Bma422 = 27, + Bmi323 = 28, + Bmi220 = 29, + Cm32183 = 30, + Veml3328 = 31, +} + +#[repr(C, packed)] +pub struct EcRequestMotionSenseInfo { + /// MotionSenseCmd::Info + pub cmd: u8, + /// Sensor index + pub sensor_num: u8, +} + +#[repr(C)] +pub struct EcResponseMotionSenseInfo { + /// See enum MotionSenseInfo + pub sensor_type: u8, + /// See enum MotionSenseLocation + pub location: u8, + /// See enum MotionSenseChip + pub chip: u8, +} + +#[derive(Debug)] +pub struct MotionSenseInfo { + pub sensor_type: MotionSenseType, + pub location: MotionSenseLocation, + pub chip: MotionSenseChip, +} + +impl EcRequest for EcRequestMotionSenseInfo { + fn command_id() -> EcCommands { + EcCommands::MotionSense + } + fn command_version() -> u8 { + 1 + } +} + pub enum TabletModeOverride { Default = 0, ForceTablet = 1, diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index afdaef0c..fcda9ecb 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -353,6 +353,43 @@ impl CrosEc { )) } + pub fn motionsense_sensor_count(&self) -> EcResult { + EcRequestMotionSenseDump { + cmd: MotionSenseCmd::Dump as u8, + max_sensor_count: 0, + } + .send_command(self) + .map(|res| res.sensor_count) + } + + pub fn motionsense_sensor_info(&self) -> EcResult> { + let count = self.motionsense_sensor_count()?; + + let mut sensors = vec![]; + for sensor_num in 0..count { + let info = EcRequestMotionSenseInfo { + cmd: MotionSenseCmd::Info as u8, + sensor_num, + } + .send_command(self)?; + sensors.push(MotionSenseInfo { + sensor_type: FromPrimitive::from_u8(info.sensor_type).unwrap(), + location: FromPrimitive::from_u8(info.location).unwrap(), + chip: FromPrimitive::from_u8(info.chip).unwrap(), + }); + } + Ok(sensors) + } + + pub fn motionsense_sensor_list(&self) -> EcResult { + EcRequestMotionSenseDump { + cmd: MotionSenseCmd::Dump as u8, + max_sensor_count: 0, + } + .send_command(self) + .map(|res| res.sensor_count) + } + /// Get current status of Framework Laptop's microphone and camera privacy switches /// [true = device enabled/connected, false = device disabled] pub fn get_privacy_info(&self) -> EcResult<(bool, bool)> { diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index f365d57a..ba89af1d 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -1,5 +1,6 @@ //! Get information about system power (battery, AC, PD ports) +use alloc::format; use alloc::string::String; use alloc::vec; use alloc::vec::Vec; @@ -10,11 +11,11 @@ use log::Level; use crate::ccgx::{AppVersion, Application, BaseVersion, ControllerVersion, MainPdVersions}; use crate::chromium_ec::command::EcRequestRaw; -use crate::chromium_ec::commands::{EcRequestReadPdVersion, EcRequestUsbPdPowerInfo}; -use crate::chromium_ec::{print_err_ref, CrosEc, CrosEcDriver, EcResult}; +use crate::chromium_ec::commands::*; +use crate::chromium_ec::*; use crate::smbios; use crate::smbios::get_platform; -use crate::util::Platform; +use crate::util::{Platform, PlatformFamily}; /// Maximum length of strings in memmap const EC_MEMMAP_TEXT_MAX: u16 = 8; @@ -245,9 +246,15 @@ pub fn print_memmap_version_info(ec: &CrosEc) { } /// Not supported on TGL EC -pub fn get_als_reading(ec: &CrosEc) -> Option { +pub fn get_als_reading(ec: &CrosEc, index: usize) -> Option { let als = ec.read_memory(EC_MEMMAP_ALS, 0x04)?; - Some(u32::from_le_bytes([als[0], als[1], als[2], als[3]])) + let offset = index + 4 * index; + Some(u32::from_le_bytes([ + als[offset], + als[1 + offset], + als[2 + offset], + als[3 + offset], + ])) } pub fn get_accel_data(ec: &CrosEc) -> (AccelData, AccelData, LidAngle) { @@ -274,8 +281,40 @@ pub fn get_accel_data(ec: &CrosEc) -> (AccelData, AccelData, LidAngle) { } pub fn print_sensors(ec: &CrosEc) { - let als_int = get_als_reading(ec).unwrap(); - println!("ALS: {:>4} Lux", als_int); + let mut has_als = false; + let mut accel_locations = vec![]; + + match ec.motionsense_sensor_info() { + Ok(sensors) => { + info!("Sensors: {}", sensors.len()); + for sensor in sensors { + info!(" Type: {:?}", sensor.sensor_type); + info!(" Location: {:?}", sensor.location); + info!(" Chip: {:?}", sensor.chip); + if sensor.sensor_type == MotionSenseType::Light { + has_als = true; + } + if sensor.sensor_type == MotionSenseType::Accel { + accel_locations.push(sensor.location); + } + } + } + Err(EcError::Response(EcResponseStatus::InvalidCommand)) => { + debug!("Motionsense commands not supported") + } + err => _ = print_err(err), + } + + // If we can't detect it based on motionsense + let als_family = matches!( + smbios::get_family(), + Some(PlatformFamily::Framework13) | Some(PlatformFamily::Framework16) + ); + + if has_als || als_family { + let als_int = get_als_reading(ec, 0).unwrap(); + println!("ALS: {:>4} Lux", als_int); + } // bit 4 = busy // bit 7 = present @@ -294,18 +333,22 @@ pub fn print_sensors(ec: &CrosEc) { debug!(" Status Bit: {} 0x{:X}", acc_status, acc_status); debug!(" Present: {}", present); debug!(" Busy: {}", (acc_status & 0x8) > 0); - print!(" Lid Angle: "); + print!(" Lid Angle: "); if lid_angle == LID_ANGLE_UNRELIABLE { println!("Unreliable"); } else { println!("{} Deg", lid_angle); } - println!(" Sensor 1: {}", AccelData::from(accel_1)); - println!(" Sensor 2: {}", AccelData::from(accel_2)); - // Accelerometers - // Lid Angle: 26 Deg - // Sensor 1: 00.00 X 00.00 Y 00.00 Z - // Sensor 2: 00.00 X 00.00 Y 00.00 Z + println!( + " {:<12} {}", + format!("{:?} Sensor:", accel_locations[0]), + AccelData::from(accel_1) + ); + println!( + " {:<12} {}", + format!("{:?} Sensor:", accel_locations[1]), + AccelData::from(accel_2) + ); } } diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index c5cfef29..935f0dc7 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -6,7 +6,7 @@ use std::prelude::v1::*; use std::io::ErrorKind; use crate::util::Config; -pub use crate::util::Platform; +pub use crate::util::{Platform, PlatformFamily}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; use smbioslib::*; @@ -271,6 +271,10 @@ pub fn get_baseboard_version() -> Option { }) } +pub fn get_family() -> Option { + get_platform().and_then(Platform::which_family) +} + pub fn get_platform() -> Option { #[cfg(feature = "uefi")] let mut cached_platform = CACHED_PLATFORM.lock(); From 98810c5f3e94b43e47c28605f01f0310ad8c0a11 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 18:26:47 +0800 Subject: [PATCH 071/174] --ps2-enable: Hide command Normally this command will not do what you think. Unless you know what you're doing, you should not be using it. It works in the following case: ``` framework_tool --ps2-enable true sudo modprobe -r i2c_hid_acpi sudo modprobe i2c_hid_acpi ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 9ace517f..735267d8 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -188,8 +188,8 @@ struct ClapCli { #[arg(long, value_parser=maybe_hex::)] rgbkbd: Vec, - /// Enable/disable PS2 touchpad emulation - #[clap(value_enum)] + /// Control PS2 touchpad emulation (DEBUG COMMAND, if touchpad not working, reboot system) + #[clap(value_enum, hide(true))] #[arg(long)] ps2_enable: Option, From 6db35792397adca663a3ebb8b4c28faf361eaca8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 20:03:50 +0800 Subject: [PATCH 072/174] EXAMPLES: Add --dp-hdmi-info Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 2098d223..3705fa87 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -114,6 +114,17 @@ LED Matrix [...] ``` +### DisplayPort or HDMI Expansion Card + +``` +> framework_tool --dp-hdmi-info +DisplayPort Expansion Card + Serial Number: 11AD1D0030123F17142C0B00 + Active Firmware: 101 (3.0.11.065) + Inactive Firmware: 008 (3.0.11.008) + Operating Mode: MainFw (#2) +``` + ## Check input deck status ### On Framework 12 From 441ec74eff7e78474fa36dcb5a1cfd4086989b53 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 20:12:14 +0800 Subject: [PATCH 073/174] versions: Print DP and HDMI card firmware Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 8 ++++ framework_lib/src/ccgx/hid.rs | 56 ++++++++++++++++------------ framework_lib/src/commandline/mod.rs | 10 +++-- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 3705fa87..d0d94f53 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -123,6 +123,14 @@ DisplayPort Expansion Card Active Firmware: 101 (3.0.11.065) Inactive Firmware: 008 (3.0.11.008) Operating Mode: MainFw (#2) + +# Or +> framework_tool --versions +[...] +DisplayPort Expansion Card + Active Firmware: 101 (3.0.11.065) + Inactive Firmware: 008 (3.0.11.008) + Operating Mode: MainFw (#2) ``` ## Check input deck status diff --git a/framework_lib/src/ccgx/hid.rs b/framework_lib/src/ccgx/hid.rs index 64dcc748..c36aeebb 100644 --- a/framework_lib/src/ccgx/hid.rs +++ b/framework_lib/src/ccgx/hid.rs @@ -132,10 +132,10 @@ fn get_fw_info(device: &HidDevice) -> HidFirmwareInfo { decode_fw_info(&buf) } -pub fn check_ccg_fw_version(device: &HidDevice) { +pub fn check_ccg_fw_version(device: &HidDevice, verbose: bool) { magic_unlock(device); let info = get_fw_info(device); - print_fw_info(&info); + print_fw_info(&info, verbose); } fn decode_fw_info(buf: &[u8]) -> HidFirmwareInfo { @@ -152,13 +152,13 @@ fn decode_fw_info(buf: &[u8]) -> HidFirmwareInfo { info } -fn print_fw_info(info: &HidFirmwareInfo) { +fn print_fw_info(info: &HidFirmwareInfo, verbose: bool) { assert_eq!(info.report_id, ReportIdCmd::E0Read as u8); info!(" Signature: {:X?}", info.signature); // Something's totally off if the signature is invalid if info.signature != [b'C', b'Y'] { - println!("Firmware Signature is invalid."); + error!("Firmware Signature is invalid."); return; } @@ -219,23 +219,33 @@ fn print_fw_info(info: &HidFirmwareInfo) { FwMode::BackupFw => (base_version_1, image_1_valid, base_version_2, image_2_valid), }; - println!( - " Active Firmware: {:03} ({}){}", - active_ver.build_number, - active_ver, - if active_valid { "" } else { " - INVALID!" } - ); - println!( - " Inactive Firmware: {:03} ({}){}", - inactive_ver.build_number, - inactive_ver, - if inactive_valid { "" } else { " - INVALID!" } - ); - println!( - " Operating Mode: {:?} (#{})", - FwMode::try_from(info.operating_mode).unwrap(), - info.operating_mode - ); + if verbose || active_ver != inactive_ver { + println!( + " Active Firmware: {:03} ({}){}", + active_ver.build_number, + active_ver, + if active_valid { "" } else { " - INVALID!" } + ); + println!( + " Inactive Firmware: {:03} ({}){}", + inactive_ver.build_number, + inactive_ver, + if inactive_valid { "" } else { " - INVALID!" } + ); + println!( + " Operating Mode: {:?} (#{})", + FwMode::try_from(info.operating_mode).unwrap(), + info.operating_mode + ); + } else { + println!( + " Active Firmware: {:03} ({}, {:?}){}", + active_ver.build_number, + active_ver, + FwMode::try_from(info.operating_mode).unwrap(), + if active_valid { "" } else { " - INVALID!" } + ); + } } /// Turn CCG3 Expansion Card VID/PID into their name @@ -332,7 +342,7 @@ pub fn flash_firmware(fw_binary: &[u8]) { magic_unlock(&device); let info = get_fw_info(&device); println!("Before Updating"); - print_fw_info(&info); + print_fw_info(&info, true); println!("Updating..."); match info.operating_mode { @@ -369,7 +379,7 @@ pub fn flash_firmware(fw_binary: &[u8]) { wait_to_reappear(&mut api, &filter_devs, sn).expect("Device did not reappear"); println!("After Updating"); - print_fw_info(&info); + print_fw_info(&info, true); } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 6676ca07..0632aa22 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -246,7 +246,7 @@ fn print_audio_card_details() { } #[cfg(feature = "hidapi")] -fn print_dp_hdmi_details() { +fn print_dp_hdmi_details(verbose: bool) { match HidApi::new() { Ok(api) => { for dev_info in find_devices(&api, &[HDMI_CARD_PID, DP_CARD_PID], None) { @@ -264,11 +264,11 @@ fn print_dp_hdmi_details() { dev_info.product_string().unwrap_or(NOT_SET) ); - println!( + debug!( " Serial Number: {}", dev_info.serial_number().unwrap_or(NOT_SET) ); - check_ccg_fw_version(&device); + check_ccg_fw_version(&device, verbose); } } Err(e) => { @@ -521,6 +521,8 @@ fn print_versions(ec: &CrosEc) { if let Some(Platform::Framework12IntelGen13) = smbios::get_platform() { let _ignore_err = touchscreen::print_fw_ver(); } + #[cfg(feature = "hidapi")] + print_dp_hdmi_details(false); } fn print_esrt() { @@ -913,7 +915,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { print_pd_details(&ec); } else if args.dp_hdmi_info { #[cfg(feature = "hidapi")] - print_dp_hdmi_details(); + print_dp_hdmi_details(true); } else if let Some(pd_bin_path) = &args.dp_hdmi_update { #[cfg(feature = "hidapi")] flash_dp_hdmi_card(pd_bin_path); From f4cb313a9ba80fc71f97617c96cdcb6b322239a6 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 22:08:01 +0800 Subject: [PATCH 074/174] --versions: Print protocols in a single line Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 3 +-- framework_lib/src/touchscreen.rs | 10 ++++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index d0d94f53..c341ee13 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -56,8 +56,7 @@ Laptop Webcam Module (2nd Gen) [...] Touchscreen Firmware Version: v7.0.0.5.0.0.0.0 - USI Protocol: false - MPP Protocol: true + Protocols: USI ``` ### Stylus (Framework 12) diff --git a/framework_lib/src/touchscreen.rs b/framework_lib/src/touchscreen.rs index 256df139..de74c9f9 100644 --- a/framework_lib/src/touchscreen.rs +++ b/framework_lib/src/touchscreen.rs @@ -207,8 +207,14 @@ pub trait TouchScreen { println!(" Firmware Version: v{}", ver); let res = self.send_message(0x20, 16, vec![0])?; - println!(" USI Protocol: {:?}", (res[15] & USI_BITMAP) > 0); - println!(" MPP Protocol: {:?}", (res[15] & MPP_BITMAP) > 0); + let mut protocols = vec![]; + if (res[15] & USI_BITMAP) > 0 { + protocols.push("USI"); + } + if (res[15] & MPP_BITMAP) > 0 { + protocols.push("MPP"); + } + println!(" Protocols: {}", protocols.join(", ")); Some(()) } From 48b08f3dba1d699536ec2c8f4495161e398db241 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 22:13:48 +0800 Subject: [PATCH 075/174] --versions: Don't show both EC versions if they're the same Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 4 +--- framework_lib/src/commandline/mod.rs | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index c341ee13..ecaf2de4 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -15,9 +15,7 @@ UEFI BIOS Version: 03.00 Release Date: 03/10/2025 EC Firmware - Build version: "lilac-3.0.0-1541dc6 2025-05-05 11:31:24 zoid@localhost" - RO Version: "lilac-3.0.0-1541dc6" - RW Version: "lilac-3.0.0-1541dc6" + Build version: lilac-3.0.0-1541dc6 2025-05-05 11:31:24 zoid@localhost Current image: RO PD Controllers Right (01) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 0632aa22..b9705dc5 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -373,11 +373,13 @@ fn print_versions(ec: &CrosEc) { println!("EC Firmware"); let ver = print_err(ec.version_info()).unwrap_or_else(|| "UNKNOWN".to_string()); - println!(" Build version: {:?}", ver); + println!(" Build version: {}", ver); if let Some((ro, rw, curr)) = ec.flash_version() { - println!(" RO Version: {:?}", ro); - println!(" RW Version: {:?}", rw); + if ro != rw || log_enabled!(Level::Info) { + println!(" RO Version: {}", ro); + println!(" RW Version: {}", rw); + } print!(" Current image: "); if curr == chromium_ec::EcCurrentImage::RO { println!("RO"); From fefe256f08fe98792f4c5cea0873b65633c6f07c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 22:24:35 +0800 Subject: [PATCH 076/174] --versions: Hide touchpad IC type It's not normally possible to use a different type of touchpad IC on our systems. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 1 - framework_lib/src/touchpad.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index ecaf2de4..1a4a2e85 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -76,7 +76,6 @@ Stylus > framework_tool --versions [...] Touchpad - IC Type: 0239 Firmware Version: v0E07 ``` diff --git a/framework_lib/src/touchpad.rs b/framework_lib/src/touchpad.rs index e767c7e6..35c4fc55 100644 --- a/framework_lib/src/touchpad.rs +++ b/framework_lib/src/touchpad.rs @@ -59,7 +59,7 @@ pub fn print_touchpad_fw_ver() -> Result<(), HidError> { let device = dev_info.open_device(&api).unwrap(); println!("Touchpad"); - println!(" IC Type: {:04X}", pid); + info!(" IC Type: {:04X}", pid); let ver = match pid { 0x0239 => format!("{:04X}", read_239_ver(&device)?), From ec0d113ae80b1f1d18e7f4ab2e84497a3b399bbb Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 22:27:05 +0800 Subject: [PATCH 077/174] --versions: Hide extra CSME versions if they're the same Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 10 ++++++++++ framework_lib/src/commandline/mod.rs | 10 ++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 1a4a2e85..08948ae8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -129,6 +129,16 @@ DisplayPort Expansion Card Operating Mode: MainFw (#2) ``` +### CSME Version (Linux on Intel systems) + +``` +> framework_tool --versions +[...] +CSME + Firmware Version: 0:16.1.32.2473 +[...] +``` + ## Check input deck status ### On Framework 12 diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b9705dc5..b115140c 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -502,10 +502,12 @@ fn print_versions(ec: &CrosEc) { { println!("CSME"); if let Ok(csme) = csme::csme_from_sysfs() { - println!(" Enabled: {}", csme.enabled); - println!(" Version: {}", csme.main_ver); - println!(" Recovery Ver: {}", csme.recovery_ver); - println!(" Original Ver: {}", csme.fitc_ver); + info!(" Enabled: {}", csme.enabled); + println!(" Firmware Version: {}", csme.main_ver); + if csme.main_ver != csme.recovery_ver || csme.main_ver != csme.fitc_ver { + println!(" Recovery Ver: {}", csme.recovery_ver); + println!(" Original Ver: {}", csme.fitc_ver); + } } else { println!(" Unknown"); } From 46114f91ccf52beab5d8897d114d2e729c2e3f70 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 13 May 2025 22:37:35 +0800 Subject: [PATCH 078/174] --versions: Hide PD backup version if it's the same Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 8 +-- framework_lib/src/commandline/mod.rs | 77 ++++++++++++++++++---------- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 08948ae8..33445eeb 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -18,12 +18,8 @@ EC Firmware Build version: lilac-3.0.0-1541dc6 2025-05-05 11:31:24 zoid@localhost Current image: RO PD Controllers - Right (01) - Main: 0.0.0E (Active) - Backup: 0.0.0E - Left (23) - Main: 0.0.0E (Active) - Backup: 0.0.0E + Right (01): 0.0.0E (MainFw) + Left (23): 0.0.0E (MainFw) [...] ``` diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b115140c..22d3c73e 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -399,53 +399,78 @@ fn print_versions(ec: &CrosEc) { if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { let right = &pd_versions.controller01; let left = &pd_versions.controller23; - println!(" Right (01)"); // let active_mode = if let Some(Platform::IntelGen11) = smbios::get_platform() { + if right.main_fw.base != right.backup_fw.base { + println!(" Right (01)"); + println!( + " Main: {}{}", + right.main_fw.base, + active_mode(&right.active_fw, FwMode::MainFw) + ); + println!( + " Backup: {}{}", + right.backup_fw.base, + active_mode(&right.active_fw, FwMode::BackupFw) + ); + } else { + println!( + " Right (01): {} ({:?})", + right.main_fw.base, right.active_fw + ); + } + } else if right.main_fw.app != right.backup_fw.app { println!( - " Main: {}{}", - right.main_fw.base, + " Main: {}{}", + right.main_fw.app, active_mode(&right.active_fw, FwMode::MainFw) ); println!( - " Backup: {}{}", - right.backup_fw.base, + " Backup: {}{}", + right.backup_fw.app, active_mode(&right.active_fw, FwMode::BackupFw) ); } else { println!( - " Main: {}{}", - right.main_fw.app, - active_mode(&right.active_fw, FwMode::MainFw) - ); - println!( - " Backup: {}{}", - right.backup_fw.app, - active_mode(&right.active_fw, FwMode::BackupFw) + " Right (01): {} ({:?})", + right.main_fw.app, right.active_fw ); } - println!(" Left (23)"); if let Some(Platform::IntelGen11) = smbios::get_platform() { + if left.main_fw.base != left.backup_fw.base { + println!(" Left (23)"); + println!( + " Main: {}{}", + left.main_fw.base, + active_mode(&left.active_fw, FwMode::MainFw) + ); + println!( + " Backup: {}{}", + left.backup_fw.base, + active_mode(&left.active_fw, FwMode::BackupFw) + ); + } else { + println!( + " Left (23): {} ({:?})", + left.main_fw.base, left.active_fw + ); + } + } else if left.main_fw.app != left.backup_fw.app { + println!(" Left (23)"); println!( - " Main: {}{}", - left.main_fw.base, + " Main: {}{}", + left.main_fw.app, active_mode(&left.active_fw, FwMode::MainFw) ); println!( - " Backup: {}{}", - left.backup_fw.base, + " Backup: {}{}", + left.backup_fw.app, active_mode(&left.active_fw, FwMode::BackupFw) ); } else { println!( - " Main: {}{}", - left.main_fw.app, - active_mode(&left.active_fw, FwMode::MainFw) - ); - println!( - " Backup: {}{}", - left.backup_fw.app, - active_mode(&left.active_fw, FwMode::BackupFw) + " Left (23): {} ({:?})", + left.main_fw.app, left.active_fw ); } } else if let Ok(pd_versions) = power::read_pd_version(ec) { From c75cff2a2c04ac6a85b5a84536d9281e1376bba4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 9 May 2025 15:04:50 +0800 Subject: [PATCH 079/174] Allow keyboard remapping Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 25 +++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 14 +++++++++++++ 3 files changed, 40 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index c6c68385..e009a9ec 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -62,6 +62,7 @@ pub enum EcCommands { /// Change charge limit ChargeLimitControl = 0x3E03, DisablePs2Emulation = 0x3E08, + UpdateKeyboardMatrix = 0x3E0C, /// Get/Set Fingerprint LED brightness FpLedLevelControl = 0x3E0E, /// Get information about the current chassis open/close status diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 2e72c947..4a8fd2bf 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -927,6 +927,31 @@ impl EcRequest for EcRequestFlashNotify { } } +#[repr(C, packed)] +pub struct KeyboardMatrixMap { + pub row: u8, + pub col: u8, + pub scanset: u16, +} +#[repr(C, packed)] +pub struct EcRequestUpdateKeyboardMatrix { + pub num_items: u32, + pub write: u32, + pub scan_update: [KeyboardMatrixMap; 1], +} +#[repr(C, packed)] +pub struct EcResponseUpdateKeyboardMatrix { + pub num_items: u32, + pub write: u32, + pub scan_update: [KeyboardMatrixMap; 32], +} + +impl EcRequest for EcRequestUpdateKeyboardMatrix { + fn command_id() -> EcCommands { + EcCommands::UpdateKeyboardMatrix + } +} + #[repr(C, packed)] pub struct EcRequestChassisOpenCheck {} diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 9ba22117..9bd367a0 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -390,6 +390,20 @@ impl CrosEc { .map(|res| res.sensor_count) } + pub fn remap_caps_to_ctrl(&self) -> EcResult<()> { + self.remap_key(6, 15, 0x0014) + } + + pub fn remap_key(&self, row: u8, col: u8, scanset: u16) -> EcResult<()> { + let _current_matrix = EcRequestUpdateKeyboardMatrix { + num_items: 1, + write: 1, + scan_update: [KeyboardMatrixMap { row, col, scanset }], + } + .send_command(self)?; + Ok(()) + } + /// Get current status of Framework Laptop's microphone and camera privacy switches /// [true = device enabled/connected, false = device disabled] pub fn get_privacy_info(&self) -> EcResult<(bool, bool)> { From 51776de85c1543c399889be1130f5b2881d58958 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 9 May 2025 23:50:06 +0800 Subject: [PATCH 080/174] Add --remap-key command Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 36 +++++++++++++++++++++++ framework_lib/src/commandline/clap_std.rs | 14 +++++++++ framework_lib/src/commandline/mod.rs | 17 ++++++----- framework_lib/src/commandline/uefi.rs | 1 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 2098d223..c36da6e2 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -382,3 +382,39 @@ sudo framework_tool --rgbkbd 2 0xFF0000 > sudo framework_tool --stylus-battery Stylus Battery Strength: 77% ``` + +## Remap keyboard + +Note that the keyboard matrix on Framework 12 and Framework 13 are +different. +The scancodes are the same. + +- Left-Ctrl 0x0014 +- Left-Alt 0x0014 +- Tab 0x0058 + +### Framework 12 + +``` +# Remap capslock key as left-ctrl +> framework_tool --remap-key 6 15 0x0014 + +# Swap left-ctrl and alt +> framework_tool --remap-key 1 14 0x0011 +> framework_tool --remap-key 6 13 0x0014 +``` + +### Framework 13 + +``` +# Remap capslock key as left-ctrl +> framework_tool --remap-key 4 4 0x0014 + +# Swap left-ctrl and alt +> framework_tool --remap-key 1 12 0x0011 +> framework_tool --remap-key 1 3 0x0014 +``` + +### Framework 16 + +It's not controlled by the EC, use https://keyboard.frame.work. diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 4b17646c..aabd5468 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -181,6 +181,11 @@ struct ClapCli { #[arg(long)] kblight: Option>, + /// Set keyboard backlight percentage or get, if no value provided + #[arg(long, value_parser=maybe_hex::)] + #[clap(num_args = 3)] + remap_key: Vec, + /// Set the color of to . Multiple colors for adjacent keys can be set at once. /// [ ...] /// Example: 0 0xFF000 0x00FF00 0x0000FF @@ -338,6 +343,14 @@ pub fn parse(args: &[String]) -> Cli { 1 => Some((args.charge_rate_limit[0], None)), _ => None, }; + let remap_key = match args.remap_key.len() { + 3 => Some(( + args.remap_key[0] as u8, + args.remap_key[1] as u8, + args.remap_key[2], + )), + _ => None, + }; Cli { verbosity: args.verbosity.log_level_filter(), @@ -397,6 +410,7 @@ pub fn parse(args: &[String]) -> Cli { fp_led_level: args.fp_led_level, fp_brightness: args.fp_brightness, kblight: args.kblight, + remap_key, rgbkbd: args.rgbkbd, ps2_enable: args.ps2_enable, tablet_mode: args.tablet_mode, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ce11646d..0b9aa39d 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -181,6 +181,7 @@ pub struct Cli { pub fp_led_level: Option>, pub fp_brightness: Option>, pub kblight: Option>, + pub remap_key: Option<(u8, u8, u16)>, pub rgbkbd: Vec, pub ps2_enable: Option, pub tablet_mode: Option, @@ -809,6 +810,15 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(Some(kblight)) = args.kblight { assert!(kblight <= 100); ec.set_keyboard_backlight(kblight); + } else if let Some(None) = args.kblight { + print!("Keyboard backlight: "); + if let Some(percentage) = print_err(ec.get_keyboard_backlight()) { + println!("{}%", percentage); + } else { + println!("Unable to tell"); + } + } else if let Some((row, col, scanset)) = args.remap_key { + print_err(ec.remap_key(row, col, scanset)); } else if !args.rgbkbd.is_empty() { if args.rgbkbd.len() < 2 { println!( @@ -826,13 +836,6 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } } else if let Some(enable) = args.ps2_enable { print_err(ec.ps2_emulation_enable(enable)); - } else if let Some(None) = args.kblight { - print!("Keyboard backlight: "); - if let Some(percentage) = print_err(ec.get_keyboard_backlight()) { - println!("{}%", percentage); - } else { - println!("Unable to tell"); - } } else if let Some(tablet_arg) = &args.tablet_mode { let mode = match tablet_arg { TabletModeArg::Auto => TabletModeOverride::Default, diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 63924450..097c006f 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -94,6 +94,7 @@ pub fn parse(args: &[String]) -> Cli { fp_led_level: None, fp_brightness: None, kblight: None, + remap_key: None, rgbkbd: vec![], ps2_enable: None, tablet_mode: None, From 6ebb5de044bc5efc0e24c0f6417348a48facf48b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 10:10:07 +0800 Subject: [PATCH 081/174] Build Rufus compatible ISO Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 13 +++++++++++-- framework_uefi/Makefile | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92f5299d..3f8dc7d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,12 +74,21 @@ jobs: name: framework.efi path: framework_uefi/build/x86_64-unknown-uefi/boot.efi - - name: Install mtools to build ESP (Linux) - run: sudo apt-get install -y mtools + - name: Install mtools to build ESP and ISO (Linux) + run: sudo apt-get install -y mtools genisoimage - name: Build ESP (Linux) run: make -C framework_uefi + - name: Build ISO (Linux) + run: make -C framework_uefi iso + + - name: Upload UEFI App ISO + uses: actions/upload-artifact@v4 + with: + name: UEFI-Shell-fwk.iso + path: framework_uefi/build/x86_64-unknown-uefi/UEFI-Shell-fwk.iso + build-windows: name: Build Windows runs-on: windows-2022 diff --git a/framework_uefi/Makefile b/framework_uefi/Makefile index 6963d8f2..a571ba17 100644 --- a/framework_uefi/Makefile +++ b/framework_uefi/Makefile @@ -15,6 +15,8 @@ QEMU_FLAGS=\ all: $(BUILD)/boot.img +iso: $(BUILD)/UEFI-Shell-fwk.iso + clean: rm -r $(BUILD) @@ -39,6 +41,19 @@ $(BUILD)/efi.img: $(BUILD)/boot.efi mcopy -i $@.tmp $< ::efi/boot/bootx64.efi mv $@.tmp $@ +$(BUILD)/shellx64.efi: + wget https://github.com/pbatard/UEFI-Shell/releases/download/24H2/shellx64.efi -O $@ + +$(BUILD)/UEFI-Shell-fwk.iso: $(BUILD)/boot.efi $(BUILD)/shellx64.efi + mkdir -p $(BUILD)/$@.tmp/efi/boot + cp $(BUILD)/boot.efi $(BUILD)/$@.tmp/efi/boot/fwk.efi + cp $(BUILD)/shellx64.efi $(BUILD)/$@.tmp/efi/boot/bootx64.efi + genisoimage -v \ + -V "UEFI SHELL with fwk.efi" \ + -JR \ + -o "$(BUILD)/UEFI-Shell-fwk.iso" \ + $(BUILD)/$@.tmp + $(BUILD)/boot.efi: ../Cargo.lock $(SRC_DIR)/Cargo.toml $(SRC_DIR)/src/* mkdir -p $(BUILD) cargo rustc \ From 7c075b1803008d5e4dca50c787badc5c73c1be03 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 14 May 2025 18:26:05 +0800 Subject: [PATCH 082/174] Allow installing with cargo install ``` > cargo install --path framework_tool > which framework_tool /home/zoid/.cargo/bin/framework_tool ``` Signed-off-by: Daniel Schaefer --- README.md | 12 +++++------- framework_tool/Cargo.toml | 4 ++++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2841b579..7fbcea32 100644 --- a/README.md +++ b/README.md @@ -126,14 +126,12 @@ make -C framework_uefi ls -l framework_uefi/build/x86_64-unknown-uefi/boot.efi ``` -Building on Windows or in general with fewer features: +## Install local package -```ps1 -# Build the library and tool -cargo build - -# Running the tool -cargo run +``` +> cargo install --path framework_tool +> which framework_tool +/home/zoid/.cargo/bin/framework_tool ``` ## Running diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 7cdfd1f6..92cc7d04 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -3,6 +3,10 @@ name = "framework_tool" version = "0.4.1" edition = "2021" +[[bin]] +name = "framework_tool" +path = "src/main.rs" + [dependencies.framework_lib] path = "../framework_lib" From f69b4fc1c43ca2ddc2b8f42cfc799d4e00d92adb Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 14 May 2025 22:43:04 +0800 Subject: [PATCH 083/174] bump version to 0.4.2 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 0f0b1984..ccb8e0d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,7 +408,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.4.1" +version = "0.4.2" dependencies = [ "built", "clap", @@ -440,7 +440,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.4.1" +version = "0.4.2" dependencies = [ "embed-resource", "framework_lib", @@ -451,7 +451,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.4.1" +version = "0.4.2" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index f0477678..28a58354 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.4.1" +version = "0.4.2" 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 92cc7d04..d31040db 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.4.1" +version = "0.4.2" edition = "2021" [[bin]] diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index d0ad0cc4..9e2a185f 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.4.1" +version = "0.4.2" edition = "2021" # Minimum Supported Rust Version rust-version = "1.74" From cf24bdd6f5b0246a639229e4e5814b9c73403b2f Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 14 May 2025 23:03:53 +0800 Subject: [PATCH 084/174] Add more fields in Cargo.toml What's required based on https://doc.rust-lang.org/cargo/reference/publishing.html#before-publishing-a-new-crate Signed-off-by: Daniel Schaefer --- framework_lib/Cargo.toml | 5 +++++ framework_tool/Cargo.toml | 5 +++++ framework_uefi/Cargo.toml | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 28a58354..5e19867a 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,11 @@ [package] name = "framework_lib" version = "0.4.2" +description = "Library to control Framework Computer systems" +homepage = "https://github.com/FrameworkComputer/framework-system" +repository = "https://github.com/FrameworkComputer/framework-system" +readme = "README.md" +license = "BSD-3-Clause" 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 d31040db..8da6a35c 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,11 @@ [package] name = "framework_tool" version = "0.4.2" +description = "Tool to control Framework Computer systems" +homepage = "https://github.com/FrameworkComputer/framework-system" +repository = "https://github.com/FrameworkComputer/framework-system" +readme = "README.md" +license = "BSD-3-Clause" edition = "2021" [[bin]] diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index 9e2a185f..852fa7ec 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,11 @@ [package] name = "framework_uefi" version = "0.4.2" +description = "UEFI Tool to control Framework Computer systems" +homepage = "https://github.com/FrameworkComputer/framework-system" +repository = "https://github.com/FrameworkComputer/framework-system" +readme = "README.md" +license = "BSD-3-Clause" edition = "2021" # Minimum Supported Rust Version rust-version = "1.74" From 7b010b65c3673ed8347b736a0e4fc71028dc4b1d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 14 May 2025 23:46:00 +0800 Subject: [PATCH 085/174] Remove vendored guid_macros crate Replace with guid-create from crates.io Signed-off-by: Daniel Schaefer --- Cargo.lock | 11 +- Cargo.toml | 3 - framework_lib/Cargo.toml | 2 +- framework_lib/src/capsule.rs | 10 +- framework_lib/src/commandline/mod.rs | 9 +- framework_lib/src/esrt/mod.rs | 193 +++++++++++++++++---------- framework_lib/src/guid.rs | 173 ------------------------ framework_lib/src/lib.rs | 2 - guid_macros/Cargo.toml | 23 ---- guid_macros/src/lib.rs | 101 -------------- 10 files changed, 138 insertions(+), 389 deletions(-) delete mode 100644 framework_lib/src/guid.rs delete mode 100644 guid_macros/Cargo.toml delete mode 100644 guid_macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ccb8e0d1..2f9f5c9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -415,7 +415,7 @@ dependencies = [ "clap-num", "clap-verbosity-flag", "env_logger", - "guid_macros", + "guid-create", "hidapi", "lazy_static", "libc", @@ -581,12 +581,11 @@ dependencies = [ ] [[package]] -name = "guid_macros" -version = "0.11.0" +name = "guid-create" +version = "0.4.1" +source = "git+https://github.com/FrameworkComputer/guid-create?branch=no-rand#84c3ad2e8b64a12beebb460804a65da55434cfd9" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", + "winapi", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 41f48e81..d8108e1b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,6 @@ members = [ "framework_uefi", # Catchall library that we'll probably want to split up further "framework_lib", - # Fork of https://github.com/rust-osdev/uefi-rs/blob/main/uefi-macros - # To avoid pulling in UEFI dependencies when building for an OS - "guid_macros", ] # Don't build UEFI by default. Needs special cargo invocation diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 5e19867a..8d362370 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -31,9 +31,9 @@ num-traits = { version = "0.2", default-features = false } log = { version = "0.4", default-features = true } spin = { version = "0.9.8" } no-std-compat = { version = "0.4.1", features = [ "alloc" ] } -guid_macros = { path = "../guid_macros" } hidapi = { version = "2.6.3", features = [ "windows-native" ], optional = true } rusb = { version = "0.9.4", optional = true } +guid-create = { git = "https://github.com/FrameworkComputer/guid-create", branch = "no-rand", default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] uefi = { version = "0.20", features = ["alloc"] } diff --git a/framework_lib/src/capsule.rs b/framework_lib/src/capsule.rs index 971db631..e2dff320 100644 --- a/framework_lib/src/capsule.rs +++ b/framework_lib/src/capsule.rs @@ -11,16 +11,12 @@ use std::prelude::v1::*; use core::prelude::rust_2021::derive; +use guid_create::Guid; #[cfg(not(feature = "uefi"))] use std::fs::File; #[cfg(not(feature = "uefi"))] use std::io::prelude::*; -#[cfg(not(feature = "uefi"))] -use crate::guid::Guid; -#[cfg(feature = "uefi")] -use uefi::Guid; - #[derive(Copy, Clone, Debug, Eq, PartialEq)] #[repr(C)] pub struct EfiCapsuleHeader { @@ -209,14 +205,14 @@ mod tests { let data = fs::read(capsule_path).unwrap(); let cap = parse_capsule_header(&data).unwrap(); let expected_header = EfiCapsuleHeader { - capsule_guid: esrt::WINUX_GUID, + capsule_guid: Guid::from(esrt::WINUX_GUID), header_size: 28, flags: 65536, capsule_image_size: 676898, }; assert_eq!(cap, expected_header); - assert_eq!(cap.capsule_guid, esrt::WINUX_GUID); + assert_eq!(cap.capsule_guid, Guid::from(esrt::WINUX_GUID)); let ux_header = parse_ux_header(&data); assert_eq!( ux_header, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b137f511..0477cbbe 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -7,6 +7,7 @@ use alloc::format; use alloc::string::String; use alloc::string::ToString; use alloc::vec::Vec; +use guid_create::{Guid, GUID}; use log::Level; use num_traits::FromPrimitive; @@ -494,7 +495,7 @@ fn print_versions(ec: &CrosEc) { let mut right_retimer: Option = None; if let Some(esrt) = esrt::get_esrt() { for entry in &esrt.entries { - match entry.fw_class { + match GUID::from(entry.fw_class) { esrt::TGL_RETIMER01_GUID | esrt::ADL_RETIMER01_GUID | esrt::RPL_RETIMER01_GUID @@ -700,7 +701,7 @@ fn compare_version(device: Option, version: String, ec: &Cro if let Some(esrt) = esrt::get_esrt() { for entry in &esrt.entries { - match entry.fw_class { + match GUID::from(entry.fw_class) { esrt::TGL_RETIMER01_GUID | esrt::ADL_RETIMER01_GUID | esrt::RPL_RETIMER01_GUID => { if device == Some(HardwareDeviceType::RTM01) { println!("Comparing RTM01 version {:?}", entry.fw_version.to_string()); @@ -1032,7 +1033,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); if let Some(header) = analyze_capsule(&data) { - if header.capsule_guid == esrt::WINUX_GUID { + if header.capsule_guid == Guid::from(esrt::WINUX_GUID) { let ux_header = capsule::parse_ux_header(&data); if let Some(dump_path) = &args.dump { // TODO: Better error handling, rather than just panicking @@ -1434,7 +1435,7 @@ pub fn analyze_capsule(data: &[u8]) -> Option { let header = capsule::parse_capsule_header(data)?; capsule::print_capsule_header(&header); - match header.capsule_guid { + match GUID::from(header.capsule_guid) { esrt::TGL_BIOS_GUID => { println!(" Type: Framework TGL Insyde BIOS"); } diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 16770df5..74221196 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -14,13 +14,8 @@ use log::{debug, error, info, trace}; use std::prelude::v1::*; -#[cfg(not(feature = "uefi"))] -use crate::guid::Guid; use core::prelude::v1::derive; -#[cfg(not(feature = "uefi"))] -use guid_macros::guid; -#[cfg(feature = "uefi")] -use uefi::{guid, Guid}; +use guid_create::{Guid, GUID}; #[cfg(target_os = "linux")] use std::fs; @@ -38,66 +33,115 @@ use std::os::fd::AsRawFd; #[cfg(target_os = "freebsd")] use std::os::unix::fs::OpenOptionsExt; -/// Decode from GUID string version -/// -/// # Examples -/// ``` -/// use framework_lib::esrt::*; -/// use framework_lib::guid::*; -/// -/// let valid_guid = Guid::from_values(0xA9C91B0C, 0xC0B8, 0x463D, 0xA7DA, 0xA5D6EC646333); -/// // Works with lower-case -/// let guid = guid_from_str("a9c91b0c-c0b8-463d-a7da-a5d6ec646333"); -/// assert_eq!(guid, Some(valid_guid)); -/// // And upper-case -/// let guid = guid_from_str("A9C91B0C-C0B8-463D-A7DA-A5D6EC646333"); -/// assert_eq!(guid, Some(valid_guid)); -/// -/// let guid = guid_from_str("invalid-guid"); -/// assert_eq!(guid, None); -/// ``` -pub fn guid_from_str(string: &str) -> Option { - let string = string.strip_suffix('\n').unwrap_or(string); - let sections: Vec<&str> = string.split('-').collect(); - let time_low = u32::from_str_radix(sections[0], 16).ok()?; - let time_mid = u16::from_str_radix(sections[1], 16).ok()?; - let time_high_and_version = u16::from_str_radix(sections[2], 16).ok()?; - let clock_seq_and_variant = u16::from_str_radix(sections[3], 16).ok()?; - let node = u64::from_str_radix(sections[4], 16).ok()?; - - Some(Guid::from_values( - time_low, - time_mid, - time_high_and_version, - clock_seq_and_variant, - node, - )) -} - -pub const TGL_BIOS_GUID: Guid = guid!("b3bdb2e4-c5cb-5c1b-bdc3-e6fc132462ff"); -pub const ADL_BIOS_GUID: Guid = guid!("a30a8cf3-847f-5e59-bd59-f9ec145c1a8c"); -pub const RPL_BIOS_GUID: Guid = guid!("13fd4ed2-cba9-50ba-bb91-aece0acb4cc3"); -pub const MTL_BIOS_GUID: Guid = guid!("72cecb9b-2b37-5ec2-a9ff-c739aabaadf3"); - -pub const TGL_RETIMER01_GUID: Guid = guid!("832af090-2ef9-7c47-8f6d-b405c8c7f156"); -pub const TGL_RETIMER23_GUID: Guid = guid!("20ef4108-6c64-d049-b6de-11ee35980b8f"); -pub const ADL_RETIMER01_GUID: Guid = guid!("a9c91b0c-c0b8-463d-a7da-a5d6ec646333"); -pub const ADL_RETIMER23_GUID: Guid = guid!("ba2e4e6e-3b0c-4f25-8a59-4c553fc86ea2"); -pub const RPL_RETIMER01_GUID: Guid = guid!("0c42b824-818f-428f-8687-5efcaf059bea"); -pub const RPL_RETIMER23_GUID: Guid = guid!("268ccbde-e087-420b-bf82-2212bd3f9bfc"); -pub const MTL_RETIMER01_GUID: Guid = guid!("c57fd615-2ac9-4154-bf34-4dc715344408"); -pub const MTL_RETIMER23_GUID: Guid = guid!("bdffce36-809c-4fa6-aecc-54536922f0e0"); - -pub const FL16_BIOS_GUID: Guid = guid!("6ae76af1-c002-5d64-8e18-658d205acf34"); -pub const AMD13_BIOS_GUID: Guid = guid!("b5f7dcc1-568c-50f8-a4dd-e39d1f93fda1"); -pub const RPL_CSME_GUID: Guid = guid!("865d322c-6ac7-4734-b43e-55db5a557d63"); -pub const MTL_CSME_GUID: Guid = guid!("32d8d677-eebc-4947-8f8a-0693a45240e5"); +pub const TGL_BIOS_GUID: GUID = GUID::build_from_components( + 0xb3bdb2e4, + 0xc5cb, + 0x5c1b, + &[0xbd, 0xc3, 0xe6, 0xfc, 0x13, 0x24, 0x62, 0xff], +); +pub const ADL_BIOS_GUID: GUID = GUID::build_from_components( + 0xa30a8cf3, + 0x847f, + 0x5e59, + &[0xbd, 0x59, 0xf9, 0xec, 0x14, 0x5c, 0x1a, 0x8c], +); +pub const RPL_BIOS_GUID: GUID = GUID::build_from_components( + 0x13fd4ed2, + 0xcba9, + 0x50ba, + &[0xbb, 0x91, 0xae, 0xce, 0x0a, 0xcb, 0x4c, 0xc3], +); +pub const MTL_BIOS_GUID: GUID = GUID::build_from_components( + 0x72cecb9b, + 0x2b37, + 0x5ec2, + &[0xa9, 0xff, 0xc7, 0x39, 0xaa, 0xba, 0xad, 0xf3], +); + +pub const TGL_RETIMER01_GUID: GUID = GUID::build_from_components( + 0x832af090, + 0x2ef9, + 0x7c47, + &[0x8f, 0x6d, 0xb4, 0x05, 0xc8, 0xc7, 0xf1, 0x56], +); +pub const TGL_RETIMER23_GUID: GUID = GUID::build_from_components( + 0x20ef4108, + 0x6c64, + 0xd049, + &[0xb6, 0xde, 0x11, 0xee, 0x35, 0x98, 0x0b, 0x8f], +); +pub const ADL_RETIMER01_GUID: GUID = GUID::build_from_components( + 0xa9c91b0c, + 0xc0b8, + 0x463d, + &[0xa7, 0xda, 0xa5, 0xd6, 0xec, 0x64, 0x63, 0x33], +); +pub const ADL_RETIMER23_GUID: GUID = GUID::build_from_components( + 0xba2e4e6e, + 0x3b0c, + 0x4f25, + &[0x8a, 0x59, 0x4c, 0x55, 0x3f, 0xc8, 0x6e, 0xa2], +); +pub const RPL_RETIMER01_GUID: GUID = GUID::build_from_components( + 0x0c42b824, + 0x818f, + 0x428f, + &[0x86, 0x87, 0x5e, 0xfc, 0xaf, 0x05, 0x9b, 0xea], +); +pub const RPL_RETIMER23_GUID: GUID = GUID::build_from_components( + 0x268ccbde, + 0xe087, + 0x420b, + &[0xbf, 0x82, 0x22, 0x12, 0xbd, 0x3f, 0x9b, 0xfc], +); +pub const MTL_RETIMER01_GUID: GUID = GUID::build_from_components( + 0xc57fd615, + 0x2ac9, + 0x4154, + &[0xbf, 0x34, 0x4d, 0xc7, 0x15, 0x34, 0x44, 0x08], +); +pub const MTL_RETIMER23_GUID: GUID = GUID::build_from_components( + 0xbdffce36, + 0x809c, + 0x4fa6, + &[0xae, 0xcc, 0x54, 0x53, 0x69, 0x22, 0xf0, 0xe0], +); + +pub const FL16_BIOS_GUID: GUID = GUID::build_from_components( + 0x6ae76af1, + 0xc002, + 0x5d64, + &[0x8e, 0x18, 0x65, 0x8d, 0x20, 0x5a, 0xcf, 0x34], +); +pub const AMD13_BIOS_GUID: GUID = GUID::build_from_components( + 0xb5f7dcc1, + 0x568c, + 0x50f8, + &[0xa4, 0xdd, 0xe3, 0x9d, 0x1f, 0x93, 0xfd, 0xa1], +); +pub const RPL_CSME_GUID: GUID = GUID::build_from_components( + 0x865d322c, + 0x6ac7, + 0x4734, + &[0xb4, 0x3e, 0x55, 0xdb, 0x5a, 0x55, 0x7d, 0x63], +); +pub const MTL_CSME_GUID: GUID = GUID::build_from_components( + 0x32d8d677, + 0xeebc, + 0x4947, + &[0x8f, 0x8a, 0x06, 0x93, 0xa4, 0x52, 0x40, 0xe5], +); // In EDK2 // Handled by MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.c // Defined by MdePkg/Include/IndustryStandard/WindowsUxCapsule.h /// gWindowsUxCapsuleGuid from MdePkg/MdePkg.dec -pub const WINUX_GUID: Guid = guid!("3b8c8162-188c-46a4-aec9-be43f1d65697"); +pub const WINUX_GUID: GUID = GUID::build_from_components( + 0x3b8c8162, + 0x188c, + 0x46a4, + &[0xae, 0xc9, 0xbe, 0x43, 0xf1, 0xd6, 0x56, 0x97], +); #[derive(Debug)] pub enum FrameworkGuidKind { @@ -105,6 +149,7 @@ pub enum FrameworkGuidKind { AdlBios, RplBios, MtlBios, + Fw12RplBios, TglRetimer01, TglRetimer23, AdlRetimer01, @@ -122,7 +167,7 @@ pub enum FrameworkGuidKind { } pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { - match *guid { + match GUID::from(*guid) { TGL_BIOS_GUID => FrameworkGuidKind::TglBios, ADL_BIOS_GUID => FrameworkGuidKind::AdlBios, RPL_BIOS_GUID => FrameworkGuidKind::RplBios, @@ -288,8 +333,9 @@ fn esrt_from_sysfs(dir: &Path) -> io::Result { let last_attempt_version = fs::read_to_string(path.join("last_attempt_version"))?; let last_attempt_status = fs::read_to_string(path.join("last_attempt_status"))?; let esrt = EsrtResourceEntry { - // TODO: Parse GUID - fw_class: guid_from_str(&fw_class).expect("Kernel provided wrong value"), + fw_class: Guid::from( + GUID::parse(fw_class.trim()).expect("Kernel provided wrong value"), + ), fw_type: fw_type .trim() .parse::() @@ -358,8 +404,8 @@ pub fn get_esrt() -> Option { let guid_str = caps.get(1).unwrap().as_str().to_string(); let ver_str = caps.get(2).unwrap().as_str().to_string(); - let guid = guid_from_str(&guid_str).unwrap(); - let guid_kind = match_guid_kind(&guid); + let guid = GUID::parse(guid_str.trim()).expect("Kernel provided wrong value"); + let guid_kind = match_guid_kind(&Guid::from(guid)); let ver = u32::from_str_radix(&ver_str, 16).unwrap(); debug!("ESRT Entry {}", i); debug!(" Name: {:?}", guid_kind); @@ -379,7 +425,7 @@ pub fn get_esrt() -> Option { // TODO: The missing fields are present in Device Manager // So there must be a way to get at them let esrt = EsrtResourceEntry { - fw_class: guid, + fw_class: Guid::from(guid), fw_type, fw_version: ver, // TODO: Not exposed by windows @@ -428,7 +474,7 @@ pub fn get_esrt() -> Option { let mut buf: Vec = Vec::new(); let mut table = EfiGetTableIoc { buf: std::ptr::null_mut(), - uuid: SYSTEM_RESOURCE_TABLE_GUID.to_bytes(), + uuid: SYSTEM_RESOURCE_TABLE_GUID_BYTES, buf_len: 0, table_len: 0, }; @@ -448,7 +494,15 @@ pub fn get_esrt() -> Option { } /// gEfiSystemResourceTableGuid from MdePkg/MdePkg.dec -pub const SYSTEM_RESOURCE_TABLE_GUID: Guid = guid!("b122a263-3661-4f68-9929-78f8b0d62180"); +pub const SYSTEM_RESOURCE_TABLE_GUID: GUID = GUID::build_from_components( + 0xb122a263, + 0x3661, + 0x4f68, + &[0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80], +); +pub const SYSTEM_RESOURCE_TABLE_GUID_BYTES: [u8; 16] = [ + 0xb1, 0x22, 0xa2, 0x63, 0x36, 0x61, 0x4f, 0x68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80, +]; #[cfg(feature = "uefi")] pub fn get_esrt() -> Option { @@ -459,6 +513,7 @@ pub fn get_esrt() -> Option { // TODO: Why aren't they the same type? //debug!("Table: {:?}", table); let table_guid: Guid = unsafe { std::mem::transmute(table.guid) }; + let table_guid = GUID::from(table_guid); match table_guid { SYSTEM_RESOURCE_TABLE_GUID => unsafe { return esrt_from_buf(table.address as *const u8); diff --git a/framework_lib/src/guid.rs b/framework_lib/src/guid.rs deleted file mode 100644 index 2de63088..00000000 --- a/framework_lib/src/guid.rs +++ /dev/null @@ -1,173 +0,0 @@ -// Taken from https://github.com/rust-osdev/uefi-rs/blob/main/uefi/src/data_types/guid.rs -use core::fmt; - -/// A globally unique identifier -/// -/// GUIDs are used by UEFI to identify protocols and other objects. They are -/// mostly like variant 2 UUIDs as specified by RFC 4122, but differ from them -/// in that the first 3 fields are little endian instead of big endian. -/// -/// The `Display` formatter prints GUIDs in the canonical format defined by -/// RFC 4122, which is also used by UEFI. -#[derive(Debug, Default, Copy, Clone, Eq, Ord, PartialEq, PartialOrd)] -#[repr(C)] -pub struct Guid { - /// The low field of the timestamp. - a: u32, - /// The middle field of the timestamp. - b: u16, - /// The high field of the timestamp multiplexed with the version number. - c: u16, - /// Contains, in this order: - /// - The high field of the clock sequence multiplexed with the variant. - /// - The low field of the clock sequence. - /// - The spatially unique node identifier. - d: [u8; 8], -} - -impl Guid { - /// Creates a new GUID from its canonical representation - #[must_use] - pub const fn from_values( - time_low: u32, - time_mid: u16, - time_high_and_version: u16, - clock_seq_and_variant: u16, - node: u64, - ) -> Self { - assert!(node.leading_zeros() >= 16, "node must be a 48-bit integer"); - // intentional shadowing - let node = node.to_be_bytes(); - - Guid { - a: time_low, - b: time_mid, - c: time_high_and_version, - d: [ - (clock_seq_and_variant / 0x100) as u8, - (clock_seq_and_variant % 0x100) as u8, - // first two elements of node are ignored, we only want the low 48 bits - node[2], - node[3], - node[4], - node[5], - node[6], - node[7], - ], - } - } - - /// Create a GUID from a 16-byte array. No changes to byte order are made. - #[must_use] - pub const fn from_bytes(bytes: [u8; 16]) -> Self { - let a = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]); - let b = u16::from_le_bytes([bytes[4], bytes[5]]); - let c = u16::from_le_bytes([bytes[6], bytes[7]]); - let d = [ - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], - ]; - - Self { a, b, c, d } - } - - /// Convert to a 16-byte array. - #[must_use] - #[rustfmt::skip] - pub const fn to_bytes(self) -> [u8; 16] { - let a = self.a.to_le_bytes(); - let b = self.b.to_le_bytes(); - let c = self.c.to_le_bytes(); - let d = self.d; - - [ - a[0], a[1], a[2], a[3], - b[0], b[1], c[0], c[1], - d[0], d[1], d[2], d[3], - d[4], d[5], d[6], d[7], - ] - } -} - -impl fmt::Display for Guid { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - let a = self.a; - let b = self.b; - let c = self.c; - - let d = { - let mut buf = [0u8; 2]; - buf[..].copy_from_slice(&self.d[0..2]); - u16::from_be_bytes(buf) - }; - - let e = { - let mut buf = [0u8; 8]; - // first two elements of node are ignored, we only want the low 48 bits - buf[2..].copy_from_slice(&self.d[2..8]); - u64::from_be_bytes(buf) - }; - - write!(fmt, "{a:08x}-{b:04x}-{c:04x}-{d:04x}-{e:012x}",) - } -} - -/// Several entities in the UEFI specification can be referred to by their GUID, -/// this trait is a building block to interface them in uefi-rs. -/// -/// You should never need to use the `Identify` trait directly, but instead go -/// for more specific traits such as `Protocol` or `FileProtocolInfo`, which -/// indicate in which circumstances an `Identify`-tagged type should be used. -/// -/// For the common case of implementing this trait for a protocol, use -/// the `unsafe_protocol` macro. -/// -/// # Safety -/// -/// Implementing `Identify` is unsafe because attaching an incorrect GUID to a -/// type can lead to type unsafety on both the Rust and UEFI side. -pub unsafe trait Identify { - /// Unique protocol identifier. - const GUID: Guid; -} - -#[cfg(test)] -mod tests { - use super::*; - use guid_macros::guid; - - #[test] - fn test_guid_display() { - assert_eq!( - alloc::format!( - "{}", - Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0) - ), - "12345678-9abc-def0-1234-56789abcdef0" - ); - } - - #[test] - fn test_guid_macro() { - assert_eq!( - guid!("12345678-9abc-def0-1234-56789abcdef0"), - Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0) - ); - } - - #[test] - fn test_to_from_bytes() { - #[rustfmt::skip] - let bytes = [ - 0x78, 0x56, 0x34, 0x12, - 0xbc, 0x9a, - 0xf0, 0xde, - 0x12, 0x34, - 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, - ]; - assert_eq!( - Guid::from_bytes(bytes), - Guid::from_values(0x12345678, 0x9abc, 0xdef0, 0x1234, 0x56789abcdef0) - ); - assert_eq!(Guid::from_bytes(bytes).to_bytes(), bytes); - } -} diff --git a/framework_lib/src/lib.rs b/framework_lib/src/lib.rs index 661f9e14..93d91e2b 100644 --- a/framework_lib/src/lib.rs +++ b/framework_lib/src/lib.rs @@ -37,8 +37,6 @@ pub mod commandline; pub mod csme; pub mod ec_binary; pub mod esrt; -#[cfg(not(feature = "uefi"))] -pub mod guid; mod os_specific; pub mod power; pub mod smbios; diff --git a/guid_macros/Cargo.toml b/guid_macros/Cargo.toml deleted file mode 100644 index d1680814..00000000 --- a/guid_macros/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "guid_macros" -version = "0.11.0" -authors = ["The Rust OSDev team"] -readme = "README.md" -edition = "2021" -description = "Procedural macros for the `uefi` crate." -repository = "https://github.com/rust-osdev/uefi-rs" -keywords = ["uefi", "efi"] -categories = ["embedded", "no-std", "api-bindings"] -license = "MPL-2.0" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1.0.28" -quote = "1.0.9" -syn = { version = "2.0.4", features = ["full"] } - -#[dev-dependencies] -#trybuild = "1.0.61" -#uefi = { version = "0.20.0", default-features = false } diff --git a/guid_macros/src/lib.rs b/guid_macros/src/lib.rs deleted file mode 100644 index 7556a5d5..00000000 --- a/guid_macros/src/lib.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Taken from - -use proc_macro::TokenStream; - -use proc_macro2::{TokenStream as TokenStream2, TokenTree}; -use quote::{quote, ToTokens}; -use syn::spanned::Spanned; -use syn::{parse_macro_input, Error, LitStr}; - -macro_rules! err { - ($span:expr, $message:expr $(,)?) => { - Error::new($span.span(), $message).to_compile_error() - }; - ($span:expr, $message:expr, $($args:expr),*) => { - Error::new($span.span(), format!($message, $($args),*)).to_compile_error() - }; -} - -/// Create a `Guid` at compile time. -/// -/// # Example -/// -/// ``` -/// use uefi::{guid, Guid}; -/// const EXAMPLE_GUID: Guid = guid!("12345678-9abc-def0-1234-56789abcdef0"); -/// ``` -#[proc_macro] -pub fn guid(args: TokenStream) -> TokenStream { - let (time_low, time_mid, time_high_and_version, clock_seq_and_variant, node) = - match parse_guid(parse_macro_input!(args as LitStr)) { - Ok(data) => data, - Err(tokens) => return tokens.into(), - }; - - quote!({ - const g: crate::guid::Guid = crate::guid::Guid::from_values( - #time_low, - #time_mid, - #time_high_and_version, - #clock_seq_and_variant, - #node, - ); - g - }) - .into() -} - -fn parse_guid(guid_lit: LitStr) -> Result<(u32, u16, u16, u16, u64), TokenStream2> { - let guid_str = guid_lit.value(); - - // We expect a canonical GUID string, such as "12345678-9abc-def0-fedc-ba9876543210" - if guid_str.len() != 36 { - return Err(err!( - guid_lit, - "\"{}\" is not a canonical GUID string (expected 36 bytes, found {})", - guid_str, - guid_str.len() - )); - } - let mut offset = 1; // 1 is for the starting quote - let mut guid_hex_iter = guid_str.split('-'); - let mut next_guid_int = |len: usize| -> Result { - let guid_hex_component = guid_hex_iter.next().unwrap(); - - // convert syn::LitStr to proc_macro2::Literal.. - let lit = match guid_lit.to_token_stream().into_iter().next().unwrap() { - TokenTree::Literal(lit) => lit, - _ => unreachable!(), - }; - // ..so that we can call subspan and nightly users (us) will get the fancy span - let span = lit - .subspan(offset..offset + guid_hex_component.len()) - .unwrap_or_else(|| lit.span()); - - if guid_hex_component.len() != len * 2 { - return Err(err!( - span, - "GUID component \"{}\" is not a {}-bit hexadecimal string", - guid_hex_component, - len * 8 - )); - } - offset += guid_hex_component.len() + 1; // + 1 for the dash - u64::from_str_radix(guid_hex_component, 16).map_err(|_| { - err!( - span, - "GUID component \"{}\" is not a hexadecimal number", - guid_hex_component - ) - }) - }; - - // The GUID string is composed of a 32-bit integer, three 16-bit ones, and a 48-bit one - Ok(( - next_guid_int(4)? as u32, - next_guid_int(2)? as u16, - next_guid_int(2)? as u16, - next_guid_int(2)? as u16, - next_guid_int(6)?, - )) -} From faa158701ce8b700a01e4b30b4045ccb96b8439b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 00:13:41 +0800 Subject: [PATCH 086/174] esrt: Add Framework 12 GUIDs Signed-off-by: Daniel Schaefer --- framework_lib/src/esrt/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 74221196..492821e9 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -57,6 +57,12 @@ pub const MTL_BIOS_GUID: GUID = GUID::build_from_components( 0x5ec2, &[0xa9, 0xff, 0xc7, 0x39, 0xaa, 0xba, 0xad, 0xf3], ); +pub const FW12_RPL_BIOS_GUID: GUID = GUID::build_from_components( + 0x6bc0986c, + 0xd281, + 0x5ba3, + &[0x96, 0x5c, 0x2f, 0x8d, 0x13, 0xe1, 0xee, 0xe8], +); pub const TGL_RETIMER01_GUID: GUID = GUID::build_from_components( 0x832af090, @@ -125,6 +131,12 @@ pub const RPL_CSME_GUID: GUID = GUID::build_from_components( 0x4734, &[0xb4, 0x3e, 0x55, 0xdb, 0x5a, 0x55, 0x7d, 0x63], ); +pub const RPL_U_CSME_GUID: GUID = GUID::build_from_components( + 0x0f74c56d, + 0xd5ba, + 0x4942, + &[0x96, 0xfa, 0xd3, 0x75, 0x60, 0xf4, 0x05, 0x54], +); pub const MTL_CSME_GUID: GUID = GUID::build_from_components( 0x32d8d677, 0xeebc, @@ -159,6 +171,7 @@ pub enum FrameworkGuidKind { MtlRetimer01, MtlRetimer23, RplCsme, + RplUCsme, MtlCsme, Fl16Bios, Amd13Bios, @@ -172,6 +185,7 @@ pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { ADL_BIOS_GUID => FrameworkGuidKind::AdlBios, RPL_BIOS_GUID => FrameworkGuidKind::RplBios, MTL_BIOS_GUID => FrameworkGuidKind::MtlBios, + FW12_RPL_BIOS_GUID => FrameworkGuidKind::Fw12RplBios, FL16_BIOS_GUID => FrameworkGuidKind::Fl16Bios, AMD13_BIOS_GUID => FrameworkGuidKind::Amd13Bios, TGL_RETIMER01_GUID => FrameworkGuidKind::TglRetimer01, @@ -183,6 +197,7 @@ pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { MTL_RETIMER01_GUID => FrameworkGuidKind::MtlRetimer01, MTL_RETIMER23_GUID => FrameworkGuidKind::MtlRetimer23, RPL_CSME_GUID => FrameworkGuidKind::RplCsme, + RPL_U_CSME_GUID => FrameworkGuidKind::RplUCsme, MTL_CSME_GUID => FrameworkGuidKind::MtlCsme, WINUX_GUID => FrameworkGuidKind::WinUx, _ => FrameworkGuidKind::Unknown, From a087756c8ab990e501abd1ed52474c62d79e32c2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 09:02:46 +0800 Subject: [PATCH 087/174] esrt: Add Framework 13 AMD Ryzen AI 300 GUID Signed-off-by: Daniel Schaefer --- framework_lib/src/esrt/mod.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 492821e9..15daea4c 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -119,12 +119,18 @@ pub const FL16_BIOS_GUID: GUID = GUID::build_from_components( 0x5d64, &[0x8e, 0x18, 0x65, 0x8d, 0x20, 0x5a, 0xcf, 0x34], ); -pub const AMD13_BIOS_GUID: GUID = GUID::build_from_components( +pub const AMD13_RYZEN7040_BIOS_GUID: GUID = GUID::build_from_components( 0xb5f7dcc1, 0x568c, 0x50f8, &[0xa4, 0xdd, 0xe3, 0x9d, 0x1f, 0x93, 0xfd, 0xa1], ); +pub const AMD13_AI300_BIOS_GUID: GUID = GUID::build_from_components( + 0x9c13b7f1, + 0xd618, + 0x5d68, + &[0xbe, 0x61, 0x6b, 0x17, 0x88, 0x10, 0x14, 0xa7], +); pub const RPL_CSME_GUID: GUID = GUID::build_from_components( 0x865d322c, 0x6ac7, @@ -174,7 +180,8 @@ pub enum FrameworkGuidKind { RplUCsme, MtlCsme, Fl16Bios, - Amd13Bios, + Amd13Ryzen7040Bios, + Amd13Ai300Bios, WinUx, Unknown, } @@ -187,7 +194,8 @@ pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { MTL_BIOS_GUID => FrameworkGuidKind::MtlBios, FW12_RPL_BIOS_GUID => FrameworkGuidKind::Fw12RplBios, FL16_BIOS_GUID => FrameworkGuidKind::Fl16Bios, - AMD13_BIOS_GUID => FrameworkGuidKind::Amd13Bios, + AMD13_RYZEN7040_BIOS_GUID => FrameworkGuidKind::Amd13Ryzen7040Bios, + AMD13_AI300_BIOS_GUID => FrameworkGuidKind::Amd13Ai300Bios, TGL_RETIMER01_GUID => FrameworkGuidKind::TglRetimer01, TGL_RETIMER23_GUID => FrameworkGuidKind::TglRetimer23, ADL_RETIMER01_GUID => FrameworkGuidKind::AdlRetimer01, From 521907282a35a1f875b5c46b359ef82eba0febdc Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 14:38:18 +0800 Subject: [PATCH 088/174] Allow flashing EC RW region Works well on Linux. It's disabled by the windows driver at the moment though. No need to block it in this application, users could also use ectool. Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 0477cbbe..7f11c87f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -570,19 +570,14 @@ fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) { let data = crate::uefi::fs::shell_read_file(ec_bin_path); #[cfg(not(feature = "uefi"))] let data: Option> = { - let _data = match fs::read(ec_bin_path) { + match fs::read(ec_bin_path) { Ok(data) => Some(data), // TODO: Perhaps a more user-friendly error Err(e) => { println!("Error {:?}", e); None } - }; - - // EC communication from OS is not stable enough yet, - // it can't be trusted to reliably flash the EC without risk of damage. - println!("Sorry, flashing EC from the OS is not supported yet."); - None + } }; if let Some(data) = data { From eec9a1e4e4071416ed7306cbb9668ee19c8de072 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 17:38:54 +0800 Subject: [PATCH 089/174] gh-actions: Split UEFI build into separate action Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3f8dc7d1..61c0f70a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: path: target/x86_64-unknown-freebsd/debug/framework_tool build: - name: Build Linux and UEFI + name: Build Linux runs-on: ubuntu-24.04 env: CARGO_NET_GIT_FETCH_WITH_CLI: true @@ -65,6 +65,17 @@ jobs: name: framework_tool path: target/debug/framework_tool + build-uefi: + name: Build UEFI + runs-on: ubuntu-24.04 + env: + CARGO_NET_GIT_FETCH_WITH_CLI: true + steps: + - uses: actions/checkout@v4 + + - name: Setup Rust toolchain + run: rustup show + - name: Build UEFI application (no ESP) run: make -C framework_uefi build/x86_64-unknown-uefi/boot.efi From b470e3fefb98dcea9fd32565151ef2816572381c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 21:32:19 +0800 Subject: [PATCH 090/174] esrt: Add framework desktop GUID Signed-off-by: Daniel Schaefer --- framework_lib/src/esrt/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 15daea4c..982d3bc0 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -131,6 +131,12 @@ pub const AMD13_AI300_BIOS_GUID: GUID = GUID::build_from_components( 0x5d68, &[0xbe, 0x61, 0x6b, 0x17, 0x88, 0x10, 0x14, 0xa7], ); +pub const DESKTOP_AMD_AI300_BIOS_GUID: GUID = GUID::build_from_components( + 0xeb68dbae, + 0x3aef, + 0x5077, + &[0x92, 0xae, 0x90, 0x16, 0xd1, 0xf0, 0xc8, 0x56], +); pub const RPL_CSME_GUID: GUID = GUID::build_from_components( 0x865d322c, 0x6ac7, @@ -182,6 +188,7 @@ pub enum FrameworkGuidKind { Fl16Bios, Amd13Ryzen7040Bios, Amd13Ai300Bios, + DesktopAmdAi300Bios, WinUx, Unknown, } @@ -196,6 +203,7 @@ pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { FL16_BIOS_GUID => FrameworkGuidKind::Fl16Bios, AMD13_RYZEN7040_BIOS_GUID => FrameworkGuidKind::Amd13Ryzen7040Bios, AMD13_AI300_BIOS_GUID => FrameworkGuidKind::Amd13Ai300Bios, + DESKTOP_AMD_AI300_BIOS_GUID => FrameworkGuidKind::DesktopAmdAi300Bios, TGL_RETIMER01_GUID => FrameworkGuidKind::TglRetimer01, TGL_RETIMER23_GUID => FrameworkGuidKind::TglRetimer23, ADL_RETIMER01_GUID => FrameworkGuidKind::AdlRetimer01, From a75264781cf7e44e4cbe1c4cea0544b0020ef20d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 15 May 2025 21:32:34 +0800 Subject: [PATCH 091/174] EXAMPLES: Add --esrt example Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index f688c635..6c3d50aa 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -439,3 +439,28 @@ The scancodes are the same. ### Framework 16 It's not controlled by the EC, use https://keyboard.frame.work. + +## Advanced commands + +Mostly for debugging firmware. + +### Check EFI Resource Table + +On Framework Desktop: + +``` +> sudo framework_tool --esrt +ESRT Table + ResourceCount: 1 + ResourceCountMax: 1 + ResourceVersion: 1 +ESRT Entry 0 + GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856 + GUID: DesktopAmdAi300Bios + Type: SystemFirmware + Version: 0x204 (516) + Min FW Version: 0x100 (256) + Capsule Flags: 0x0 + Last Attempt Version: 0x108 (264) + Last Attempt Status: Success +``` From 184f89ba53220424ceee750dfb3b25e4076821ee Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 18 May 2025 13:27:50 +0800 Subject: [PATCH 092/174] Also require root before initializing MEC portio Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index 152d5f40..df489921 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -90,14 +90,6 @@ fn init() -> bool { Initialized::NotYet => {} } - // First try on MEC - portio_mec::init(); - let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); - if ec_id[0] == b'E' && ec_id[1] == b'C' { - *init = Initialized::SucceededMec; - return true; - } - // In Linux userspace has to first request access to ioports // TODO: Close these again after we're done #[cfg(target_os = "linux")] @@ -106,6 +98,15 @@ fn init() -> bool { *init = Initialized::Failed; return false; } + + // First try on MEC + portio_mec::init(); + let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); + if ec_id[0] == b'E' && ec_id[1] == b'C' { + *init = Initialized::SucceededMec; + return true; + } + #[cfg(target_os = "linux")] unsafe { // 8 for request/response header, 0xFF for response From 056ec3103f6c577e0d809ba8744804159c30d158 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 18 May 2025 13:28:16 +0800 Subject: [PATCH 093/174] mec init: Fail if ioperm didn't succeed Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio_mec.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 9d3664e2..774acb99 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -22,12 +22,20 @@ const _MEC_LPC_DATA_REGISTER1: u16 = 0x0805; const MEC_LPC_DATA_REGISTER2: u16 = 0x0806; const _MEC_LPC_DATA_REGISTER3: u16 = 0x0807; -pub fn init() { +pub fn init() -> bool { #[cfg(target_os = "linux")] unsafe { - ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); - ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); + println!("Init MEC"); + let res = ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); + if res != 0 { + error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); + return false; + } + let res = ioperm(MEC_LPC_ADDRESS_REGISTER0 as u64, 10, 1); + assert_eq!(res, 0); } + + true } // TODO: Create a wrapper From 42c816ef07bef8a5191dd26ede384ca4eaa08ade Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 18 May 2025 22:17:55 +0800 Subject: [PATCH 094/174] portio: Don't segfault if ioperm failed Have to return and never try again Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/portio.rs b/framework_lib/src/chromium_ec/portio.rs index df489921..d76f8a6b 100644 --- a/framework_lib/src/chromium_ec/portio.rs +++ b/framework_lib/src/chromium_ec/portio.rs @@ -100,7 +100,10 @@ fn init() -> bool { } // First try on MEC - portio_mec::init(); + if !portio_mec::init() { + *init = Initialized::Failed; + return false; + } let ec_id = portio_mec::transfer_read(MEC_MEMMAP_OFFSET + EC_MEMMAP_ID, 2); if ec_id[0] == b'E' && ec_id[1] == b'C' { *init = Initialized::SucceededMec; @@ -113,6 +116,7 @@ fn init() -> bool { let res = ioperm(EC_LPC_ADDR_HOST_ARGS as u64, 8 + 0xFF, 1); if res != 0 { error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); + *init = Initialized::Failed; return false; } From 06ff4b74b0bc28ce706b01d28b860946e09c01b2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 24 Apr 2025 00:45:02 +0800 Subject: [PATCH 095/174] Add devenv.nix Must have only Nix and devenv installed ``` devenv shell cargo build sudo ./target/debug/framework_tool ``` Signed-off-by: Daniel Schaefer --- .envrc | 5 ++ .gitignore | 12 +++++ devenv.lock | 140 ++++++++++++++++++++++++++++++++++++++++++++++++++++ devenv.nix | 11 +++++ devenv.yaml | 8 +++ nix.md | 12 +++++ 6 files changed, 188 insertions(+) create mode 100644 .envrc create mode 100644 devenv.lock create mode 100644 devenv.nix create mode 100644 devenv.yaml create mode 100644 nix.md diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..30da14fd --- /dev/null +++ b/.envrc @@ -0,0 +1,5 @@ +export DIRENV_WARN_TIMEOUT=20s + +eval "$(devenv direnvrc)" + +use devenv diff --git a/.gitignore b/.gitignore index a34615ec..5e33b24d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,15 @@ build/ *.efi *.exe *.pdb + +# Devenv +.devenv* +devenv.local.nix +# Nix +result* + +# direnv +.direnv + +# pre-commit +.pre-commit-config.yaml diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 00000000..bd0c7fbf --- /dev/null +++ b/devenv.lock @@ -0,0 +1,140 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1747543288, + "owner": "cachix", + "repo": "devenv", + "rev": "3a8a52386bde1cf14fc2f4c4df80f91417348480", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": "rust-analyzer-src" + }, + "locked": { + "lastModified": 1747392669, + "owner": "nix-community", + "repo": "fenix", + "rev": "c3c27e603b0d9b5aac8a16236586696338856fbb", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "git-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1747372754, + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "git-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1746807397, + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "c5208b594838ea8e6cca5997fbf784b7cca1ca90", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "fenix": "fenix", + "git-hooks": "git-hooks", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": [ + "git-hooks" + ] + } + }, + "rust-analyzer-src": { + "flake": false, + "locked": { + "lastModified": 1747557850, + "owner": "rust-lang", + "repo": "rust-analyzer", + "rev": "e464ff8c755c6e12540a45b83274ec4de4829191", + "type": "github" + }, + "original": { + "owner": "rust-lang", + "ref": "nightly", + "repo": "rust-analyzer", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 00000000..413d6a66 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,11 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + packages = with pkgs; [ + systemd # libudev + ]; + + languages.rust.enable = true; + # https://devenv.sh/reference/options/#languagesrustchannel + languages.rust.channel = "stable"; +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 00000000..1d180750 --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,8 @@ +inputs: + fenix: + url: github:nix-community/fenix + inputs: + nixpkgs: + follows: nixpkgs + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling diff --git a/nix.md b/nix.md new file mode 100644 index 00000000..19b140ca --- /dev/null +++ b/nix.md @@ -0,0 +1,12 @@ +# Building with Nix + +## Building with devenv + + +Must have Nix and devenv installed + +``` +devenv shell +cargo build +sudo ./target/debug/framework_tool +``` From bb3909364a871eac2c2a5c500bdfdceea84795df Mon Sep 17 00:00:00 2001 From: SHENGYI HUNG Date: Fri, 23 May 2025 00:14:01 +0800 Subject: [PATCH 096/174] esrt: fix ESRT GUID for EFI device The EFI GUID in FreeBSD is layouted as following typedef struct efi_guid { uint32_t Data1; uint16_t Data2; uint16_t Data3; uint8_t Data4[8]; } efi_guid_t; Which means the Data1, Data2, Data3 should follows the little endian rule as the MSB should be in the right side. Also, the table_len in the ioctl argument of EFI_GET_TABLE should be follow the uuid structure. Fix it by reorder it. Sponsored By: FreeBSD Foundation Sponsored By: Framework Laptop Inc. --- framework_lib/src/esrt/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 982d3bc0..90bfdbdd 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -15,7 +15,7 @@ use log::{debug, error, info, trace}; use std::prelude::v1::*; use core::prelude::v1::derive; -use guid_create::{Guid, GUID}; +use guid_create::{GUID, Guid}; #[cfg(target_os = "linux")] use std::fs; @@ -506,8 +506,8 @@ pub fn get_esrt() -> Option { let mut table = EfiGetTableIoc { buf: std::ptr::null_mut(), uuid: SYSTEM_RESOURCE_TABLE_GUID_BYTES, - buf_len: 0, table_len: 0, + buf_len: 0, }; unsafe { let fd = file.as_raw_fd(); @@ -532,7 +532,7 @@ pub const SYSTEM_RESOURCE_TABLE_GUID: GUID = GUID::build_from_components( &[0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80], ); pub const SYSTEM_RESOURCE_TABLE_GUID_BYTES: [u8; 16] = [ - 0xb1, 0x22, 0xa2, 0x63, 0x36, 0x61, 0x4f, 0x68, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80, + 0x63, 0xa2, 0x22, 0xb1, 0x61, 0x36, 0x68, 0x4f, 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80, ]; #[cfg(feature = "uefi")] From db61c36f543c85c72045a9df57c4ff94fae0f761 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 23 May 2025 18:10:05 +0800 Subject: [PATCH 097/174] Add support for EcRequestReadPdVersionV1 Not all systems have exactly 2 PD controllers, e.g. Framework Desktop has just one. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/commands.rs | 26 ++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 4a8fd2bf..0a367ab3 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -987,18 +987,38 @@ impl EcRequest for EcRequestChassisIntrusionC } #[repr(C, packed)] -pub struct EcRequestReadPdVersion {} +pub struct EcRequestReadPdVersionV0 {} #[repr(C, packed)] -pub struct _EcResponseReadPdVersion { +pub struct _EcResponseReadPdVersionV0 { pub controller01: [u8; 8], pub controller23: [u8; 8], } -impl EcRequest<_EcResponseReadPdVersion> for EcRequestReadPdVersion { +impl EcRequest<_EcResponseReadPdVersionV0> for EcRequestReadPdVersionV0 { fn command_id() -> EcCommands { EcCommands::ReadPdVersion } + fn command_version() -> u8 { + 0 + } +} + +#[repr(C, packed)] +pub struct EcRequestReadPdVersionV1 {} +#[repr(C, packed)] +pub struct _EcResponseReadPdVersionV1 { + pub pd_chip_count: u8, + pub pd_controllers: [u8; 0], +} + +impl EcRequest<_EcResponseReadPdVersionV1> for EcRequestReadPdVersionV1 { + fn command_id() -> EcCommands { + EcCommands::ReadPdVersion + } + fn command_version() -> u8 { + 1 + } } #[repr(C, packed)] From ace46f9d9cd4ef6ae99a73fb6760e6d7f68aab76 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 23 May 2025 18:26:54 +0800 Subject: [PATCH 098/174] --versions: Return any number of PD versions Based on EC host command. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/mod.rs | 8 ++++-- framework_lib/src/commandline/mod.rs | 23 +++++++++++++-- framework_lib/src/power.rs | 43 ++++++++++++++++++++++------ 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 80b1b763..ea89f4b6 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -3,6 +3,7 @@ use alloc::format; use alloc::string::String; use alloc::string::ToString; +use alloc::vec::Vec; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; use num_derive::FromPrimitive; @@ -241,9 +242,10 @@ pub struct PdVersions { /// Same as PdVersions but only the main FW #[derive(Debug)] -pub struct MainPdVersions { - pub controller01: ControllerVersion, - pub controller23: ControllerVersion, +pub enum MainPdVersions { + RightLeft((ControllerVersion, ControllerVersion)), + Single(ControllerVersion), + Many(Vec), } pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 7f11c87f..057826db 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -33,7 +33,7 @@ use crate::capsule_content::{ use crate::ccgx::device::{FwMode, PdController, PdPort}; #[cfg(feature = "hidapi")] use crate::ccgx::hid::{check_ccg_fw_version, find_devices, DP_CARD_PID, HDMI_CARD_PID}; -use crate::ccgx::{self, SiliconId::*}; +use crate::ccgx::{self, MainPdVersions, SiliconId::*}; use crate::chromium_ec; use crate::chromium_ec::commands::DeckStateMode; use crate::chromium_ec::commands::FpLedBrightnessLevel; @@ -478,8 +478,25 @@ fn print_versions(ec: &CrosEc) { } } else if let Ok(pd_versions) = power::read_pd_version(ec) { // As fallback try to get it from the EC. But not all EC versions have this command - println!(" Right (01): {}", pd_versions.controller01.app); - println!(" Left (23): {}", pd_versions.controller23.app); + match pd_versions { + MainPdVersions::RightLeft((controller01, controller23)) => { + if let Some(Platform::IntelGen11) = smbios::get_platform() { + println!(" Right (01): {}", controller01.base); + println!(" Left (23): {}", controller23.base); + } else { + println!(" Right (01): {}", controller01.app); + println!(" Left (23): {}", controller23.app); + } + } + MainPdVersions::Single(version) => { + println!(" Version: {}", version.app); + } + MainPdVersions::Many(versions) => { + for (i, version) in versions.into_iter().enumerate() { + println!(" PD {}: {}", i, version.app); + } + } + } } else { println!(" Unknown") } diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index ba89af1d..af5963ec 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -1,7 +1,7 @@ //! Get information about system power (battery, AC, PD ports) use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec; use alloc::vec::Vec; use core::convert::TryInto; @@ -798,6 +798,11 @@ pub fn is_charging(ec: &CrosEc) -> EcResult<(bool, bool)> { Ok((port0 || port1, port2 || port3)) } +fn parse_pd_ver_slice(data: &[u8]) -> ControllerVersion { + parse_pd_ver(&[ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]) +} fn parse_pd_ver(data: &[u8; 8]) -> ControllerVersion { ControllerVersion { base: BaseVersion { @@ -815,15 +820,37 @@ fn parse_pd_ver(data: &[u8; 8]) -> ControllerVersion { } } -// NOTE: Only works on ADL at the moment! -// TODO: Not on TGL, need to check if RPL and later have it. +// NOTE: TGL (hx20) does not have this host command pub fn read_pd_version(ec: &CrosEc) -> EcResult { - let info = EcRequestReadPdVersion {}.send_command(ec)?; + let info = EcRequestReadPdVersionV1 {}.send_command_vec(ec); - Ok(MainPdVersions { - controller01: parse_pd_ver(&info.controller01), - controller23: parse_pd_ver(&info.controller23), - }) + // If v1 not available, fall back + if let Err(EcError::Response(EcResponseStatus::InvalidVersion)) = info { + let info = EcRequestReadPdVersionV0 {}.send_command(ec)?; + + return Ok(if info.controller23 == [0, 0, 0, 0, 0, 0, 0, 0] { + MainPdVersions::Single(parse_pd_ver(&info.controller01)) + } else { + MainPdVersions::RightLeft(( + parse_pd_ver(&info.controller01), + parse_pd_ver(&info.controller23), + )) + }); + } + // If any other error, exit + let info = info?; + + let mut versions = vec![]; + let pd_count = info[0] as usize; + for i in 0..pd_count { + // TODO: Is there a safer way to check the range? + if info.len() < 1 + 8 * (i + 1) { + return Err(EcError::DeviceError("Not enough data returned".to_string())); + } + versions.push(parse_pd_ver_slice(&info[1 + 8 * i..1 + 8 * (i + 1)])); + } + + Ok(MainPdVersions::Many(versions)) } pub fn standalone_mode(ec: &CrosEc) -> bool { From 3e0d595ee4f24410b9e123f47b362e7689275eb8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 25 May 2025 21:05:45 +0800 Subject: [PATCH 099/174] --version: Properly fetch single PD controller ver from ccgx For Framework Desktop. It has only a single PD controller. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 38 +++++++++++++++++++++++----- framework_lib/src/ccgx/mod.rs | 23 ++++++++++++----- framework_lib/src/commandline/mod.rs | 33 ++++++++++++++++-------- 3 files changed, 70 insertions(+), 24 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 532ad10e..7cdd6839 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -26,6 +26,7 @@ enum ControlRegisters { pub enum PdPort { Left01, Right23, + Back, } impl PdPort { @@ -33,6 +34,9 @@ impl PdPort { fn i2c_address(&self) -> EcResult { let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; + let unsupported = Err(EcError::DeviceError( + "Controller does not exist on this platform".to_string(), + )); Ok(match (platform, self) { (Platform::GenericFramework((left, _), _), PdPort::Left01) => *left, @@ -50,14 +54,29 @@ impl PdPort { | Platform::Framework16Amd7080, PdPort::Right23, ) => 0x40, - // TODO: It only has a single PD controller - (Platform::FrameworkDesktopAmdAiMax300, _) => 0x08, + (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 0x08, + (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, + // Framework Intel Platforms (CCG5 and CCG6) + ( + Platform::Framework12IntelGen13 + | Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1, + PdPort::Left01, + ) => 0x08, + ( + Platform::Framework12IntelGen13 + | Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1, + PdPort::Right23, + ) => 0x40, (Platform::UnknownSystem, _) => { Err(EcError::DeviceError("Unsupported platform".to_string()))? } - // Framework Intel Platforms (CCG5 and CCG6) - (_, PdPort::Left01) => 0x08, - (_, PdPort::Right23) => 0x40, + (_, PdPort::Back) => unsupported?, }) } @@ -65,6 +84,10 @@ impl PdPort { fn i2c_port(&self) -> EcResult { let config = Config::get(); let platform = &(*config).as_ref().unwrap().platform; + let unsupported = Err(EcError::DeviceError(format!( + "Controller {:?}, does not exist on {:?}", + self, platform + ))); Ok(match (platform, self) { (Platform::GenericFramework(_, (left, _)), PdPort::Left01) => *left, @@ -88,11 +111,12 @@ impl PdPort { | Platform::Framework12IntelGen13, PdPort::Right23, ) => 2, - // TODO: It only has a single PD controller - (Platform::FrameworkDesktopAmdAiMax300, _) => 1, + (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 1, + (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, (Platform::UnknownSystem, _) => { Err(EcError::DeviceError("Unsupported platform".to_string()))? } + (_, PdPort::Back) => unsupported?, }) } } diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index ea89f4b6..38997ace 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -3,6 +3,7 @@ use alloc::format; use alloc::string::String; use alloc::string::ToString; +use alloc::vec; use alloc::vec::Vec; #[cfg(feature = "uefi")] use core::prelude::rust_2021::derive; @@ -235,9 +236,10 @@ impl ControllerFirmwares { } #[derive(Debug, PartialEq)] -pub struct PdVersions { - pub controller01: ControllerFirmwares, - pub controller23: ControllerFirmwares, +pub enum PdVersions { + RightLeft((ControllerFirmwares, ControllerFirmwares)), + Single(ControllerFirmwares), + Many(Vec), } /// Same as PdVersions but only the main FW @@ -249,10 +251,17 @@ pub enum MainPdVersions { } pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult { - Ok(PdVersions { - controller01: PdController::new(PdPort::Left01, ec.clone()).get_fw_versions()?, - controller23: PdController::new(PdPort::Right23, ec.clone()).get_fw_versions()?, - }) + let pd01 = PdController::new(PdPort::Left01, ec.clone()).get_fw_versions(); + let pd23 = PdController::new(PdPort::Right23, ec.clone()).get_fw_versions(); + let pd_back = PdController::new(PdPort::Back, ec.clone()).get_fw_versions(); + + match (pd01, pd23, pd_back) { + (Err(_), Err(_), Ok(pd_back)) => Ok(PdVersions::Single(pd_back)), + (Ok(pd01), Ok(pd23), Err(_)) => Ok(PdVersions::RightLeft((pd01, pd23))), + (Ok(pd01), Ok(pd23), Ok(pd_back)) => Ok(PdVersions::Many(vec![pd01, pd23, pd_back])), + (Err(err), _, _) => Err(err), + (_, Err(err), _) => Err(err), + } } fn parse_metadata_ccg3(buffer: &[u8]) -> Option<(u32, u32)> { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 057826db..581f884e 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -33,7 +33,7 @@ use crate::capsule_content::{ use crate::ccgx::device::{FwMode, PdController, PdPort}; #[cfg(feature = "hidapi")] use crate::ccgx::hid::{check_ccg_fw_version, find_devices, DP_CARD_PID, HDMI_CARD_PID}; -use crate::ccgx::{self, MainPdVersions, SiliconId::*}; +use crate::ccgx::{self, MainPdVersions, PdVersions, SiliconId::*}; use crate::chromium_ec; use crate::chromium_ec::commands::DeckStateMode; use crate::chromium_ec::commands::FpLedBrightnessLevel; @@ -398,11 +398,8 @@ fn print_versions(ec: &CrosEc) { } println!("PD Controllers"); - - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let right = &pd_versions.controller01; - let left = &pd_versions.controller23; - // let active_mode = + let ccgx_pd_vers = ccgx::get_pd_controller_versions(ec); + if let Ok(PdVersions::RightLeft((right, left))) = ccgx_pd_vers { if let Some(Platform::IntelGen11) = smbios::get_platform() { if right.main_fw.base != right.backup_fw.base { println!(" Right (01)"); @@ -476,8 +473,24 @@ fn print_versions(ec: &CrosEc) { left.main_fw.app, left.active_fw ); } + } else if let Ok(PdVersions::Single(pd)) = ccgx_pd_vers { + if pd.main_fw.app != pd.backup_fw.app { + println!( + " Main: {}{}", + pd.main_fw.app, + active_mode(&pd.active_fw, FwMode::MainFw) + ); + println!( + " Backup: {}{}", + pd.backup_fw.app, + active_mode(&pd.active_fw, FwMode::BackupFw) + ); + } else { + println!(" Version: {} ({:?})", pd.main_fw.app, pd.active_fw); + } } else if let Ok(pd_versions) = power::read_pd_version(ec) { // As fallback try to get it from the EC. But not all EC versions have this command + debug!(" Fallback to PD Host command"); match pd_versions { MainPdVersions::RightLeft((controller01, controller23)) => { if let Some(Platform::IntelGen11) = smbios::get_platform() { @@ -655,8 +668,8 @@ fn compare_version(device: Option, version: String, ec: &Cro } } Some(HardwareDeviceType::PD0) => { - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller01.active_fw_ver(); + if let Ok(PdVersions::RightLeft((pd01, _pd23))) = ccgx::get_pd_controller_versions(ec) { + let ver = pd01.active_fw_ver(); println!("Comparing PD0 version {:?}", ver); if ver.contains(&version) { @@ -667,8 +680,8 @@ fn compare_version(device: Option, version: String, ec: &Cro } } Some(HardwareDeviceType::PD1) => { - if let Ok(pd_versions) = ccgx::get_pd_controller_versions(ec) { - let ver = pd_versions.controller23.active_fw_ver(); + if let Ok(PdVersions::RightLeft((_pd01, pd23))) = ccgx::get_pd_controller_versions(ec) { + let ver = pd23.active_fw_ver(); println!("Comparing PD1 version {:?}", ver); if ver.contains(&version) { From dd599124a415b13efb0881ea2f1d729a58e70a1a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 25 May 2025 21:09:17 +0800 Subject: [PATCH 100/174] --versions: Hide CSME on AMD Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 5 ++--- framework_lib/src/util.rs | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 581f884e..3906cbb1 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -59,8 +59,7 @@ use crate::touchpad::print_touchpad_fw_ver; use crate::touchscreen; #[cfg(feature = "uefi")] use crate::uefi::enable_page_break; -use crate::util; -use crate::util::{Config, Platform, PlatformFamily}; +use crate::util::{self, Config, Platform, PlatformFamily}; #[cfg(feature = "hidapi")] use hidapi::HidApi; use sha2::{Digest, Sha256, Sha384, Sha512}; @@ -557,7 +556,7 @@ fn print_versions(ec: &CrosEc) { } #[cfg(target_os = "linux")] - { + if smbios::get_platform().and_then(Platform::which_cpu_vendor) != Some(util::CpuVendor::Amd) { println!("CSME"); if let Ok(csme) = csme::csme_from_sysfs() { info!(" Enabled: {}", csme.enabled); diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 022475f1..69bdbd32 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -49,7 +49,28 @@ pub enum PlatformFamily { FrameworkDesktop, } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum CpuVendor { + Intel, + Amd, +} + impl Platform { + pub fn which_cpu_vendor(self) -> Option { + match self { + Platform::Framework12IntelGen13 + | Platform::IntelGen11 + | Platform::IntelGen12 + | Platform::IntelGen13 + | Platform::IntelCoreUltra1 => Some(CpuVendor::Intel), + Platform::Framework13Amd7080 + | Platform::Framework13AmdAi300 + | Platform::Framework16Amd7080 + | Platform::FrameworkDesktopAmdAiMax300 => Some(CpuVendor::Amd), + Platform::GenericFramework(..) => None, + Platform::UnknownSystem => None, + } + } pub fn which_family(self) -> Option { match self { Platform::Framework12IntelGen13 => Some(PlatformFamily::Framework12), From bfe232e54bf9ea903dbff5ad88af0912fd27001e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 26 May 2025 10:01:49 +0800 Subject: [PATCH 101/174] Fix formatting Broken by bb3909364a871eac2c2a5c500bdfdceea84795df Signed-off-by: Daniel Schaefer --- framework_lib/src/esrt/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index 90bfdbdd..aa517bf4 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -15,7 +15,7 @@ use log::{debug, error, info, trace}; use std::prelude::v1::*; use core::prelude::v1::derive; -use guid_create::{GUID, Guid}; +use guid_create::{Guid, GUID}; #[cfg(target_os = "linux")] use std::fs; From 39e20f5dcbd1aa0057a3eefce44d921502420d6b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 26 May 2025 23:49:17 +0800 Subject: [PATCH 102/174] ec_binary: Make it less misleading if legacy cookie not found Signed-off-by: Daniel Schaefer --- framework_lib/src/ec_binary.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/framework_lib/src/ec_binary.rs b/framework_lib/src/ec_binary.rs index 092367de..15193105 100644 --- a/framework_lib/src/ec_binary.rs +++ b/framework_lib/src/ec_binary.rs @@ -145,38 +145,47 @@ pub fn parse_ec_version_str(version: &str) -> Option { /// Parse version information from EC FW image buffer pub fn read_ec_version(data: &[u8], ro: bool) -> Option { + // First try to find the legacy EC version let offset = if ro { EC_RO_VER_OFFSET } else { EC_RW_VER_OFFSET }; - let offset_zephyr = if ro { - EC_RO_VER_OFFSET_ZEPHYR - } else { - EC_RW_VER_OFFSET_ZEPHYR - }; - if data.len() < offset + core::mem::size_of::<_ImageVersionData>() { return None; } let v: _ImageVersionData = unsafe { std::ptr::read(data[offset..].as_ptr() as *const _) }; if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 { - debug!("Failed to find Cookie 1. Found: {:X?}", { v.cookie1 }); + debug!("Failed to find legacy Cookie 1. Found: {:X?}", { + v.cookie1 + }); } else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 { - debug!("Failed to find Cookie 2. Found: {:X?}", { v.cookie2 }); + debug!("Failed to find legacy Cookie 2. Found: {:X?}", { + v.cookie2 + }); } else { return parse_ec_version(&v); } + // If not present, find Zephyr EC version + let offset_zephyr = if ro { + EC_RO_VER_OFFSET_ZEPHYR + } else { + EC_RW_VER_OFFSET_ZEPHYR + }; if data.len() < offset_zephyr + core::mem::size_of::<_ImageVersionData>() { return None; } let v: _ImageVersionData = unsafe { std::ptr::read(data[offset_zephyr..].as_ptr() as *const _) }; if v.cookie1 != CROS_EC_IMAGE_DATA_COOKIE1 { - debug!("Failed to find Cookie 1. Found: {:X?}", { v.cookie1 }); + debug!("Failed to find Zephyr Cookie 1. Found: {:X?}", { + v.cookie1 + }); } else if v.cookie2 != CROS_EC_IMAGE_DATA_COOKIE2 { - debug!("Failed to find Cookie 2. Found: {:X?}", { v.cookie2 }); + debug!("Failed to find Zephyr Cookie 2. Found: {:X?}", { + v.cookie2 + }); } else { return parse_ec_version(&v); } From 0a6f8a79aeeeee4a80454612f86b294c55e26bb7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 13:49:56 +0800 Subject: [PATCH 103/174] chromium_ec: Erase EC in sections Erasing a big section takes too long sometimes and the linux kernel driver times out, so split it up into chunks. One chunk is 1/8 of EC ROM size. Found while flashing RW on lotus: `framework_tool --flash-rw-ec ec.bin` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 9bd367a0..147754f9 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -832,7 +832,30 @@ impl CrosEc { } fn erase_ec_flash(&self, offset: u32, size: u32) -> EcResult<()> { - EcRequestFlashErase { offset, size }.send_command(self) + // Erasing a big section takes too long sometimes and the linux kernel driver times out, so + // split it up into chunks. One chunk is 1/8 of EC ROM size. + let chunk_size = 0x10000; + let mut cur_offset = offset; + + while cur_offset < offset + size { + let rem_size = offset + size - cur_offset; + let cur_size = if rem_size < chunk_size { + rem_size + } else { + chunk_size + }; + debug!( + "EcRequestFlashErase (0x{:05X}, 0x{:05X})", + cur_offset, cur_size + ); + EcRequestFlashErase { + offset: cur_offset, + size: cur_size, + } + .send_command(self)?; + cur_offset += chunk_size; + } + Ok(()) } pub fn flash_notify(&self, flag: MecFlashNotify) -> EcResult<()> { From 3e25b80566f1c7db2f04668701a252788389cbdc Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 15:40:43 +0800 Subject: [PATCH 104/174] chromium_ec: Simplify flash output a bit ``` Will write flash from 0x40000 to 0x39000 in 1824*128B chunks Writing chunk 0: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 100: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 200: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 300: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 400: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 500: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 600: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 700: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 800: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 900: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1000: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1100: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1200: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1300: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1400: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1500: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1600: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1700: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX Writing chunk 1800: XXXXXXXXXXXXXXXXXXXXXXXX ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 37 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 147754f9..689c591d 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -742,14 +742,16 @@ impl CrosEc { println!("Erasing RW region"); self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?; + println!(" Done"); println!("Writing RW region"); self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data)?; + println!(" Done"); println!("Verifying RW region"); let flash_rw_data = self.read_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?; if rw_data == flash_rw_data { - println!("RW verify success"); + println!(" RW verify success"); } else { println!("RW verify fail"); } @@ -760,14 +762,16 @@ impl CrosEc { println!("Erasing RO region"); self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?; + println!(" Done"); println!("Writing RO region"); self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data)?; + println!(" Done"); println!("Verifying RO region"); let flash_ro_data = self.read_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?; if ro_data == flash_ro_data { - println!("RO verify success"); + println!(" RO verify success"); } else { println!("RO verify fail"); } @@ -777,34 +781,39 @@ impl CrosEc { self.flash_notify(MecFlashNotify::AccessSpiDone)?; self.flash_notify(MecFlashNotify::FirmwareDone)?; - println!("Flashing EC done. You can reboot the EC now"); - // TODO: Should we force a reboot if currently running one was reflashed? + if res.is_ok() { + println!("Flashing EC done. You can reboot the EC now"); + } Ok(()) } /// Write a big section of EC flash. Must be unlocked already fn write_ec_flash(&self, addr: u32, data: &[u8]) -> EcResult<()> { - let info = EcRequestFlashInfo {}.send_command(self)?; - println!("Flash info: {:?}", info); + // TODO: Use flash info to help guide ideal chunk size + // let info = EcRequestFlashInfo {}.send_command(self)?; //let chunk_size = ((0x80 / info.write_ideal_size) * info.write_ideal_size) as usize; + let chunk_size = 0x80; let chunks = data.len() / chunk_size; + println!( + " Will write flash from 0x{:X} to 0x{:X} in {}*{}B chunks", + addr, + data.len(), + chunks, + chunk_size + ); for chunk_no in 0..chunks { let offset = chunk_no * chunk_size; // Current chunk might be smaller if it's the last let cur_chunk_size = std::cmp::min(chunk_size, data.len() - chunk_no * chunk_size); if chunk_no % 100 == 0 { - println!(); - print!( - "Writing chunk {:>4}/{:>4} ({:>6}/{:>6}): X", - chunk_no, - chunks, - offset, - cur_chunk_size * chunks - ); + if chunk_no != 0 { + println!(); + } + print!(" Chunk {:>4}: X", chunk_no); } else { print!("X"); } From 5baf7a99e473167b21edc64ffdc284e1fd4f2ed9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 23:07:35 +0800 Subject: [PATCH 105/174] --flash-ec: Fail if verify failed The whole operation should fail Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 689c591d..a8e47c0d 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -704,6 +704,7 @@ impl CrosEc { /// | 40000 | 3C000 | 39000 | RO Region | /// | 79000 | 79FFF | 01000 | Flash Flags | pub fn reflash(&self, data: &[u8], ft: EcFlashType) -> EcResult<()> { + let mut res = Ok(()); if ft == EcFlashType::Full || ft == EcFlashType::Ro { if let Some(version) = ec_binary::read_ec_version(data, true) { println!("EC RO Version in File: {:?}", version.version); @@ -753,7 +754,8 @@ impl CrosEc { if rw_data == flash_rw_data { println!(" RW verify success"); } else { - println!("RW verify fail"); + error!("RW verify fail!"); + res = Err(EcError::DeviceError("RW verify fail!".to_string())); } } @@ -773,7 +775,8 @@ impl CrosEc { if ro_data == flash_ro_data { println!(" RO verify success"); } else { - println!("RO verify fail"); + error!("RO verify fail!"); + res = Err(EcError::DeviceError("RW verify fail!".to_string())); } } @@ -785,7 +788,7 @@ impl CrosEc { println!("Flashing EC done. You can reboot the EC now"); } - Ok(()) + res } /// Write a big section of EC flash. Must be unlocked already From a470530f7ca9f348f2aeda50c8600cca3bd859f7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 23:16:25 +0800 Subject: [PATCH 106/174] --flash-rw-ec: Allow --dry-run If you want to simulate flashing the EC but not actually do it. Goes through all the steps to make sure the execution plan is correct. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 20 +++++++++++ framework_lib/src/chromium_ec/mod.rs | 43 +++++++++++------------ framework_lib/src/commandline/clap_std.rs | 14 ++++++-- framework_lib/src/commandline/mod.rs | 22 +++++++++--- framework_lib/src/commandline/uefi.rs | 8 +++++ 5 files changed, 78 insertions(+), 29 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 6c3d50aa..51c2b0e8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -464,3 +464,23 @@ ESRT Entry 0 Last Attempt Version: 0x108 (264) Last Attempt Status: Success ``` + +## Flashing EC firmware + +**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render +your hardware unbootable. Please update your firmware using the official BIOS +update methods (Windows .exe, LVFS/FWUPD, EFI updater)! + +This command has not been thoroughly tested on all Framework Computer systems + +``` +# Simulate flashing RW (to see which blocks are updated) +> framework_tool --flash-rw-ec ec.bin --dry-run + +# Actually flash RW +> framework_tool --flash-rw-ec ec.bin + +# Boot into EC RW firmware (will crash your OS and reboot immediately) +# EC will boot back into RO if the system turned off for 30s +> framework_tool --reboot-ec jump-rw +``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a8e47c0d..d9bd6375 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -703,7 +703,7 @@ impl CrosEc { /// | 3C000 | 3FFFF | 04000 | Preserved | /// | 40000 | 3C000 | 39000 | RO Region | /// | 79000 | 79FFF | 01000 | Flash Flags | - pub fn reflash(&self, data: &[u8], ft: EcFlashType) -> EcResult<()> { + pub fn reflash(&self, data: &[u8], ft: EcFlashType, dry_run: bool) -> EcResult<()> { let mut res = Ok(()); if ft == EcFlashType::Full || ft == EcFlashType::Ro { if let Some(version) = ec_binary::read_ec_version(data, true) { @@ -724,11 +724,6 @@ impl CrosEc { } } - if ft == EcFlashType::Full || ft == EcFlashType::Ro { - println!("For safety reasons flashing RO firmware is disabled."); - return Ok(()); - } - println!("Unlocking flash"); self.flash_notify(MecFlashNotify::AccessSpi)?; self.flash_notify(MecFlashNotify::FirmwareStart)?; @@ -741,12 +736,12 @@ impl CrosEc { if ft == EcFlashType::Full || ft == EcFlashType::Rw { let rw_data = &data[FLASH_RW_BASE as usize..(FLASH_RW_BASE + FLASH_RW_SIZE) as usize]; - println!("Erasing RW region"); - self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE)?; + println!("Erasing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); + self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE, dry_run)?; println!(" Done"); - println!("Writing RW region"); - self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data)?; + println!("Writing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); + self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data, dry_run)?; println!(" Done"); println!("Verifying RW region"); @@ -763,11 +758,11 @@ impl CrosEc { let ro_data = &data[FLASH_RO_BASE as usize..(FLASH_RO_BASE + FLASH_RO_SIZE) as usize]; println!("Erasing RO region"); - self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE)?; + self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE, dry_run)?; println!(" Done"); println!("Writing RO region"); - self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data)?; + self.write_ec_flash(FLASH_BASE + FLASH_RO_BASE, ro_data, dry_run)?; println!(" Done"); println!("Verifying RO region"); @@ -792,7 +787,7 @@ impl CrosEc { } /// Write a big section of EC flash. Must be unlocked already - fn write_ec_flash(&self, addr: u32, data: &[u8]) -> EcResult<()> { + fn write_ec_flash(&self, addr: u32, data: &[u8], dry_run: bool) -> EcResult<()> { // TODO: Use flash info to help guide ideal chunk size // let info = EcRequestFlashInfo {}.send_command(self)?; //let chunk_size = ((0x80 / info.write_ideal_size) * info.write_ideal_size) as usize; @@ -822,10 +817,12 @@ impl CrosEc { } let chunk = &data[offset..offset + cur_chunk_size]; - let res = self.write_ec_flash_chunk(addr + offset as u32, chunk); - if let Err(err) = res { - println!(" Failed to write chunk: {:?}", err); - return Err(err); + if !dry_run { + let res = self.write_ec_flash_chunk(addr + offset as u32, chunk); + if let Err(err) = res { + println!(" Failed to write chunk: {:?}", err); + return Err(err); + } } } println!(); @@ -843,7 +840,7 @@ impl CrosEc { .send_command_extra(self, data) } - fn erase_ec_flash(&self, offset: u32, size: u32) -> EcResult<()> { + fn erase_ec_flash(&self, offset: u32, size: u32, dry_run: bool) -> EcResult<()> { // Erasing a big section takes too long sometimes and the linux kernel driver times out, so // split it up into chunks. One chunk is 1/8 of EC ROM size. let chunk_size = 0x10000; @@ -860,11 +857,13 @@ impl CrosEc { "EcRequestFlashErase (0x{:05X}, 0x{:05X})", cur_offset, cur_size ); - EcRequestFlashErase { - offset: cur_offset, - size: cur_size, + if !dry_run { + EcRequestFlashErase { + offset: cur_offset, + size: cur_size, + } + .send_command(self)?; } - .send_command(self)?; cur_offset += chunk_size; } Ok(()) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index aabd5468..2796057d 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -123,11 +123,11 @@ struct ClapCli { #[arg(long)] dump_ec_flash: Option, - /// Flash EC with new firmware from file + /// Flash EC (RO+RW) with new firmware from file - may render your hardware unbootable! #[arg(long)] flash_ec: Option, - /// Flash EC with new RO firmware from file + /// Flash EC with new RO firmware from file - may render your hardware unbootable! #[arg(long)] flash_ro_ec: Option, @@ -250,6 +250,14 @@ struct ClapCli { /// Run self-test to check if interaction with EC is possible #[arg(long, short)] test: bool, + + /// Force execution of an unsafe command - may render your hardware unbootable! + #[arg(long, short)] + force: bool, + + /// Simulate execution of a command (e.g. --flash-ec) + #[arg(long)] + dry_run: bool, } /// Parse a list of commandline arguments and return the struct @@ -424,6 +432,8 @@ pub fn parse(args: &[String]) -> Cli { pd_addrs, pd_ports, test: args.test, + dry_run: args.dry_run, + force: args.force, // TODO: Set help. Not very important because Clap handles this by itself help: false, // UEFI only for now. Don't need to handle diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 3906cbb1..ce7071ed 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -170,6 +170,8 @@ pub struct Cli { pub flash_rw_ec: Option, pub driver: Option, pub test: bool, + pub dry_run: bool, + pub force: bool, pub intrusion: bool, pub inputdeck: bool, pub inputdeck_mode: Option, @@ -594,7 +596,7 @@ fn print_esrt() { } } -fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) { +fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType, dry_run: bool) { #[cfg(feature = "uefi")] let data = crate::uefi::fs::shell_read_file(ec_bin_path); #[cfg(not(feature = "uefi"))] @@ -613,7 +615,7 @@ fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType) { println!("File"); println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); - if let Err(err) = ec.reflash(&data, flash_type) { + if let Err(err) = ec.reflash(&data, flash_type, dry_run) { println!("Error: {:?}", err); } else { println!("Success!"); @@ -1107,11 +1109,19 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { // TODO: Should have progress indicator dump_ec_flash(&ec, dump_path); } else if let Some(ec_bin_path) = &args.flash_ec { - flash_ec(&ec, ec_bin_path, EcFlashType::Full); + if args.force { + flash_ec(&ec, ec_bin_path, EcFlashType::Full, args.dry_run); + } else { + error!("Flashing EC RO region is unsafe. Use --flash-ec-rw instead"); + } } else if let Some(ec_bin_path) = &args.flash_ro_ec { - flash_ec(&ec, ec_bin_path, EcFlashType::Ro); + if args.force { + flash_ec(&ec, ec_bin_path, EcFlashType::Ro, args.dry_run); + } else { + error!("Flashing EC RO region is unsafe. Use --flash-ec-rw instead"); + } } else if let Some(ec_bin_path) = &args.flash_rw_ec { - flash_ec(&ec, ec_bin_path, EcFlashType::Rw); + flash_ec(&ec, ec_bin_path, EcFlashType::Rw, args.dry_run); } else if let Some(hash_file) = &args.hash { println!("Hashing file: {}", hash_file); #[cfg(feature = "uefi")] @@ -1193,6 +1203,8 @@ Options: --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] --hash Hash a file of arbitrary data --flash-gpu-descriptor <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type. + -f, --force Force execution of an unsafe command - may render your hardware unbootable! + --dry-run Simulate execution of a command (e.g. --flash-ec) -t, --test Run self-test to check if interaction with EC is possible -h, --help Print help information -b Print output one screen at a time diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 097c006f..09a43399 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -109,6 +109,8 @@ pub fn parse(args: &[String]) -> Cli { pd_addrs: None, pd_ports: None, test: false, + dry_run: false, + force: false, help: false, flash_gpu_descriptor: None, allupdate: false, @@ -504,6 +506,12 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "-t" || arg == "--test" { cli.test = true; found_an_option = true; + } else if arg == "-f" || arg == "--force" { + cli.force = true; + found_an_option = true; + } else if arg == "--dry-run" { + cli.dry_run = true; + found_an_option = true; } else if arg == "-h" || arg == "--help" { cli.help = true; found_an_option = true; From aafd4cd63631f699152fbc9676ce2d657492f74b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 May 2025 23:45:31 +0800 Subject: [PATCH 107/174] Use flashinfo to determine optimal erase size Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 52 ++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index d9bd6375..02631e50 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -724,6 +724,23 @@ impl CrosEc { } } + // Determine recommended flash parameters + let info = EcRequestFlashInfo {}.send_command(self)?; + + // Check that our hardcoded offsets are valid for the available flash + if FLASH_RO_SIZE + FLASH_RW_SIZE > info.flash_size { + return Err(EcError::DeviceError(format!( + "RO+RW larger than flash 0x{:X}", + { info.flash_size } + ))); + } + if FLASH_RW_BASE + FLASH_RW_SIZE > info.flash_size { + return Err(EcError::DeviceError(format!( + "RW overruns end of flash 0x{:X}", + { info.flash_size } + ))); + } + println!("Unlocking flash"); self.flash_notify(MecFlashNotify::AccessSpi)?; self.flash_notify(MecFlashNotify::FirmwareStart)?; @@ -736,11 +753,22 @@ impl CrosEc { if ft == EcFlashType::Full || ft == EcFlashType::Rw { let rw_data = &data[FLASH_RW_BASE as usize..(FLASH_RW_BASE + FLASH_RW_SIZE) as usize]; - println!("Erasing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); - self.erase_ec_flash(FLASH_BASE + FLASH_RW_BASE, FLASH_RW_SIZE, dry_run)?; + println!( + "Erasing RW region{}", + if dry_run { " (DRY RUN)" } else { "" } + ); + self.erase_ec_flash( + FLASH_BASE + FLASH_RW_BASE, + FLASH_RW_SIZE, + dry_run, + info.erase_block_size, + )?; println!(" Done"); - println!("Writing RW region{}", if dry_run { " (DRY RUN)" } else { "" }); + println!( + "Writing RW region{}", + if dry_run { " (DRY RUN)" } else { "" } + ); self.write_ec_flash(FLASH_BASE + FLASH_RW_BASE, rw_data, dry_run)?; println!(" Done"); @@ -758,7 +786,12 @@ impl CrosEc { let ro_data = &data[FLASH_RO_BASE as usize..(FLASH_RO_BASE + FLASH_RO_SIZE) as usize]; println!("Erasing RO region"); - self.erase_ec_flash(FLASH_BASE + FLASH_RO_BASE, FLASH_RO_SIZE, dry_run)?; + self.erase_ec_flash( + FLASH_BASE + FLASH_RO_BASE, + FLASH_RO_SIZE, + dry_run, + info.erase_block_size, + )?; println!(" Done"); println!("Writing RO region"); @@ -840,10 +873,15 @@ impl CrosEc { .send_command_extra(self, data) } - fn erase_ec_flash(&self, offset: u32, size: u32, dry_run: bool) -> EcResult<()> { + fn erase_ec_flash( + &self, + offset: u32, + size: u32, + dry_run: bool, + chunk_size: u32, + ) -> EcResult<()> { // Erasing a big section takes too long sometimes and the linux kernel driver times out, so - // split it up into chunks. One chunk is 1/8 of EC ROM size. - let chunk_size = 0x10000; + // split it up into chunks. let mut cur_offset = offset; while cur_offset < offset + size { From 22e39c259f614e58047203101c5bc44a99f91d18 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 28 May 2025 00:42:15 +0800 Subject: [PATCH 108/174] EXAMPLES: Add intel core ultra No example for retimer version and ESRT yet. Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 6c3d50aa..43d3e5e4 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -2,7 +2,7 @@ ## Check firmware versions -### BIOS (Mainboard, UEFI, EC, PD) +### BIOS (Mainboard, UEFI, EC, PD, Retimer) Example on Framework 13 AMD Ryzen AI 300 Series: @@ -23,6 +23,25 @@ PD Controllers [...] ``` +Example on Framework 13 Intel Core Ultra Series 1: + +``` +> framework_tool --versions +Mainboard Hardware + Type: Laptop 13 (AMD Ryzen AI 300 Series) + Revision: MassProduction +UEFI BIOS + Version: 03.03 + Release Date: 10/07/2024 +EC Firmware + Build version: marigold-3.0.3-278d300 2024-10-04 03:03:58 marigold1@ip-172-26-3-226 + Current image: RO +PD Controllers + Right (01): 0.0.08 (MainFw) + Left (23): 0.0.08 (MainFw) +[...] +``` + ### Camera (Framework 12, Framework 13, Framework 16) Example on Framework 12: @@ -135,6 +154,56 @@ CSME [...] ``` +### Firmware Version using ESRT (BIOS, Retimer, CSME) + +All systems have at least an entry for BIOS. Intel systems also have CSME and some Retimers. + +Example on Framework 13 Intel Core Ultra Series 1: + +``` +> sudo framework_tool --esrt +ESRT Table + ResourceCount: 4 + ResourceCountMax: 4 + ResourceVersion: 1 +ESRT Entry 0 + GUID: BDFFCE36-809C-4FA6-AECC-54536922F0E0 + GUID: MtlRetimer23 + Type: DeviceFirmware + Version: 0x270 (624) + Min FW Version: 0x0 (0) + Capsule Flags: 0x0 + Last Attempt Version: 0x270 (624) + Last Attempt Status: Success +ESRT Entry 1 + GUID: 32D8D677-EEBC-4947-8F8A-0693A45240E5 + GUID: MtlCsme + Type: DeviceFirmware + Version: 0x85D (2141) + Min FW Version: 0x3E8 (1000) + Capsule Flags: 0x0 + Last Attempt Version: 0x0 (0) + Last Attempt Status: Success +ESRT Entry 2 + GUID: C57FD615-2AC9-4154-BF34-4DC715344408 + GUID: MtlRetimer01 + Type: DeviceFirmware + Version: 0x270 (624) + Min FW Version: 0x0 (0) + Capsule Flags: 0x0 + Last Attempt Version: 0x270 (624) + Last Attempt Status: Success +ESRT Entry 3 + GUID: 72CECB9B-2B37-5EC2-A9FF-C739AABAADF3 + GUID: MtlBios + Type: SystemFirmware + Version: 0x303 (771) + Min FW Version: 0x303 (771) + Capsule Flags: 0x0 + Last Attempt Version: 0x303 (771) + Last Attempt Status: Success +``` + ## Check input deck status ### On Framework 12 From 51c266685dedbb804b26a2038954fe80d1c067ff Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 28 May 2025 22:38:37 +0800 Subject: [PATCH 109/174] README: Explain how to install with winget Available since https://github.com/microsoft/winget-pkgs/pull/259873 Signed-off-by: Daniel Schaefer --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 7fbcea32..38f2a2be 100644 --- a/README.md +++ b/README.md @@ -253,11 +253,13 @@ Capsule Header ``` ###### Running on Windows -Windows does not ship with a Chrome EC driver. However there is an open-source implementation that this tool can take advantage of. -The project is hosted on GitHub and you can download pre-built binaries -[there](https://github.com/DHowett/FrameworkWindowsUtils/releases). -The driver is not signed by Microsoft, so you will have to enable testsigning. +On newly released systems since 2025 the Framework driver installer includes the EC driver. +This includes Framework 12, Framework 13 AMD Ryzen AI 300, Framework Desktop. + +Previous platforms will be enabled next. + +Installing: `winget install FrameworkComputer.framework_tool` ##### Running on ChromeOS From 55e127a8993e76a3a01a073e13d1a34e765dfa68 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 3 Jun 2025 17:04:48 +0800 Subject: [PATCH 110/174] Add support for directly querying 3 PDs ``` > framework_tool --versions [...] PD Controllers PD 0: 0.0.0C (MainFw) PD 1: 0.0.0C (MainFw) PD 2: 0.0.0C (MainFw) [...] ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ce7071ed..793182d8 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -474,6 +474,27 @@ fn print_versions(ec: &CrosEc) { left.main_fw.app, left.active_fw ); } + } else if let Ok(PdVersions::Many(versions)) = ccgx_pd_vers { + for (i, version) in versions.into_iter().enumerate() { + if version.main_fw.app != version.backup_fw.app { + println!(" PD {}", 1); + println!( + " Main: {}{}", + version.main_fw.app, + active_mode(&version.active_fw, FwMode::MainFw) + ); + println!( + " Backup: {}{}", + version.backup_fw.app, + active_mode(&version.active_fw, FwMode::BackupFw) + ); + } else { + println!( + " PD {}: {} ({:?})", + i, version.main_fw.app, version.active_fw + ); + } + } } else if let Ok(PdVersions::Single(pd)) = ccgx_pd_vers { if pd.main_fw.app != pd.backup_fw.app { println!( From 28be5a2573a4c3e4d36d2e04a79356f889fa5771 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 3 Jun 2025 17:08:09 +0800 Subject: [PATCH 111/174] Framework16Amd7080: Add support for 3rd PD Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 7cdd6839..c22bf30e 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -54,6 +54,7 @@ impl PdPort { | Platform::Framework16Amd7080, PdPort::Right23, ) => 0x40, + (Platform::Framework16Amd7080, PdPort::Back) => 0x42, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 0x08, (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, // Framework Intel Platforms (CCG5 and CCG6) @@ -111,6 +112,7 @@ impl PdPort { | Platform::Framework12IntelGen13, PdPort::Right23, ) => 2, + (Platform::Framework16Amd7080, PdPort::Back) => 5, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 1, (Platform::FrameworkDesktopAmdAiMax300, _) => unsupported?, (Platform::UnknownSystem, _) => { From 1e58fdfda2a8af07ac000243dcf51dc5eff1fa2c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 18:24:10 +0800 Subject: [PATCH 112/174] Show ALS sensor on unknown systems If the system doesn't have motionsense we can't detect whether ALS is present or not based on EC information. Signed-off-by: Daniel Schaefer --- framework_lib/src/power.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/power.rs b/framework_lib/src/power.rs index af5963ec..91f87bda 100644 --- a/framework_lib/src/power.rs +++ b/framework_lib/src/power.rs @@ -305,10 +305,11 @@ pub fn print_sensors(ec: &CrosEc) { err => _ = print_err(err), } - // If we can't detect it based on motionsense + // If we can't detect it based on motionsense, check the system family + // If family is unknown, assume it has let als_family = matches!( smbios::get_family(), - Some(PlatformFamily::Framework13) | Some(PlatformFamily::Framework16) + Some(PlatformFamily::Framework13) | Some(PlatformFamily::Framework16) | None ); if has_als || als_family { From b7d82f28b9df38572cdba65b924b8c0a5816d54e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 18:40:11 +0800 Subject: [PATCH 113/174] EXAMPLES: Document fingerprint reader brightness Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 52b8c22c..73b4fb81 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -453,6 +453,37 @@ Keyboard backlight: 5% > framework_tool.exe --kblight 20 ``` +## Fingerprint/Powerbutton brightness + +On Framework 13 and Framework 16 the power button has an integrated fingerprint reader, hence the name. +On Framework 12 it does not, but the same command can be used. + +``` +# Check the current brightness +> framework_tool --fp-brightness +Fingerprint LED Brightness + Requested: Auto + Brightness: 55% + +# Set it to a custom perfentage +> framework_tool --fp-brightness 42 +Fingerprint LED Brightness + Requested: Custom + Brightness: 42% + +# Set to a specific level (like the BIOS setting does) +> framework_tool --fp-led-level high +Fingerprint LED Brightness + Requested: High + Brightness: 55% + +# Set it back to auto +> framework_tool --fp-led-level auto +Fingerprint LED Brightness + Requested: Auto + Brightness: 15% +``` + ## RGB LED (Framework Desktop) ``` From c8494e4a779aa250d87a9c929c5b3451407cdb15 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 3 Jun 2025 17:16:38 +0800 Subject: [PATCH 114/174] Allow --pd-ports and --pd-addrs to accept 3 ports If the platforms has only 2 or 1, pass same arguments. Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 10 +++++---- framework_lib/src/commandline/clap_std.rs | 8 +++---- framework_lib/src/commandline/mod.rs | 4 ++-- framework_lib/src/commandline/uefi.rs | 26 +++++++++++++---------- framework_lib/src/smbios.rs | 4 ++-- framework_lib/src/util.rs | 2 +- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index c22bf30e..ed55c8ee 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -39,8 +39,9 @@ impl PdPort { )); Ok(match (platform, self) { - (Platform::GenericFramework((left, _), _), PdPort::Left01) => *left, - (Platform::GenericFramework((_, right), _), PdPort::Right23) => *right, + (Platform::GenericFramework((left, _, _), _), PdPort::Left01) => *left, + (Platform::GenericFramework((_, right, _), _), PdPort::Right23) => *right, + (Platform::GenericFramework((_, _, back), _), PdPort::Back) => *back, // Framework AMD Platforms (CCG8) ( Platform::Framework13Amd7080 @@ -91,8 +92,9 @@ impl PdPort { ))); Ok(match (platform, self) { - (Platform::GenericFramework(_, (left, _)), PdPort::Left01) => *left, - (Platform::GenericFramework(_, (_, right)), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (left, _, _)), PdPort::Left01) => *left, + (Platform::GenericFramework(_, (_, right, _)), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (_, _, back)), PdPort::Back) => *back, (Platform::IntelGen11, _) => 6, (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left01) => 6, (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right23) => 7, diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 2796057d..8203e7c9 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -238,12 +238,12 @@ struct ClapCli { driver: Option, /// Specify I2C addresses of the PD chips (Advanced) - #[clap(number_of_values = 2, requires("pd_ports"))] + #[clap(number_of_values = 3, requires("pd_ports"))] #[arg(long)] pd_addrs: Vec, /// Specify I2C ports of the PD chips (Advanced) - #[clap(number_of_values = 2, requires("pd_addrs"))] + #[clap(number_of_values = 3, requires("pd_addrs"))] #[arg(long)] pd_ports: Vec, @@ -317,13 +317,13 @@ pub fn parse(args: &[String]) -> Cli { .unwrap(); let pd_addrs = match args.pd_addrs.len() { - 2 => Some((args.pd_addrs[0], args.pd_addrs[1])), + 3 => Some((args.pd_addrs[0], args.pd_addrs[1], args.pd_addrs[2])), 0 => None, // Checked by clap _ => unreachable!(), }; let pd_ports = match args.pd_ports.len() { - 2 => Some((args.pd_ports[0], args.pd_ports[1])), + 3 => Some((args.pd_ports[0], args.pd_ports[1], args.pd_ports[2])), 0 => None, // Checked by clap _ => unreachable!(), diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 793182d8..cc7283bb 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -193,8 +193,8 @@ pub struct Cli { pub reboot_ec: Option, pub ec_hib_delay: Option>, pub hash: Option, - pub pd_addrs: Option<(u16, u16)>, - pub pd_ports: Option<(u8, u8)>, + pub pd_addrs: Option<(u16, u16, u16)>, + pub pd_ports: Option<(u8, u8, u8)>, pub help: bool, pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 09a43399..98c27152 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -602,35 +602,39 @@ pub fn parse(args: &[String]) -> Cli { }; found_an_option = true; } else if arg == "--pd-addrs" { - cli.pd_addrs = if args.len() > i + 2 { + cli.pd_addrs = if args.len() > i + 3 { let left = args[i + 1].parse::(); let right = args[i + 2].parse::(); - if left.is_ok() && right.is_ok() { - Some((left.unwrap(), right.unwrap())) + let back = args[i + 3].parse::(); + if left.is_ok() && right.is_ok() && back.is_ok() { + Some((left.unwrap(), right.unwrap(), back.unwrap())) } else { println!( - "Invalid values for --pd-addrs: '{} {}'. Must be u16 integers.", + "Invalid values for --pd-addrs: '{} {} {}'. Must be u16 integers.", args[i + 1], - args[i + 2] + args[i + 2], + args[i + 3] ); None } } else { - println!("--pd-addrs requires two arguments, one for each address"); + println!("--pd-addrs requires three arguments, one for each address"); None }; found_an_option = true; } else if arg == "--pd-ports" { - cli.pd_ports = if args.len() > i + 2 { + cli.pd_ports = if args.len() > i + 3 { let left = args[i + 1].parse::(); let right = args[i + 2].parse::(); - if left.is_ok() && right.is_ok() { - Some((left.unwrap(), right.unwrap())) + let back = args[i + 3].parse::(); + if left.is_ok() && right.is_ok() && back.is_ok() { + Some((left.unwrap(), right.unwrap(), back.unwrap())) } else { println!( - "Invalid values for --pd-ports: '{} {}'. Must be u16 integers.", + "Invalid values for --pd-ports: '{} {} {}'. Must be u16 integers.", args[i + 1], - args[i + 2] + args[i + 2], + args[i + 3] ); None } diff --git a/framework_lib/src/smbios.rs b/framework_lib/src/smbios.rs index 935f0dc7..144ad210 100644 --- a/framework_lib/src/smbios.rs +++ b/framework_lib/src/smbios.rs @@ -47,7 +47,7 @@ pub enum ConfigDigit0 { pub fn is_framework() -> bool { if matches!( get_platform(), - Some(Platform::GenericFramework((_, _), (_, _))) | Some(Platform::UnknownSystem) + Some(Platform::GenericFramework((_, _, _), (_, _, _))) | Some(Platform::UnknownSystem) ) { return true; } @@ -292,7 +292,7 @@ pub fn get_platform() -> Option { let platform = &(*config).as_ref().unwrap().platform; if matches!( platform, - Platform::GenericFramework((_, _), (_, _)) | Platform::UnknownSystem + Platform::GenericFramework((_, _, _), (_, _, _)) | Platform::UnknownSystem ) { return Some(*platform); } diff --git a/framework_lib/src/util.rs b/framework_lib/src/util.rs index 69bdbd32..1716ed65 100644 --- a/framework_lib/src/util.rs +++ b/framework_lib/src/util.rs @@ -37,7 +37,7 @@ pub enum Platform { FrameworkDesktopAmdAiMax300, /// Generic Framework device /// pd_addrs, pd_ports - GenericFramework((u16, u16), (u8, u8)), + GenericFramework((u16, u16, u16), (u8, u8, u8)), UnknownSystem, } From 26d6eca2d0d6ed86a79b41ceb1454e4b1d4f4d28 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 18:49:29 +0800 Subject: [PATCH 115/174] --pd-info: Print 3rd pd info ``` > framework_tool --pd-info [...] Back Silicon ID: 0x???? Mode: MainFw Flash Row Size: 256 B Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0D FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0D ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index cc7283bb..9df8ae37 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -234,11 +234,14 @@ fn print_pd_details(ec: &CrosEc) { } let pd_01 = PdController::new(PdPort::Left01, ec.clone()); let pd_23 = PdController::new(PdPort::Right23, ec.clone()); + let pd_back = PdController::new(PdPort::Back, ec.clone()); println!("Left / Ports 01"); print_single_pd_details(&pd_01); println!("Right / Ports 23"); print_single_pd_details(&pd_23); + println!("Back"); + print_single_pd_details(&pd_back); } #[cfg(feature = "hidapi")] From b1169beb3c3162f805b58d39705a07e5b2e69eb7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:01:49 +0800 Subject: [PATCH 116/174] ccgx: Add silicon ID of Framework 13 Intel 12th Gen Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/binary.rs | 3 ++- framework_lib/src/ccgx/mod.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/ccgx/binary.rs b/framework_lib/src/ccgx/binary.rs index 09c16855..cb324221 100644 --- a/framework_lib/src/ccgx/binary.rs +++ b/framework_lib/src/ccgx/binary.rs @@ -106,7 +106,7 @@ fn read_metadata( let buffer = read_256_bytes(file_buffer, metadata_offset, flash_row_size)?; match ccgx { SiliconId::Ccg3 => parse_metadata_ccg3(&buffer), - SiliconId::Ccg5 | SiliconId::Ccg6 => parse_metadata_cyacd(&buffer), + SiliconId::Ccg5 | SiliconId::Ccg6Adl | SiliconId::Ccg6 => parse_metadata_cyacd(&buffer), SiliconId::Ccg8 => parse_metadata_cyacd2(&buffer) .map(|(fw_row_start, fw_size)| (fw_row_start / (flash_row_size as u32), fw_size)), } @@ -172,6 +172,7 @@ pub fn read_versions(file_buffer: &[u8], ccgx: SiliconId) -> Option (SMALL_ROW, 0x03FF, 0x03FE), SiliconId::Ccg5 => (LARGE_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG5), + SiliconId::Ccg6Adl => (SMALL_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG6), SiliconId::Ccg6 => (SMALL_ROW, FW1_METADATA_ROW, FW2_METADATA_ROW_CCG6), SiliconId::Ccg8 => (LARGE_ROW, FW1_METADATA_ROW_CCG8, FW2_METADATA_ROW_CCG8), }; diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 38997ace..1dea7ddf 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -105,7 +105,8 @@ struct CyAcd2Metadata { pub enum SiliconId { Ccg3 = 0x1D00, Ccg5 = 0x2100, - Ccg6 = 0x3000, + Ccg6Adl = 0x3000, + Ccg6 = 0x30A0, Ccg8 = 0x3580, } From f6b1353a3b692faa1d210f3f78a8793aaa92af0e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:05:17 +0800 Subject: [PATCH 117/174] ccgx: Add function to write to ccgx Pass writes through via I2C passthrough on the EC Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 44 +++++++++++++++++++ .../src/chromium_ec/i2c_passthrough.rs | 2 +- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index ed55c8ee..298e7f14 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -182,6 +182,21 @@ impl PdController { ) } + pub fn i2c_write(&self, addr: u16, data: &[u8]) -> EcResult { + trace!( + "I2C passthrough from I2C Port {} to I2C Addr {}", + self.port.i2c_port()?, + self.port.i2c_address()? + ); + i2c_write( + &self.ec, + self.port.i2c_port()?, + self.port.i2c_address()?, + addr, + data, + ) + } + fn ccgx_read(&self, reg: ControlRegisters, len: u16) -> EcResult> { let mut data: Vec = Vec::with_capacity(len.into()); @@ -204,6 +219,35 @@ impl PdController { Ok(data) } + fn ccgx_write(&self, reg: ControlRegisters, data: &[u8]) -> EcResult<()> { + let addr = reg as u16; + trace!( + "ccgx_write(reg: {:?}, addr: {}, data.len(): {}", + reg, + addr, + data.len() + ); + let mut data_written = 0; + + while data_written < data.len() { + let chunk_len = std::cmp::min(MAX_I2C_CHUNK, data.len()); + let buffer = &data[data_written..data_written + chunk_len]; + let offset = addr + data_written as u16; + + let i2c_response = self.i2c_write(offset, buffer)?; + if let Err(EcError::DeviceError(err)) = i2c_response.is_successful() { + return Err(EcError::DeviceError(format!( + "I2C write was not successful: {:?}", + err + ))); + } + + data_written += chunk_len; + } + + Ok(()) + } + pub fn get_silicon_id(&self) -> EcResult { let data = self.ccgx_read(ControlRegisters::SiliconId, 2)?; assert_win_len(data.len(), 2); diff --git a/framework_lib/src/chromium_ec/i2c_passthrough.rs b/framework_lib/src/chromium_ec/i2c_passthrough.rs index dfebaafc..03cb974e 100644 --- a/framework_lib/src/chromium_ec/i2c_passthrough.rs +++ b/framework_lib/src/chromium_ec/i2c_passthrough.rs @@ -155,7 +155,7 @@ pub fn i2c_write( let data = ec.send_command(EcCommands::I2cPassthrough as u16, 0, &buffer)?; let res: _EcI2cPassthruResponse = unsafe { std::ptr::read(data.as_ptr() as *const _) }; - assert_eq!(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header + util::assert_win_len(data.len(), size_of::<_EcI2cPassthruResponse>()); // No extra data other than the header debug_assert_eq!(res.messages as usize, messages.len()); Ok(EcI2cPassthruResponse { i2c_status: res.i2c_status, From 7c9c7dcadaf03796feb51a9fd644e115f3853bc1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:13:20 +0800 Subject: [PATCH 118/174] ccgx: Add more command definitions Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 298e7f14..4a0134e9 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -14,12 +14,30 @@ use crate::util::{assert_win_len, Config, Platform}; use super::*; +const _HPI_FLASH_ENTER_SIGNATURE: char = 'P'; +const _HPI_JUMP_TO_ALT_SIGNATURE: char = 'A'; +const _HPI_JUMP_TO_BOOT_SIGNATURE: char = 'J'; +const HPI_RESET_SIGNATURE: char = 'R'; +const _HPI_FLASH_RW_SIGNATURE: char = 'F'; +const HPI_RESET_DEV_CMD: u8 = 1; +const _HPI_FLASH_READ_CMD: u8 = 0; +const _HPI_FLASH_WRITE_CMD: u8 = 1; + enum ControlRegisters { DeviceMode = 0, SiliconId = 2, // Two bytes long, First LSB, then MSB + _InterruptStatus = 0x06, + _JumpToBoot = 0x07, + ResetRequest = 0x08, + _FlashmodeEnter = 0x0A, + _ValidateFw = 0x0B, + _FlashSignature = 0x0C, BootLoaderVersion = 0x10, Firmware1Version = 0x18, Firmware2Version = 0x20, + PdPortsEnable = 0x2C, + _ResponseType = 0x7E, + _FlashRwMem = 0x0200, } #[derive(Debug)] From cdf538d927cf0f1fa671c1a08ab58d1d65ef54f2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:14:19 +0800 Subject: [PATCH 119/174] ccgx: Derive more traits on the structs Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 4a0134e9..31945b32 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -23,6 +23,7 @@ const HPI_RESET_DEV_CMD: u8 = 1; const _HPI_FLASH_READ_CMD: u8 = 0; const _HPI_FLASH_WRITE_CMD: u8 = 1; +#[derive(Debug, Copy, Clone)] enum ControlRegisters { DeviceMode = 0, SiliconId = 2, // Two bytes long, First LSB, then MSB @@ -40,7 +41,7 @@ enum ControlRegisters { _FlashRwMem = 0x0200, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum PdPort { Left01, Right23, @@ -148,7 +149,7 @@ pub struct PdController { ec: CrosEc, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone, Copy)] pub enum FwMode { BootLoader = 0, /// Backup CCGX firmware (No 1) From c71105ff81c6046ed1414138532d32f618d4fcd8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:15:00 +0800 Subject: [PATCH 120/174] ccgx: Add reset_device function framwork_tool --pd-reset 0 framwork_tool --pd-reset 1 Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 8 ++++++++ framework_lib/src/commandline/clap_std.rs | 5 +++++ framework_lib/src/commandline/mod.rs | 12 ++++++++++++ framework_lib/src/commandline/uefi.rs | 17 +++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 31945b32..c3038c5b 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -358,4 +358,12 @@ impl PdController { base_ver, app_ver ); } + + pub fn reset_device(&self) -> EcResult<()> { + self.ccgx_write( + ControlRegisters::ResetRequest, + &[HPI_RESET_SIGNATURE as u8, HPI_RESET_DEV_CMD], + )?; + Ok(()) + } } diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 8203e7c9..1c201ce9 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -83,6 +83,10 @@ struct ClapCli { #[arg(long)] pd_info: bool, + /// Reset a specific PD controller (for debugging only) + #[arg(long)] + pd_reset: Option, + /// Show details about connected DP or HDMI Expansion Cards #[arg(long)] dp_hdmi_info: bool, @@ -376,6 +380,7 @@ pub fn parse(args: &[String]) -> Cli { autofanctrl: args.autofanctrl, pdports: args.pdports, pd_info: args.pd_info, + pd_reset: args.pd_reset, dp_hdmi_info: args.dp_hdmi_info, dp_hdmi_update: args .dp_hdmi_update diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 9df8ae37..b3adb575 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -156,6 +156,7 @@ pub struct Cli { pub pdports: bool, pub privacy: bool, pub pd_info: bool, + pub pd_reset: Option, pub dp_hdmi_info: bool, pub dp_hdmi_update: Option, pub audio_card_info: bool, @@ -999,6 +1000,17 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { smbios_info(); } else if args.pd_info { print_pd_details(&ec); + } else if let Some(pd) = args.pd_reset { + println!("Resetting PD {}...", pd); + print_err(match pd { + 0 => PdController::new(PdPort::Left01, ec.clone()).reset_device(), + 1 => PdController::new(PdPort::Right23, ec.clone()).reset_device(), + 2 => PdController::new(PdPort::Back, ec.clone()).reset_device(), + _ => { + error!("PD {} does not exist", pd); + Ok(()) + } + }); } else if args.dp_hdmi_info { #[cfg(feature = "hidapi")] print_dp_hdmi_details(true); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 98c27152..49c42490 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -70,6 +70,7 @@ pub fn parse(args: &[String]) -> Cli { autofanctrl: false, pdports: false, pd_info: false, + pd_reset: None, dp_hdmi_info: false, dp_hdmi_update: None, audio_card_info: false, @@ -518,6 +519,22 @@ pub fn parse(args: &[String]) -> Cli { } else if arg == "--pd-info" { cli.pd_info = true; found_an_option = true; + } else if arg == "--pd-reset" { + cli.pd_reset = if args.len() > i + 1 { + if let Ok(pd) = args[i + 1].parse::() { + Some(pd) + } else { + println!( + "Invalid value for --pd-reset: '{}'. Must be 0 or 1.", + args[i + 1], + ); + None + } + } else { + println!("--pd-reset requires specifying the PD controller"); + None + }; + found_an_option = true; } else if arg == "--privacy" { cli.privacy = true; found_an_option = true; From 5b10d0ffec0053d716d67c1a3bbab6302dd6f353 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 4 Jun 2025 21:16:29 +0800 Subject: [PATCH 121/174] ccgx: Add enable_ports function To disable or enable all ports of a PD controller framwork_tool --pd-disable 0 framwork_tool --pd-enable 1 framwork_tool --pd-disable 0 framwork_tool --pd-enable 1 Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/device.rs | 6 ++++ framework_lib/src/commandline/clap_std.rs | 10 +++++++ framework_lib/src/commandline/mod.rs | 24 ++++++++++++++++ framework_lib/src/commandline/uefi.rs | 34 +++++++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index c3038c5b..8f841b4f 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -366,4 +366,10 @@ impl PdController { )?; Ok(()) } + + pub fn enable_ports(&self, enable: bool) -> EcResult<()> { + let mask = if enable { 0b11 } else { 0b00 }; + self.ccgx_write(ControlRegisters::PdPortsEnable, &[mask])?; + Ok(()) + } } diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 1c201ce9..52eddbeb 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -87,6 +87,14 @@ struct ClapCli { #[arg(long)] pd_reset: Option, + /// Disable all ports on a specific PD controller (for debugging only) + #[arg(long)] + pd_disable: Option, + + /// Enable all ports on a specific PD controller (for debugging only) + #[arg(long)] + pd_enable: Option, + /// Show details about connected DP or HDMI Expansion Cards #[arg(long)] dp_hdmi_info: bool, @@ -381,6 +389,8 @@ pub fn parse(args: &[String]) -> Cli { pdports: args.pdports, pd_info: args.pd_info, pd_reset: args.pd_reset, + pd_disable: args.pd_disable, + pd_enable: args.pd_enable, dp_hdmi_info: args.dp_hdmi_info, dp_hdmi_update: args .dp_hdmi_update diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index b3adb575..637b8f3f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -157,6 +157,8 @@ pub struct Cli { pub privacy: bool, pub pd_info: bool, pub pd_reset: Option, + pub pd_disable: Option, + pub pd_enable: Option, pub dp_hdmi_info: bool, pub dp_hdmi_update: Option, pub audio_card_info: bool, @@ -1011,6 +1013,28 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Ok(()) } }); + } else if let Some(pd) = args.pd_disable { + println!("Disabling PD {}...", pd); + print_err(match pd { + 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(false), + 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(false), + 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(false), + _ => { + error!("PD {} does not exist", pd); + Ok(()) + } + }); + } else if let Some(pd) = args.pd_enable { + println!("Enabling PD {}...", pd); + print_err(match pd { + 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(true), + 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(true), + 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(true), + _ => { + error!("PD {} does not exist", pd); + Ok(()) + } + }); } else if args.dp_hdmi_info { #[cfg(feature = "hidapi")] print_dp_hdmi_details(true); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 49c42490..d21938ae 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -71,6 +71,8 @@ pub fn parse(args: &[String]) -> Cli { pdports: false, pd_info: false, pd_reset: None, + pd_disable: None, + pd_enable: None, dp_hdmi_info: false, dp_hdmi_update: None, audio_card_info: false, @@ -535,6 +537,38 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--pd-disable" { + cli.pd_reset = if args.len() > i + 1 { + if let Ok(pd) = args[i + 1].parse::() { + Some(pd) + } else { + println!( + "Invalid value for --pd-disable: '{}'. Must be 0 or 1.", + args[i + 1], + ); + None + } + } else { + println!("--pd-disable requires specifying the PD controller"); + None + }; + found_an_option = true; + } else if arg == "--pd-enable" { + cli.pd_enable = if args.len() > i + 1 { + if let Ok(pd) = args[i + 1].parse::() { + Some(pd) + } else { + println!( + "Invalid value for --pd-enable: '{}'. Must be 0 or 1.", + args[i + 1], + ); + None + } + } else { + println!("--pd-enable requires specifying the PD controller"); + None + }; + found_an_option = true; } else if arg == "--privacy" { cli.privacy = true; found_an_option = true; From 64381fcc03ca5b9406d4f1514d188736b76836a5 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 14:39:27 +0800 Subject: [PATCH 122/174] EXAMPLES: Move advanced examples to a separate file Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 41 +---------------------- EXAMPLES_ADVANCED.md | 78 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 40 deletions(-) create mode 100644 EXAMPLES_ADVANCED.md diff --git a/EXAMPLES.md b/EXAMPLES.md index 73b4fb81..1ea7f82c 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -544,43 +544,4 @@ It's not controlled by the EC, use https://keyboard.frame.work. Mostly for debugging firmware. -### Check EFI Resource Table - -On Framework Desktop: - -``` -> sudo framework_tool --esrt -ESRT Table - ResourceCount: 1 - ResourceCountMax: 1 - ResourceVersion: 1 -ESRT Entry 0 - GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856 - GUID: DesktopAmdAi300Bios - Type: SystemFirmware - Version: 0x204 (516) - Min FW Version: 0x100 (256) - Capsule Flags: 0x0 - Last Attempt Version: 0x108 (264) - Last Attempt Status: Success -``` - -## Flashing EC firmware - -**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render -your hardware unbootable. Please update your firmware using the official BIOS -update methods (Windows .exe, LVFS/FWUPD, EFI updater)! - -This command has not been thoroughly tested on all Framework Computer systems - -``` -# Simulate flashing RW (to see which blocks are updated) -> framework_tool --flash-rw-ec ec.bin --dry-run - -# Actually flash RW -> framework_tool --flash-rw-ec ec.bin - -# Boot into EC RW firmware (will crash your OS and reboot immediately) -# EC will boot back into RO if the system turned off for 30s -> framework_tool --reboot-ec jump-rw -``` +See [EXAMPLES_ADVANCED.md](EXAMPLES_ADVANCED.md) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md new file mode 100644 index 00000000..18146858 --- /dev/null +++ b/EXAMPLES_ADVANCED.md @@ -0,0 +1,78 @@ +# Advanced debugging + +## PD + +### Check PD state + +Example on Framework 13 AMD Ryzen AI 300 +``` +> sudo framework_tool.exe --pd-info +Left / Ports 01 + Silicon ID: 0x3580 + Mode: MainFw + Flash Row Size: 256 B + Bootloader Version: Base: 3.6.0.009, App: 0.0.01 + FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B + FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B +Right / Ports 23 + Silicon ID: 0x3580 + Mode: MainFw + Flash Row Size: 256 B + Bootloader Version: Base: 3.6.0.009, App: 0.0.01 + FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B + FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B +``` + +### Disable/enable/reset PD + +``` +# Disable all ports on PD 0 +> sudo framework_tool --pd-disable 0 + +# Reset PD 0 (enables all ports again) +> sudo framework_tool --pd-reset 0 + +# Or enable all ports on PD 0 without resetting it +> sudo framework_tool --pd-enable 0 +``` + +### Check EFI Resource Table + +On Framework Desktop: + +``` +> sudo framework_tool --esrt +ESRT Table + ResourceCount: 1 + ResourceCountMax: 1 + ResourceVersion: 1 +ESRT Entry 0 + GUID: EB68DBAE-3AEF-5077-92AE-9016D1F0C856 + GUID: DesktopAmdAi300Bios + Type: SystemFirmware + Version: 0x204 (516) + Min FW Version: 0x100 (256) + Capsule Flags: 0x0 + Last Attempt Version: 0x108 (264) + Last Attempt Status: Success +``` + +## Flashing EC firmware + +**IMPORTANT** Flashing EC firmware yourself is not recommended. It may render +your hardware unbootable. Please update your firmware using the official BIOS +update methods (Windows .exe, LVFS/FWUPD, EFI updater)! + +This command has not been thoroughly tested on all Framework Computer systems + +``` +# Simulate flashing RW (to see which blocks are updated) +> framework_tool --flash-rw-ec ec.bin --dry-run + +# Actually flash RW +> framework_tool --flash-rw-ec ec.bin + +# Boot into EC RW firmware (will crash your OS and reboot immediately) +# EC will boot back into RO if the system turned off for 30s +> framework_tool --reboot-ec jump-rw +``` From 8c5ce06b6768cd4df72ed72aa7dc0ef54b0b8df7 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 14:51:02 +0800 Subject: [PATCH 123/174] --pd-info: Print which ports are enabled Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 3 +++ framework_lib/src/ccgx/device.rs | 6 ++++++ framework_lib/src/commandline/mod.rs | 11 +++++++++++ 3 files changed, 20 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 18146858..cd7d9b25 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -5,12 +5,14 @@ ### Check PD state Example on Framework 13 AMD Ryzen AI 300 + ``` > sudo framework_tool.exe --pd-info Left / Ports 01 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B + Ports Enabled: 0, 1 Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B @@ -18,6 +20,7 @@ Right / Ports 23 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B + Ports Enabled: 0, 1 Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 8f841b4f..064e4acf 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -372,4 +372,10 @@ impl PdController { self.ccgx_write(ControlRegisters::PdPortsEnable, &[mask])?; Ok(()) } + + pub fn get_port_status(&self) -> EcResult { + let data = self.ccgx_read(ControlRegisters::PdPortsEnable, 1)?; + assert_win_len(data.len(), 1); + Ok(data[0]) + } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 637b8f3f..dab6b10f 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -227,6 +227,17 @@ fn print_single_pd_details(pd: &PdController) { } else { println!(" Failed to device info"); } + if let Ok(port_mask) = pd.get_port_status() { + let ports = match port_mask { + 1 => "0", + 2 => "1", + 3 => "0, 1", + _ => "None", + }; + println!(" Ports Enabled: {}", ports); + } else { + println!(" Ports Enabled: Unknown"); + } pd.print_fw_info(); } From 3f02cdcb805aee212b801ecf9befd970622c3fa2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 15:14:20 +0800 Subject: [PATCH 124/174] --pd-info: Bail early if PD isn't there If silicon ID can't be read then likely the PD isn't there. Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index dab6b10f..2da426ac 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -220,6 +220,7 @@ fn print_single_pd_details(pd: &PdController) { println!(" Silicon ID: 0x{:X}", si); } else { println!(" Failed to read Silicon ID/Family"); + return; } if let Ok((mode, frs)) = pd.get_device_info() { println!(" Mode: {:?}", mode); From a36356dcfccb318f207f1de672d4708aae4d329b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 15:53:41 +0800 Subject: [PATCH 125/174] Bump version to 0.4.3 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 2f9f5c9a..f6c79ef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,7 +408,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.4.2" +version = "0.4.3" dependencies = [ "built", "clap", @@ -440,7 +440,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.4.2" +version = "0.4.3" dependencies = [ "embed-resource", "framework_lib", @@ -451,7 +451,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.4.2" +version = "0.4.3" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 8d362370..b3af9f25 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.4.2" +version = "0.4.3" description = "Library to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 8da6a35c..5af4bf0d 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.4.2" +version = "0.4.3" description = "Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index 852fa7ec..adc8b14b 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.4.2" +version = "0.4.3" description = "UEFI Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" From f8e4022a8f362978f68500e177691db32b7dfb88 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 29 May 2025 18:05:19 +0800 Subject: [PATCH 126/174] README: Add nix-shell Signed-off-by: Daniel Schaefer --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 38f2a2be..fc913e21 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,14 @@ make -C framework_uefi ls -l framework_uefi/build/x86_64-unknown-uefi/boot.efi ``` +### Dependencies + +``` +# NixOS +nix-shell --run fish -p cargo systemd udev hidapi pkg-config +direnv shell +``` + ## Install local package ``` From 3daf204fe965b5a87d06638f2b5f8d5352f6af2a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 16:11:21 +0800 Subject: [PATCH 127/174] portio_mec: Don't print init Would also appear on non-mec systems if we can't detect whether they have mec or not. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/portio_mec.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/portio_mec.rs b/framework_lib/src/chromium_ec/portio_mec.rs index 774acb99..1f855d0f 100644 --- a/framework_lib/src/chromium_ec/portio_mec.rs +++ b/framework_lib/src/chromium_ec/portio_mec.rs @@ -25,7 +25,6 @@ const _MEC_LPC_DATA_REGISTER3: u16 = 0x0807; pub fn init() -> bool { #[cfg(target_os = "linux")] unsafe { - println!("Init MEC"); let res = ioperm(EC_LPC_ADDR_HOST_DATA as u64, 8, 1); if res != 0 { error!("ioperm failed. portio driver is likely block by Linux kernel lockdown mode"); From 0b0fa1d6b0128a4d79d23bf9bfcc500eb205b244 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 16:32:03 +0800 Subject: [PATCH 128/174] Correctly identify right and left PD controllers I wrote the PD code long long ago when I was confused about which PD controller was which, now I know that on every system ports 0 and 1 are on the right side, ports 1 and 2 on the left Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 4 +-- framework_lib/src/ccgx/device.rs | 28 +++++++++---------- framework_lib/src/ccgx/mod.rs | 4 +-- framework_lib/src/commandline/mod.rs | 40 ++++++++++++++-------------- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index cd7d9b25..e69817ee 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -8,7 +8,7 @@ Example on Framework 13 AMD Ryzen AI 300 ``` > sudo framework_tool.exe --pd-info -Left / Ports 01 +Right / Ports 01 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B @@ -16,7 +16,7 @@ Left / Ports 01 Bootloader Version: Base: 3.6.0.009, App: 0.0.01 FW1 (Backup) Version: Base: 3.7.0.197, App: 0.0.0B FW2 (Main) Version: Base: 3.7.0.197, App: 0.0.0B -Right / Ports 23 +Left / Ports 23 Silicon ID: 0x3580 Mode: MainFw Flash Row Size: 256 B diff --git a/framework_lib/src/ccgx/device.rs b/framework_lib/src/ccgx/device.rs index 064e4acf..bd13f6df 100644 --- a/framework_lib/src/ccgx/device.rs +++ b/framework_lib/src/ccgx/device.rs @@ -43,8 +43,8 @@ enum ControlRegisters { #[derive(Debug, PartialEq, Clone, Copy)] pub enum PdPort { - Left01, - Right23, + Right01, + Left23, Back, } @@ -58,21 +58,21 @@ impl PdPort { )); Ok(match (platform, self) { - (Platform::GenericFramework((left, _, _), _), PdPort::Left01) => *left, - (Platform::GenericFramework((_, right, _), _), PdPort::Right23) => *right, + (Platform::GenericFramework((left, _, _), _), PdPort::Right01) => *left, + (Platform::GenericFramework((_, right, _), _), PdPort::Left23) => *right, (Platform::GenericFramework((_, _, back), _), PdPort::Back) => *back, // Framework AMD Platforms (CCG8) ( Platform::Framework13Amd7080 | Platform::Framework13AmdAi300 | Platform::Framework16Amd7080, - PdPort::Left01, + PdPort::Right01, ) => 0x42, ( Platform::Framework13Amd7080 | Platform::Framework13AmdAi300 | Platform::Framework16Amd7080, - PdPort::Right23, + PdPort::Left23, ) => 0x40, (Platform::Framework16Amd7080, PdPort::Back) => 0x42, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 0x08, @@ -84,7 +84,7 @@ impl PdPort { | Platform::IntelGen12 | Platform::IntelGen13 | Platform::IntelCoreUltra1, - PdPort::Left01, + PdPort::Right01, ) => 0x08, ( Platform::Framework12IntelGen13 @@ -92,7 +92,7 @@ impl PdPort { | Platform::IntelGen12 | Platform::IntelGen13 | Platform::IntelCoreUltra1, - PdPort::Right23, + PdPort::Left23, ) => 0x40, (Platform::UnknownSystem, _) => { Err(EcError::DeviceError("Unsupported platform".to_string()))? @@ -111,19 +111,19 @@ impl PdPort { ))); Ok(match (platform, self) { - (Platform::GenericFramework(_, (left, _, _)), PdPort::Left01) => *left, - (Platform::GenericFramework(_, (_, right, _)), PdPort::Right23) => *right, + (Platform::GenericFramework(_, (left, _, _)), PdPort::Right01) => *left, + (Platform::GenericFramework(_, (_, right, _)), PdPort::Left23) => *right, (Platform::GenericFramework(_, (_, _, back)), PdPort::Back) => *back, (Platform::IntelGen11, _) => 6, - (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left01) => 6, - (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right23) => 7, + (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Right01) => 6, + (Platform::IntelGen12 | Platform::IntelGen13, PdPort::Left23) => 7, ( Platform::Framework13Amd7080 | Platform::Framework16Amd7080 | Platform::IntelCoreUltra1 | Platform::Framework13AmdAi300 | Platform::Framework12IntelGen13, - PdPort::Left01, + PdPort::Right01, ) => 1, ( Platform::Framework13Amd7080 @@ -131,7 +131,7 @@ impl PdPort { | Platform::IntelCoreUltra1 | Platform::Framework13AmdAi300 | Platform::Framework12IntelGen13, - PdPort::Right23, + PdPort::Left23, ) => 2, (Platform::Framework16Amd7080, PdPort::Back) => 5, (Platform::FrameworkDesktopAmdAiMax300, PdPort::Back) => 1, diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index 1dea7ddf..f7269b90 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -252,8 +252,8 @@ pub enum MainPdVersions { } pub fn get_pd_controller_versions(ec: &CrosEc) -> EcResult { - let pd01 = PdController::new(PdPort::Left01, ec.clone()).get_fw_versions(); - let pd23 = PdController::new(PdPort::Right23, ec.clone()).get_fw_versions(); + let pd01 = PdController::new(PdPort::Right01, ec.clone()).get_fw_versions(); + let pd23 = PdController::new(PdPort::Left23, ec.clone()).get_fw_versions(); let pd_back = PdController::new(PdPort::Back, ec.clone()).get_fw_versions(); match (pd01, pd23, pd_back) { diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 2da426ac..ca92266a 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -247,13 +247,13 @@ fn print_pd_details(ec: &CrosEc) { println!("Only supported on Framework systems"); return; } - let pd_01 = PdController::new(PdPort::Left01, ec.clone()); - let pd_23 = PdController::new(PdPort::Right23, ec.clone()); + let pd_01 = PdController::new(PdPort::Right01, ec.clone()); + let pd_23 = PdController::new(PdPort::Left23, ec.clone()); let pd_back = PdController::new(PdPort::Back, ec.clone()); - println!("Left / Ports 01"); + println!("Right / Ports 01"); print_single_pd_details(&pd_01); - println!("Right / Ports 23"); + println!("Left / Ports 23"); print_single_pd_details(&pd_23); println!("Back"); print_single_pd_details(&pd_back); @@ -570,13 +570,13 @@ fn print_versions(ec: &CrosEc) { | esrt::ADL_RETIMER01_GUID | esrt::RPL_RETIMER01_GUID | esrt::MTL_RETIMER01_GUID => { - left_retimer = Some(entry.fw_version); + right_retimer = Some(entry.fw_version); } esrt::TGL_RETIMER23_GUID | esrt::ADL_RETIMER23_GUID | esrt::RPL_RETIMER23_GUID | esrt::MTL_RETIMER23_GUID => { - right_retimer = Some(entry.fw_version); + left_retimer = Some(entry.fw_version); } _ => {} } @@ -1017,8 +1017,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(pd) = args.pd_reset { println!("Resetting PD {}...", pd); print_err(match pd { - 0 => PdController::new(PdPort::Left01, ec.clone()).reset_device(), - 1 => PdController::new(PdPort::Right23, ec.clone()).reset_device(), + 0 => PdController::new(PdPort::Right01, ec.clone()).reset_device(), + 1 => PdController::new(PdPort::Left23, ec.clone()).reset_device(), 2 => PdController::new(PdPort::Back, ec.clone()).reset_device(), _ => { error!("PD {} does not exist", pd); @@ -1028,8 +1028,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(pd) = args.pd_disable { println!("Disabling PD {}...", pd); print_err(match pd { - 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(false), - 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(false), + 0 => PdController::new(PdPort::Right01, ec.clone()).enable_ports(false), + 1 => PdController::new(PdPort::Left23, ec.clone()).enable_ports(false), 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(false), _ => { error!("PD {} does not exist", pd); @@ -1039,8 +1039,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if let Some(pd) = args.pd_enable { println!("Enabling PD {}...", pd); print_err(match pd { - 0 => PdController::new(PdPort::Left01, ec.clone()).enable_ports(true), - 1 => PdController::new(PdPort::Right23, ec.clone()).enable_ports(true), + 0 => PdController::new(PdPort::Right01, ec.clone()).enable_ports(true), + 1 => PdController::new(PdPort::Left23, ec.clone()).enable_ports(true), 2 => PdController::new(PdPort::Back, ec.clone()).enable_ports(true), _ => { error!("PD {} does not exist", pd); @@ -1376,8 +1376,8 @@ fn selftest(ec: &CrosEc) -> Option<()> { println!(" - OK"); } - let pd_01 = PdController::new(PdPort::Left01, ec.clone()); - let pd_23 = PdController::new(PdPort::Right23, ec.clone()); + let pd_01 = PdController::new(PdPort::Right01, ec.clone()); + let pd_23 = PdController::new(PdPort::Left23, ec.clone()); print!(" Getting PD01 info through I2C tunnel"); print_err(pd_01.get_silicon_id())?; print_err(pd_01.get_device_info())?; @@ -1554,22 +1554,22 @@ pub fn analyze_capsule(data: &[u8]) -> Option { println!(" Type: Framework RPL Insyde BIOS"); } esrt::TGL_RETIMER01_GUID => { - println!(" Type: Framework TGL Retimer01 (Left)"); + println!(" Type: Framework TGL Retimer01 (Right)"); } esrt::TGL_RETIMER23_GUID => { - println!(" Type: Framework TGL Retimer23 (Right)"); + println!(" Type: Framework TGL Retimer23 (Left)"); } esrt::ADL_RETIMER01_GUID => { - println!(" Type: Framework ADL Retimer01 (Left)"); + println!(" Type: Framework ADL Retimer01 (Right)"); } esrt::ADL_RETIMER23_GUID => { - println!(" Type: Framework ADL Retimer23 (Right)"); + println!(" Type: Framework ADL Retimer23 (Left)"); } esrt::RPL_RETIMER01_GUID => { - println!(" Type: Framework RPL Retimer01 (Left)"); + println!(" Type: Framework RPL Retimer01 (Right)"); } esrt::RPL_RETIMER23_GUID => { - println!(" Type: Framework RPL Retimer23 (Right)"); + println!(" Type: Framework RPL Retimer23 (Left)"); } esrt::WINUX_GUID => { println!(" Type: Windows UX capsule"); From f8b5f58cbd115c8721c9b1f3b0aa124ddfc4ad08 Mon Sep 17 00:00:00 2001 From: Kieran Levin Date: Thu, 5 Jun 2025 01:38:07 -0700 Subject: [PATCH 129/174] fw16: add command to write expansion bay eeprom from a file Example invocation: framework_tool.efi --flash-gpu-descriptor-file pcie_4x2.bin TEST=fail: if the write fails we will error correctly TEST=pass: the command will succeed. TEST=dump contents using ec console: i2c read I2C_PORT5 0x50 0x0000 16 00000000: 32 ac 00 00 30 00 00 00 00 00 01 00 08 00 00 00 |2...0... ........| And compare to xxd dump of source file to ensure they match. Signed-off-by: Kieran Levin --- framework_lib/src/chromium_ec/mod.rs | 45 ++++++++++++++++++++++- framework_lib/src/commandline/clap_std.rs | 7 ++++ framework_lib/src/commandline/mod.rs | 30 +++++++++++++++ framework_lib/src/commandline/uefi.rs | 9 +++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 02631e50..c768b33c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1177,7 +1177,50 @@ impl CrosEc { Ok(result.valid) } - /// Requests console output from EC and constantly asks for more + pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { + let result = i2c_passthrough::i2c_write(self, 5, 0x50, offset, data)?; + result.is_successful() + } + + /// Writes EC GPU descriptor to the GPU EEPROM. + pub fn set_gpu_descriptor(&self, data: &[u8]) -> EcResult<()> { + // Need to program the EEPROM 32 bytes at a time. + let chunk_size = 32; + + let chunks = data.len() / chunk_size; + for chunk_no in 0..chunks { + let offset = chunk_no * chunk_size; + // Current chunk might be smaller if it's the last + let cur_chunk_size = std::cmp::min(chunk_size, data.len() - chunk_no * chunk_size); + + if chunk_no % 100 == 0 { + println!(); + print!( + "Writing chunk {:>4}/{:>4} ({:>6}/{:>6}): X", + chunk_no, + chunks, + offset, + cur_chunk_size * chunks + ); + } else { + print!("X"); + } + + let chunk = &data[offset..offset + cur_chunk_size]; + let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); + let res = self.write_ec_gpu_chunk(offset_le, chunk); + // Don't read too fast, wait 100ms before writing more to allow for page erase/write cycle. + os_specific::sleep(100_000); + if let Err(err) = res { + println!(" Failed to write chunk: {:?}", err); + return Err(err); + } + } + println!(); + Ok(()) + } + + /// 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<()> { EcRequestConsoleSnapshot {}.send_command(self)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 52eddbeb..bd65139e 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -270,6 +270,10 @@ struct ClapCli { /// Simulate execution of a command (e.g. --flash-ec) #[arg(long)] dry_run: bool, + + /// File to write to the gpu EEPROM + #[arg(long)] + flash_gpu_descriptor_file: Option, } /// Parse a list of commandline arguments and return the struct @@ -457,6 +461,9 @@ pub fn parse(args: &[String]) -> Cli { paginate: false, info: args.info, flash_gpu_descriptor, + flash_gpu_descriptor_file: args + .flash_gpu_descriptor_file + .map(|x| x.into_os_string().into_string().unwrap()), raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ca92266a..80831d83 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -201,6 +201,7 @@ pub struct Cli { pub help: bool, pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, + pub flash_gpu_descriptor_file: Option, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -1220,6 +1221,34 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Ok(x) => println!("GPU Descriptor write failed with status code: {}", x), Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), } + } else if let Some(gpu_descriptor_file) = &args.flash_gpu_descriptor_file { + if let Some(PlatformFamily::Framework16) = + smbios::get_platform().and_then(Platform::which_family) + { + #[cfg(feature = "uefi")] + let data: Option> = crate::uefi::fs::shell_read_file(gpu_descriptor_file); + #[cfg(not(feature = "uefi"))] + let data = match fs::read(gpu_descriptor_file) { + Ok(data) => Some(data), + // TODO: Perhaps a more user-friendly error + Err(e) => { + println!("Error {:?}", e); + None + } + }; + if let Some(data) = data { + println!("File"); + println!(" Size: {:>20} B", data.len()); + println!(" Size: {:>20} KB", data.len() / 1024); + let res = ec.set_gpu_descriptor(&data); + match res { + Ok(()) => println!("GPU Descriptor successfully written"), + Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), + } + } + } else { + println!("Unsupported on this platform"); + } } 0 @@ -1275,6 +1304,7 @@ Options: --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] --hash Hash a file of arbitrary data --flash-gpu-descriptor <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type. + --flash-gpu-descriptor-file Write the GPU bay descriptor with a descriptor file. -f, --force Force execution of an unsafe command - may render your hardware unbootable! --dry-run Simulate execution of a command (e.g. --flash-ec) -t, --test Run self-test to check if interaction with EC is possible diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index d21938ae..a5e8c4c5 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -116,6 +116,7 @@ pub fn parse(args: &[String]) -> Cli { force: false, help: false, flash_gpu_descriptor: None, + flash_gpu_descriptor_file: None, allupdate: false, info: false, raw_command: vec![], @@ -764,6 +765,14 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--flash-gpu-descriptor-file" { + cli.flash_gpu_descriptor_file = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("Need to provide a value for --flash_gpu_descriptor_file. PATH"); + None + }; + found_an_option = true; } } From e7fc0d96abd8ec641a0f6a8749cb001e5f8b8944 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:23:06 +0800 Subject: [PATCH 130/174] --flash-gpu-descriptor-file: Allow --dry-run Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 9 ++++++++- framework_lib/src/commandline/mod.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index c768b33c..e6247e26 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1183,7 +1183,11 @@ impl CrosEc { } /// Writes EC GPU descriptor to the GPU EEPROM. - pub fn set_gpu_descriptor(&self, data: &[u8]) -> EcResult<()> { + pub fn set_gpu_descriptor(&self, data: &[u8], dry_run: bool) -> EcResult<()> { + println!( + "Writing GPU EEPROM {}", + if dry_run { " (DRY RUN)" } else { "" } + ); // Need to program the EEPROM 32 bytes at a time. let chunk_size = 32; @@ -1205,6 +1209,9 @@ impl CrosEc { } else { print!("X"); } + if dry_run { + continue; + } let chunk = &data[offset..offset + cur_chunk_size]; let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 80831d83..08c755e8 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1240,7 +1240,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!("File"); println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); - let res = ec.set_gpu_descriptor(&data); + let res = ec.set_gpu_descriptor(&data, args.dry_run); match res { Ok(()) => println!("GPU Descriptor successfully written"), Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), From 0d65135be31076776750e7a104f63d2712cb758f Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:24:05 +0800 Subject: [PATCH 131/174] --flash-gpu-descriptor-file: Don't block if unknown system Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 08c755e8..ae6c1f08 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1222,9 +1222,10 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Err(err) => println!("GPU Descriptor write failed with error: {:?}", err), } } else if let Some(gpu_descriptor_file) = &args.flash_gpu_descriptor_file { - if let Some(PlatformFamily::Framework16) = - smbios::get_platform().and_then(Platform::which_family) - { + if matches!( + smbios::get_family(), + Some(PlatformFamily::Framework16) | None + ) { #[cfg(feature = "uefi")] let data: Option> = crate::uefi::fs::shell_read_file(gpu_descriptor_file); #[cfg(not(feature = "uefi"))] From 74042d268e8f1f526ae6ef1427b2822bc8e8bb05 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:36:04 +0800 Subject: [PATCH 132/174] write_ec_gpu_chunk: Use std to swap byte order X64 is LE natively, so BE must be intended. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index e6247e26..a6a2fed7 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1214,8 +1214,7 @@ impl CrosEc { } let chunk = &data[offset..offset + cur_chunk_size]; - let offset_le = (((offset as u16) << 8) & 0xff00) | (((offset as u16) >> 8) & 0x00ff); - let res = self.write_ec_gpu_chunk(offset_le, chunk); + let res = self.write_ec_gpu_chunk((offset as u16).to_be(), chunk); // Don't read too fast, wait 100ms before writing more to allow for page erase/write cycle. os_specific::sleep(100_000); if let Err(err) = res { From c8a1f957bbdf827fff6d8bea231adb8732acc03e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 17:45:52 +0800 Subject: [PATCH 133/174] framework_tool: Add copyright into .exe properties Windows right-click => Properties => Details => Copyright Signed-off-by: Daniel Schaefer --- framework_tool/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index 8da6a35c..4ad76b8f 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -26,3 +26,6 @@ version = "0.3.9" features = [ "wincon" ] + +[package.metadata.winresource] +LegalCopyright = "Framework Computer Inc © 2022" From 6fa84c72223e3c370a29393f9c181273a8e3d91d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:06:39 +0800 Subject: [PATCH 134/174] check_mem_magic: Return error instead of printing Let the caller decide whether or not to print the error Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/mod.rs | 26 +++++++++++++------------- framework_lib/src/commandline/mod.rs | 3 ++- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 02631e50..fa23d2bb 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -276,25 +276,25 @@ impl CrosEc { } } - pub fn check_mem_magic(&self) -> Option<()> { + pub fn check_mem_magic(&self) -> EcResult<()> { match self.read_memory(EC_MEMMAP_ID, 2) { Some(ec_id) => { if ec_id.len() != 2 { - error!(" Unexpected length returned: {:?}", ec_id.len()); - return None; - } - if ec_id[0] != b'E' || ec_id[1] != b'C' { - error!(" This machine doesn't look like it has a Framework EC"); - None + Err(EcError::DeviceError(format!( + " Unexpected length returned: {:?}", + ec_id.len() + ))) + } else if ec_id[0] != b'E' || ec_id[1] != b'C' { + Err(EcError::DeviceError( + "This machine doesn't look like it has a Framework EC".to_string(), + )) } else { - println!(" Verified that Framework EC is present!"); - Some(()) + Ok(()) } } - None => { - error!(" Failed to read EC ID from memory map"); - None - } + None => Err(EcError::DeviceError( + "Failed to read EC ID from memory map".to_string(), + )), } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ca92266a..8b57fa77 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1340,7 +1340,8 @@ fn selftest(ec: &CrosEc) -> Option<()> { } println!(" Checking EC memory mapped magic bytes"); - ec.check_mem_magic()?; + print_err(ec.check_mem_magic())?; + println!(" Verified that Framework EC is present!"); println!(" Reading EC Build Version"); print_err(ec.version_info())?; From 249b8f0a76f6f7292bf6bfb55a0043590dbd1b04 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:07:15 +0800 Subject: [PATCH 135/174] windows: Print a hint if no driver is available Avoid confusion on platforms where the driver is not enabled in bios and driver bundle yet. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/windows.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/framework_lib/src/chromium_ec/windows.rs b/framework_lib/src/chromium_ec/windows.rs index 5b0cca68..557ab430 100644 --- a/framework_lib/src/chromium_ec/windows.rs +++ b/framework_lib/src/chromium_ec/windows.rs @@ -14,6 +14,8 @@ use windows::{ use crate::chromium_ec::protocol::HEADER_LEN; use crate::chromium_ec::EC_MEMMAP_SIZE; use crate::chromium_ec::{EcError, EcResponseStatus, EcResult}; +use crate::smbios; +use crate::util::Platform; // Create a wrapper around HANDLE to mark it as Send. // I'm not sure, but I think it's safe to do that for this type of HANDL. @@ -46,6 +48,20 @@ fn init() -> bool { let handle = match res { Ok(h) => h, Err(err) => { + let platform = smbios::get_platform(); + match platform { + Some(platform @ Platform::IntelGen11) + | Some(platform @ Platform::IntelGen12) + | Some(platform @ Platform::IntelGen13) + | Some(platform @ Platform::Framework13Amd7080) + | Some(platform @ Platform::Framework16Amd7080) => { + println!("The windows driver is not enabled on {:?}.", platform); + println!("Please stay tuned for future BIOS and driver updates."); + println!(); + } + _ => (), + } + error!("Failed to find Windows driver. {:?}", err); return false; } From 62bdc229e55f5c0e1d41bd2bfbfffbf93278d474 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:13:26 +0800 Subject: [PATCH 136/174] --features: Don't unwrap, just print error Less verbose and doesn't crash Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 8b57fa77..05247bca 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -844,7 +844,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else if args.version { print_tool_version(); } else if args.features { - ec.get_features().unwrap(); + print_err(ec.get_features()); } else if args.esrt { print_esrt(); } else if let Some(compare_version_ver) = &args.compare_version { From 85c4b2f6a89e81465bdd1fe751d1e12f639f5957 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:58:20 +0800 Subject: [PATCH 137/174] EXAMPLES: Document flashing eeprom Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index e69817ee..6882c761 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -79,3 +79,19 @@ This command has not been thoroughly tested on all Framework Computer systems # EC will boot back into RO if the system turned off for 30s > framework_tool --reboot-ec jump-rw ``` + +## Flashing Expansion Bay EEPROM (Framework 16) + +This will render your dGPU unsuable if you flash the wrong file! +It's intended for advanced users who build their own expansion bay module. +The I2C address of the EEPROM is hardcoded to 0x50. + +``` +# Update just the serial number +> framework_tool --flash_gpu_descriptor GPU FRAKMQCP41500ASSY1 +> framework_tool --flash_gpu_descriptor 13 FRAKMQCP41500ASSY1 +> framework_tool --flash_gpu_descriptor 0x0D FRAKMQCP41500ASSY1 + +# Update everything from a file +> framework_tool --flash-gpu-descriptor-file pcie_4x2.bin +``` From e8acfa7fac3ad7a5b9c8a65897987d8cbde4202d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 18:58:43 +0800 Subject: [PATCH 138/174] --expansion-bay: Read eeprom directly and dump extra details Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 5 +++ framework_lib/src/chromium_ec/mod.rs | 62 ++++++++++++++++++++++++++++ framework_lib/src/commandline/mod.rs | 20 +++++++++ 3 files changed, 87 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 1ea7f82c..61b4aac8 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -317,8 +317,13 @@ Expansion Bay Serial Number: FRAXXXXXXXXXXXXXXX Config: Pcie4x2 Vendor: SsdHolder + Expansion Bay EEPROM + Valid: true + HW Version: 8.0 ``` +Add `-vv` for more verbose details. + ## Check charger and battery status (Framework 12/13/16) ``` diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a6a2fed7..a596c7cd 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1177,6 +1177,34 @@ impl CrosEc { Ok(result.valid) } + pub fn read_ec_gpu_chunk(&self, addr: u16, len: u16) -> EcResult> { + let eeprom_port = 0x05; + let eeprom_addr = 0x50; + let mut data: Vec = Vec::with_capacity(len.into()); + + while data.len() < len.into() { + let remaining = len - data.len() as u16; + let chunk_len = std::cmp::min(i2c_passthrough::MAX_I2C_CHUNK, remaining.into()); + let offset = addr + data.len() as u16; + let i2c_response = i2c_passthrough::i2c_read( + self, + eeprom_port, + eeprom_addr, + offset, + chunk_len as u16, + )?; + if let Err(EcError::DeviceError(err)) = i2c_response.is_successful() { + return Err(EcError::DeviceError(format!( + "I2C read was not successful: {:?}", + err + ))); + } + data.extend(i2c_response.data); + } + + Ok(data) + } + pub fn write_ec_gpu_chunk(&self, offset: u16, data: &[u8]) -> EcResult<()> { let result = i2c_passthrough::i2c_write(self, 5, 0x50, offset, data)?; result.is_successful() @@ -1226,6 +1254,15 @@ impl CrosEc { Ok(()) } + pub fn read_gpu_desc_header(&self) -> EcResult { + let bytes = + self.read_ec_gpu_chunk(0x00, core::mem::size_of::() as u16)?; + let header: *const GpuCfgDescriptor = unsafe { std::mem::transmute(bytes.as_ptr()) }; + let header = unsafe { *header }; + + Ok(header) + } + /// 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<()> { @@ -1633,3 +1670,28 @@ pub struct IntrusionStatus { /// That means we only know if it was opened at least once, while off, not how many times. pub vtr_open_count: u8, } + +#[derive(Clone, Debug, Copy, PartialEq)] +#[repr(C, packed)] +pub struct GpuCfgDescriptor { + /// Expansion bay card magic value that is unique + pub magic: [u8; 4], + /// Length of header following this field + pub length: u32, + /// descriptor version, if EC max version is lower than this, ec cannot parse + pub desc_ver_major: u16, + pub desc_ver_minor: u16, + /// Hardware major version + pub hardware_version: u16, + /// Hardware minor revision + pub hardware_revision: u16, + /// 18 digit Framework Serial that starts with FRA + /// the first 10 digits must be allocated by framework + pub serial: [u8; 20], + /// Length of descriptor following heade + pub descriptor_length: u32, + /// CRC of descriptor + pub descriptor_crc32: u32, + /// CRC of header before this value + pub crc32: u32, +} diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index ae6c1f08..a7a10111 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -884,6 +884,26 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { if let Err(err) = ec.check_bay_status() { error!("{:?}", err); } + if let Ok(header) = ec.read_gpu_desc_header() { + println!(" Expansion Bay EEPROM"); + println!( + " Valid: {:?}", + header.magic == [0x32, 0xAC, 0x00, 0x00] + ); + println!(" HW Version: {}.{}", { header.hardware_version }, { + header.hardware_revision + }); + if log_enabled!(Level::Info) { + println!(" Hdr Length {} B", { header.length }); + println!(" Desc Ver: {}.{}", { header.desc_ver_major }, { + header.desc_ver_minor + }); + println!(" Serialnumber:{:X?}", { header.serial }); + println!(" Desc Length: {} B", { header.descriptor_length }); + println!(" Desc CRC: {:X}", { header.descriptor_crc32 }); + println!(" Hdr CRC: {:X}", { header.crc32 }); + } + } } 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 { From d13b41b3fa6154eb93899c53638c5b9a3180ffb1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 5 Jun 2025 19:12:11 +0800 Subject: [PATCH 139/174] Add --dump-gpu-descriptor-file Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 5 +++++ framework_lib/src/chromium_ec/mod.rs | 10 ++++++++++ framework_lib/src/commandline/clap_std.rs | 7 +++++++ framework_lib/src/commandline/mod.rs | 22 ++++++++++++++++++++++ framework_lib/src/commandline/uefi.rs | 9 +++++++++ 5 files changed, 53 insertions(+) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 6882c761..f8fb2172 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -87,6 +87,11 @@ It's intended for advanced users who build their own expansion bay module. The I2C address of the EEPROM is hardcoded to 0x50. ``` +# Dump current descriptor (e.g. for backup) +> framework_tool --dump-gpu-descriptor-file foo.bin +Dumping to foo.bin +Wrote 153 bytes to foo.bin + # Update just the serial number > framework_tool --flash_gpu_descriptor GPU FRAKMQCP41500ASSY1 > framework_tool --flash_gpu_descriptor 13 FRAKMQCP41500ASSY1 diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index a596c7cd..22e7134e 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -1254,6 +1254,16 @@ impl CrosEc { Ok(()) } + pub fn read_gpu_descriptor(&self) -> EcResult> { + let header = self.read_gpu_desc_header()?; + if header.magic != [0x32, 0xAC, 0x00, 0x00] { + return Err(EcError::DeviceError( + "Invalid descriptor hdr magic".to_string(), + )); + } + self.read_ec_gpu_chunk(0x00, header.descriptor_length as u16) + } + pub fn read_gpu_desc_header(&self) -> EcResult { let bytes = self.read_ec_gpu_chunk(0x00, core::mem::size_of::() as u16)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index bd65139e..148042b2 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -274,6 +274,10 @@ struct ClapCli { /// File to write to the gpu EEPROM #[arg(long)] flash_gpu_descriptor_file: Option, + + /// File to dump the gpu EEPROM to + #[arg(long)] + dump_gpu_descriptor_file: Option, } /// Parse a list of commandline arguments and return the struct @@ -464,6 +468,9 @@ pub fn parse(args: &[String]) -> Cli { flash_gpu_descriptor_file: args .flash_gpu_descriptor_file .map(|x| x.into_os_string().into_string().unwrap()), + dump_gpu_descriptor_file: args + .dump_gpu_descriptor_file + .map(|x| x.into_os_string().into_string().unwrap()), raw_command: vec![], } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index a7a10111..11a64cce 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -202,6 +202,7 @@ pub struct Cli { pub info: bool, pub flash_gpu_descriptor: Option<(u8, String)>, pub flash_gpu_descriptor_file: Option, + pub dump_gpu_descriptor_file: Option, // UEFI only pub allupdate: bool, pub paginate: bool, @@ -680,6 +681,24 @@ fn dump_ec_flash(ec: &CrosEc, dump_path: &str) { } } +fn dump_dgpu_eeprom(ec: &CrosEc, dump_path: &str) { + let flash_bin = ec.read_gpu_descriptor().unwrap(); + + #[cfg(not(feature = "uefi"))] + { + let mut file = fs::File::create(dump_path).unwrap(); + file.write_all(&flash_bin).unwrap(); + } + #[cfg(feature = "uefi")] + { + let ret = crate::uefi::fs::shell_write_file(dump_path, &flash_bin); + if ret.is_err() { + println!("Failed to dump EC FW image."); + } + } + println!("Wrote {} bytes to {}", flash_bin.len(), dump_path); +} + fn compare_version(device: Option, version: String, ec: &CrosEc) -> i32 { println!("Target Version {:?}", version); @@ -1270,6 +1289,9 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!("Unsupported on this platform"); } + } else if let Some(dump_path) = &args.dump_gpu_descriptor_file { + println!("Dumping to {}", dump_path); + dump_dgpu_eeprom(&ec, dump_path); } 0 diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index a5e8c4c5..82d6c253 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -117,6 +117,7 @@ pub fn parse(args: &[String]) -> Cli { help: false, flash_gpu_descriptor: None, flash_gpu_descriptor_file: None, + dump_gpu_descriptor_file: None, allupdate: false, info: false, raw_command: vec![], @@ -773,6 +774,14 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; + } else if arg == "--dump-gpu-descriptor-file" { + cli.dump_gpu_descriptor_file = if args.len() > i + 1 { + Some(args[i + 1].clone()) + } else { + println!("Need to provide a value for --dump_gpu_descriptor_file. PATH"); + None + }; + found_an_option = true; } } From 00f7100946f875a7919ecf13a00c0e04688db116 Mon Sep 17 00:00:00 2001 From: Josh Vander Hook Date: Thu, 5 Jun 2025 10:00:20 -0500 Subject: [PATCH 140/174] commandline: Fix docstring for --remap-key Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 52eddbeb..c744ad31 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -193,7 +193,7 @@ struct ClapCli { #[arg(long)] kblight: Option>, - /// Set keyboard backlight percentage or get, if no value provided + /// Remap a key by changing the scancode #[arg(long, value_parser=maybe_hex::)] #[clap(num_args = 3)] remap_key: Vec, From 2d1aaa72403d602f063c18995af60f1b812644fd Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 10 Jun 2025 02:06:57 +0800 Subject: [PATCH 141/174] ccgx: derive Ord for AppVersion and BaseVersion Allow easily comparing them Signed-off-by: Daniel Schaefer --- framework_lib/src/ccgx/mod.rs | 45 ++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/framework_lib/src/ccgx/mod.rs b/framework_lib/src/ccgx/mod.rs index f7269b90..4495cc7d 100644 --- a/framework_lib/src/ccgx/mod.rs +++ b/framework_lib/src/ccgx/mod.rs @@ -110,7 +110,7 @@ pub enum SiliconId { Ccg8 = 0x3580, } -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub struct BaseVersion { /// Major part of the version. X of X.Y.Z.BB pub major: u8, @@ -154,7 +154,7 @@ impl From for BaseVersion { } } -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub enum Application { Notebook, Monitor, @@ -162,7 +162,7 @@ pub enum Application { Invalid, } -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] pub struct AppVersion { pub application: Application, /// Major part of the version. X of X.Y.Z @@ -309,3 +309,42 @@ fn parse_metadata_cyacd2(buffer: &[u8]) -> Option<(u32, u32)> { None } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + // Make sure deriving does what I expect, properly comparing with multiple fields + fn derive_ord() { + let v0_0_0 = AppVersion { + application: Application::Notebook, + major: 0, + minor: 0, + circuit: 0, + }; + let v1_0_1 = AppVersion { + application: Application::Notebook, + major: 1, + minor: 0, + circuit: 1, + }; + let v0_1_0 = AppVersion { + application: Application::Notebook, + major: 0, + minor: 1, + circuit: 0, + }; + let v1_1_1 = AppVersion { + application: Application::Notebook, + major: 1, + minor: 1, + circuit: 1, + }; + assert_eq!(v0_0_0, v0_0_0.clone()); + assert!(v0_0_0 < v1_0_1); + assert!(v0_1_0 < v1_0_1); + assert!(v1_0_1 < v1_1_1); + assert!(v1_1_1 > v1_0_1); + } +} From ba0bad02be76efe869653438ba7cc4569fb4ad8b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 15 Jun 2025 23:49:25 +0800 Subject: [PATCH 142/174] docs: Move file analysis to advanced examples Too low level for the main readme. Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 65 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 55 ------------------------------------- 2 files changed, 65 insertions(+), 55 deletions(-) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index f8fb2172..0ece899d 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -100,3 +100,68 @@ Wrote 153 bytes to foo.bin # Update everything from a file > framework_tool --flash-gpu-descriptor-file pcie_4x2.bin ``` + +## Analyzing binaries + +### EC + +Note that only since Framework 13 Intel Core Ultra (and later) the version number embedded in the ED binary is meaningful. As you can see below, in this example on Intel Core 12th/13th Gen (hx30) it's always 0.0.1. +The commit hash though is accurate and reflects the git commit it was built from. + +``` +> framework-tool --ec--bin ec.bin +File + Size: 524288 B + Size: 512 KB +EC + Version: hx30_v0.0.1-7a61a89 + RollbackVer: 0 + Platform: hx30 + Version: 0.0.1 + Commit: 7a61a89 + Size: 2868 B + Size: 2 KB +``` + +### PD + +``` +> framework_tool --pd-bin pd-0.1.14.bin +File + Size: 65536 B + Size: 64 KB +FW 1 + Silicon ID: 0x3000 + Version: 0.1.14 + Row size: 128 B + Start Row: 22 + Rows: 95 + Size: 12160 B + Size: 11 KB +FW 2 + Silicon ID: 0x3000 + Version: 0.1.14 + Row size: 128 B + Start Row: 118 + Rows: 381 + Size: 48768 B + Size: 47 KB +``` + +### UEFI Capsule + +``` +> framework_tool --capsule retimer23.cap +File + Size: 2232676 B + Size: 2180 KB +Capsule Header + Capsule GUID: (ba2e4e6e, 3b0c, 4f25, [8a,59,4c,55,3f,c8,6e,a2]) + Header size: 28 B + Flags: 0x50000 + Persist across reset (0x10000) + Initiate reset (0x40000) + Capsule Size: 2232676 B + Capsule Size: 2180 KB + Type: Framework Retimer23 (Right) +``` diff --git a/README.md b/README.md index fc913e21..848389f6 100644 --- a/README.md +++ b/README.md @@ -205,61 +205,6 @@ Many actions require root. First build with cargo and then run the binary with s cargo build && sudo ./target/debug/framework_tool ``` -Dumping version information from firmware binaries: - -``` -# Dumping PD FW Binary Information: -> cargo run -q -- --pd-bin pd-0.1.14.bin -File - Size: 65536 B - Size: 64 KB -FW 1 - Silicon ID: 0x3000 - Version: 0.1.14 - Row size: 128 B - Start Row: 22 - Rows: 95 - Size: 12160 B - Size: 11 KB -FW 2 - Silicon ID: 0x3000 - Version: 0.1.14 - Row size: 128 B - Start Row: 118 - Rows: 381 - Size: 48768 B - Size: 47 KB - -# Dumping EC FW Binary Information -> cargo run -q -- --ec--bin ec.bin -File - Size: 524288 B - Size: 512 KB -EC - Version: hx30_v0.0.1-7a61a89 - RollbackVer: 0 - Platform: hx30 - Version: 0.0.1 - Commit: 7a61a89 - Size: 2868 B - Size: 2 KB - -# Dumping Capsule Binary Information: -> cargo run -q -- --capsule retimer23.cap -File - Size: 2232676 B - Size: 2180 KB -Capsule Header - Capsule GUID: (ba2e4e6e, 3b0c, 4f25, [8a,59,4c,55,3f,c8,6e,a2]) - Header size: 28 B - Flags: 0x50000 - Persist across reset (0x10000) - Initiate reset (0x40000) - Capsule Size: 2232676 B - Capsule Size: 2180 KB - Type: Framework Retimer23 (Right) -``` - ###### Running on Windows On newly released systems since 2025 the Framework driver installer includes the EC driver. From c55d6bd7cddbac82ce697c62de9ec6f6665f52f2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sun, 15 Jun 2025 23:51:46 +0800 Subject: [PATCH 143/174] README: Add platform list And which OS version is supported. Signed-off-by: Daniel Schaefer --- README.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 848389f6..c40160a5 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,32 @@ see the [Support Matrices](support-matrices.md). ###### Operating System Support -- [x] OS Tool (`framework_tool`) - - [x] Tested on Linux - - [x] Tested on Windows - - [x] Tested on FreeBSD -- [x] UEFI Shell tool (`framework_uefi`) +The following operating environments are supported. + +- Linux +- Windows +- UEFI +- FreeBSD + +Most functionality depends communication with the EC. +For Linux and Windows there are dedicated drivers. +On UEFI and FreeBSD raw port I/O is used - on Linux this can also be used as a fallback, if the driver is not available or not working. + +| | Port I/O | Linux | Windows | +|---------------------|----------| ------|---------| +| Framework 12 | | | | +| Intel Core 12th Gen | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | +| Framework 13 | | | | +| Intel Core 11th Gen | Yes | [6.11](https://github.com/torvalds/linux/commit/04ca0a51f1e63bd553fd4af8e9af0fe094fa4f0a) | Not yet | +| Intel Core 12th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | +| Intel Core 13th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | +| AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | Soon | +| Intel Core Ultra 1S | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Soon | +| AMD Ryzen AI 300 | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | +| Framework 16 | | | | +| AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | Soon | +| Framework Desktop | | | | +| AMD Ryzen AI Max | Yes | [6.15](https://github.com/torvalds/linux/commit/d83c45aeec9b223fe6db4175e9d1c4f5699cc37a) | Yes | ###### Firmware Information From fcb1ba86e04d7be3fd902a4c761b2b2d78a9aff1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 18 Jun 2025 14:13:37 +0800 Subject: [PATCH 144/174] --inputdeck: On FW16 also print sleep_l gpio state To help debug keyboard/EC issues it's useful to know what the state of that GPIO is. EC uses it to tell the keyboard about system sleep and lid state. ``` > framework_tool --inputdeck Chassis Closed: true Input Deck State: On Touchpad present: true SLEEP# GPIO high: true Positions: Pos 0: KeyboardA Pos 1: Disconnected Pos 2: Disconnected Pos 3: KeyboardB Pos 4: Disconnected ``` Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 1 + framework_lib/src/chromium_ec/mod.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/EXAMPLES.md b/EXAMPLES.md index 61b4aac8..2361b196 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -234,6 +234,7 @@ Input Deck Chassis Closed: true Input Deck State: On Touchpad present: true +SLEEP# GPIO high: true Positions: Pos 0: GenericC Pos 1: KeyboardA diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 0adba41b..ed1210e6 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -603,9 +603,11 @@ impl CrosEc { pub fn print_fw16_inputdeck_status(&self) -> EcResult<()> { let intrusion = self.get_intrusion_status()?; let status = self.get_input_deck_status()?; + let sleep_l = self.get_gpio("sleep_l")?; println!("Chassis Closed: {}", !intrusion.currently_open); println!("Input Deck State: {:?}", status.state); println!("Touchpad present: {}", status.touchpad_present); + println!("SLEEP# GPIO high: {}", sleep_l); println!("Positions:"); println!(" Pos 0: {:?}", status.top_row.pos0); println!(" Pos 1: {:?}", status.top_row.pos1); From df72eb6e9657f23f4d43edd68a8e1b5a738d6577 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 18 Jun 2025 13:39:50 +0800 Subject: [PATCH 145/174] --versions: Add OS and tool version It's useful to know which Linux/FreeBSD kernel or windows version people are running when reporting an issue. Example: ``` > sudo framework_tool --versions Tool Version: 0.4.3 OS Version: Linux 6.15.1 #1-NixOS SMP PREEMPT_DYNAMIC Wed Jun 4 12:46:27 UTC 2025 x86_64 ``` Signed-off-by: Daniel Schaefer --- Cargo.lock | 16 ++++++++++++++ framework_lib/Cargo.toml | 1 + framework_lib/src/commandline/mod.rs | 3 +++ framework_lib/src/os_specific.rs | 33 ++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f6c79ef6..25672fc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,6 +435,7 @@ dependencies = [ "uefi", "uefi-services", "windows 0.59.0", + "windows-version", "wmi", ] @@ -1559,6 +1560,12 @@ dependencies = [ "syn 2.0.98", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-result" version = "0.3.0" @@ -1642,6 +1649,15 @@ dependencies = [ "windows_x86_64_msvc 0.53.0", ] +[[package]] +name = "windows-version" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e04a5c6627e310a23ad2358483286c7df260c964eb2d003d8efd6d0f4e79265c" +dependencies = [ + "windows-link", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index b3af9f25..a440d752 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -49,6 +49,7 @@ env_logger = "0.11" clap = { version = "4.5", features = ["derive", "cargo"] } clap-num = { version = "1.2.0" } clap-verbosity-flag = { version = "2.2.1" } +windows-version = "0.1.4" [target.'cfg(unix)'.dependencies] libc = "0.2.155" diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 602ca09e..78390863 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -49,6 +49,7 @@ use crate::ec_binary; use crate::esrt; #[cfg(feature = "rusb")] use crate::inputmodule::check_inputmodule_version; +use crate::os_specific; use crate::power; use crate::smbios; use crate::smbios::ConfigDigit0; @@ -374,6 +375,8 @@ fn print_stylus_battery_level() { } fn print_versions(ec: &CrosEc) { + println!("Tool Version: {}", built_info::PKG_VERSION); + println!("OS Version: {}", os_specific::get_os_version()); println!("Mainboard Hardware"); if let Some(ver) = smbios::get_product_name() { println!(" Type: {}", ver); diff --git a/framework_lib/src/os_specific.rs b/framework_lib/src/os_specific.rs index 83ae2f37..b77deb87 100644 --- a/framework_lib/src/os_specific.rs +++ b/framework_lib/src/os_specific.rs @@ -3,6 +3,39 @@ #[cfg(not(feature = "uefi"))] use std::{thread, time}; +#[cfg(feature = "uefi")] +use alloc::string::{String, ToString}; + +// Could report the implemented UEFI spec version +// But that's not very useful. Just look at the BIOS version +// But at least it's useful to see that the tool was run on UEFI +#[cfg(feature = "uefi")] +pub fn get_os_version() -> String { + "UEFI".to_string() +} + +#[cfg(target_family = "windows")] +pub fn get_os_version() -> String { + let ver = windows_version::OsVersion::current(); + format!("{}.{}.{}.{}", ver.major, ver.minor, ver.pack, ver.build) +} + +#[cfg(target_family = "unix")] +pub fn get_os_version() -> String { + if let Ok(uts) = nix::sys::utsname::uname() { + // uname -a without hostname + format!( + "{} {} {} {}", + uts.sysname().to_string_lossy(), + uts.release().to_string_lossy(), + uts.version().to_string_lossy(), + uts.machine().to_string_lossy(), + ) + } else { + "Unknown".to_string() + } +} + /// Sleep a number of microseconds pub fn sleep(micros: u64) { #[cfg(not(feature = "uefi"))] From d10a7825dd089caea4ee9b095e408e7f9ef51e0b Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 26 Jun 2025 00:55:14 +0800 Subject: [PATCH 146/174] Add support for EC_CD_AP_RESET Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 1 + framework_lib/src/chromium_ec/commands.rs | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index e009a9ec..df433da9 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -53,6 +53,7 @@ pub enum EcCommands { /// Get information about PD controller power UsbPdPowerInfo = 0x0103, AdcRead = 0x0123, + ApReset = 0x0125, RgbKbdSetColor = 0x013A, RgbKbd = 0x013B, diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 0a367ab3..65f37482 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -879,6 +879,15 @@ impl EcRequest for EcRequestAdcRead { } } +#[repr(C)] +pub struct EcRequestApReset {} + +impl EcRequest<()> for EcRequestApReset { + fn command_id() -> EcCommands { + EcCommands::ApReset + } +} + // 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; From 1acf72b21aa5e1c2670dc0fd3d7d88d914e86ece Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 26 Jun 2025 00:55:40 +0800 Subject: [PATCH 147/174] Add support for EC_CMD_REBOOT_AP_ON_G3 Reset AP after entering G3 by the EC after configurable delay. V0 resets immediately. Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 2 ++ framework_lib/src/chromium_ec/commands.rs | 27 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index df433da9..608f29bc 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -54,6 +54,8 @@ pub enum EcCommands { UsbPdPowerInfo = 0x0103, AdcRead = 0x0123, ApReset = 0x0125, + LocateChip = 0x0126, + RebootApOnG3 = 0x0127, RgbKbdSetColor = 0x013A, RgbKbd = 0x013B, diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 65f37482..48763c06 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -888,6 +888,33 @@ impl EcRequest<()> for EcRequestApReset { } } +#[repr(C)] +pub struct EcRequestRebootApOnG3V0 {} + +impl EcRequest<()> for EcRequestRebootApOnG3V0 { + fn command_id() -> EcCommands { + EcCommands::RebootApOnG3 + } + fn command_version() -> u8 { + 0 + } +} + +#[repr(C)] +pub struct EcRequestRebootApOnG3V1 { + /// Delay in seconds after entering G3 state + pub delay: u32, +} + +impl EcRequest<()> for EcRequestRebootApOnG3V1 { + fn command_id() -> EcCommands { + EcCommands::RebootApOnG3 + } + fn command_version() -> u8 { + 1 + } +} + // 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; From 7424ad0c22d6d8a614e34c879d3b2dd229649701 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 27 Jun 2025 15:05:58 +0800 Subject: [PATCH 148/174] clippy: Ignore uninlined_format_args See: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args Recently introduced - I think it looks a bit messier inline. Signed-off-by: Daniel Schaefer --- framework_lib/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework_lib/src/lib.rs b/framework_lib/src/lib.rs index 93d91e2b..05adf8fb 100644 --- a/framework_lib/src/lib.rs +++ b/framework_lib/src/lib.rs @@ -1,6 +1,7 @@ //! A library to interact with [Framework Computer](https://frame.work) hardware and building tools to do so. #![cfg_attr(feature = "uefi", no_std)] +#![allow(clippy::uninlined_format_args)] extern crate alloc; #[cfg(feature = "uefi")] From aa3638888d2606fb6f3e32c99a38fdb559fbb3b4 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 27 Jun 2025 06:05:45 +0800 Subject: [PATCH 149/174] Current and charge rate limit cannot be read Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 6d7f50ea..600efa74 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -167,14 +167,14 @@ struct ClapCli { #[arg(long)] charge_limit: Option>, - /// Get or set max charge current limit + /// Set max charge current limit #[arg(long)] - #[clap(num_args = ..=2)] + #[clap(num_args = 1..=2)] charge_current_limit: Vec, - /// Get or set max charge current limit + /// Set max charge current limit #[arg(long)] - #[clap(num_args = ..=2)] + #[clap(num_args = 1..=2)] charge_rate_limit: Vec, /// Get GPIO value by name or all, if no name provided From 1b64d90a3474e0bb0ecea9a3623b18b792f39739 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 1 Jul 2025 10:43:15 +0800 Subject: [PATCH 150/174] uefi: Fix shell args when run from a script The UEFI tool wouldn't get any arugments when run from a UEFI shell script. Manually runnning from the shell would work, but not from e.g. startup.nsh Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/uefi.rs | 40 ++++----------------------- framework_uefi/src/main.rs | 4 +-- 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 82d6c253..1ab55307 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -6,8 +6,7 @@ use alloc::vec::Vec; use log::{debug, error, info, trace}; use uefi::prelude::BootServices; use uefi::proto::shell_params::*; -use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams, SearchType}; -use uefi::Identify; +use uefi::Handle; use crate::chromium_ec::commands::SetGpuSerialMagic; use crate::chromium_ec::{CrosEcDriverType, HardwareDeviceType}; @@ -16,40 +15,13 @@ use crate::commandline::Cli; use super::{ConsoleArg, FpBrightnessArg, InputDeckModeArg, RebootEcArg, TabletModeArg}; /// Get commandline arguments from UEFI environment -pub fn get_args(boot_services: &BootServices) -> Vec { - // TODO: I think i should open this from the ImageHandle? - let shell_params_h = - boot_services.locate_handle_buffer(SearchType::ByProtocol(&ShellParameters::GUID)); - let shell_params_h = if let Ok(shell_params_h) = shell_params_h { - shell_params_h +pub fn get_args(bs: &BootServices, image_handle: Handle) -> Vec { + if let Ok(shell_params) = bs.open_protocol_exclusive::(image_handle) { + shell_params.get_args() } else { - error!("ShellParameters protocol not found"); - return vec![]; - }; - - for handle in &*shell_params_h { - let params_handle = unsafe { - boot_services - .open_protocol::( - OpenProtocolParams { - handle: *handle, - agent: boot_services.image_handle(), - controller: None, - }, - OpenProtocolAttributes::GetProtocol, - ) - .expect("Failed to open ShellParameters handle") - }; - - // Ehm why are there two and one has no args? - // Maybe one is the shell itself? - if params_handle.argc == 0 { - continue; - } - - return params_handle.get_args(); + // No protocol found if the application wasn't executed by the shell + vec![] } - vec![] } pub fn parse(args: &[String]) -> Cli { diff --git a/framework_uefi/src/main.rs b/framework_uefi/src/main.rs index b81cbe63..caeeee2e 100644 --- a/framework_uefi/src/main.rs +++ b/framework_uefi/src/main.rs @@ -12,11 +12,11 @@ extern crate alloc; use framework_lib::commandline; #[entry] -fn main(_handle: Handle, mut system_table: SystemTable) -> Status { +fn main(image_handle: Handle, mut system_table: SystemTable) -> Status { uefi_services::init(&mut system_table).unwrap(); let bs = system_table.boot_services(); - let args = commandline::uefi::get_args(bs); + let args = commandline::uefi::get_args(bs, image_handle); let args = commandline::parse(&args); if commandline::run_with_args(&args, false) == 0 { return Status::SUCCESS; From 902e2fae8e46db83c3488fc54c147c3ff9808e5c Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 1 Jul 2025 10:44:03 +0800 Subject: [PATCH 151/174] framework_uefi: Add startup.nsh Make it easy to test execution from a script Signed-off-by: Daniel Schaefer --- framework_uefi/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework_uefi/Makefile b/framework_uefi/Makefile index a571ba17..76b03e5d 100644 --- a/framework_uefi/Makefile +++ b/framework_uefi/Makefile @@ -38,6 +38,9 @@ $(BUILD)/efi.img: $(BUILD)/boot.efi mkfs.vfat $@.tmp mmd -i $@.tmp efi mmd -i $@.tmp efi/boot + echo 'efi\boot\bootx64.efi --version' > startup.nsh + mcopy -i $@.tmp startup.nsh ::efi/boot/startup.nsh + rm -f startup.nsh mcopy -i $@.tmp $< ::efi/boot/bootx64.efi mv $@.tmp $@ From 7eb45087a7e54583cf8a52987cce0809e37814d9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 1 Jul 2025 10:52:02 +0800 Subject: [PATCH 152/174] Bump to v0.4.4 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 f6c79ef6..8bac5086 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,7 +408,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.4.3" +version = "0.4.4" dependencies = [ "built", "clap", @@ -440,7 +440,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.4.3" +version = "0.4.4" dependencies = [ "embed-resource", "framework_lib", @@ -451,7 +451,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.4.3" +version = "0.4.4" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index b3af9f25..770b7b15 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.4.3" +version = "0.4.4" description = "Library to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index cdd90a2a..0443f3e9 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.4.3" +version = "0.4.4" description = "Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index adc8b14b..f0098cb7 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.4.3" +version = "0.4.4" description = "UEFI Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" From 438f9dc603254160274d86900bdfc6bad6ed8e9a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 10 May 2025 09:25:23 +0800 Subject: [PATCH 153/174] Cli: Derive Default So that we can instantiate Cli to a safe default with everything disabled. Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/clap_std.rs | 4 ++-- framework_lib/src/commandline/mod.rs | 17 +++++++++++++---- framework_lib/src/commandline/uefi.rs | 14 +++++++------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 600efa74..a26aa4ba 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -9,7 +9,7 @@ use clap_num::maybe_hex; use crate::chromium_ec::commands::SetGpuSerialMagic; use crate::chromium_ec::CrosEcDriverType; use crate::commandline::{ - Cli, ConsoleArg, FpBrightnessArg, HardwareDeviceType, InputDeckModeArg, RebootEcArg, + Cli, ConsoleArg, FpBrightnessArg, HardwareDeviceType, InputDeckModeArg, LogLevel, RebootEcArg, TabletModeArg, }; @@ -381,7 +381,7 @@ pub fn parse(args: &[String]) -> Cli { }; Cli { - verbosity: args.verbosity.log_level_filter(), + verbosity: LogLevel(args.verbosity.log_level_filter()), versions: args.versions, version: args.version, features: args.features, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 602ca09e..2c2922b0 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -134,13 +134,22 @@ impl From for DeckStateMode { } } +#[derive(Debug)] +pub struct LogLevel(log::LevelFilter); + +impl Default for LogLevel { + fn default() -> Self { + LogLevel(log::LevelFilter::Error) + } +} + /// Shadows `clap_std::ClapCli` with extras for UEFI /// /// The UEFI commandline currently doesn't use clap, so we need to shadow the struct. /// Also it has extra options. -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Cli { - pub verbosity: log::LevelFilter, + pub verbosity: LogLevel, pub versions: bool, pub version: bool, pub features: bool, @@ -815,7 +824,7 @@ fn compare_version(device: Option, version: String, ec: &Cro pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { #[cfg(feature = "uefi")] { - log::set_max_level(args.verbosity); + log::set_max_level(args.verbosity.0); } #[cfg(not(feature = "uefi"))] { @@ -824,7 +833,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { // .filter("FRAMEWORK_COMPUTER_LOG") // .write_style("FRAMEWORK_COMPUTER_LOG_STYLE"); - let level = args.verbosity.as_str(); + let level = args.verbosity.0.as_str(); env_logger::Builder::from_env(env_logger::Env::default().default_filter_or(level)) .format_target(false) .format_timestamp(None) diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 1ab55307..2a18cabd 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -10,7 +10,7 @@ use uefi::Handle; use crate::chromium_ec::commands::SetGpuSerialMagic; use crate::chromium_ec::{CrosEcDriverType, HardwareDeviceType}; -use crate::commandline::Cli; +use crate::commandline::{Cli, LogLevel}; use super::{ConsoleArg, FpBrightnessArg, InputDeckModeArg, RebootEcArg, TabletModeArg}; @@ -26,7 +26,7 @@ pub fn get_args(bs: &BootServices, image_handle: Handle) -> Vec { pub fn parse(args: &[String]) -> Cli { let mut cli = Cli { - verbosity: log::LevelFilter::Error, + verbosity: LogLevel(log::LevelFilter::Error), paginate: false, versions: false, version: false, @@ -103,15 +103,15 @@ pub fn parse(args: &[String]) -> Cli { for (i, arg) in args.iter().enumerate() { if arg == "-q" { - cli.verbosity = log::LevelFilter::Off; + cli.verbosity = LogLevel(log::LevelFilter::Off); } else if arg == "-v" { - cli.verbosity = log::LevelFilter::Warn; + cli.verbosity = LogLevel(log::LevelFilter::Warn); } else if arg == "-vv" { - cli.verbosity = log::LevelFilter::Info; + cli.verbosity = LogLevel(log::LevelFilter::Info); } else if arg == "-vvv" { - cli.verbosity = log::LevelFilter::Debug; + cli.verbosity = LogLevel(log::LevelFilter::Debug); } else if arg == "-vvvv" { - cli.verbosity = log::LevelFilter::Trace; + cli.verbosity = LogLevel(log::LevelFilter::Trace); } else if arg == "--versions" { cli.versions = true; found_an_option = true; From 1f5c3464609ab768dd3a2a964c6b5b322448833e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Sat, 10 May 2025 09:26:31 +0800 Subject: [PATCH 154/174] Add --feature readonly, to build a bin without risky commands ``` > cargo build --features readonly && sudo ./target/debug/framework_tool --dump-ec-flash ec.bin && ls -l ec.bin Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s ls: cannot access 'ec.bin': No such file or directory > cargo build && sudo ./target/debug/framework_tool --dump-ec-flash ec.bin && ls -l ec.bin Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.09s Dumping to ec.bin -rw-r--r--. 1 root root 524288 May 10 09:24 ec.bin ``` Signed-off-by: Daniel Schaefer --- framework_lib/Cargo.toml | 1 + framework_lib/src/commandline/mod.rs | 68 +++++++++++++++++++++++++++- framework_tool/Cargo.toml | 4 ++ framework_uefi/Cargo.toml | 4 ++ 4 files changed, 75 insertions(+), 2 deletions(-) diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index b3af9f25..97f30244 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -14,6 +14,7 @@ build = "build.rs" [features] default = ["hidapi", "rusb"] +readonly = [ ] rusb = ["dep:rusb"] hidapi = ["dep:hidapi"] uefi = [ "lazy_static/spin_no_std" ] diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 2c2922b0..94475583 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -221,9 +221,73 @@ pub struct Cli { pub fn parse(args: &[String]) -> Cli { #[cfg(feature = "uefi")] - return uefi::parse(args); + let cli = uefi::parse(args); #[cfg(not(feature = "uefi"))] - return clap_std::parse(args); + let cli = clap_std::parse(args); + + if cfg!(feature = "readonly") { + // Initialize a new Cli with no arguments + // Set all arguments that are readonly/safe + // We explicitly only cope the safe ones so that if we add new arguments in the future, + // which might be unsafe, we can't forget to exclude them from the safe set. + // TODO: Instead of silently ignoring blocked command, we should remind the user + Cli { + verbosity: cli.verbosity, + versions: cli.versions, + version: cli.version, + esrt: cli.esrt, + device: cli.device, + power: cli.power, + thermal: cli.thermal, + sensors: cli.sensors, + // fansetduty + // fansetrpm + // autofanctrl + privacy: cli.privacy, + pd_info: cli.version, + dp_hdmi_info: cli.dp_hdmi_info, + // dp_hdmi_update + audio_card_info: cli.audio_card_info, + pd_bin: cli.pd_bin, + ec_bin: cli.ec_bin, + capsule: cli.capsule, + dump: cli.dump, + h2o_capsule: cli.h2o_capsule, + // dump_ec_flash + // flash_ec + // flash_ro_ec + driver: cli.driver, + test: cli.test, + intrusion: cli.intrusion, + inputdeck: cli.inputdeck, + inputdeck_mode: cli.inputdeck_mode, + expansion_bay: cli.expansion_bay, + // charge_limit + // charge_current_limit + get_gpio: cli.get_gpio, + fp_led_level: cli.fp_led_level, + fp_brightness: cli.fp_brightness, + kblight: cli.kblight, + rgbkbd: cli.rgbkbd, + // tablet_mode + // touchscreen_enable + stylus_battery: cli.stylus_battery, + console: cli.console, + reboot_ec: cli.reboot_ec, + // ec_hib_delay + hash: cli.hash, + pd_addrs: cli.pd_addrs, + pd_ports: cli.pd_ports, + help: cli.help, + info: cli.info, + // allupdate + paginate: cli.paginate, + // raw_command + ..Default::default() + } + } else { + cli + } } fn print_single_pd_details(pd: &PdController) { diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index cdd90a2a..2bc741dd 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -12,6 +12,10 @@ edition = "2021" name = "framework_tool" path = "src/main.rs" +[features] +default = [ ] +readonly = [ "framework_lib/readonly" ] + [dependencies.framework_lib] path = "../framework_lib" diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index adc8b14b..d27a7599 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -14,6 +14,10 @@ rust-version = "1.74" name = "uefitool" path = "src/main.rs" +[features] +default = [ ] +readonly = [ "framework_lib/readonly" ] + [dependencies] uefi = { version = "0.20", features = ["alloc"] } uefi-services = "0.17" From acb91b9f8d48736aa98acc6eb82630cfaa8a1954 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 1 Jul 2025 14:00:03 +0800 Subject: [PATCH 155/174] Build and upload readonly version of UEFI app Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 10 +++++++--- framework_uefi/Makefile | 4 +++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61c0f70a..0f5604d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,13 +77,17 @@ jobs: run: rustup show - name: Build UEFI application (no ESP) - run: make -C framework_uefi build/x86_64-unknown-uefi/boot.efi + run: | + make -C framework_uefi build/x86_64-unknown-uefi/boot.efi + mv framework_uefi/build/x86_64-unknown-uefi/boot.efi framework_tool_full.efi + make -C framework_uefi FEATURES=readonly build/x86_64-unknown-uefi/boot.efi + cp framework_uefi/build/x86_64-unknown-uefi/boot.efi framework_tool.efi - name: Upload UEFI App uses: actions/upload-artifact@v4 with: - name: framework.efi - path: framework_uefi/build/x86_64-unknown-uefi/boot.efi + name: framework_efi.zip + path: ./*.efi - name: Install mtools to build ESP and ISO (Linux) run: sudo apt-get install -y mtools genisoimage diff --git a/framework_uefi/Makefile b/framework_uefi/Makefile index 76b03e5d..0814ff42 100644 --- a/framework_uefi/Makefile +++ b/framework_uefi/Makefile @@ -1,5 +1,6 @@ TARGET?=x86_64-unknown-uefi BUILD=build/$(TARGET) +FEATURES?='' SRC_DIR=. @@ -18,7 +19,7 @@ all: $(BUILD)/boot.img iso: $(BUILD)/UEFI-Shell-fwk.iso clean: - rm -r $(BUILD) + rm -rf $(BUILD) qemu: $(BUILD)/boot.img $(QEMU) $(QEMU_FLAGS) $< @@ -61,6 +62,7 @@ $(BUILD)/boot.efi: ../Cargo.lock $(SRC_DIR)/Cargo.toml $(SRC_DIR)/src/* mkdir -p $(BUILD) cargo rustc \ --target $(TARGET) \ + --features $(FEATURES) \ --release \ -- \ --emit link=framework_uefi/$@ From 4b2a935231688403c0f38f8d0356a3459beb0944 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 2 Jul 2025 10:11:58 +0800 Subject: [PATCH 156/174] Update blocklist of unsafe commands With newly added arguments Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 94475583..265f6796 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -235,16 +235,22 @@ pub fn parse(args: &[String]) -> Cli { verbosity: cli.verbosity, versions: cli.versions, version: cli.version, + features: cli.features, esrt: cli.esrt, device: cli.device, + compare_version: cli.compare_version, power: cli.power, thermal: cli.thermal, sensors: cli.sensors, // fansetduty // fansetrpm // autofanctrl + pdports: cli.pdports, privacy: cli.privacy, pd_info: cli.version, + // pd_reset + // pd_disable + // pd_enable dp_hdmi_info: cli.dp_hdmi_info, // dp_hdmi_update audio_card_info: cli.audio_card_info, @@ -256,19 +262,25 @@ pub fn parse(args: &[String]) -> Cli { // dump_ec_flash // flash_ec // flash_ro_ec + // flash_rw_ec driver: cli.driver, test: cli.test, + dry_run: cli.dry_run, + // force intrusion: cli.intrusion, inputdeck: cli.inputdeck, inputdeck_mode: cli.inputdeck_mode, expansion_bay: cli.expansion_bay, // charge_limit // charge_current_limit + // charge_rate_limit get_gpio: cli.get_gpio, fp_led_level: cli.fp_led_level, fp_brightness: cli.fp_brightness, kblight: cli.kblight, + remap_key: cli.remap_key, rgbkbd: cli.rgbkbd, + ps2_enable: cli.ps2_enable, // tablet_mode // touchscreen_enable stylus_battery: cli.stylus_battery, @@ -280,6 +292,8 @@ pub fn parse(args: &[String]) -> Cli { pd_ports: cli.pd_ports, help: cli.help, info: cli.info, + // flash_gpu_descriptor + // flash_gpu_descriptor_file // allupdate paginate: cli.paginate, // raw_command From 3b290e5786a0e4b9a9827c32dd4b733629b6892e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 2 Jul 2025 10:18:03 +0800 Subject: [PATCH 157/174] Bump to 0.4.5 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 8bac5086..feb35869 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,7 +408,7 @@ dependencies = [ [[package]] name = "framework_lib" -version = "0.4.4" +version = "0.4.5" dependencies = [ "built", "clap", @@ -440,7 +440,7 @@ dependencies = [ [[package]] name = "framework_tool" -version = "0.4.4" +version = "0.4.5" dependencies = [ "embed-resource", "framework_lib", @@ -451,7 +451,7 @@ dependencies = [ [[package]] name = "framework_uefi" -version = "0.4.4" +version = "0.4.5" dependencies = [ "framework_lib", "log", diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 001dbcc8..3ba39e3d 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_lib" -version = "0.4.4" +version = "0.4.5" description = "Library to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_tool/Cargo.toml b/framework_tool/Cargo.toml index fa240e3d..6dceeabb 100644 --- a/framework_tool/Cargo.toml +++ b/framework_tool/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_tool" -version = "0.4.4" +version = "0.4.5" description = "Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" diff --git a/framework_uefi/Cargo.toml b/framework_uefi/Cargo.toml index 6c73a81e..1995042a 100644 --- a/framework_uefi/Cargo.toml +++ b/framework_uefi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "framework_uefi" -version = "0.4.4" +version = "0.4.5" description = "UEFI Tool to control Framework Computer systems" homepage = "https://github.com/FrameworkComputer/framework-system" repository = "https://github.com/FrameworkComputer/framework-system" From 0bd4f5ce34bb2bc9ac9bf59beebfdb4d2449f7d2 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 23 Jul 2025 16:04:07 +0800 Subject: [PATCH 158/174] --inputdeck: Try Framework 16 if can't detect system type In some cases we can't detect the system type, but if sleep_l GPIO is present, we know it's a Framework 16. Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 265f6796..d96cc0ca 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -980,7 +980,16 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { Some(PlatformFamily::Framework12) => ec.print_fw12_inputdeck_status(), Some(PlatformFamily::Framework13) => ec.print_fw13_inputdeck_status(), Some(PlatformFamily::Framework16) => ec.print_fw16_inputdeck_status(), - _ => Ok(()), + // If we don't know which platform it is, we can use some heuristics + _ => { + // Only Framework 16 has this GPIO + if ec.get_gpio("sleep_l").is_ok() { + ec.print_fw16_inputdeck_status() + } else { + println!(" Unable to tell"); + Ok(()) + } + } }; print_err(res); } else if let Some(mode) = &args.inputdeck_mode { From fd71dac89899a3ca5700e084008341da3a2a1744 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 24 Jul 2025 21:57:33 +0800 Subject: [PATCH 159/174] devenv: Add dependencies to build and run uefi tool in QEMU Signed-off-by: Daniel Schaefer --- devenv.nix | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/devenv.nix b/devenv.nix index 413d6a66..e84d1529 100644 --- a/devenv.nix +++ b/devenv.nix @@ -3,9 +3,16 @@ { packages = with pkgs; [ systemd # libudev + # For UEFI building and testing + parted + gnumake + qemu ]; - languages.rust.enable = true; - # https://devenv.sh/reference/options/#languagesrustchannel - languages.rust.channel = "stable"; + languages.rust = { + enable = true; + targets = [ "x86_64-unknown-uefi" ]; + # https://devenv.sh/reference/options/#languagesrustchannel + channel = "stable"; + }; } From 3bae3bc285436d825548be03a3f9c7968e5b0fca Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 1 Aug 2025 18:06:25 +0800 Subject: [PATCH 160/174] Add PID For internal test purposes. Signed-off-by: Daniel Schaefer --- framework_lib/src/inputmodule.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/framework_lib/src/inputmodule.rs b/framework_lib/src/inputmodule.rs index 4e280065..43a7ad5d 100644 --- a/framework_lib/src/inputmodule.rs +++ b/framework_lib/src/inputmodule.rs @@ -6,6 +6,7 @@ pub const FRAMEWORK16_INPUTMODULE_PIDS: [u16; 6] = [ 0x0014, // Keyboard White Backlight Numpad 0x0018, // Keyboard White Backlight ISO 0x0019, // Keyboard White Backlight JIS + 0x0030, LEDMATRIX_PID, ]; From 77ca7a2a1982b22d834d224f0b53c072b027b558 Mon Sep 17 00:00:00 2001 From: tripplehelix Date: Fri, 1 Aug 2025 17:02:32 +0100 Subject: [PATCH 161/174] framework_lib: Fix array length --- framework_lib/src/inputmodule.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework_lib/src/inputmodule.rs b/framework_lib/src/inputmodule.rs index 43a7ad5d..f9e6cc02 100644 --- a/framework_lib/src/inputmodule.rs +++ b/framework_lib/src/inputmodule.rs @@ -1,6 +1,6 @@ pub const FRAMEWORK_VID: u16 = 0x32AC; pub const LEDMATRIX_PID: u16 = 0x0020; -pub const FRAMEWORK16_INPUTMODULE_PIDS: [u16; 6] = [ +pub const FRAMEWORK16_INPUTMODULE_PIDS: [u16; 7] = [ 0x0012, // Keyboard White Backlight ANSI 0x0013, // Keyboard RGB Backlight Numpad 0x0014, // Keyboard White Backlight Numpad From 9b28ae42bc4e7bab3fce5ac0890058ed649b121e Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Wed, 6 Aug 2025 13:22:52 +0800 Subject: [PATCH 162/174] gh-actions: Also upload linux release build (#194) Signed-off-by: Daniel Schaefer --- .github/workflows/ci.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f5604d1..c22589df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -62,9 +62,18 @@ jobs: - name: Upload Linux App uses: actions/upload-artifact@v4 with: - name: framework_tool + name: framework_tool_debug path: target/debug/framework_tool + - name: Build Linux tool (Release) + run: cargo build -p framework_tool --release + + - name: Upload Linux App + uses: actions/upload-artifact@v4 + with: + name: framework_tool + path: target/release/framework_tool + build-uefi: name: Build UEFI runs-on: ubuntu-24.04 From 4ab51be489c612dbb20e5adfcf1b55df8bde0c15 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 7 Aug 2025 19:32:15 +0800 Subject: [PATCH 163/174] Framework 13 and 16 AMD Ryzen 7040 Windows driver (#197) ACPI device included in new BIOS to let the windows driver load. Signed-off-by: Daniel Schaefer --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c40160a5..0c72bef5 100644 --- a/README.md +++ b/README.md @@ -27,18 +27,18 @@ On UEFI and FreeBSD raw port I/O is used - on Linux this can also be used as a f | | Port I/O | Linux | Windows | |---------------------|----------| ------|---------| | Framework 12 | | | | -| Intel Core 12th Gen | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | +| Intel Core 12th Gen | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | | Framework 13 | | | | -| Intel Core 11th Gen | Yes | [6.11](https://github.com/torvalds/linux/commit/04ca0a51f1e63bd553fd4af8e9af0fe094fa4f0a) | Not yet | -| Intel Core 12th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | -| Intel Core 13th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | -| AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | Soon | -| Intel Core Ultra 1S | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Soon | -| AMD Ryzen AI 300 | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | +| Intel Core 11th Gen | Yes | [6.11](https://github.com/torvalds/linux/commit/04ca0a51f1e63bd553fd4af8e9af0fe094fa4f0a) | Not yet | +| Intel Core 12th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | +| Intel Core 13th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | +| AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | BIOS 3.16+ | +| Intel Core Ultra 1S | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Soon | +| AMD Ryzen AI 300 | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | | Framework 16 | | | | -| AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | Soon | +| AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | BIOS 3.06+ | | Framework Desktop | | | | -| AMD Ryzen AI Max | Yes | [6.15](https://github.com/torvalds/linux/commit/d83c45aeec9b223fe6db4175e9d1c4f5699cc37a) | Yes | +| AMD Ryzen AI Max | Yes | [6.15](https://github.com/torvalds/linux/commit/d83c45aeec9b223fe6db4175e9d1c4f5699cc37a) | Yes | ###### Firmware Information From 655936854924c287da7c0a0e97cc578b10f7ccd6 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 11 Aug 2025 16:23:28 +0800 Subject: [PATCH 164/174] Return unnecessary return statements (#199) Recent cargo lint warns about it Signed-off-by: Daniel Schaefer --- framework_lib/src/commandline/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 86440267..06edb01a 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -1683,7 +1683,6 @@ fn analyze_ccgx_pd_fw(data: &[u8]) { println!("FW 2"); ccgx::binary::print_fw(&versions.main_fw); - return; } else if let Some(versions) = ccgx::binary::read_versions(data, Ccg6) { println!("Detected CCG6 firmware"); println!("FW 1 (Backup)"); @@ -1691,7 +1690,6 @@ fn analyze_ccgx_pd_fw(data: &[u8]) { println!("FW 2 (Main)"); ccgx::binary::print_fw(&versions.main_fw); - return; } else { println!("Failed to read PD versions") } From 56522d2d463e2dcbb77dc8d163a1e37cbb09d95a Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 15 Aug 2025 08:33:43 +0800 Subject: [PATCH 165/174] EXAMPLES_ADVANCED: Document --tablet-mode (#200) Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 0ece899d..8e938e1b 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -39,7 +39,7 @@ Left / Ports 23 > sudo framework_tool --pd-enable 0 ``` -### Check EFI Resource Table +## Check EFI Resource Table On Framework Desktop: @@ -60,6 +60,30 @@ ESRT Entry 0 Last Attempt Status: Success ``` +## Manually overriding tablet mode status + +If you have a suspicion that the embedded controller does not control tablet +mode correctly based on Hall and G-Sensor, you can manually force a mode. + +This may also be useful if you want to use the touchpad and keyboard while the +lid is folded back - for example if you're using an external display only (Cyberdeck). +In this case you can force laptop mode. + +Tablet mode: +- Sets a GPIO connected to the touchpad to disable it +- Stops the EC from sending keypresses to the CPU + +``` +# Force tablet mode to disable touchpad and keyboard +> framework_tool --tablet-mode tablet + +# Force laptop mode to always keep touchpad and keyboard enabled +> framework_tool --tablet-mode laptop + +# Let the EC handle tablet mode automatically based on sensors +> framework_tool --tablet-mode auto +``` + ## Flashing EC firmware **IMPORTANT** Flashing EC firmware yourself is not recommended. It may render From 440157c6a375a6daee250cd7638551a2a6054357 Mon Sep 17 00:00:00 2001 From: quinchou Date: Wed, 20 Aug 2025 22:15:00 +0800 Subject: [PATCH 166/174] usbhub: Print firmware version of usb hub (#202) For example on Framework Desktop: [...] USB Hub RTL5424 Firmware Version: 1.8.8 [...] --- framework_lib/src/commandline/mod.rs | 5 +++++ framework_lib/src/lib.rs | 2 ++ framework_lib/src/usbhub.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 framework_lib/src/usbhub.rs diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 06edb01a..646ea8d1 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -60,6 +60,8 @@ use crate::touchpad::print_touchpad_fw_ver; use crate::touchscreen; #[cfg(feature = "uefi")] use crate::uefi::enable_page_break; +#[cfg(feature = "rusb")] +use crate::usbhub::check_usbhub_version; use crate::util::{self, Config, Platform, PlatformFamily}; #[cfg(feature = "hidapi")] use hidapi::HidApi; @@ -705,6 +707,9 @@ fn print_versions(ec: &CrosEc) { #[cfg(feature = "rusb")] let _ignore_err = check_camera_version(); + #[cfg(feature = "rusb")] + let _ignore_err = check_usbhub_version(); + #[cfg(feature = "rusb")] let _ignore_err = check_inputmodule_version(); diff --git a/framework_lib/src/lib.rs b/framework_lib/src/lib.rs index 05adf8fb..f205403e 100644 --- a/framework_lib/src/lib.rs +++ b/framework_lib/src/lib.rs @@ -25,6 +25,8 @@ pub mod touchpad; pub mod touchscreen; #[cfg(all(feature = "hidapi", windows))] pub mod touchscreen_win; +#[cfg(feature = "rusb")] +pub mod usbhub; #[cfg(feature = "uefi")] #[macro_use] diff --git a/framework_lib/src/usbhub.rs b/framework_lib/src/usbhub.rs new file mode 100644 index 00000000..53f25576 --- /dev/null +++ b/framework_lib/src/usbhub.rs @@ -0,0 +1,26 @@ +pub const REALTEK_VID: u16 = 0x0BDA; +pub const RTL5432_PID: u16 = 0x5432; +pub const RTL5424_PID: u16 = 0x5424; + +/// Get and print the firmware version of the usbhub +pub fn check_usbhub_version() -> Result<(), rusb::Error> { + for dev in rusb::devices().unwrap().iter() { + let dev_descriptor = dev.device_descriptor().unwrap(); + if dev_descriptor.vendor_id() != REALTEK_VID + || (dev_descriptor.product_id() != RTL5432_PID + && dev_descriptor.product_id() != RTL5424_PID) + { + debug!( + "Skipping {:04X}:{:04X}", + dev_descriptor.vendor_id(), + dev_descriptor.product_id() + ); + continue; + } + + let dev_descriptor = dev.device_descriptor()?; + println!("USB Hub RTL{:04X}", dev_descriptor.product_id()); + println!(" Firmware Version: {}", dev_descriptor.device_version()); + } + Ok(()) +} From 0dde5cda9d19d5e2e739624bd80f7bdd15827843 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 18 Aug 2025 13:21:31 +0800 Subject: [PATCH 167/174] README: Document installation on distros Signed-off-by: Daniel Schaefer --- README.md | 51 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 0c72bef5..69e52ab2 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,33 @@ The tool works on Linux, Windows and the UEFI shell. Download it from the latest [GH Actions](https://github.com/FrameworkComputer/framework-system/actions?query=branch%3Amain) run on the main branch. Most features are supported on every "OS". See below for details. +## Installation + +### Linux + +- [NixOS](https://github.com/NixOS/nixpkgs/blob/nixos-25.05/pkgs/by-name/fr/framework-tool/package.nix) + - `nix-shell -p framework-tool` +- [ArchLinux](https://archlinux.org/packages/extra/x86_64/framework-system/) + - `pacman -Sy framework-system` +- [Bazzite](https://github.com/ublue-os/bazzite/pull/3026) +- Others + - Build from source + - Or download [latest binary](https://github.com/FrameworkComputer/framework-system/releases/latest/download/framework_tool) +- ChromeOS + - Build from source + +### Windows + +``` +winget install FrameworkComputer.framework_tool +``` + +### FreeBSD + +``` +sudo pkg install framework-system +``` + ## Features To check which features are supported on which OS and platform, @@ -153,6 +180,9 @@ ls -l framework_uefi/build/x86_64-unknown-uefi/boot.efi # NixOS nix-shell --run fish -p cargo systemd udev hidapi pkg-config direnv shell + +# FreeBSD +sudo pkg install hidapi ``` ## Install local package @@ -226,15 +256,6 @@ Many actions require root. First build with cargo and then run the binary with s cargo build && sudo ./target/debug/framework_tool ``` -###### Running on Windows - -On newly released systems since 2025 the Framework driver installer includes the EC driver. -This includes Framework 12, Framework 13 AMD Ryzen AI 300, Framework Desktop. - -Previous platforms will be enabled next. - -Installing: `winget install FrameworkComputer.framework_tool` - ##### Running on ChromeOS The application can run on ChromeOS but most commands rely on custom host @@ -329,15 +350,3 @@ Keyboard backlight: 0% [DEBUG] send_command(command=0x22, ver=0, data_len=0) Keyboard backlight: 0% ``` - -## FreeBSD - -``` -sudo pkg install hidapi - -# Build the library and tool -cargo build - -# Running the tool -cargo run -``` From a9ebb6976bfadc65b3d8264d3d98e0cc31a8f3f8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 18 Aug 2025 13:21:56 +0800 Subject: [PATCH 168/174] README: Move more into advanced example file Clean up readme Signed-off-by: Daniel Schaefer --- EXAMPLES_ADVANCED.md | 83 +++++++++++++++++++++++++++++++++++++++++ README.md | 88 ++------------------------------------------ 2 files changed, 86 insertions(+), 85 deletions(-) diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 8e938e1b..a350df39 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -1,5 +1,33 @@ # Advanced debugging +## Verbosity + +To debug, increase the verbosity from the commandline with `-v`. +The verbosity levels are: + +| Commandline | Level | +|-------------|--------| +| `-q` | No log | +| None | Error | +| `-v` | Warn | +| `-vv` | Info | +| `-vvv` | Debug | +| `-vvvv` | Trace | + +For example it is useful to check which EC driver is used: + +``` +> framework_tool --kblight -vvv +[DEBUG] Chromium EC Driver: CrosEc +[DEBUG] send_command(command=0x22, ver=0, data_len=0) +Keyboard backlight: 0% + +> framework_tool --driver portio --kblight -vvv +[DEBUG] Chromium EC Driver: Portio +[DEBUG] send_command(command=0x22, ver=0, data_len=0) +Keyboard backlight: 0% +``` + ## PD ### Check PD state @@ -189,3 +217,58 @@ Capsule Header Capsule Size: 2180 KB Type: Framework Retimer23 (Right) ``` + +## Version Check + +Check if the firmware version is what you expect, returns exit code 0 on +succcess, 1 on failure. + +``` +# Check which devices it's available for +> ./framework_tool --device + [possible values: bios, ec, pd0, pd1, rtm01, rtm23, ac-left, ac-right] + +For more information try '--help' + +# Successful compare +> ./framework_tool --device bios --compare-version 03.01 +Target Version "03.01" +Comparing BIOS version "03.01" +Compared version: 0 +> echo $? +0 + +# Failed compare +> ./framework_tool --device bios --compare-version 03.00 + Finished dev [unoptimized + debuginfo] target(s) in 0.05s +Target Version "03.00" +Comparing BIOS version "03.01" +Compared version: 1 +Error: "Fail" + +> echo $? +1 +``` + +On UEFI Shell: + +``` +# Check if AC is attached on left side +Shell> fs0:framework_tool.efi --device ac-left --compare-version 1 +Target Version "1" +Comparing AcLeft "1" +Comparison Result: 0 +# It is +Shell> echo %lasterror% +0x0 + +# Check if AC is attached on right side +Shell> fs0:framework_tool.efi --device ac-right --compare-version 1 +Target Version "1" +Comparing AcLeft "0" +Comparison Result: 1 + +# It is not +Shell> echo %lasterror% +0x1 +``` diff --git a/README.md b/README.md index 69e52ab2..018fcba5 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,9 @@ Rust libraries and tools to interact with the system. The tool works on Linux, Windows and the UEFI shell. -Download it from the latest [GH Actions](https://github.com/FrameworkComputer/framework-system/actions?query=branch%3Amain) run on the main branch. -Most features are supported on every "OS". See below for details. +Most features are supported on every "OS". + +You can find lots of examples in [EXAMPLES.md](./EXAMPLES.md). ## Installation @@ -267,86 +268,3 @@ own EC firmware and flash it. - [x] Basic unit tests - [x] Test parsing real binaries - -## Version Check - -Check if the firmware version is what you expect, returns exit code 0 on -succcess, 1 on failure. - -``` -# Check which devices it's available for -> ./framework_tool --device - [possible values: bios, ec, pd0, pd1, rtm01, rtm23, ac-left, ac-right] - -For more information try '--help' - -# Successful compare -> ./framework_tool --device bios --compare-version 03.01 -Target Version "03.01" -Comparing BIOS version "03.01" -Compared version: 0 -> echo $? -0 - -# Failed compare -> ./framework_tool --device bios --compare-version 03.00 - Finished dev [unoptimized + debuginfo] target(s) in 0.05s -Target Version "03.00" -Comparing BIOS version "03.01" -Compared version: 1 -Error: "Fail" - -> echo $? -1 -``` - -On UEFI Shell: - -``` -# Check if AC is attached on left side -Shell> fs0:framework_tool.efi --device ac-left --compare-version 1 -Target Version "1" -Comparing AcLeft "1" -Comparison Result: 0 -# It is -Shell> echo %lasterror% -0x0 - -# Check if AC is attached on right side -Shell> fs0:framework_tool.efi --device ac-right --compare-version 1 -Target Version "1" -Comparing AcLeft "0" -Comparison Result: 1 - -# It is not -Shell> echo %lasterror% -0x1 -``` - -## Debugging - -To debug, increase the verbosity from the commandline with `-v`. -The verbosity levels are: - -| Commandline | Level | -|-------------|--------| -| `-q` | No log | -| None | Error | -| `-v` | Warn | -| `-vv` | Info | -| `-vvv` | Debug | -| `-vvvv` | Trace | - -For example it is useful to check which EC driver is used: - -``` -> framework_tool --kblight -vvv -[DEBUG] Chromium EC Driver: CrosEc -[DEBUG] send_command(command=0x22, ver=0, data_len=0) -Keyboard backlight: 0% - -> framework_tool --driver portio --kblight -vvv -[DEBUG] Chromium EC Driver: Portio -[DEBUG] send_command(command=0x22, ver=0, data_len=0) -Keyboard backlight: 0% -``` From 6419801fdbf4b2ec8d560133b49ebc5852293316 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 18 Aug 2025 13:27:20 +0800 Subject: [PATCH 169/174] README: Move more examples away Signed-off-by: Daniel Schaefer --- EXAMPLES.md | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 100 ++++++++------------------------------ 2 files changed, 156 insertions(+), 81 deletions(-) diff --git a/EXAMPLES.md b/EXAMPLES.md index 2361b196..557d8349 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,5 +1,142 @@ # Example usage +Built-in help: + +``` +> framework_tool +Swiss army knife for Framework laptops + +Usage: framework_tool [OPTIONS] + +Options: + --flash-gpu-descriptor + + -v, --verbose... + Increase logging verbosity + -q, --quiet... + Decrease logging verbosity + --versions + List current firmware versions + --version + Show tool version information (Add -vv for more details) + --features + Show features support by the firmware + --esrt + Display the UEFI ESRT table + --device + [possible values: bios, ec, pd0, pd1, rtm01, rtm23, ac-left, ac-right] + --compare-version + + --power + Show current power status of battery and AC (Add -vv for more details) + --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 + --pd-reset + Reset a specific PD controller (for debugging only) + --pd-disable + Disable all ports on a specific PD controller (for debugging only) + --pd-enable + Enable all ports on a specific PD controller (for debugging only) + --dp-hdmi-info + Show details about connected DP or HDMI Expansion Cards + --dp-hdmi-update + Update the DisplayPort or HDMI Expansion Card + --audio-card-info + Show details about connected Audio Expansion Cards (Needs root privileges) + --privacy + Show privacy switch statuses (camera and microphone) + --pd-bin + Parse versions from PD firmware binary file + --ec-bin + Parse versions from EC firmware binary file + --capsule + Parse UEFI Capsule information from binary file + --dump + Dump extracted UX capsule bitmap image to a file + --h2o-capsule + Parse UEFI Capsule information from binary file + --dump-ec-flash + Dump EC flash contents + --flash-ec + Flash EC (RO+RW) with new firmware from file - may render your hardware unbootable! + --flash-ro-ec + Flash EC with new RO firmware from file - may render your hardware unbootable! + --flash-rw-ec + Flash EC with new RW firmware from file + --intrusion + Show status of intrusion switch + --inputdeck + Show status of the input modules (Framework 16 only) + --inputdeck-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 + --charge-current-limit ... + Set max charge current limit + --charge-rate-limit ... + Set max charge current limit + --get-gpio [] + Get GPIO value by name or all, if no name provided + --fp-led-level [] + Get or set fingerprint LED brightness level [possible values: high, medium, low, ultra-low, auto] + --fp-brightness [] + Get or set fingerprint LED brightness percentage + --kblight [] + Set keyboard backlight percentage or get, if no value provided + --remap-key + Remap a key by changing the scancode + --rgbkbd ... + Set the color of to . Multiple colors for adjacent keys can be set at once. [ ...] Example: 0 0xFF000 0x00FF00 0x0000FF + --tablet-mode + Set tablet mode override [possible values: auto, tablet, laptop] + --touchscreen-enable + Enable/disable touchscreen [possible values: true, false] + --stylus-battery + Check stylus battery level (USI 2.0 stylus only) + --console + Get EC console, choose whether recent or to follow the output [possible values: recent, follow] + --reboot-ec + Control EC RO/RW jump [possible values: reboot, jump-ro, jump-rw, cancel-jump, disable-jump] + --ec-hib-delay [] + Get or set EC hibernate delay (S5 to G3) + --hash + Hash a file of arbitrary data + --driver + Select which driver is used. By default portio is used [possible values: portio, cros-ec, windows] + --pd-addrs + Specify I2C addresses of the PD chips (Advanced) + --pd-ports + Specify I2C ports of the PD chips (Advanced) + -t, --test + Run self-test to check if interaction with EC is possible + -f, --force + Force execution of an unsafe command - may render your hardware unbootable! + --dry-run + Simulate execution of a command (e.g. --flash-ec) + --flash-gpu-descriptor-file + File to write to the gpu EEPROM + --dump-gpu-descriptor-file + File to dump the gpu EEPROM to + -h, --help + Print help +``` + ## Check firmware versions ### BIOS (Mainboard, UEFI, EC, PD, Retimer) diff --git a/README.md b/README.md index 018fcba5..440cff0b 100644 --- a/README.md +++ b/README.md @@ -137,18 +137,32 @@ All of these need EC communication support in order to work. - [x] Using [Framework EC Windows driver](https://github.com/FrameworkComputer/crosecbus) based on [coolstar's](https://github.com/coolstar/crosecbus) - [x] Using [DHowett's Windows CrosEC driver](https://github.com/DHowett/FrameworkWindowsUtils) -## Prerequisites +## Building -Only [Rustup](https://rustup.rs/) is needed. Based on `rust-toolchain.toml` it -will install the right toolchain and version for this project. +### Dependencies -## Building +[Rustup](https://rustup.rs/) is convenient for setting up the right Rust version. +Based on `rust-toolchain.toml` it will install the right toolchain and version for this project. MSRV (Minimum Supported Rust Version): - 1.74 for Linux/Windows - 1.74 for UEFI +System dependencies + +``` +# NixOS +nix-shell --run fish -p cargo systemd udev hidapi pkg-config +direnv shell + +# Fedora +sudo dnf install systemd-devel hidapi-devel + +# FreeBSD +sudo pkg install hidapi +``` + ```sh # Running linter cargo clippy @@ -175,17 +189,6 @@ make -C framework_uefi ls -l framework_uefi/build/x86_64-unknown-uefi/boot.efi ``` -### Dependencies - -``` -# NixOS -nix-shell --run fish -p cargo systemd udev hidapi pkg-config -direnv shell - -# FreeBSD -sudo pkg install hidapi -``` - ## Install local package ``` @@ -196,75 +199,10 @@ sudo pkg install hidapi ## Running -Run without any arguments to see the help: - -``` -> cargo run -Swiss army knife for Framework laptops - -Usage: framework_tool [OPTIONS] - -Options: - -v, --verbose... More output per occurrence - -q, --quiet... Less output per occurrence - --versions List current firmware versions version - --esrt Display the UEFI ESRT table - --power Show current power status (battery and AC) - --pdports Show information about USB-C PD ports - --info Show info from SMBIOS (Only on UEFI) - --pd-info Show details about the PD controllers - --dp-hdmi-info Show details about connected DP or HDMI Expansion Cards - --dp-hdmi-update Update the DisplayPort or HDMI Expansion Card - --audio-card-info Show details about connected Audio Expansion Cards (Needs root privileges) - --privacy Show privacy switch statuses (camera and microphone) - --pd-bin Parse versions from PD firmware binary file - --ec-bin Parse versions from EC firmware binary file - --capsule Parse UEFI Capsule information from binary file - --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 - --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) - --charge-limit [] - Get or set max charge limit - --get-gpio [] - Get GPIO value by name or all, if no name provided - --fp-led-level [] - Get or set fingerprint LED brightness level [possible values: high, medium, low, ultra-low, auto] - --fp-brightness [] - Get or set fingerprint LED brightness percentage - --kblight [] Set keyboard backlight percentage or get, if no value provided - --tablet-mode Set tablet mode override [possible values: auto, tablet, laptop] - --touchscreen-enable - Enable/disable touchscreen [possible values: true, false] - --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] - --reboot-ec Control EC RO/RW jump [possible values: reboot, jump-ro, jump-rw, cancel-jump, disable-jump] - --hash Hash a file of arbitrary data - --driver Select which driver is used. By default portio is used [possible values: portio, cros-ec, windows] - --pd-addrs - Specify I2C addresses of the PD chips (Advanced) - --pd-ports - Specify I2C ports of the PD chips (Advanced) - -t, --test Run self-test to check if interaction with EC is possible - -h, --help Print help information -``` +Run without any arguments to see the help. Many actions require root. First build with cargo and then run the binary with sudo: ```sh cargo build && sudo ./target/debug/framework_tool ``` - -##### Running on ChromeOS - -The application can run on ChromeOS but most commands rely on custom host -commands that we built into the EC firmware of non-Chromebook Framework laptops. -In theory you could add those patches to the Chromebook platform, build your -own EC firmware and flash it. - -## Tests - -- [x] Basic unit tests -- [x] Test parsing real binaries From 1fcbe45d32c402f1fd7a680805cd3441f75740d8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 18 Aug 2025 17:33:58 +0800 Subject: [PATCH 170/174] Intel Core Ultra Windows driver supported in BIOS 3.06 https://community.frame.work/t/framework-laptop-13-intel-core-ultra-series-1-bios-3-06-release-beta/74084 Signed-off-by: Daniel Schaefer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 440cff0b..eb44489e 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ On UEFI and FreeBSD raw port I/O is used - on Linux this can also be used as a f | Intel Core 12th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | | Intel Core 13th Gen | Yes | [6.13](https://github.com/torvalds/linux/commit/dcd59d0d7d51b2a4b768fc132b0d74a97dfd6d6a) | Not yet | | AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | BIOS 3.16+ | -| Intel Core Ultra 1S | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Soon | +| Intel Core Ultra S1 | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | BIOS 3.06+ | | AMD Ryzen AI 300 | Yes | [6.12](https://github.com/torvalds/linux/commit/62be134abf4250474a7a694837064bc783d2b291) | Yes | | Framework 16 | | | | | AMD Ryzen 7040 | Yes | [6.10](https://github.com/torvalds/linux/commit/c8f460d991df93d87de01a96b783cad5a2da9616) | BIOS 3.06+ | From 6a0369bd97073f13dd790d7d274fb4f0308c4a34 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Thu, 21 Aug 2025 16:33:42 +0800 Subject: [PATCH 171/174] guid-create: All changes merged upstream Just waiting for release Signed-off-by: Daniel Schaefer --- Cargo.lock | 2 +- framework_lib/Cargo.toml | 2 +- framework_lib/src/capsule.rs | 8 ++++---- framework_lib/src/commandline/mod.rs | 4 ++-- framework_lib/src/esrt/mod.rs | 14 +++++++------- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8a41533a..eda88734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -584,7 +584,7 @@ dependencies = [ [[package]] name = "guid-create" version = "0.4.1" -source = "git+https://github.com/FrameworkComputer/guid-create?branch=no-rand#84c3ad2e8b64a12beebb460804a65da55434cfd9" +source = "git+https://github.com/kurtlawrence/guid-create#14b53de6d816b9892b8590bbad0f3ba1e282aff3" dependencies = [ "winapi", ] diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index 348b6bc0..ef3d600e 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -34,7 +34,7 @@ spin = { version = "0.9.8" } no-std-compat = { version = "0.4.1", features = [ "alloc" ] } hidapi = { version = "2.6.3", features = [ "windows-native" ], optional = true } rusb = { version = "0.9.4", optional = true } -guid-create = { git = "https://github.com/FrameworkComputer/guid-create", branch = "no-rand", default-features = false } +guid-create = { git = "https://github.com/kurtlawrence/guid-create", default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] uefi = { version = "0.20", features = ["alloc"] } diff --git a/framework_lib/src/capsule.rs b/framework_lib/src/capsule.rs index e2dff320..8ded6493 100644 --- a/framework_lib/src/capsule.rs +++ b/framework_lib/src/capsule.rs @@ -11,7 +11,7 @@ use std::prelude::v1::*; use core::prelude::rust_2021::derive; -use guid_create::Guid; +use guid_create::CGuid; #[cfg(not(feature = "uefi"))] use std::fs::File; #[cfg(not(feature = "uefi"))] @@ -21,7 +21,7 @@ use std::io::prelude::*; #[repr(C)] pub struct EfiCapsuleHeader { /// A GUID that defines the contents of a capsule. - pub capsule_guid: Guid, + pub capsule_guid: CGuid, /// The size of the capsule header. This may be larger than the size of /// the EFI_CAPSULE_HEADER since CapsuleGuid may imply @@ -205,14 +205,14 @@ mod tests { let data = fs::read(capsule_path).unwrap(); let cap = parse_capsule_header(&data).unwrap(); let expected_header = EfiCapsuleHeader { - capsule_guid: Guid::from(esrt::WINUX_GUID), + capsule_guid: CGuid::from(esrt::WINUX_GUID), header_size: 28, flags: 65536, capsule_image_size: 676898, }; assert_eq!(cap, expected_header); - assert_eq!(cap.capsule_guid, Guid::from(esrt::WINUX_GUID)); + assert_eq!(cap.capsule_guid, CGuid::from(esrt::WINUX_GUID)); let ux_header = parse_ux_header(&data); assert_eq!( ux_header, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 646ea8d1..e56d348d 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -7,7 +7,7 @@ use alloc::format; use alloc::string::String; use alloc::string::ToString; use alloc::vec::Vec; -use guid_create::{Guid, GUID}; +use guid_create::{CGuid, GUID}; use log::Level; use num_traits::FromPrimitive; @@ -1275,7 +1275,7 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { println!(" Size: {:>20} B", data.len()); println!(" Size: {:>20} KB", data.len() / 1024); if let Some(header) = analyze_capsule(&data) { - if header.capsule_guid == Guid::from(esrt::WINUX_GUID) { + if header.capsule_guid == CGuid::from(esrt::WINUX_GUID) { let ux_header = capsule::parse_ux_header(&data); if let Some(dump_path) = &args.dump { // TODO: Better error handling, rather than just panicking diff --git a/framework_lib/src/esrt/mod.rs b/framework_lib/src/esrt/mod.rs index aa517bf4..77ecbf74 100644 --- a/framework_lib/src/esrt/mod.rs +++ b/framework_lib/src/esrt/mod.rs @@ -15,7 +15,7 @@ use log::{debug, error, info, trace}; use std::prelude::v1::*; use core::prelude::v1::derive; -use guid_create::{Guid, GUID}; +use guid_create::{CGuid, GUID}; #[cfg(target_os = "linux")] use std::fs; @@ -193,7 +193,7 @@ pub enum FrameworkGuidKind { Unknown, } -pub fn match_guid_kind(guid: &Guid) -> FrameworkGuidKind { +pub fn match_guid_kind(guid: &CGuid) -> FrameworkGuidKind { match GUID::from(*guid) { TGL_BIOS_GUID => FrameworkGuidKind::TglBios, ADL_BIOS_GUID => FrameworkGuidKind::AdlBios, @@ -292,7 +292,7 @@ impl UpdateStatus { // TODO: Decode into proper Rust types #[derive(Clone)] pub struct EsrtResourceEntry { - pub fw_class: Guid, + pub fw_class: CGuid, pub fw_type: u32, // ResourceType pub fw_version: u32, pub lowest_supported_fw_version: u32, @@ -364,7 +364,7 @@ fn esrt_from_sysfs(dir: &Path) -> io::Result { let last_attempt_version = fs::read_to_string(path.join("last_attempt_version"))?; let last_attempt_status = fs::read_to_string(path.join("last_attempt_status"))?; let esrt = EsrtResourceEntry { - fw_class: Guid::from( + fw_class: CGuid::from( GUID::parse(fw_class.trim()).expect("Kernel provided wrong value"), ), fw_type: fw_type @@ -436,7 +436,7 @@ pub fn get_esrt() -> Option { let ver_str = caps.get(2).unwrap().as_str().to_string(); let guid = GUID::parse(guid_str.trim()).expect("Kernel provided wrong value"); - let guid_kind = match_guid_kind(&Guid::from(guid)); + let guid_kind = match_guid_kind(&CGuid::from(guid)); let ver = u32::from_str_radix(&ver_str, 16).unwrap(); debug!("ESRT Entry {}", i); debug!(" Name: {:?}", guid_kind); @@ -456,7 +456,7 @@ pub fn get_esrt() -> Option { // TODO: The missing fields are present in Device Manager // So there must be a way to get at them let esrt = EsrtResourceEntry { - fw_class: Guid::from(guid), + fw_class: CGuid::from(guid), fw_type, fw_version: ver, // TODO: Not exposed by windows @@ -543,7 +543,7 @@ pub fn get_esrt() -> Option { for table in config_tables { // TODO: Why aren't they the same type? //debug!("Table: {:?}", table); - let table_guid: Guid = unsafe { std::mem::transmute(table.guid) }; + let table_guid: CGuid = unsafe { std::mem::transmute(table.guid) }; let table_guid = GUID::from(table_guid); match table_guid { SYSTEM_RESOURCE_TABLE_GUID => unsafe { From 40556f03d8249f5510b7b4b8a80677244b233696 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 22 Aug 2025 08:28:35 +0800 Subject: [PATCH 172/174] guid-create: Update to 0.5.0 All changes upstreamed and released! Signed-off-by: Daniel Schaefer --- Cargo.lock | 100 ++++++++++++++++++++++++++++++++++++++- framework_lib/Cargo.toml | 2 +- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eda88734..00574d83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,6 +568,18 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi", +] + [[package]] name = "git2" version = "0.15.0" @@ -583,9 +595,11 @@ dependencies = [ [[package]] name = "guid-create" -version = "0.4.1" -source = "git+https://github.com/kurtlawrence/guid-create#14b53de6d816b9892b8590bbad0f3ba1e282aff3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c332e3cc6970b85f68ff39438fdb87b5c9e27a0260d720d7d550701d9964baa0" dependencies = [ + "rand", "winapi", ] @@ -955,6 +969,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.93" @@ -993,6 +1016,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_hwio" version = "0.1.6" @@ -1421,6 +1479,15 @@ dependencies = [ "libc", ] +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -1825,6 +1892,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags 2.6.0", +] + [[package]] name = "wmi" version = "0.15.0" @@ -1839,3 +1915,23 @@ dependencies = [ "windows 0.59.0", "windows-core", ] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.98", +] diff --git a/framework_lib/Cargo.toml b/framework_lib/Cargo.toml index ef3d600e..62e0e5d8 100644 --- a/framework_lib/Cargo.toml +++ b/framework_lib/Cargo.toml @@ -34,7 +34,7 @@ spin = { version = "0.9.8" } no-std-compat = { version = "0.4.1", features = [ "alloc" ] } hidapi = { version = "2.6.3", features = [ "windows-native" ], optional = true } rusb = { version = "0.9.4", optional = true } -guid-create = { git = "https://github.com/kurtlawrence/guid-create", default-features = false } +guid-create = { version = "0.5.0", default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] uefi = { version = "0.20", features = ["alloc"] } From 8ab61a92dde29b0677257261ea007919854570f9 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 22 Aug 2025 08:59:58 +0800 Subject: [PATCH 173/174] gh-actions: fail if anything changed 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 c22589df..32037c69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,6 +74,9 @@ jobs: name: framework_tool path: target/release/framework_tool + - name: Make sure nothing changed (e.g. Cargo.lock) + run: git diff --exit-code + build-uefi: name: Build UEFI runs-on: ubuntu-24.04 From a24ace300650e4df21289502295ad819b46cba5d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Fri, 22 Aug 2025 08:46:52 +0800 Subject: [PATCH 174/174] Update lockfile Signed-off-by: Daniel Schaefer --- Cargo.lock | 95 ------------------------------------------------------ 1 file changed, 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 00574d83..dfee2ca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -568,18 +568,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "getrandom" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" -dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasi", -] - [[package]] name = "git2" version = "0.15.0" @@ -599,7 +587,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c332e3cc6970b85f68ff39438fdb87b5c9e27a0260d720d7d550701d9964baa0" dependencies = [ - "rand", "winapi", ] @@ -969,15 +956,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "proc-macro2" version = "1.0.93" @@ -1016,41 +994,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" -dependencies = [ - "getrandom", -] - [[package]] name = "redox_hwio" version = "0.1.6" @@ -1479,15 +1422,6 @@ dependencies = [ "libc", ] -[[package]] -name = "wasi" -version = "0.14.2+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" -dependencies = [ - "wit-bindgen-rt", -] - [[package]] name = "wasm-bindgen" version = "0.2.84" @@ -1892,15 +1826,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "wit-bindgen-rt" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.6.0", -] - [[package]] name = "wmi" version = "0.15.0" @@ -1915,23 +1840,3 @@ dependencies = [ "windows 0.59.0", "windows-core", ] - -[[package]] -name = "zerocopy" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.98", -]