diff --git a/EXAMPLES_ADVANCED.md b/EXAMPLES_ADVANCED.md index 54d0137b..4fabe4b5 100644 --- a/EXAMPLES_ADVANCED.md +++ b/EXAMPLES_ADVANCED.md @@ -278,6 +278,38 @@ Capsule Header Type: Framework Retimer23 (Right) ``` +## Raw EC Host Commands + +Send an arbitrary EC host command by specifying a command ID, version, and +optional payload bytes. The response is displayed in xxd-style hex+ASCII format. + +``` +# Send EC_CMD_GET_VERSION (0x0002) with version 0, no payload +> sudo framework_tool --host-command 0x0002 0 +Response (120 bytes): +00000000: 7375 6e66 6c6f 7765 722d 332e 302e 332d sunflower-3.0.3- +00000010: 3838 6664 6135 3400 0000 0000 0000 0000 88fda54......... +00000020: 7375 6e66 6c6f 7765 722d 332e 302e 332d sunflower-3.0.3- +00000030: 3838 6664 6135 3400 0000 0000 0000 0000 88fda54......... +00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +00000060: 0100 0000 0000 0000 0000 0000 0000 0000 ................ +00000070: 0000 0000 0000 0000 ........ + +# Query supported versions of EC_CMD_GET_VERSION (0x0002) +# EC_CMD_GET_CMD_VERSIONS (0x0008), version 0, payload: command ID byte +# Command 2 supports version 0 and 1 (0b11 = 3) +> framework_tool --host-command 0x0008 0 2 +Response (24 bytes): +00000000: 0300 0000 0000 0000 0000 0000 0000 0000 ................ +00000010: 0000 0000 0000 0000 ........ +# Command 1 only supports version 0 (0b01 = 1) +> framework_tool --host-command 0x0008 0 1 +Response (24 bytes): +00000000: 0100 0000 0000 0000 0000 0000 0000 0000 ................ +00000010: 0000 0000 0000 0000 ........ +``` + ## Version Check Check if the firmware version is what you expect, returns exit code 0 on diff --git a/completions/bash/framework_tool b/completions/bash/framework_tool index 73377e16..ade3813d 100755 --- a/completions/bash/framework_tool +++ b/completions/bash/framework_tool @@ -23,7 +23,7 @@ _framework_tool() { case "${cmd}" in framework_tool) - opts="-v -q -t -f -h --flash-gpu-descriptor --verbose --quiet --versions --version --features --esrt --device --compare-version --power --thermal --sensors --fansetduty --fansetrpm --autofanctrl --pdports --info --meinfo --pd-info --pd-reset --pd-disable --pd-enable --dp-hdmi-info --dp-hdmi-update --audio-card-info --privacy --pd-bin --ec-bin --capsule --dump --h2o-capsule --dump-ec-flash --flash-ec --flash-ro-ec --flash-rw-ec --intrusion --inputdeck --inputdeck-mode --expansion-bay --charge-limit --charge-current-limit --charge-rate-limit --get-gpio --fp-led-level --fp-brightness --kblight --remap-key --rgbkbd --ps2-enable --tablet-mode --touchscreen-enable --stylus-battery --console --reboot-ec --ec-hib-delay --uptimeinfo --s0ix-counter --hash --driver --pd-addrs --pd-ports --test --test-retimer --boardid --force --dry-run --flash-gpu-descriptor-file --dump-gpu-descriptor-file --nvidia --generate-completions --help" + opts="-v -q -t -f -h --flash-gpu-descriptor --verbose --quiet --versions --version --features --esrt --device --compare-version --power --thermal --sensors --fansetduty --fansetrpm --autofanctrl --pdports --info --meinfo --pd-info --pd-reset --pd-disable --pd-enable --dp-hdmi-info --dp-hdmi-update --audio-card-info --privacy --pd-bin --ec-bin --capsule --dump --h2o-capsule --dump-ec-flash --flash-ec --flash-ro-ec --flash-rw-ec --intrusion --inputdeck --inputdeck-mode --expansion-bay --charge-limit --charge-current-limit --charge-rate-limit --get-gpio --fp-led-level --fp-brightness --kblight --remap-key --rgbkbd --ps2-enable --tablet-mode --touchscreen-enable --stylus-battery --console --reboot-ec --ec-hib-delay --uptimeinfo --s0ix-counter --hash --driver --pd-addrs --pd-ports --test --test-retimer --boardid --force --dry-run --flash-gpu-descriptor-file --dump-gpu-descriptor-file --nvidia --host-command --generate-completions --help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -197,6 +197,10 @@ _framework_tool() { COMPREPLY=($(compgen -f "${cur}")) return 0 ;; + --host-command) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; --generate-completions) COMPREPLY=($(compgen -W "bash elvish fish powershell zsh" -- "${cur}")) return 0 diff --git a/completions/fish/framework_tool.fish b/completions/fish/framework_tool.fish index ac7520db..00a70a97 100644 --- a/completions/fish/framework_tool.fish +++ b/completions/fish/framework_tool.fish @@ -64,6 +64,7 @@ complete -c framework_tool -l pd-addrs -d 'Specify I2C addresses of the PD chips complete -c framework_tool -l pd-ports -d 'Specify I2C ports of the PD chips (Advanced)' -r complete -c framework_tool -l flash-gpu-descriptor-file -d 'File to write to the gpu EEPROM' -r -F complete -c framework_tool -l dump-gpu-descriptor-file -d 'File to dump the gpu EEPROM to' -r -F +complete -c framework_tool -l host-command -d 'Send an EC host command. Args: [DATA...]' -r complete -c framework_tool -l generate-completions -d 'Generate shell completions and print to stdout' -r -f -a "bash\t'' elvish\t'' fish\t'' diff --git a/completions/zsh/_framework_tool b/completions/zsh/_framework_tool index 7ea91b80..3b80dcdc 100644 --- a/completions/zsh/_framework_tool +++ b/completions/zsh/_framework_tool @@ -57,6 +57,7 @@ _framework_tool() { '*--pd-ports=[Specify I2C ports of the PD chips (Advanced)]:PD_PORTS:_default:PD_PORTS:_default:PD_PORTS:_default' \ '--flash-gpu-descriptor-file=[File to write to the gpu EEPROM]:FLASH_GPU_DESCRIPTOR_FILE:_files' \ '--dump-gpu-descriptor-file=[File to dump the gpu EEPROM to]:DUMP_GPU_DESCRIPTOR_FILE:_files' \ +'*--host-command=[Send an EC host command. Args\: \[DATA...\]]:HOST_COMMAND:_default:HOST_COMMAND:_default' \ '--generate-completions=[Generate shell completions and print to stdout]:SHELL:(bash elvish fish powershell zsh)' \ '*-v[Increase logging verbosity]' \ '*--verbose[Increase logging verbosity]' \ diff --git a/framework_lib/src/commandline/clap_std.rs b/framework_lib/src/commandline/clap_std.rs index fe59de5e..ba1cc43f 100644 --- a/framework_lib/src/commandline/clap_std.rs +++ b/framework_lib/src/commandline/clap_std.rs @@ -305,6 +305,11 @@ struct ClapCli { #[arg(long)] nvidia: bool, + /// Send an EC host command. Args: [DATA...] + #[arg(long, value_parser=maybe_hex::)] + #[clap(num_args = 2..)] + host_command: Vec, + /// Generate shell completions and print to stdout #[arg(long, value_name = "SHELL", hide = true)] generate_completions: Option, @@ -419,6 +424,37 @@ pub fn parse(args: &[String]) -> Cli { )), _ => None, }; + let host_command = if args.host_command.len() >= 2 { + let cmd_ver = if let Ok(cmd_ver) = u8::try_from(args.host_command[1]) { + cmd_ver + } else { + cli.error( + ErrorKind::InvalidValue, + "Second argument of --host-command must be a one byte command version", + ) + .exit(); + }; + Some(( + args.host_command[0], + cmd_ver, + args.host_command[2..] + .iter() + .map(|&x| { + if let Ok(x) = u8::try_from(x) { + x + } else { + cli.error( + ErrorKind::InvalidValue, + "All payload values of --host-command must be one byte each", + ) + .exit(); + } + }) + .collect(), + )) + } else { + None + }; Cli { verbosity: LogLevel(args.verbosity.log_level_filter()), @@ -517,6 +553,6 @@ pub fn parse(args: &[String]) -> Cli { .dump_gpu_descriptor_file .map(|x| x.into_os_string().into_string().unwrap()), nvidia: args.nvidia, - raw_command: vec![], + host_command, } } diff --git a/framework_lib/src/commandline/mod.rs b/framework_lib/src/commandline/mod.rs index 2068893a..ae7dcfce 100644 --- a/framework_lib/src/commandline/mod.rs +++ b/framework_lib/src/commandline/mod.rs @@ -43,7 +43,7 @@ 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::{EcError, EcResult}; +use crate::chromium_ec::{CrosEcDriver, EcError, EcResult}; use crate::csme; use crate::ec_binary; use crate::esrt::{self, ResourceType}; @@ -230,8 +230,7 @@ pub struct Cli { // UEFI only pub allupdate: bool, pub paginate: bool, - // TODO: This is not actually implemented yet - pub raw_command: Vec, + pub host_command: Option<(u16, u8, Vec)>, } pub fn parse(args: &[String]) -> Cli { @@ -316,7 +315,7 @@ pub fn parse(args: &[String]) -> Cli { nvidia: cli.nvidia, // allupdate paginate: cli.paginate, - // raw_command + // host_command ..Default::default() } } else { @@ -1603,9 +1602,18 @@ pub fn run_with_args(args: &Cli, _allupdate: bool) -> i32 { } else { println!("Not all EC versions support this comand.") }; - // TODO: - //} else if arg == "-raw-command" { - // raw_command(&args[1..]); + } 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> = crate::uefi::fs::shell_read_file(pd_bin_path); diff --git a/framework_lib/src/commandline/uefi.rs b/framework_lib/src/commandline/uefi.rs index d50f6b60..4251d15b 100644 --- a/framework_lib/src/commandline/uefi.rs +++ b/framework_lib/src/commandline/uefi.rs @@ -98,7 +98,7 @@ pub fn parse(args: &[String]) -> Cli { info: false, meinfo: None, nvidia: false, - raw_command: vec![], + host_command: None, }; if args.len() == 0 { @@ -708,8 +708,39 @@ pub fn parse(args: &[String]) -> Cli { None }; found_an_option = true; - } else if arg == "--raw-command" { - cli.raw_command = args[1..].to_vec(); + } else if arg == "--host-command" { + cli.host_command = if args.len() > i + 2 { + let cmd_id = parse_hex_or_dec_u16(&args[i + 1]); + let version = parse_hex_or_dec_u8(&args[i + 2]); + if let (Some(cmd_id), Some(version)) = (cmd_id, version) { + let mut data = Vec::new(); + let mut parse_error = false; + for j in (i + 3)..args.len() { + if args[j].starts_with('-') { + break; + } + if let Some(byte) = parse_hex_or_dec_u8(&args[j]) { + data.push(byte); + } else { + println!("Invalid data byte for --host-command: '{}'", args[j]); + parse_error = true; + break; + } + } + if parse_error { + None + } else { + Some((cmd_id, version, data)) + } + } else { + println!("Invalid values for --host-command. Usage: --host-command [DATA...]"); + None + } + } else { + println!("--host-command requires at least two arguments: "); + None + }; + found_an_option = true; } else if arg == "--compare-version" { cli.compare_version = if args.len() > i + 1 { Some(args[i + 1].clone()) @@ -817,3 +848,19 @@ pub fn parse(args: &[String]) -> Cli { cli } + +fn parse_hex_or_dec_u16(s: &str) -> Option { + if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) { + u16::from_str_radix(hex, 16).ok() + } else { + s.parse::().ok() + } +} + +fn parse_hex_or_dec_u8(s: &str) -> Option { + if let Some(hex) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) { + u8::from_str_radix(hex, 16).ok() + } else { + s.parse::().ok() + } +}