diff --git a/framework_lib/src/chromium_ec/commands.rs b/framework_lib/src/chromium_ec/commands.rs index 1265c79..dfed664 100644 --- a/framework_lib/src/chromium_ec/commands.rs +++ b/framework_lib/src/chromium_ec/commands.rs @@ -844,10 +844,14 @@ pub enum FpLedBrightnessLevel { High = 0, Medium = 1, Low = 2, + UltraLow = 3, + /// Custom: Only get, never set + Custom = 0xFE, + Auto = 0xFF, } #[repr(C, packed)] -pub struct EcRequestFpLedLevelControl { +pub struct EcRequestFpLedLevelControlV0 { /// See enum FpLedBrightnessLevel pub set_level: u8, /// Boolean. >1 to get the level @@ -856,12 +860,42 @@ pub struct EcRequestFpLedLevelControl { #[repr(C, packed)] #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct EcResponseFpLedLevelControl { +pub struct EcResponseFpLedLevelControlV0 { + /// Current brightness, 1-100% + pub percentage: u8, +} + +impl EcRequest for EcRequestFpLedLevelControlV0 { + fn command_id() -> EcCommands { + EcCommands::FpLedLevelControl + } + fn command_version() -> u8 { + 0 + } +} + +#[repr(C, packed)] +pub struct EcRequestFpLedLevelControlV1 { + /// Percentage 1-100 + pub set_percentage: u8, + /// Boolean. >1 to get the level + pub get_level: u8, +} + +#[repr(C, packed)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub struct EcResponseFpLedLevelControlV1 { + /// Current brightness, 1-100% + pub percentage: u8, + /// Requested level. See enum FpLedBrightnessLevel pub level: u8, } -impl EcRequest for EcRequestFpLedLevelControl { +impl EcRequest for EcRequestFpLedLevelControlV1 { fn command_id() -> EcCommands { EcCommands::FpLedLevelControl } + fn command_version() -> u8 { + 1 + } } diff --git a/framework_lib/src/chromium_ec/mod.rs b/framework_lib/src/chromium_ec/mod.rs index 137a37f..2dc5320 100644 --- a/framework_lib/src/chromium_ec/mod.rs +++ b/framework_lib/src/chromium_ec/mod.rs @@ -313,6 +313,17 @@ impl CrosEc { Ok((limits.min_percentage, limits.max_percentage)) } + 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 + let limits = &[percentage, 0x00]; + let data = self.send_command(EcCommands::FpLedLevelControl as u16, 1, limits)?; + + util::assert_win_len(data.len(), 0); + + Ok(()) + } + 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 @@ -325,16 +336,33 @@ impl CrosEc { } /// Get fingerprint led brightness level - pub fn get_fp_led_level(&self) -> EcResult { - let res = EcRequestFpLedLevelControl { - set_level: 0xFF, + pub fn get_fp_led_level(&self) -> EcResult<(u8, Option)> { + let res = EcRequestFpLedLevelControlV1 { + set_percentage: 0xFF, get_level: 0xFF, } - .send_command(self)?; + .send_command(self); + + // If V1 does not exist, fall back + if let Err(EcError::Response(EcResponseStatus::InvalidVersion)) = res { + let res = EcRequestFpLedLevelControlV0 { + set_level: 0xFF, + get_level: 0xFF, + } + .send_command(self)?; + debug!("Current Brightness: {}%", res.percentage); + return Ok((res.percentage, None)); + } + + let res = res?; - debug!("Level Raw: {}", res.level); + debug!("Current Brightness: {}%", res.percentage); + debug!("Level Raw: {}", res.level); - Ok(res.level) + // TODO: can turn this into None and log + let level = FromPrimitive::from_u8(res.level) + .ok_or(EcError::DeviceError(format!("Invalid level {}", res.level)))?; + Ok((res.percentage, Some(level))) } /// Get the intrusion switch status (whether the chassis is open or not) diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index 140cb15..96b4cd6 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -137,9 +137,13 @@ struct ClapCli { #[arg(long)] get_gpio: Option, - /// Get or set fingerprint LED brightness + /// Get or set fingerprint LED brightness level #[arg(long)] - fp_brightness: Option>, + fp_led_level: Option>, + + /// Get or set fingerprint LED brightness percentage + #[arg(long)] + fp_brightness: Option>, /// Set keyboard backlight percentage or get, if no value provided #[arg(long)] @@ -267,6 +271,7 @@ pub fn parse(args: &[String]) -> Cli { input_deck_mode: args.input_deck_mode, charge_limit: args.charge_limit, get_gpio: args.get_gpio, + fp_led_level: args.fp_led_level, fp_brightness: args.fp_brightness, kblight: args.kblight, tablet_mode: args.tablet_mode, diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index e911f77..567e9e8 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -93,6 +93,8 @@ pub enum FpBrightnessArg { High, Medium, Low, + UltraLow, + Auto, } impl From for FpLedBrightnessLevel { fn from(w: FpBrightnessArg) -> FpLedBrightnessLevel { @@ -100,6 +102,8 @@ impl From for FpLedBrightnessLevel { FpBrightnessArg::High => FpLedBrightnessLevel::High, FpBrightnessArg::Medium => FpLedBrightnessLevel::Medium, FpBrightnessArg::Low => FpLedBrightnessLevel::Low, + FpBrightnessArg::UltraLow => FpLedBrightnessLevel::UltraLow, + FpBrightnessArg::Auto => FpLedBrightnessLevel::Auto, } } } @@ -159,7 +163,8 @@ pub struct Cli { pub input_deck_mode: Option, pub charge_limit: Option>, pub get_gpio: Option, - pub fp_brightness: Option>, + pub fp_led_level: Option>, + pub fp_brightness: Option>, pub kblight: Option>, pub tablet_mode: Option, pub console: Option, @@ -741,6 +746,8 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!("Not found"); } + } else if let Some(maybe_led_level) = &args.fp_led_level { + print_err(handle_fp_led_level(&ec, *maybe_led_level)); } 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 { @@ -1009,7 +1016,8 @@ Options: --input-deck-mode Set input deck power mode [possible values: auto, off, on] (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-brightness []Get or set fingerprint LED brightness level [possible values: high, medium, low] + --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 --console Get EC console, choose whether recent or to follow the output [possible values: recent, follow] --hash Hash a file of arbitrary data @@ -1355,13 +1363,34 @@ fn handle_charge_limit(ec: &CrosEc, maybe_limit: Option) -> EcResult<()> { Ok(()) } -fn handle_fp_brightness(ec: &CrosEc, maybe_brightness: Option) -> EcResult<()> { +fn handle_fp_led_level(ec: &CrosEc, maybe_led_level: Option) -> EcResult<()> { + if let Some(led_level) = maybe_led_level { + ec.set_fp_led_level(led_level.into())?; + } + + let (brightness, level) = ec.get_fp_led_level()?; + // TODO: Rename to power button + println!("Fingerprint LED Brightness"); + if let Some(level) = level { + println!(" Requested: {:?}", level); + } + println!(" Brightness: {}%", brightness); + + Ok(()) +} + +fn handle_fp_brightness(ec: &CrosEc, maybe_brightness: Option) -> EcResult<()> { if let Some(brightness) = maybe_brightness { - ec.set_fp_led_level(brightness.into())?; + ec.set_fp_led_percentage(brightness)?; } - let level = ec.get_fp_led_level()?; - println!("Fingerprint LED Brightness: {:?}%", level); + let (brightness, level) = ec.get_fp_led_level()?; + // TODO: Rename to power button + println!("Fingerprint LED Brightness"); + if let Some(level) = level { + println!(" Requested: {:?}", level); + } + println!(" Brightness: {}%", brightness); Ok(()) } diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index 69dee46..e826cb2 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -84,6 +84,7 @@ pub fn parse(args: &[String]) -> Cli { input_deck_mode: None, charge_limit: None, get_gpio: None, + fp_led_level: None, fp_brightness: None, kblight: None, tablet_mode: None, @@ -237,17 +238,41 @@ pub fn parse(args: &[String]) -> Cli { 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" { + } else if arg == "--fp-led-level" { + cli.fp_led_level = if args.len() > i + 1 { + let fp_led_level_arg = &args[i + 1]; + if fp_led_level_arg == "high" { Some(Some(FpBrightnessArg::High)) - } else if fp_brightness_arg == "medium" { + } else if fp_led_level_arg == "medium" { Some(Some(FpBrightnessArg::Medium)) - } else if fp_brightness_arg == "low" { + } else if fp_led_level_arg == "low" { Some(Some(FpBrightnessArg::Low)) + } else if fp_led_level_arg == "ultra-low" { + Some(Some(FpBrightnessArg::UltraLow)) + } else if fp_led_level_arg == "auto" { + Some(Some(FpBrightnessArg::Auto)) + } else { + println!("Invalid value for --fp-led-level: {}", fp_led_level_arg); + None + } + } else { + Some(None) + }; + found_an_option = true; + } else if arg == "--fp-brightness" { + cli.fp_brightness = if args.len() > i + 1 { + if let Ok(fp_brightness_arg) = args[i + 1].parse::() { + if fp_brightness_arg == 0 || fp_brightness_arg > 100 { + println!( + "Invalid value for --fp-brightness: {}. Must be in the range of 1-100", + fp_brightness_arg + ); + None + } else { + Some(Some(fp_brightness_arg)) + } } else { - println!("Invalid value for --fp-brightness: {}", fp_brightness_arg); + println!("Invalid value for --fp-brightness. Must be in the range of 1-100"); None } } else {