use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use guid_create::{CGuid, GUID};
use log::Level;
use num_traits::FromPrimitive;
#[cfg(not(feature = "uefi"))]
pub mod clap_std;
#[cfg(feature = "uefi")]
pub mod uefi;
#[cfg(not(feature = "uefi"))]
use std::fs;
#[cfg(not(feature = "uefi"))]
use std::io::prelude::*;
#[cfg(feature = "rusb")]
use crate::audio_card::check_synaptics_fw_version;
use crate::built_info;
#[cfg(feature = "rusb")]
use crate::camera::check_camera_version;
use crate::capsule;
use crate::capsule_content::{
find_bios_version, find_ec_in_bios_cap, find_pd_in_bios_cap, find_retimer_version,
};
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, PdVersions, SiliconId::*};
use crate::chromium_ec;
use crate::chromium_ec::commands::BoardIdType;
use crate::chromium_ec::commands::DeckStateMode;
use crate::chromium_ec::commands::FpLedBrightnessLevel;
use crate::chromium_ec::commands::RebootEcCmd;
use crate::chromium_ec::commands::RgbS;
use crate::chromium_ec::commands::TabletModeOverride;
use crate::chromium_ec::EcResponseStatus;
use crate::chromium_ec::{print_err, EcFlashType};
use crate::chromium_ec::{CrosEcDriver, EcError, EcResult};
use crate::csme;
use crate::ec_binary;
use crate::esrt::{self, ResourceType};
#[cfg(feature = "uefi")]
use crate::fw_uefi::enable_page_break;
#[cfg(feature = "rusb")]
use crate::inputmodule::check_inputmodule_version;
#[cfg(target_os = "linux")]
use crate::nvme;
use crate::os_specific;
use crate::parade_retimer;
use crate::power;
use crate::smbios;
use crate::smbios::ConfigDigit0;
use crate::smbios::{get_smbios, is_framework};
#[cfg(feature = "hidapi")]
use crate::touchpad::print_touchpad_fw_ver;
#[cfg(feature = "hidapi")]
use crate::touchscreen;
#[cfg(feature = "rusb")]
use crate::usbhub::check_usbhub_version;
use crate::util::{self, Config, Platform, PlatformFamily};
use dmidecode::Structure;
#[cfg(feature = "hidapi")]
use hidapi::HidApi;
use sha2::{Digest, Sha256, Sha384, Sha512};
#[cfg(feature = "nvidia")]
use nvml_wrapper::{enum_wrappers::device::TemperatureSensor, Nvml};
use crate::chromium_ec::{CrosEc, CrosEcDriverType, HardwareDeviceType};
#[cfg(feature = "uefi")]
use core::prelude::rust_2021::derive;
#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))]
#[derive(Clone, Debug, PartialEq)]
pub enum TabletModeArg {
Auto,
Tablet,
Laptop,
}
#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))]
#[derive(Clone, Debug, PartialEq)]
pub enum ConsoleArg {
Recent,
Follow,
}
#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))]
#[derive(Clone, Debug, PartialEq)]
pub enum RebootEcArg {
Reboot,
JumpRo,
JumpRw,
CancelJump,
DisableJump,
}
#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum FpBrightnessArg {
High,
Medium,
Low,
UltraLow,
Auto,
}
impl From<FpBrightnessArg> for FpLedBrightnessLevel {
fn from(w: FpBrightnessArg) -> FpLedBrightnessLevel {
match w {
FpBrightnessArg::High => FpLedBrightnessLevel::High,
FpBrightnessArg::Medium => FpLedBrightnessLevel::Medium,
FpBrightnessArg::Low => FpLedBrightnessLevel::Low,
FpBrightnessArg::UltraLow => FpLedBrightnessLevel::UltraLow,
FpBrightnessArg::Auto => FpLedBrightnessLevel::Auto,
}
}
}
#[cfg_attr(not(feature = "uefi"), derive(clap::ValueEnum))]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum InputDeckModeArg {
Auto,
Off,
On,
}
impl From<InputDeckModeArg> for DeckStateMode {
fn from(w: InputDeckModeArg) -> DeckStateMode {
match w {
InputDeckModeArg::Auto => DeckStateMode::Required,
InputDeckModeArg::Off => DeckStateMode::ForceOff,
InputDeckModeArg::On => DeckStateMode::ForceOn,
}
}
}
#[derive(Debug)]
pub struct LogLevel(log::LevelFilter);
impl Default for LogLevel {
fn default() -> Self {
LogLevel(log::LevelFilter::Error)
}
}
#[derive(Debug, Default)]
pub struct Cli {
pub verbosity: LogLevel,
pub versions: bool,
pub version: bool,
pub features: bool,
pub esrt: bool,
pub device: Option<HardwareDeviceType>,
pub compare_version: Option<String>,
pub power: bool,
pub thermal: bool,
pub sensors: bool,
pub fansetduty: Option<(Option<u32>, u32)>,
pub fansetrpm: Option<(Option<u32>, u32)>,
pub autofanctrl: Option<Option<u8>>,
pub pdports: bool,
pub pdports_chromebook: bool,
pub privacy: bool,
pub pd_info: bool,
pub pd_reset: Option<u8>,
pub pd_disable: Option<u8>,
pub pd_enable: Option<u8>,
pub dp_hdmi_info: bool,
pub dp_hdmi_update: Option<String>,
pub audio_card_info: bool,
pub pd_bin: Option<String>,
pub ec_bin: Option<String>,
pub capsule: Option<String>,
pub dump: Option<String>,
pub h2o_capsule: Option<String>,
pub dump_ec_flash: Option<String>,
pub flash_full_ec: Option<String>,
pub flash_ec: Option<String>,
pub flash_ro_ec: Option<String>,
pub flash_rw_ec: Option<String>,
pub driver: Option<CrosEcDriverType>,
pub test: bool,
pub test_retimer: bool,
pub boardid: bool,
pub dry_run: bool,
pub force: bool,
pub intrusion: bool,
pub inputdeck: bool,
pub inputdeck_mode: Option<InputDeckModeArg>,
pub expansion_bay: bool,
pub charge_limit: Option<Option<u8>>,
pub charge_current_limit: Option<(u32, Option<u32>)>,
pub charge_rate_limit: Option<(f32, Option<f32>)>,
pub get_gpio: Option<Option<String>>,
pub fp_led_level: Option<Option<FpBrightnessArg>>,
pub fp_brightness: Option<Option<u8>>,
pub kblight: Option<Option<u8>>,
pub remap_key: Option<(u8, u8, u16)>,
pub rgbkbd: Vec<u64>,
pub ps2_enable: Option<bool>,
pub tablet_mode: Option<TabletModeArg>,
pub touchscreen_enable: Option<bool>,
pub stylus_battery: bool,
pub console: Option<ConsoleArg>,
pub reboot_ec: Option<RebootEcArg>,
pub ec_hib_delay: Option<Option<u32>>,
pub uptimeinfo: bool,
pub s0ix_counter: bool,
pub hash: Option<String>,
pub pd_addrs: Option<(u16, u16, u16)>,
pub pd_ports: Option<(u8, u8, u8)>,
pub help: bool,
pub info: bool,
pub meinfo: Option<Option<String>>,
pub flash_gpu_descriptor: Option<(u8, String)>,
pub flash_gpu_descriptor_file: Option<String>,
pub dump_gpu_descriptor_file: Option<String>,
pub nvidia: bool,
pub allupdate: bool,
pub paginate: bool,
pub host_command: Option<(u16, u8, Vec<u8>)>,
}
pub fn parse(args: &[String]) -> Cli {
#[cfg(feature = "uefi")]
let cli = uefi::parse(args);
#[cfg(not(feature = "uefi"))]
let cli = clap_std::parse(args);
if cfg!(feature = "readonly") {
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,
pdports: cli.pdports,
pdports_chromebook: cli.pdports_chromebook,
privacy: cli.privacy,
pd_info: cli.version,
dp_hdmi_info: cli.dp_hdmi_info,
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,
driver: cli.driver,
test: cli.test,
test_retimer: cli.test_retimer,
boardid: cli.boardid,
dry_run: cli.dry_run,
intrusion: cli.intrusion,
inputdeck: cli.inputdeck,
inputdeck_mode: cli.inputdeck_mode,
expansion_bay: cli.expansion_bay,
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,
stylus_battery: cli.stylus_battery,
console: cli.console,
reboot_ec: cli.reboot_ec,
uptimeinfo: cli.uptimeinfo,
s0ix_counter: cli.s0ix_counter,
hash: cli.hash,
pd_addrs: cli.pd_addrs,
pd_ports: cli.pd_ports,
help: cli.help,
info: cli.info,
nvidia: cli.nvidia,
paginate: cli.paginate,
..Default::default()
}
} else {
cli
}
}
fn print_single_pd_details(pd: &PdController) {
if let Ok(si) = pd.get_silicon_id() {
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);
println!(" Flash Row Size: {} B", frs);
} 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();
}
fn print_pd_details(ec: &CrosEc) {
if !is_framework() {
println!("Only supported on Framework systems");
return;
}
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!("Right / Ports 01");
print_single_pd_details(&pd_01);
println!("Left / Ports 23");
print_single_pd_details(&pd_23);
println!("Back");
print_single_pd_details(&pd_back);
}
#[cfg(feature = "hidapi")]
const NOT_SET: &str = "NOT SET";
#[cfg(feature = "rusb")]
fn print_audio_card_details() {
check_synaptics_fw_version();
}
#[cfg(feature = "hidapi")]
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) {
let vid = dev_info.vendor_id();
let pid = dev_info.product_id();
let device = dev_info.open_device(&api).unwrap();
if let Some(name) = ccgx::hid::device_name(vid, pid) {
println!("{}", name);
}
debug!(
" Product String: {}",
dev_info.product_string().unwrap_or(NOT_SET)
);
debug!(
" Serial Number: {}",
dev_info.serial_number().unwrap_or(NOT_SET)
);
check_ccg_fw_version(&device, verbose);
}
}
Err(e) => {
eprintln!("Error: {e}");
}
};
}
fn print_tool_version() {
let q = "?".to_string();
println!("Tool Version Information");
println!(" Version: {}", built_info::PKG_VERSION);
println!(" Built At: {}", built_info::BUILT_TIME_UTC);
println!(
" Git Commit: {}",
built_info::GIT_COMMIT_HASH.unwrap_or(&q)
);
println!(
" Git Dirty: {}",
built_info::GIT_DIRTY
.map(|x| x.to_string())
.unwrap_or(q.clone())
);
if log_enabled!(Level::Info) {
println!(
" Built on CI: {:?}",
built_info::CI_PLATFORM.unwrap_or("None")
);
println!(
" Git ref: {:?}",
built_info::GIT_HEAD_REF.unwrap_or(&q)
);
println!(" rustc Ver: {}", built_info::RUSTC_VERSION);
println!(" Features {:?}", built_info::FEATURES);
println!(" DEBUG: {}", built_info::DEBUG);
println!(" Target OS: {}", built_info::CFG_OS);
}
}
#[cfg(feature = "hidapi")]
fn flash_dp_hdmi_card(pd_bin_path: &str) {
let data = match fs::read(pd_bin_path) {
Ok(data) => Some(data),
Err(e) => {
println!("Error {:?}", e);
None
}
};
if let Some(data) = data {
ccgx::hid::flash_firmware(&data);
} else {
error!("Failed to open firmware file");
}
}
fn active_mode(mode: &FwMode, reference: FwMode) -> &'static str {
if mode == &reference {
" (Active)"
} else {
""
}
}
#[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!("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);
} 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() {
if let Some(bios) = smbios.structures().find_map(|r| match r {
Ok(Structure::Bios(b)) => Some(b),
_ => None,
}) {
println!(" Version: {}", bios.bios_version);
println!(" Release Date: {}", bios.bios_release_date);
} else {
println!(" Version: Unknown");
}
} else {
println!(" Version: Unknown");
}
println!("EC Firmware");
let ver = print_err(ec.version_info()).unwrap_or_else(|| "UNKNOWN".to_string());
println!(" Build version: {}", ver);
if let Some((ro, rw, curr)) = ec.flash_version() {
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");
} else if curr == chromium_ec::EcCurrentImage::RW {
println!("RW");
} else {
println!("Unknown");
}
} else {
println!(" RO Version: Unknown");
println!(" RW Version: Unknown");
println!(" Current image: Unknown");
}
println!("PD Controllers");
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)");
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.app,
active_mode(&right.active_fw, FwMode::MainFw)
);
println!(
" Backup: {}{}",
right.backup_fw.app,
active_mode(&right.active_fw, FwMode::BackupFw)
);
} else {
println!(
" Right (01): {} ({:?})",
right.main_fw.app, right.active_fw
);
}
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.app,
active_mode(&left.active_fw, FwMode::MainFw)
);
println!(
" Backup: {}{}",
left.backup_fw.app,
active_mode(&left.active_fw, FwMode::BackupFw)
);
} else {
println!(
" Left (23): {} ({:?})",
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!(
" 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) {
debug!(" Fallback to PD Host command");
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")
}
let has_retimer = matches!(
smbios::get_platform(),
Some(Platform::IntelGen11)
| Some(Platform::IntelGen12)
| Some(Platform::IntelGen13)
| Some(Platform::IntelCoreUltra1)
);
let mut left_retimer: Option<u32> = None;
let mut right_retimer: Option<u32> = None;
if let Some(esrt) = esrt::get_esrt() {
for entry in &esrt.entries {
match GUID::from(entry.fw_class) {
esrt::TGL_RETIMER01_GUID
| esrt::ADL_RETIMER01_GUID
| esrt::RPL_RETIMER01_GUID
| esrt::MTL_RETIMER01_GUID => {
right_retimer = Some(entry.fw_version);
}
esrt::TGL_RETIMER23_GUID
| esrt::ADL_RETIMER23_GUID
| esrt::RPL_RETIMER23_GUID
| esrt::MTL_RETIMER23_GUID => {
left_retimer = Some(entry.fw_version);
}
_ => {}
}
}
}
if has_retimer {
println!("Intel 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() {
println!(" Unknown");
}
} else {
println!("Intel Retimers (Potential)");
if let Some(esrt) = esrt::get_esrt() {
for entry in esrt.entries.iter() {
if ResourceType::from_int(entry.fw_type) != ResourceType::DeviceFirmware {
continue;
}
println!(" GUID: {}", entry.fw_class);
println!(
" Version: 0x{:X} ({})",
entry.fw_version, entry.fw_version
);
}
} else {
println!("Could not find and parse ESRT table.");
}
}
match parade_retimer::get_version(ec) {
Ok(None) => {}
Ok(Some(ver)) => {
println!("Parade Retimers");
if let [a, b, c, d, ..] = ver.as_slice() {
println!(" dGPU: {:X}.{:X}.{:X}.{:X}", a, b, c, d);
} else {
println!(" dGPU: Unknown");
}
}
_err => {
if smbios::get_platform().and_then(Platform::which_family)
== Some(PlatformFamily::Framework16)
{
println!("Parade Retimers");
println!(" Unknown");
}
}
}
#[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);
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");
}
}
#[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();
#[cfg(feature = "hidapi")]
let _ignore_err = print_touchpad_fw_ver();
#[cfg(feature = "hidapi")]
if let Some(Platform::Framework12IntelGen13) = smbios::get_platform() {
let _ignore_err = touchscreen::print_fw_ver();
}
#[cfg(feature = "hidapi")]
print_dp_hdmi_details(false);
#[cfg(target_os = "linux")]
for i in 0..4 {
let device = format!("/dev/nvme{i}");
match nvme::get_nvme_firmware_version(&device) {
Ok(dev) => {
println!("NVMe Device: {}", device);
println!(" Model Number: {}", dev.model_number);
println!(" Firmware Version: {}", dev.firmware_version);
}
Err(_e) => {
}
}
}
#[cfg(feature = "nvidia")]
print_nvidia_details();
}
#[cfg(feature = "nvidia")]
fn probably_has_nvidia() -> bool {
match smbios::get_platform().and_then(Platform::which_family) {
Some(PlatformFamily::Framework12) => return false,
Some(PlatformFamily::Framework13) => return false,
Some(PlatformFamily::FrameworkDesktop) => return false,
_ => {}
}
if let Some(smbios) = get_smbios() {
if csme::me_version_from_smbios(&smbios).is_some() {
return false;
}
}
true
}
#[cfg(feature = "nvidia")]
fn print_nvidia_details() {
let probably_has_nvidia = probably_has_nvidia();
let nvml = match Nvml::init() {
Ok(nvml) => nvml,
Err(err) => {
if probably_has_nvidia {
error!("Nvidia, library init fail: {:?}", err);
}
return;
}
};
let device = match nvml.device_by_index(0) {
Ok(device) => device,
Err(err) => {
if probably_has_nvidia {
error!("Nvidia, device not found: {:?}", err);
}
return;
}
};
println!("NVIDIA GPU");
println!(
" Name: {}",
device.name().unwrap_or("Unknown".to_string())
);
println!(
" VBIOS Version: {}",
device.vbios_version().unwrap_or("Unknown".to_string())
);
}
#[cfg(feature = "nvidia")]
fn print_nvidia_info() {
let probably_has_nvidia = probably_has_nvidia();
let nvml = match Nvml::init() {
Ok(nvml) => nvml,
Err(err) => {
if probably_has_nvidia {
error!("Nvidia, library init fail: {:?}", err);
}
return;
}
};
let device = match nvml.device_by_index(0) {
Ok(device) => device,
Err(err) => {
if probably_has_nvidia {
error!("Nvidia, device not found: {:?}", err);
}
return;
}
};
println!("NVIDIA GPU");
println!("Identification");
println!(
" Name: {}",
device.name().unwrap_or("Unknown".to_string())
);
if let Ok(arch) = device.architecture() {
println!(" Architecture: {:?}", arch);
}
if let Ok(serial) = device.serial() {
println!(" Serial Number: {}", serial);
}
if let Ok(part_number) = device.board_part_number() {
println!(" Part Number: {}", part_number);
}
if let Ok(board_id) = device.board_id() {
println!(" Board ID: {}", board_id);
}
println!("Firmware");
println!(
" VBIOS Version: {}",
device.vbios_version().unwrap_or("Unknown".to_string())
);
println!(
" InfoROM Version: {}",
device
.info_rom_image_version()
.unwrap_or("Unknown".to_string())
);
println!("PCI");
if let Ok(pci) = device.pci_info() {
println!(" Bus: {:02X}", pci.bus);
println!(" Device: {:02X}", pci.device);
println!(" Domain: {:04X}", pci.domain);
println!(" Device ID: {:04X}", pci.pci_device_id);
if let Some(sub_id) = pci.pci_sub_system_id {
println!(" Subsystem ID: {:08X}", sub_id);
}
}
println!("Power");
if let Ok(state) = device.performance_state() {
println!(" Performance State: {:?}", state);
}
if let Ok(power) = device.power_usage() {
println!(" Current Usage: {:.2} W", power as f64 / 1000.0);
}
if let Ok(limit) = device.power_management_limit() {
println!(" Power Limit: {:.2} W", limit as f64 / 1000.0);
}
if let Ok(default) = device.power_management_limit_default() {
println!(" Default Limit: {:.2} W", default as f64 / 1000.0);
}
if let Ok(constraints) = device.power_management_limit_constraints() {
println!(
" Min Limit: {:.2} W",
constraints.min_limit as f64 / 1000.0
);
println!(
" Max Limit: {:.2} W",
constraints.max_limit as f64 / 1000.0
);
}
if let Ok(energy) = device.total_energy_consumption() {
println!(" Total Energy: {:.2} J", energy as f64 / 1000.0);
}
println!("Thermal");
if let Ok(temp) = device.temperature(TemperatureSensor::Gpu) {
println!(" GPU Temperature: {}C", temp);
}
if let Ok(num_fans) = device.num_fans() {
println!(" Number of Fans: {}", num_fans);
}
if let Ok(throttle) = device.current_throttle_reasons() {
println!("Throttle Reasons");
if throttle.is_empty() {
println!(" None");
} else {
if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::GPU_IDLE) {
println!(" GPU Idle");
}
if throttle.contains(
nvml_wrapper::bitmasks::device::ThrottleReasons::APPLICATIONS_CLOCKS_SETTING,
) {
println!(" Applications Clocks Setting");
}
if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::SW_POWER_CAP) {
println!(" Software Power Cap");
}
if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::HW_SLOWDOWN) {
println!(" Hardware Slowdown");
}
if throttle.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::SYNC_BOOST) {
println!(" Sync Boost");
}
if throttle
.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::SW_THERMAL_SLOWDOWN)
{
println!(" Software Thermal Slowdown");
}
if throttle
.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::HW_THERMAL_SLOWDOWN)
{
println!(" Hardware Thermal Slowdown");
}
if throttle
.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::HW_POWER_BRAKE_SLOWDOWN)
{
println!(" Hardware Power Brake Slowdown");
}
if throttle
.contains(nvml_wrapper::bitmasks::device::ThrottleReasons::DISPLAY_CLOCK_SETTING)
{
println!(" Display Clock Setting");
}
}
}
println!("Utilization");
if let Ok(util) = device.utilization_rates() {
println!(" GPU: {}%", util.gpu);
println!(" Memory: {}%", util.memory);
}
println!("Memory");
if let Ok(mem) = device.memory_info() {
let total_gb = mem.total as f64 / (1024.0 * 1024.0 * 1024.0);
let used_gb = mem.used as f64 / (1024.0 * 1024.0 * 1024.0);
let free_gb = mem.free as f64 / (1024.0 * 1024.0 * 1024.0);
println!(" Total: {:.2} GB", total_gb);
println!(" Used: {:.2} GB", used_gb);
println!(" Free: {:.2} GB", free_gb);
}
println!("Display");
if let Ok(active) = device.is_display_active() {
println!(" Active: {}", if active { "Yes" } else { "No" });
}
if let Ok(connected) = device.is_display_connected() {
println!(
" Connected: {}",
if connected { "Yes" } else { "No" }
);
}
}
fn print_esrt() {
if let Some(esrt) = esrt::get_esrt() {
esrt::print_esrt(&esrt);
} else {
println!("Could not find and parse ESRT table.");
}
}
fn flash_ec(ec: &CrosEc, ec_bin_path: &str, flash_type: EcFlashType, dry_run: bool) {
#[cfg(feature = "uefi")]
let data = crate::fw_uefi::fs::shell_read_file(ec_bin_path);
#[cfg(not(feature = "uefi"))]
let data: Option<Vec<u8>> = {
match fs::read(ec_bin_path) {
Ok(data) => Some(data),
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);
if let Err(err) = ec.reflash(&data, flash_type, dry_run) {
println!("Error: {:?}", err);
} else {
println!("Success!");
}
}
}
fn dump_ec_flash(ec: &CrosEc, dump_path: &str) {
let flash_bin = ec.get_entire_ec_flash().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::fw_uefi::fs::shell_write_file(dump_path, &flash_bin);
if ret.is_err() {
println!("Failed to dump EC FW image.");
}
}
}
fn dump_dgpu_eeprom(ec: &CrosEc, dump_path: &str) {
let raw_bytes = match ec.read_ec_gpu_chunk(0x00, 256) {
Ok(data) => data,
Err(err) => {
error!("Failed to read EEPROM: {:?}", err);
return;
}
};
if dump_path == "-" {
println!("{:02X?}", raw_bytes);
println!("Read {} bytes from EEPROM", raw_bytes.len());
return;
}
let expected_magic = [0x32, 0xAC, 0x00, 0x00];
let has_valid_header = raw_bytes.len() >= 4 && raw_bytes[0..4] == expected_magic;
let flash_bin = if has_valid_header {
match ec.read_gpu_descriptor() {
Ok(data) => data,
Err(err) => {
error!("GPU descriptor read failed: {:?}", err);
println!("Falling back to raw EEPROM dump (256 bytes)");
raw_bytes
}
}
} else {
error!(
"GPU descriptor invalid: magic {:02X?} != expected {:02X?}",
&raw_bytes[0..4],
expected_magic
);
println!("Dumping raw EEPROM (256 bytes)");
raw_bytes
};
#[cfg(not(feature = "uefi"))]
{
match fs::File::create(dump_path) {
Ok(mut file) => {
if let Err(err) = file.write_all(&flash_bin) {
error!("Failed to write file: {:?}", err);
return;
}
}
Err(err) => {
error!("Failed to create file: {:?}", err);
return;
}
}
}
#[cfg(feature = "uefi")]
{
if let Err(err) = crate::fw_uefi::fs::shell_write_file(dump_path, &flash_bin) {
error!("Failed to dump EC FW image: {:?}", err);
return;
}
}
println!("Wrote {} bytes to {}", flash_bin.len(), dump_path);
}
fn compare_version(device: Option<HardwareDeviceType>, version: String, ec: &CrosEc) -> i32 {
println!("Target Version {:?}", version);
if let Some(smbios) = get_smbios() {
if let Some(bios) = smbios.structures().find_map(|r| match r {
Ok(Structure::Bios(b)) => Some(b),
_ => None,
}) {
if device == Some(HardwareDeviceType::BIOS) {
println!("Comparing BIOS version {:?}", bios.bios_version);
if version.to_uppercase() == bios.bios_version.to_uppercase() {
return 0;
} else {
return 1;
}
}
}
}
match device {
Some(HardwareDeviceType::EC) => {
let ver = print_err(ec.version_info()).unwrap_or_else(|| "UNKNOWN".to_string());
println!("Comparing EC version {:?}", ver);
if ver.contains(&version) {
return 0;
} else {
return 1;
}
}
Some(HardwareDeviceType::PD0) => {
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) {
return 0;
} else {
return 1;
}
}
}
Some(HardwareDeviceType::PD1) => {
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) {
return 0;
} else {
return 1;
}
}
}
Some(HardwareDeviceType::AcLeft) => {
if let Ok((_right, left)) = power::is_charging(ec) {
let ver = format!("{}", left as i32);
println!("Comparing AcLeft {:?}", ver);
if ver == version {
return 0;
} else {
return 1;
}
} else {
error!("Failed to get charging information");
return 1;
}
}
Some(HardwareDeviceType::AcRight) => {
if let Ok((right, _left)) = power::is_charging(ec) {
let ver = format!("{}", right as i32);
println!("Comparing AcRight {:?}", ver);
if ver == version {
return 0;
} else {
return 1;
}
} else {
error!("Failed to get charging information");
return 1;
}
}
_ => {}
}
if let Some(esrt) = esrt::get_esrt() {
for entry in &esrt.entries {
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());
if entry.fw_version.to_string().contains(&version) {
return 0;
}
}
}
esrt::TGL_RETIMER23_GUID | esrt::ADL_RETIMER23_GUID | esrt::RPL_RETIMER23_GUID => {
if device == Some(HardwareDeviceType::RTM23) {
println!("Comparing RTM23 version {:?}", entry.fw_version.to_string());
if entry.fw_version.to_string().contains(&version) {
return 0;
}
}
}
_ => {}
}
}
}
1
}
pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 {
#[cfg(feature = "uefi")]
{
log::set_max_level(args.verbosity.0);
}
#[cfg(not(feature = "uefi"))]
{
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)
.init();
}
if let (Some(pd_addrs), Some(pd_ports)) = (args.pd_addrs, args.pd_ports) {
let platform = Platform::GenericFramework(pd_addrs, pd_ports);
Config::set(platform);
}
let ec = if let Some(driver) = args.driver {
if let Some(driver) = CrosEc::with(driver) {
driver
} else {
println!("Selected driver {:?} not available.", driver);
return 1;
}
} else {
CrosEc::new()
};
#[cfg(feature = "uefi")]
if args.paginate {
enable_page_break();
}
if args.help {
#[cfg(feature = "uefi")]
print_help(_allupdate);
return 2;
} else if args.versions {
print_versions(&ec);
} else if args.version {
print_tool_version();
} else if args.features {
print_err(ec.get_features());
} else if args.esrt {
print_esrt();
} else if let Some(compare_version_ver) = &args.compare_version {
let compare_ret = compare_version(args.device, compare_version_ver.to_string(), &ec);
println!("Comparison Result: {}", compare_ret);
return compare_ret;
} else if args.intrusion {
println!("Chassis status:");
if let Some(status) = print_err(ec.get_intrusion_status()) {
println!(
" Coin cell ever removed: {}",
status.coin_cell_ever_removed
);
println!(" Chassis currently open: {}", status.currently_open);
println!(" Chassis ever opened: {}", status.ever_opened);
println!(" Chassis opened: {} times", status.total_opened);
println!(
" Chassis opened while off: {} times",
status.vtr_open_count
);
} else {
println!(" Unable to tell");
}
} 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) => ec.print_fw13_inputdeck_status(),
Some(PlatformFamily::Framework16) => ec.print_fw16_inputdeck_status(),
_ => {
if ec.get_gpio("sleep_l").is_ok() {
ec.print_fw16_inputdeck_status()
} else {
if let Ok(status) = ec.get_input_deck_status() {
println!(" Deck State: {:?}", status.state);
println!(
" Touchpad present: {} ({})",
status.touchpad_present, status.touchpad_id
);
} else {
println!(" Unable to tell");
}
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();
} else if args.expansion_bay {
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 args.nvidia {
#[cfg(feature = "nvidia")]
print_nvidia_info();
#[cfg(not(feature = "nvidia"))]
error!("Not built with nvidia feature");
} 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((limit, soc)) = args.charge_rate_limit {
print_err(ec.set_charge_rate_limit(limit, soc));
} else if let Some(gpio_name) = &args.get_gpio {
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 {
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));
} 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 {
if kblight > 100 {
error!("--kblight must be percentage 0-100");
} else {
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!(
"Must provide at least 2 arguments. Provided only: {}",
args.rgbkbd.len()
);
} else {
let start_key = args.rgbkbd[0] as u8;
let colors = args.rgbkbd[1..].iter().map(|color| RgbS {
r: ((color & 0x00FF0000) >> 16) as u8,
g: ((color & 0x0000FF00) >> 8) as u8,
b: (color & 0x000000FF) as u8,
});
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(tablet_arg) = &args.tablet_mode {
let mode = match tablet_arg {
TabletModeArg::Auto => TabletModeOverride::Default,
TabletModeArg::Tablet => TabletModeOverride::ForceTablet,
TabletModeArg::Laptop => TabletModeOverride::ForceClamshell,
};
ec.set_tablet_mode(mode);
} else if let Some(_enable) = &args.touchscreen_enable {
#[cfg(feature = "hidapi")]
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 => {
let _res = ec.console_read();
}
ConsoleArg::Recent => match ec.console_read_one() {
Ok(output) => println!("{}", output),
Err(err) => println!("Failed to read console: {:?}", err),
},
}
} else if let Some(reboot_arg) = &args.reboot_ec {
match reboot_arg {
RebootEcArg::Reboot => match ec.reboot_ec(RebootEcCmd::ColdReboot) {
Ok(_) => {}
Err(err) => println!("Failed: {:?}", err),
},
RebootEcArg::JumpRo => match ec.jump_ro() {
Ok(_) => {}
Err(err) => println!("Failed: {:?}", err),
},
RebootEcArg::JumpRw => match ec.jump_rw() {
Ok(_) => {}
Err(err) => println!("Failed: {:?}", err),
},
RebootEcArg::CancelJump => match ec.cancel_jump() {
Ok(_) => {}
Err(err) => println!("Failed: {:?}", err),
},
RebootEcArg::DisableJump => match ec.disable_jump() {
Ok(_) => {}
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.uptimeinfo {
print_err(ec.get_uptime_info());
} else if args.s0ix_counter {
if let Some(counter) = print_err(ec.get_s0ix_counter()) {
println!("s0ix_counter: {}", counter);
} else {
println!("s0ix_counter: Unknown");
}
} else if args.test {
println!("Self-Test");
let result = selftest(&ec);
if result.is_none() {
println!("FAILED!!");
return 1;
}
} else if args.test_retimer {
println!("Retimer Self-Test");
if let Err(err) = selftest_retimer(&ec) {
println!(" Failed: {:?}", err);
}
} else if args.boardid {
print_board_ids(&ec);
} else if args.power {
return power::get_and_print_power_info(&ec);
} else if args.thermal {
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 let Some(Some(fan_id)) = args.autofanctrl {
print_err(ec.autofanctrl(Some(fan_id)));
} else if let Some(None) = args.autofanctrl {
print_err(ec.autofanctrl(None));
} else if args.pdports {
power::get_and_print_cypd_pd_info(&ec);
} else if args.pdports_chromebook {
power::get_and_print_pd_info(&ec);
} else if args.info {
smbios_info();
} else if let Some(dump_path) = &args.meinfo {
let verbose = args.verbosity.0 >= log::LevelFilter::Warn;
me_info(verbose, dump_path.as_deref());
} 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::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);
Ok(())
}
});
} else if let Some(pd) = args.pd_disable {
println!("Disabling PD {}...", pd);
print_err(match pd {
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);
Ok(())
}
});
} else if let Some(pd) = args.pd_enable {
println!("Enabling PD {}...", pd);
print_err(match pd {
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);
Ok(())
}
});
} else if args.dp_hdmi_info {
#[cfg(feature = "hidapi")]
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);
#[cfg(not(feature = "hidapi"))]
let _ = pd_bin_path;
} else if args.audio_card_info {
#[cfg(feature = "rusb")]
print_audio_card_details();
} else if args.privacy {
if let Some((mic, cam)) = print_err(ec.get_privacy_info()) {
println!("Privacy Slider (Black = Device Connected; Red = Device Disconnected)");
println!(
" Microphone: {}",
if mic { "Connected" } else { "Disconnected" }
);
println!(
" Camera: {}",
if cam { "Connected" } else { "Disconnected" }
);
} else {
println!("Not all EC versions support this comand.")
};
} else if let Some((command_id, command_version, ref data)) = args.host_command {
match ec.send_command(command_id, command_version, data) {
Ok(response) => {
println!("Response ({} bytes):", response.len());
if response.is_empty() {
println!(" (empty)");
} else {
util::print_multiline_buffer(&response, 0);
}
}
Err(e) => println!("EC command failed: {:?}", e),
}
} else if let Some(pd_bin_path) = &args.pd_bin {
#[cfg(feature = "uefi")]
let data: Option<Vec<u8>> = crate::fw_uefi::fs::shell_read_file(pd_bin_path);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(pd_bin_path) {
Ok(data) => Some(data),
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);
analyze_ccgx_pd_fw(&data);
}
} else if let Some(ec_bin_path) = &args.ec_bin {
#[cfg(feature = "uefi")]
let data: Option<Vec<u8>> = crate::fw_uefi::fs::shell_read_file(ec_bin_path);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(ec_bin_path) {
Ok(data) => Some(data),
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);
analyze_ec_fw(&data);
}
} else if let Some(capsule_path) = &args.capsule {
#[cfg(feature = "uefi")]
let data: Option<Vec<u8>> = crate::fw_uefi::fs::shell_read_file(capsule_path);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(capsule_path) {
Ok(data) => Some(data),
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);
if let Some(header) = analyze_capsule(&data) {
if header.capsule_guid == CGuid::from(esrt::WINUX_GUID) {
let ux_header = capsule::parse_ux_header(&data);
if let Some(dump_path) = &args.dump {
capsule::dump_winux_image(&data, &ux_header, dump_path);
}
}
} else {
println!("Capsule is invalid.");
}
}
} else if let Some(capsule_path) = &args.h2o_capsule {
#[cfg(feature = "uefi")]
let data = crate::fw_uefi::fs::shell_read_file(capsule_path);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(capsule_path) {
Ok(data) => Some(data),
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);
if let Some(cap) = find_bios_version(&data) {
println!(" BIOS Platform:{:>18}", cap.platform);
println!(" BIOS Version: {:>18}", cap.version);
}
if let Some(ec_bin) = find_ec_in_bios_cap(&data) {
debug!("Found EC binary in BIOS capsule");
analyze_ec_fw(ec_bin);
} else {
debug!("Didn't find EC binary in BIOS capsule");
}
if let Some(pd_bin) = find_pd_in_bios_cap(&data) {
debug!("Found PD binary in BIOS capsule");
analyze_ccgx_pd_fw(pd_bin);
} else {
debug!("Didn't find PD binary in BIOS capsule");
}
}
} else if let Some(dump_path) = &args.dump_ec_flash {
println!("Dumping to {}", dump_path);
dump_ec_flash(&ec, dump_path);
} else if let Some(ec_bin_path) = &args.flash_full_ec {
if args.force {
flash_ec(&ec, ec_bin_path, EcFlashType::Full, args.dry_run);
} else {
error!("Flashing full EC flash is unsafe. Use --flash-ec instead");
}
} else if let Some(ec_bin_path) = &args.flash_ec {
if args.force {
flash_ec(&ec, ec_bin_path, EcFlashType::Both, 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 {
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, args.dry_run);
} else if let Some(hash_file) = &args.hash {
println!("Hashing file: {}", hash_file);
#[cfg(feature = "uefi")]
let data = crate::fw_uefi::fs::shell_read_file(hash_file);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(hash_file) {
Ok(data) => Some(data),
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);
hash(&data);
}
} else if let Some(gpu_descriptor) = &args.flash_gpu_descriptor {
let res = ec.set_gpu_serial(gpu_descriptor.0, gpu_descriptor.1.to_ascii_uppercase());
match res {
Ok(1) => println!("GPU Descriptor successfully written"),
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 matches!(
smbios::get_family(),
Some(PlatformFamily::Framework16) | None
) {
#[cfg(feature = "uefi")]
let data: Option<Vec<u8>> = crate::fw_uefi::fs::shell_read_file(gpu_descriptor_file);
#[cfg(not(feature = "uefi"))]
let data = match fs::read(gpu_descriptor_file) {
Ok(data) => Some(data),
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, args.dry_run);
match res {
Ok(()) => println!("GPU Descriptor successfully written"),
Err(err) => println!("GPU Descriptor write failed with error: {:?}", err),
}
}
} else {
println!("Unsupported on this platform");
}
} else if let Some(dump_path) = &args.dump_gpu_descriptor_file {
if dump_path != "-" {
println!("Dumping to {}", dump_path);
}
dump_dgpu_eeprom(&ec, dump_path);
}
0
}
#[cfg(feature = "uefi")]
fn print_help(updater: bool) {
println!(
r#"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 Show tool version information (Add -vv for more detailed information)
--features Show features support by the firmware
--esrt Display the UEFI ESRT table
--device <DEVICE> Device used to compare firmware version [possible values: bios, ec, pd0, pd1, rtm01, rtm23]
--compare-version Version string used to match firmware version (use with --device)
--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 [<FANID>]Turn on automatic fan speed control (optionally provide fan index)
--pdports Show USB-C PD port state
--pdports-chromebook Show PD port info (generic Chromium EC)
--info Show info from SMBIOS (Only on UEFI)
--meinfo [<DUMPFILE>] Show Intel ME information (from SMBIOS type 0xDB)
--pd-info Show details about the PD controllers
--pd-reset <PD_NUM> Reset a specific PD controller (for debugging only)
--pd-disable <PD_NUM> Disable all ports on a specific PD controller (for debugging only)
--pd-enable <PD_NUM> Enable all ports on a specific PD controller (for debugging only)
--privacy Show privacy switch statuses (camera and microphone)
--pd-bin <PD_BIN> Parse versions from PD firmware binary file
--ec-bin <EC_BIN> Parse versions from EC firmware binary file
--capsule <CAPSULE> Parse UEFI Capsule information from binary file
--dump <DUMP> Dump extracted UX capsule bitmap image to a file
--h2o-capsule <H2O_CAPSULE> Parse UEFI Capsule information from binary file
--dump-ec-flash <DUMP_EC_FLASH> Dump EC flash contents
--flash-ec <FLASH_EC> Flash EC with new firmware from file
--flash-ro-ec <FLASH_EC> Flash EC with new firmware from file
--flash-rw-ec <FLASH_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]
--ec-hib-delay [<SECONDS>] Get or set EC hibernate delay (S5 to G3)
--uptimeinfo Show EC uptime information
--s0ix-counter Show S0ix counter
--intrusion Show status of intrusion switch
--inputdeck Show status of the input deck
--inputdeck-mode Set input deck power mode [possible values: auto, off, on] (Framework 12, 13, 16)
--expansion-bay Show status of the expansion bay (Framework 16 only)
--nvidia Show NVIDIA GPU information (Framework 16 only)
--charge-limit [<VAL>] Get or set battery charge limit (Percentage number as arg, e.g. '100')
--charge-current-limit [<VAL>] Get or set battery current charge limit (Percentage number as arg, e.g. '100')
--charge-rate-limit [<VAL>] Set max charge rate limit
--get-gpio <GET_GPIO> Get GPIO value by name or all, if no name provided
--fp-led-level [<VAL>] Get or set fingerprint LED brightness level [possible values: high, medium, low]
--fp-brightness [<VAL>]Get or set fingerprint LED brightness percentage
--kblight [<KBLIGHT>] Set keyboard backlight percentage or get, if no value provided
--remap-key <ROW> <COL> <SCANCODE>
Remap a key by changing the scancode
--rgbkbd <START> <HEXCOLOR> [<HEXCOLOR> ...]
Set the color of a key to RGB. Multiple colors for adjacent keys can be set at once
--tablet-mode <MODE> Set tablet mode override [possible values: auto, tablet, laptop]
--console <CONSOLE> Get EC console, choose whether recent or to follow the output [possible values: recent, follow]
--hash <HASH> Hash a file of arbitrary data
--flash-gpu-descriptor <MAGIC> <18 DIGIT SN> Overwrite the GPU bay descriptor SN and type.
--flash-gpu-descriptor-file <DESCRIPTOR_FILE> Write the GPU bay descriptor with a descriptor file.
--dump-gpu-descriptor-file <DESCRIPTOR_FILE> Dump the GPU bay descriptor to a 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)
--pd-addrs <PD_ADDR_LEFT> <PD_ADDR_RIGHT> <PD_ADDR_BACK>
Specify I2C addresses of the PD chips (Advanced)
--pd-ports <PD_PORT_LEFT> <PD_PORT_RIGHT> <PD_PORT_BACK>
Specify I2C ports of the PD chips (Advanced)
--has-mec <HAS_MEC>
Specify the type of EC chip (MEC/MCHP or other) [possible values: true, false]
--host-command <CMD_ID> <VERSION> [DATA...]
Send an EC host command
-t, --test Run self-test to check if interaction with EC is possible
--test-retimer Run self-test to check if interaction with retimers is possible
--boardid Print all board IDs
-h, --help Print help information
-b Print output one screen at a time
"#
);
if updater {
println!(
r#"
--allupdate - Run procedure to update everything (Involves some manual steps)
"#
);
}
}
fn hash(data: &[u8]) {
let mut sha256_hasher = Sha256::new();
let mut sha384_hasher = Sha384::new();
let mut sha512_hasher = Sha512::new();
sha256_hasher.update(data);
sha384_hasher.update(data);
sha512_hasher.update(data);
let sha256 = &sha256_hasher.finalize()[..];
let sha384 = &sha384_hasher.finalize()[..];
let sha512 = &sha512_hasher.finalize()[..];
println!("Hashes");
print!(" SHA256: ");
util::print_buffer(sha256);
print!(" SHA384: ");
util::print_buffer(sha384);
print!(" SHA512: ");
util::print_buffer(sha512);
}
fn print_board_ids(ec: &CrosEc) {
println!("Board IDs");
println!(
" Mainboard: {:?}",
ec.read_board_id_hc(BoardIdType::Mainboard)
);
println!(
" PowerButton: {:?}",
ec.read_board_id_hc(BoardIdType::PowerButtonBoard)
);
println!(
" Touchpad: {:?}",
ec.read_board_id_hc(BoardIdType::Touchpad)
);
println!(
" AudioBoard: {:?}",
ec.read_board_id_hc(BoardIdType::AudioBoard)
);
println!(
" dGPU0: {:?}",
ec.read_board_id_hc(BoardIdType::DGpu0)
);
println!(
" dGPU1: {:?}",
ec.read_board_id_hc(BoardIdType::DGpu1)
);
}
fn selftest(ec: &CrosEc) -> Option<()> {
if let Some(platform) = smbios::get_platform() {
println!(" SMBIOS Platform: {:?}", platform);
} else {
println!(" SMBIOS Platform: Unknown");
println!();
println!("Specify custom platform parameters with --pd-ports --pd-addrs");
return None;
};
println!(" Dump EC memory region");
if let Some(mem) = ec.dump_mem_region() {
util::print_multiline_buffer(&mem, 0);
} else {
println!(" Failed to read EC memory region");
return None;
}
println!(" Checking EC memory mapped magic bytes");
print_err(ec.check_mem_magic())?;
println!(" Verified that Framework EC is present!");
println!(" Reading EC Build Version");
print_err(ec.version_info())?;
print!(" Reading EC Flash by EC");
ec.flash_version()?;
println!(" - OK");
println!(" Reading EC Flash directly - See below");
ec.test_ec_flash_read().ok()?;
print!(" Getting power info from EC");
power::power_info(ec)?;
println!(" - OK");
println!(" Getting AC info from EC");
if power::get_pd_info(ec, 4).iter().any(|x| x.is_err()) {
println!(" Failed to get PD Info from EC");
return None;
}
print!("Reading PD Version from EC");
if let Err(err) = power::read_pd_version(ec) {
if err != EcError::Response(EcResponseStatus::InvalidCommand) {
println!();
println!("Err: {:?}", err);
} else {
println!(" - Skipped");
}
} else {
println!(" - OK");
}
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())?;
print_err(pd_01.get_fw_versions())?;
println!(" - OK");
print!(" Getting PD23 info through I2C tunnel");
print_err(pd_23.get_silicon_id())?;
print_err(pd_23.get_device_info())?;
print_err(pd_23.get_fw_versions())?;
println!(" - OK");
Some(())
}
fn selftest_retimer(ec: &CrosEc) -> EcResult<()> {
for i in 0..2 {
let update_mode = ec.retimer_in_fwupd_mode(i);
if update_mode == Err(EcError::Response(EcResponseStatus::InvalidParameter)) {
println!(" Retimer status not supported on this platform. Cannot test");
return Ok(());
}
println!(" Retimers on PD Controller {}", i);
println!(
" In update mode: {:?}",
ec.retimer_in_fwupd_mode(i).unwrap()
);
if update_mode? {
println!(" Disabling it to start test.");
ec.retimer_enable_fwupd(i, false)?;
}
println!(" Enabling update mode");
let success = ec.retimer_enable_fwupd(i, true);
println!(
" In update mode: {:?}",
ec.retimer_in_fwupd_mode(i).unwrap()
);
println!(" Disabling update mode");
ec.retimer_enable_fwupd(i, false)?;
os_specific::sleep(100);
println!(
" In update mode: {:?}",
ec.retimer_in_fwupd_mode(i).unwrap()
);
success?;
}
Ok(())
}
fn smbios_info() {
println!("Summary");
println!(" Is Framework: {}", is_framework());
if let Some(platform) = smbios::get_platform() {
println!(" Platform: {:?}", platform);
} else {
println!(" Platform: Unknown",);
}
let smbios = get_smbios();
if smbios.is_none() {
error!("Failed to find SMBIOS");
return;
}
for result in smbios.unwrap().structures() {
match result {
Ok(Structure::Bios(data)) => {
println!("BIOS Information");
if !data.vendor.is_empty() {
println!(" Vendor: {}", data.vendor);
}
if !data.bios_version.is_empty() {
println!(" Version: {}", data.bios_version);
}
if !data.bios_release_date.is_empty() {
println!(" Release Date: {}", data.bios_release_date);
}
}
Ok(Structure::System(data)) => {
println!("System Information");
if !data.version.is_empty() {
let version = data.version;
let config_digit0 = &version[0..1];
let config_digit0 = u8::from_str_radix(config_digit0, 16);
if let Ok(version_config) =
config_digit0.map(<ConfigDigit0 as FromPrimitive>::from_u8)
{
println!(" Version: {:?} ({})", version_config, version);
} else {
println!(" Version: '{}'", version);
}
}
if !data.manufacturer.is_empty() {
println!(" Manufacturer: {}", data.manufacturer);
}
if !data.product.is_empty() {
println!(" Product Name: {}", data.product);
}
if let Some(wakeup) = data.wakeup {
println!(" Wake-Up-Type: {:?}", wakeup);
}
if let Some(sku) = data.sku {
if !sku.is_empty() {
println!(" SKU Number: {}", sku);
}
}
if !data.serial.is_empty() {
println!(" Serial Number:{}", data.serial);
}
if let Some(family) = data.family {
if !family.is_empty() {
println!(" Family: {}", family);
}
}
}
Ok(Structure::Enclosure(data)) => {
println!("System Chassis Information");
println!(" Type: {}", data.enclosure_type);
}
Ok(Structure::BaseBoard(data)) => {
println!("BaseBoard Information");
if !data.version.is_empty() {
let version = data.version;
let config_digit0 = &version[0..1];
let config_digit0 = u8::from_str_radix(config_digit0, 16);
if let Ok(version_config) =
config_digit0.map(<ConfigDigit0 as FromPrimitive>::from_u8)
{
println!(" Version: {:?} ({})", version_config, version);
} else {
println!(" Version: '{}'", version);
}
}
if !data.manufacturer.is_empty() {
println!(" Manufacturer: {}", data.manufacturer);
}
if !data.product.is_empty() {
println!(" Product: {}", data.product);
}
if !data.serial.is_empty() {
println!(" Serial Number:{}", data.serial);
}
}
_ => {}
}
}
}
fn me_info(verbose: bool, dump_path: Option<&str>) {
let smbios = if let Some(path) = dump_path {
#[cfg(feature = "uefi")]
let data: Option<Vec<u8>> = crate::fw_uefi::fs::shell_read_file(path);
#[cfg(not(feature = "uefi"))]
let data = match std::fs::read(path) {
Ok(data) => Some(data),
Err(e) => {
error!("Failed to read SMBIOS dump file '{}': {}", path, e);
None
}
};
data.and_then(|d| smbios::SmbiosStore::from_table_data(d, 3, 0))
} else {
get_smbios()
};
if smbios.is_none() {
error!("Failed to get SMBIOS data");
return;
}
let smbios = smbios.unwrap();
let mut me_family: Option<csme::MeFamily> = None;
#[cfg(target_os = "linux")]
if dump_path.is_none() {
if let Ok(csme_info) = csme::csme_from_sysfs() {
println!("Intel ME Information (from sysfs)");
println!(" Enabled: {}", csme_info.enabled);
println!(" Firmware Version: {}", csme_info.main_ver);
if csme_info.main_ver != csme_info.recovery_ver
|| csme_info.main_ver != csme_info.fitc_ver
{
println!(" Recovery Version: {}", csme_info.recovery_ver);
println!(" FITC Version: {}", csme_info.fitc_ver);
}
let family = csme::MeFamily::from_version(csme_info.main_ver.major);
println!(" ME Family: {}", family);
me_family = Some(family);
}
}
let smbios_version = csme::me_version_from_smbios(&smbios);
if let Some(ref fvi_version) = smbios_version {
if me_family.is_none() {
println!("Intel ME Version (from SMBIOS Type 0xDD)");
println!(" Firmware Version: {}", fvi_version);
let family = csme::MeFamily::from_version(fvi_version.major as u32);
println!(" ME Family: {}", family);
me_family = Some(family);
} else if verbose {
println!("Intel ME Version (from SMBIOS Type 0xDD)");
println!(" Firmware Version: {}", fvi_version);
}
}
if verbose {
let me_handles = csme::find_me_handles_from_type14(&smbios);
if !me_handles.is_empty() {
println!("ME Handles (from SMBIOS Type 14):");
for handle in &me_handles {
println!(" Handle: 0x{:04X}", handle);
}
}
}
if let Some(me_fwsts) = csme::me_fwsts_from_smbios(&smbios) {
println!("Intel ME Status (SMBIOS Type 0xDB)");
if let Some(record) = me_fwsts.mei1() {
println!(" Working State: {}", record.hfsts.working_state());
println!(" Operation Mode: {}", record.hfsts.operation_mode());
if let Some(family) = me_family {
if let Some(bootguard) = me_fwsts.bootguard_status(family) {
println!(" Bootguard:");
println!(
" Enabled: {}",
if bootguard.enabled { "Yes" } else { "No" }
);
if let Some(verified) = bootguard.verified_boot {
println!(
" Verified Boot: {}",
if verified { "Yes" } else { "No" }
);
}
println!(
" ACM Active: {}",
if bootguard.acm_active { "Yes" } else { "No" }
);
if let Some(done) = bootguard.acm_done {
println!(" ACM Done: {}", if done { "Yes" } else { "No" });
}
if let Some(ref policy) = bootguard.policy {
println!(" Policy: {}", policy);
}
println!(
" FPF SOC Lock: {}",
if bootguard.fpf_soc_lock { "Yes" } else { "No" }
);
}
}
if verbose {
println!(" HFSTS Registers:");
println!(" HFSTS1: 0x{:08X}", record.hfsts.hfsts1);
println!(" HFSTS2: 0x{:08X}", record.hfsts.hfsts2);
println!(" HFSTS3: 0x{:08X}", record.hfsts.hfsts3);
println!(" HFSTS4: 0x{:08X}", record.hfsts.hfsts4);
println!(" HFSTS5: 0x{:08X}", record.hfsts.hfsts5);
println!(" HFSTS6: 0x{:08X}", record.hfsts.hfsts6);
}
}
} else {
error!("No Intel ME FWSTS table found in SMBIOS (type 0xDB)");
}
}
fn analyze_ccgx_pd_fw(data: &[u8]) {
if let Some(versions) = ccgx::binary::read_versions(data, Ccg3) {
println!("Detected CCG3 firmware");
println!("FW 1");
ccgx::binary::print_fw(&versions.backup_fw);
println!("FW 2");
ccgx::binary::print_fw(&versions.main_fw);
} else if let Some(versions) = ccgx::binary::read_versions(data, Ccg8) {
println!("Detected CCG8 firmware");
println!("FW 1");
ccgx::binary::print_fw(&versions.backup_fw);
println!("FW 2");
ccgx::binary::print_fw(&versions.main_fw);
} else if let Some(versions) = ccgx::binary::read_versions(data, Ccg5) {
println!("Detected CCG5 firmware");
println!("FW 1");
ccgx::binary::print_fw(&versions.backup_fw);
println!("FW 2");
ccgx::binary::print_fw(&versions.main_fw);
} else if let Some(versions) = ccgx::binary::read_versions(data, Ccg6) {
println!("Detected CCG6 firmware");
println!("FW 1 (Backup)");
ccgx::binary::print_fw(&versions.backup_fw);
println!("FW 2 (Main)");
ccgx::binary::print_fw(&versions.main_fw);
} else {
println!("Failed to read PD versions")
}
}
pub fn analyze_ec_fw(data: &[u8]) {
if let Some(ver) = ec_binary::read_ec_version(data, true) {
ec_binary::print_ec_version(&ver, true);
} else {
println!("Failed to read EC version")
}
if let Some(ver) = ec_binary::read_ec_version(data, false) {
ec_binary::print_ec_version(&ver, false);
} else {
println!("Failed to read EC version")
}
}
pub fn analyze_capsule(data: &[u8]) -> Option<capsule::EfiCapsuleHeader> {
let header = capsule::parse_capsule_header(data)?;
capsule::print_capsule_header(&header);
match GUID::from(header.capsule_guid) {
esrt::TGL_BIOS_GUID => {
println!(" Type: Framework TGL Insyde BIOS");
}
esrt::ADL_BIOS_GUID => {
println!(" Type: Framework ADL Insyde BIOS");
}
esrt::RPL_BIOS_GUID => {
println!(" Type: Framework RPL Insyde BIOS");
}
esrt::TGL_RETIMER01_GUID => {
println!(" Type: Framework TGL Retimer01 (Right)");
}
esrt::TGL_RETIMER23_GUID => {
println!(" Type: Framework TGL Retimer23 (Left)");
}
esrt::ADL_RETIMER01_GUID => {
println!(" Type: Framework ADL Retimer01 (Right)");
}
esrt::ADL_RETIMER23_GUID => {
println!(" Type: Framework ADL Retimer23 (Left)");
}
esrt::RPL_RETIMER01_GUID => {
println!(" Type: Framework RPL Retimer01 (Right)");
}
esrt::RPL_RETIMER23_GUID => {
println!(" Type: Framework RPL Retimer23 (Left)");
}
esrt::WINUX_GUID => {
println!(" Type: Windows UX capsule");
let ux_header = capsule::parse_ux_header(data);
capsule::print_ux_header(&ux_header);
}
_ => {
println!(" Type: Unknown");
}
}
match esrt::match_guid_kind(&header.capsule_guid) {
esrt::FrameworkGuidKind::TglRetimer01
| esrt::FrameworkGuidKind::TglRetimer23
| esrt::FrameworkGuidKind::AdlRetimer01
| esrt::FrameworkGuidKind::AdlRetimer23
| esrt::FrameworkGuidKind::RplRetimer01
| esrt::FrameworkGuidKind::RplRetimer23 => {
if let Some(ver) = find_retimer_version(data) {
println!(" Version: {:>18?}", ver);
}
}
_ => {}
}
Some(header)
}
fn handle_charge_limit(ec: &CrosEc, maybe_limit: Option<u8>) -> EcResult<()> {
let (cur_min, _cur_max) = ec.get_charge_limit()?;
if let Some(limit) = maybe_limit {
if limit < 25 {
return Err(EcError::DeviceError(
"Not recommended to set charge limit below 25%".to_string(),
));
} else if limit > 100 {
return Err(EcError::DeviceError(
"Charge limit cannot be set above 100%".to_string(),
));
}
ec.set_charge_limit(cur_min, limit)?;
}
let (min, max) = ec.get_charge_limit()?;
println!("Minimum {}%, Maximum {}%", min, max);
Ok(())
}
fn handle_fp_led_level(ec: &CrosEc, maybe_led_level: Option<FpBrightnessArg>) -> 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()?;
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<u8>) -> EcResult<()> {
if let Some(brightness) = maybe_brightness {
ec.set_fp_led_percentage(brightness)?;
}
let (brightness, level) = ec.get_fp_led_level()?;
println!("Fingerprint LED Brightness");
if let Some(level) = level {
println!(" Requested: {:?}", level);
}
println!(" Brightness: {}%", brightness);
Ok(())
}