From 4b1c4d48cdb3be0bbb9e0a0169b99ec880c615d1 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 23 Oct 2023 12:55:09 +0800 Subject: [PATCH 1/3] Add command to get and set charge limit The minimum limit cannot be changed. Tested on platforms: - Intel 12th Gen with BIOS 3.07 - AMD 13th inch with BIOS 3.04 Tested with drivers; - portio on UEFI Shell - portio on Linux - cros_ec on Linux Examples: ``` > framework_tool --driver portio --charge-limit Minimum 0%, Maximum 100% > framework_tool --charge-limit 90 Minimum 0%, Maximum 90% ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 2 ++ framework_lib/src/chromium_ec/commands.rs | 38 +++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 27 ++++++++++++++++ framework_lib/src/commandline/clap_std.rs | 5 +++ framework_lib/src/commandline/mod.rs | 23 ++++++++++++++ framework_lib/src/commandline/uefi.rs | 16 ++++++++++ 6 files changed, 111 insertions(+) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 10e5d1b..7448699 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -30,6 +30,8 @@ pub enum EcCommands { // Framework specific commands /// Configure the behavior of the flash notify FlashNotified = 0x3E01, + /// Change charge limit + ChargeLimitControl = 0x3E03, /// Get information about the current chassis open/close status ChassisOpenCheck = 0x3E0F, /// Get information about historical chassis open/close (intrusion) information diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 8680630..8cb5a80 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -359,3 +359,41 @@ impl EcRequest for EcRequestGetHwDiag { EcCommands::GetHwDiag } } + +#[repr(u8)] +pub enum ChargeLimitControlModes { + /// Disable all settings, handled automatically + Disable = 0x01, + /// Set maxiumum and minimum percentage + Set = 0x02, + /// Get current setting + /// ATTENTION!!! This is the only mode that will return a response + Get = 0x08, + /// Allow charge to full this time + Override = 0x80, +} + +#[repr(C, packed)] +pub struct EcRequestChargeLimitControl { + pub modes: u8, + pub max_percentage: u8, + pub min_percentage: u8, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct EcResponseChargeLimitControl { + pub max_percentage: u8, + pub min_percentage: u8, +} + +impl EcRequest for EcRequestChargeLimitControl { + fn command_id() -> EcCommands { + EcCommands::ChargeLimitControl + } +} + +/* + * Configure the behavior of the charge limit control. + */ +pub const EC_CHARGE_LIMIT_RESTORE: u8 = 0x7F; diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 921fd7e..c10f44c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -229,6 +229,33 @@ impl CrosEc { Ok((status.microphone == 1, status.camera == 1)) } + pub fn set_charge_limit(&self, min: u8, max: u8) -> EcResult<()> { + // Sending bytes manually because the Set command, as opposed to the Get command, + // does not return any data + let limits = &[ChargeLimitControlModes::Set as u8, max, min]; + let data = self.send_command(EcCommands::ChargeLimitControl as u16, 0, limits)?; + assert_eq!(data.len(), 0); + + Ok(()) + } + + /// Get charge limit in percent (min, max) + pub fn get_charge_limit(&self) -> EcResult<(u8, u8)> { + let limits = EcRequestChargeLimitControl { + modes: ChargeLimitControlModes::Get as u8, + max_percentage: 0xFF, + min_percentage: 0xFF, + } + .send_command(self)?; + + debug!( + "Min Raw: {}, Max Raw: {}", + limits.min_percentage, limits.max_percentage + ); + + Ok((limits.min_percentage, limits.max_percentage)) + } + /// Get the intrusion switch status (whether the chassis is open or not) pub fn get_intrusion_status(&self) -> EcResult { let status = EcRequestChassisOpenCheck {}.send_command(self)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 2a78d24..0832810 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -89,6 +89,10 @@ struct ClapCli { #[arg(long)] input_deck_mode: Option, + /// Get or set max charge limit + #[arg(long)] + charge_limit: Option>, + /// Set keyboard backlight percentage or get, if no value provided #[arg(long)] kblight: Option>, @@ -142,6 +146,7 @@ pub fn parse(args: &[String]) -> Cli { intrusion: args.intrusion, inputmodules: args.inputmodules, input_deck_mode: args.input_deck_mode, + charge_limit: args.charge_limit, kblight: args.kblight, console: args.console, driver: args.driver, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 279a49a..89d0b05 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -30,6 +30,8 @@ use crate::ccgx::{self, SiliconId::*}; use crate::chromium_ec; use crate::chromium_ec::commands::DeckStateMode; use crate::chromium_ec::print_err; +use crate::chromium_ec::EcError; +use crate::chromium_ec::EcResult; #[cfg(feature = "linux")] use crate::csme; use crate::ec_binary; @@ -100,6 +102,7 @@ pub struct Cli { pub intrusion: bool, pub inputmodules: bool, pub input_deck_mode: Option, + pub charge_limit: Option>, pub kblight: Option>, pub console: Option, pub help: bool, @@ -435,6 +438,8 @@ 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 let Some(maybe_limit) = args.charge_limit { + print_err(handle_charge_limit(&ec, maybe_limit)); } else if let Some(Some(kblight)) = args.kblight { assert!(kblight <= 100); ec.set_keyboard_backlight(kblight); @@ -841,3 +846,21 @@ pub fn analyze_capsule(data: &[u8]) -> Option { Some(header) } + +fn handle_charge_limit(ec: &CrosEc, maybe_limit: Option) -> EcResult<()> { + let (cur_min, _cur_max) = ec.get_charge_limit()?; + if let Some(limit) = maybe_limit { + // Prevent accidentally setting a very low limit + if limit < 25 { + return Err(EcError::DeviceError( + "Not recommended to set charge limit below 25%".to_string(), + )); + } + ec.set_charge_limit(cur_min, limit)?; + } + + let (min, max) = ec.get_charge_limit()?; + println!("Minimum {}%, Maximum {}%", min, max); + + Ok(()) +} diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index c6866c9..5cc94b2 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -73,6 +73,7 @@ pub fn parse(args: &[String]) -> Cli { intrusion: false, inputmodules: false, input_deck_mode: None, + charge_limit: None, kblight: None, console: None, // This is the only driver that works on UEFI @@ -151,6 +152,21 @@ pub fn parse(args: &[String]) -> Cli { None }; 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::() { + Some(Some(percent)) + } else { + println!( + "Invalid value for --charge_limit: '{}'. Must be integer < 100.", + args[i + 1] + ); + None + } + } else { + Some(None) + }; + found_an_option = true; } else if arg == "--kblight" { cli.kblight = if args.len() > i + 1 { if let Ok(percent) = args[i + 1].parse::() { From c30fc0b47beb3c74c30d9404efb10d23325872b8 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 23 Oct 2023 14:23:58 +0800 Subject: [PATCH 2/3] Add command to get and set fingerprint brightness Tested on platforms: - Intel 12th Gen with BIOS 3.07 - AMD 13th inch with BIOS 3.04 Tested with drivers; - portio on UEFI Shell - portio on Linux - cros_ec on Linux Examples: ``` > framework_tool --driver portio --fp-brightness Fingerprint LED Brightness: 55% > framework_tool --fp-brightness low Fingerprint LED Brightness: 15% ``` Signed-off-by: Daniel Schaefer --- framework_lib/src/chromium_ec/command.rs | 2 ++ framework_lib/src/chromium_ec/commands.rs | 31 ++++++++++++++++++++++ framework_lib/src/chromium_ec/mod.rs | 23 ++++++++++++++++ framework_lib/src/commandline/clap_std.rs | 7 ++++- framework_lib/src/commandline/mod.rs | 32 +++++++++++++++++++++++ framework_lib/src/commandline/uefi.rs | 20 +++++++++++++- 6 files changed, 113 insertions(+), 2 deletions(-) diff --git a/framework_lib/src/chromium_ec/command.rs b/framework_lib/src/chromium_ec/command.rs index 7448699..3f2230e 100644 --- a/framework_lib/src/chromium_ec/command.rs +++ b/framework_lib/src/chromium_ec/command.rs @@ -32,6 +32,8 @@ pub enum EcCommands { FlashNotified = 0x3E01, /// Change charge limit ChargeLimitControl = 0x3E03, + /// Get/Set Fingerprint LED brightness + FpLedLevelControl = 0x3E0E, /// Get information about the current chassis open/close status ChassisOpenCheck = 0x3E0F, /// Get information about historical chassis open/close (intrusion) information diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 8cb5a80..2a5ff4e 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -1,3 +1,5 @@ +use num_derive::FromPrimitive; + use super::{command::*, input_deck::INPUT_DECK_SLOTS}; #[repr(C, packed)] @@ -395,5 +397,34 @@ impl EcRequest for EcRequestChargeLimitControl { /* * Configure the behavior of the charge limit control. + * TODO: Use this */ pub const EC_CHARGE_LIMIT_RESTORE: u8 = 0x7F; + +#[repr(u8)] +#[derive(Debug, FromPrimitive)] +pub enum FpLedBrightnessLevel { + High = 0, + Medium = 1, + Low = 2, +} + +#[repr(C, packed)] +pub struct EcRequestFpLedLevelControl { + /// See enum FpLedBrightnessLevel + pub set_level: u8, + /// Boolean. >1 to get the level + pub get_level: u8, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct EcResponseFpLedLevelControl { + pub level: u8, +} + +impl EcRequest for EcRequestFpLedLevelControl { + fn command_id() -> EcCommands { + EcCommands::FpLedLevelControl + } +} diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index c10f44c..e1f306c 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -256,6 +256,29 @@ impl CrosEc { Ok((limits.min_percentage, limits.max_percentage)) } + pub fn set_fp_led_level(&self, level: FpLedBrightnessLevel) -> EcResult<()> { + // Sending bytes manually because the Set command, as opposed to the Get command, + // does not return any data + let limits = &[level as u8, 0x00]; + let data = self.send_command(EcCommands::FpLedLevelControl as u16, 0, limits)?; + assert_eq!(data.len(), 0); + + Ok(()) + } + + /// Get fingerprint led brightness level + pub fn get_fp_led_level(&self) -> EcResult { + let res = EcRequestFpLedLevelControl { + set_level: 0xFF, + get_level: 0xFF, + } + .send_command(self)?; + + debug!("Level Raw: {}", res.level); + + Ok(res.level) + } + /// Get the intrusion switch status (whether the chassis is open or not) pub fn get_intrusion_status(&self) -> EcResult { let status = EcRequestChassisOpenCheck {}.send_command(self)?; diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 0832810..085d7c5 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -4,7 +4,7 @@ use clap::Parser; use crate::chromium_ec::CrosEcDriverType; -use crate::commandline::{Cli, ConsoleArg, InputDeckModeArg}; +use crate::commandline::{Cli, ConsoleArg, FpBrightnessArg, InputDeckModeArg}; /// Swiss army knife for Framework laptops #[derive(Parser)] @@ -93,6 +93,10 @@ struct ClapCli { #[arg(long)] charge_limit: Option>, + /// Get or set fingerprint LED brightness + #[arg(long)] + fp_brightness: Option>, + /// Set keyboard backlight percentage or get, if no value provided #[arg(long)] kblight: Option>, @@ -147,6 +151,7 @@ pub fn parse(args: &[String]) -> Cli { inputmodules: args.inputmodules, input_deck_mode: args.input_deck_mode, charge_limit: args.charge_limit, + fp_brightness: args.fp_brightness, kblight: args.kblight, console: args.console, driver: args.driver, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 89d0b05..9855256 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -29,6 +29,7 @@ use crate::ccgx::hid::{check_ccg_fw_version, find_devices, DP_CARD_PID, HDMI_CAR use crate::ccgx::{self, SiliconId::*}; use crate::chromium_ec; use crate::chromium_ec::commands::DeckStateMode; +use crate::chromium_ec::commands::FpLedBrightnessLevel; use crate::chromium_ec::print_err; use crate::chromium_ec::EcError; use crate::chromium_ec::EcResult; @@ -58,6 +59,23 @@ pub enum ConsoleArg { Follow, } +#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum FpBrightnessArg { + High, + Medium, + Low, +} +impl From for FpLedBrightnessLevel { + fn from(w: FpBrightnessArg) -> FpLedBrightnessLevel { + match w { + FpBrightnessArg::High => FpLedBrightnessLevel::High, + FpBrightnessArg::Medium => FpLedBrightnessLevel::Medium, + FpBrightnessArg::Low => FpLedBrightnessLevel::Low, + } + } +} + #[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))] #[derive(Clone, Copy, Debug, PartialEq)] pub enum InputDeckModeArg { @@ -103,6 +121,7 @@ pub struct Cli { pub inputmodules: bool, pub input_deck_mode: Option, pub charge_limit: Option>, + pub fp_brightness: Option>, pub kblight: Option>, pub console: Option, pub help: bool, @@ -440,6 +459,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { ec.set_input_deck_mode((*mode).into()).unwrap(); } else if let Some(maybe_limit) = args.charge_limit { print_err(handle_charge_limit(&ec, maybe_limit)); + } else if let Some(maybe_brightness) = &args.fp_brightness { + print_err(handle_fp_brightness(&ec, *maybe_brightness)); } else if let Some(Some(kblight)) = args.kblight { assert!(kblight <= 100); ec.set_keyboard_backlight(kblight); @@ -864,3 +885,14 @@ fn handle_charge_limit(ec: &CrosEc, maybe_limit: Option) -> EcResult<()> { Ok(()) } + +fn handle_fp_brightness(ec: &CrosEc, maybe_brightness: Option) -> EcResult<()> { + if let Some(brightness) = maybe_brightness { + ec.set_fp_led_level(brightness.into())?; + } + + let level = ec.get_fp_led_level()?; + println!("Fingerprint LED Brightness: {:?}%", level); + + Ok(()) +} diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 5cc94b2..36a57c3 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -12,7 +12,7 @@ use uefi::Identify; use crate::chromium_ec::CrosEcDriverType; use crate::commandline::Cli; -use super::{ConsoleArg, InputDeckModeArg}; +use super::{ConsoleArg, FpBrightnessArg, InputDeckModeArg}; /// Get commandline arguments from UEFI environment pub fn get_args(boot_services: &BootServices) -> Vec { @@ -74,6 +74,7 @@ pub fn parse(args: &[String]) -> Cli { inputmodules: false, input_deck_mode: None, charge_limit: None, + fp_brightness: None, kblight: None, console: None, // This is the only driver that works on UEFI @@ -182,6 +183,23 @@ pub fn parse(args: &[String]) -> Cli { Some(None) }; found_an_option = true; + } else if arg == "--fp-brightness" { + cli.fp_brightness = if args.len() > i + 1 { + let fp_brightness_arg = &args[i + 1]; + if fp_brightness_arg == "high" { + Some(Some(FpBrightnessArg::High)) + } else if fp_brightness_arg == "medium" { + Some(Some(FpBrightnessArg::Medium)) + } else if fp_brightness_arg == "low" { + Some(Some(FpBrightnessArg::Low)) + } else { + println!("Invalid value for --fp-brightness: {}", fp_brightness_arg); + None + } + } else { + Some(None) + }; + found_an_option = true; } else if arg == "--console" { cli.console = if args.len() > i + 1 { let console_arg = &args[i + 1]; From d7c3004a54754520510e90e83db737e353101ca5 Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Mon, 23 Oct 2023 14:26:12 +0800 Subject: [PATCH 3/3] Update README and UEFI shell help Signed-off-by: Daniel Schaefer --- README.md | 7 +++++++ framework_lib/src/commandline/mod.rs | 2 ++ 2 files changed, 9 insertions(+) diff --git a/README.md b/README.md index bb0b350..fc30882 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,13 @@ All of these need EC communication support in order to work. - [x] Get information about CCGX PD Controllers (`--pd-info`) - [x] Show status of intrusion switches (`--intrusion`) - [x] Show status of privacy switches (`--privacy`) +- [x] Check recent EC console output (`--console recent`) + +###### Changing settings + +- [x] Get and set keyboard brightness (`--kblight`) +- [x] Get and set battery charge limit (`--charge-limit`) +- [x] Get and set fingerprint LED brightness (`--fp-brightness`) ###### Communication with Embedded Controller diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 9855256..73fcf81 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -648,6 +648,8 @@ Options: --capsule Parse UEFI Capsule information from binary file --intrusion Show status of intrusion switch --inputmodules Show status of the input modules (Framework 16 only) + --charge-limit [] Get or set battery charge limit (Percentage number as arg, e.g. '100') + --fp-brightness []Get or set fingerprint LED brightness level [possible values: high, medium, low] --kblight [] Set keyboard backlight percentage or get, if no value provided --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] -t, --test Run self-test to check if interaction with EC is possible