Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More commands #4

Open
wants to merge 38 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b84a185
Use clap for an actual ectool CLI
ChocolateLoverRaj May 17, 2024
1ba33c6
Board version command
ChocolateLoverRaj May 17, 2024
227d7ad
Get command versions
ChocolateLoverRaj May 17, 2024
fded85b
Command version constants
ChocolateLoverRaj May 17, 2024
1d61b72
Set fan target RPM
ChocolateLoverRaj May 17, 2024
a94d8ce
Get EC features
ChocolateLoverRaj May 17, 2024
ed15d21
Get number of fans
ChocolateLoverRaj May 18, 2024
14f2a73
Get fan RPM
ChocolateLoverRaj May 20, 2024
1120872
ectool console
ChocolateLoverRaj May 20, 2024
5e1b846
Battery command
ChocolateLoverRaj May 20, 2024
7f4d0aa
Charge control commands
ChocolateLoverRaj May 30, 2024
a33802e
Add hello and console commands for cros_fp
ChocolateLoverRaj Jun 5, 2024
614d48d
remove unused EcInterface enum
ChocolateLoverRaj Jun 5, 2024
4bfb52f
fp-info command
ChocolateLoverRaj Jun 5, 2024
2808fdb
fp-stats command
ChocolateLoverRaj Jun 5, 2024
1d1d30a
set fp seed command
ChocolateLoverRaj Jun 5, 2024
06efbf5
fp mode command
ChocolateLoverRaj Jun 5, 2024
cb213aa
wait_event function
ChocolateLoverRaj Jun 6, 2024
9625eb1
Use &mut File instead of raw fd for safe functions
ChocolateLoverRaj Jun 6, 2024
cc8858e
Download fp frame and templates
ChocolateLoverRaj Jun 6, 2024
4a81f0c
Move charge control subcommand to new file
ChocolateLoverRaj Jun 6, 2024
c4b3560
Upload template command
ChocolateLoverRaj Jun 6, 2024
6da00c5
Add clap feature to crosec for better fp-mode command
ChocolateLoverRaj Jun 7, 2024
80f4101
Add no-timeout mode to wait_event
ChocolateLoverRaj Jun 8, 2024
16e89ed
clap::ValueEnum for EcMkbpEventType
ChocolateLoverRaj Jun 8, 2024
b086358
Better fp-seed bad seed feedback
ChocolateLoverRaj Jun 8, 2024
37a554e
Add version and about to ectool
ChocolateLoverRaj Jun 8, 2024
22e0d43
Remove unused EcParamsReadMem
ChocolateLoverRaj Jun 8, 2024
311b3b0
Nix flake
ChocolateLoverRaj Jun 8, 2024
a64baa9
Use trait for File in `fp_mode`
ChocolateLoverRaj Jun 12, 2024
a2d5504
Some improvements
ChocolateLoverRaj Jun 20, 2024
a8121a0
Add HostEvent enum
ChocolateLoverRaj Jul 2, 2024
980e60e
No unused deps
ChocolateLoverRaj Jul 2, 2024
94f055f
Get uptime info command
ChocolateLoverRaj Jul 3, 2024
1c8d728
Multiple event types when waiting for event
ChocolateLoverRaj Jul 3, 2024
05120f1
cargo fmt
ChocolateLoverRaj Jul 3, 2024
c64772c
Meet cargo clippy rules
ChocolateLoverRaj Jul 4, 2024
e497559
Charge current limit command
ChocolateLoverRaj Oct 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,077 changes: 1,896 additions & 181 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions crosec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-std = "1.12.0"
bytemuck = { version = "1.16.0", features = ["derive"] }
clap = { version = "4.5.6", optional = true }
nix = { version = "0.27.1", features = ["ioctl"] }
num = "0.4.3"
num-derive = "0.4.2"
num-traits = "0.2.18"
strum = "0.26.2"
strum_macros = "0.26.4"
thiserror = "1.0.57"
uom = "0.36.0"

[features]
clap = ["dep:clap"]
72 changes: 72 additions & 0 deletions crosec/src/battery.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use crate::commands::get_cmd_versions::{ec_cmd_get_cmd_versions, V1};
use crate::commands::CrosEcCmd;
use crate::read_mem_any::read_mem_any;
use crate::read_mem_string::read_mem_string;
use crate::{
EcCmdResult, EC_MEM_MAP_BATTERY_CAPACITY, EC_MEM_MAP_BATTERY_CYCLE_COUNT,
EC_MEM_MAP_BATTERY_DESIGN_CAPACITY, EC_MEM_MAP_BATTERY_DESIGN_VOLTAGE,
EC_MEM_MAP_BATTERY_FLAGS, EC_MEM_MAP_BATTERY_LAST_FULL_CHARGE_CAPACITY,
EC_MEM_MAP_BATTERY_MANUFACTURER, EC_MEM_MAP_BATTERY_MODEL, EC_MEM_MAP_BATTERY_RATE,
EC_MEM_MAP_BATTERY_SERIAL, EC_MEM_MAP_BATTERY_TYPE, EC_MEM_MAP_BATTERY_VERSION,
EC_MEM_MAP_BATTERY_VOLTAGE,
};
use std::fs::File;

#[derive(Debug, Clone)]
pub struct BatteryInfo {
pub oem_name: String,
pub model_number: String,
pub chemistry: String,
pub serial_number: String,
pub design_capacity: i32,
pub last_full_charge: i32,
pub design_output_voltage: i32,
pub cycle_count: i32,
pub present_voltage: i32,
pub present_current: i32,
pub remaining_capacity: i32,
pub flags: u8,
}

pub fn battery(file: &mut File) -> EcCmdResult<BatteryInfo> {
if ec_cmd_get_cmd_versions(file, CrosEcCmd::BatteryGetStatic)? & V1 != 0 {
panic!(
"Battery info needs to be gotten with the {:?} command",
CrosEcCmd::BatteryGetStatic
);
} else {
let battery_version = read_mem_any::<i8>(file, EC_MEM_MAP_BATTERY_VERSION).unwrap();
if battery_version < 1 {
panic!("Battery version {battery_version} is not supported");
}
let flags = read_mem_any::<u8>(file, EC_MEM_MAP_BATTERY_FLAGS).unwrap();
let oem_name = read_mem_string(file, EC_MEM_MAP_BATTERY_MANUFACTURER).unwrap();
let model_number = read_mem_string(file, EC_MEM_MAP_BATTERY_MODEL).unwrap();
let chemistry = read_mem_string(file, EC_MEM_MAP_BATTERY_TYPE).unwrap();
let serial_number = read_mem_string(file, EC_MEM_MAP_BATTERY_SERIAL).unwrap();
let design_capacity =
read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_DESIGN_CAPACITY).unwrap();
let last_full_charge =
read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_LAST_FULL_CHARGE_CAPACITY).unwrap();
let design_output_voltage =
read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_DESIGN_VOLTAGE).unwrap();
let cycle_count = read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_CYCLE_COUNT).unwrap();
let present_voltage = read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_VOLTAGE).unwrap();
let present_current = read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_RATE).unwrap();
let remaining_capacity = read_mem_any::<i32>(file, EC_MEM_MAP_BATTERY_CAPACITY).unwrap();
Ok(BatteryInfo {
flags,
oem_name,
model_number,
chemistry,
serial_number,
design_capacity,
last_full_charge,
design_output_voltage,
cycle_count,
present_voltage,
present_current,
remaining_capacity,
})
}
}
10 changes: 10 additions & 0 deletions crosec/src/commands/board_version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use std::fs::File;
use std::os::fd::AsRawFd;

use crate::commands::CrosEcCmd;
use crate::ec_command::ec_command_bytemuck;
use crate::EcCmdResult;

pub fn ec_cmd_board_version(file: &mut File) -> EcCmdResult<u32> {
ec_command_bytemuck(CrosEcCmd::GetBoardVersion, 0, &(), file.as_raw_fd())
}
76 changes: 76 additions & 0 deletions crosec/src/commands/charge_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::commands::get_cmd_versions::{ec_cmd_get_cmd_versions, V2};
use crate::commands::CrosEcCmd;
use crate::ec_command::ec_command_bytemuck;
use crate::EcCmdResult;
use bytemuck::{Pod, Zeroable};
use std::os::fd::AsRawFd;

#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone, Default, Debug)]
pub struct Sustainer {
pub min_percent: i8,
pub max_percent: i8,
}

#[derive(Debug)]
pub enum ChargeControl {
Normal(Option<Sustainer>),
Idle,
Discharge,
}

pub fn supports_get_and_sustainer<File: AsRawFd>(file: &mut File) -> EcCmdResult<bool> {
let versions = ec_cmd_get_cmd_versions(file, CrosEcCmd::ChargeControl)?;
Ok(versions & V2 != 0)
}

#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone)]
pub struct EcParamsChargeControl {
mode: u32,
command: u8,
reserved: u8,
sustain: Sustainer,
}

const CHARGE_CONTROL_MODE_SET: u8 = 0;
// const CHARGE_CONTROL_MODE_GET: u8 = 1;

const CHARGE_CONTROL_COMMAND_NORMAL: u32 = 0;
const CHARGE_CONTROL_COMMAND_IDLE: u32 = 1;
const CHARGE_CONTROL_COMMAND_DISCHARGE: u32 = 2;

pub fn get_charge_control<File: AsRawFd>(_file: &mut File) -> EcCmdResult<ChargeControl> {
panic!("Not implemented yet");
}

pub fn set_charge_control<File: AsRawFd>(
file: &mut File,
charge_control: ChargeControl,
) -> EcCmdResult<()> {
Comment on lines +47 to +50

Check warning

Code scanning / clippy

this function depends on never type fallback being () Warning

this function depends on never type fallback being ()
ec_command_bytemuck(
CrosEcCmd::ChargeControl,
{
let version = ec_cmd_get_cmd_versions(file, CrosEcCmd::ChargeControl)?;
Ok(if version & V2 != 0 { 2 } else { 1 })
}?,
&EcParamsChargeControl {
command: CHARGE_CONTROL_MODE_SET,
mode: match charge_control {
ChargeControl::Normal(_) => CHARGE_CONTROL_COMMAND_NORMAL,
ChargeControl::Idle => CHARGE_CONTROL_COMMAND_IDLE,
ChargeControl::Discharge => CHARGE_CONTROL_COMMAND_DISCHARGE,
},
reserved: Default::default(),
sustain: match charge_control {
ChargeControl::Normal(sustain) => sustain.unwrap_or(Sustainer {
min_percent: -1,
max_percent: -1,
}),
_ => Default::default(),
},
},
file.as_raw_fd(),
)?;
Ok(())
}
29 changes: 29 additions & 0 deletions crosec/src/commands/charge_current_limit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use std::os::fd::AsRawFd;

use bytemuck::{Pod, Zeroable};
use uom::si::{electric_current::milliampere, f32::ElectricCurrent};

use crate::{ec_command::ec_command_bytemuck, EcCmdResult};

use super::CrosEcCmd;

#[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone)]
pub struct EcParamsChargeCurrentLimit {
limit: u32, // in mA
}

/// Limit the charging current. The EC command sends the charging current limit to the nearest mA.
pub fn set_charge_current_limit<File: AsRawFd>(
file: &mut File,
limit: ElectricCurrent,
) -> EcCmdResult<()> {
ec_command_bytemuck(
CrosEcCmd::ChargeCurrentLimit,
0,
&EcParamsChargeCurrentLimit {
limit: limit.get::<milliampere>() as u32,
},
file.as_raw_fd(),
)
}
115 changes: 115 additions & 0 deletions crosec/src/commands/fp_download.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use std::{os::fd::AsRawFd, thread::sleep, time::Duration};

use bytemuck::{bytes_of, Pod, Zeroable};

use crate::ec_command::ec_command_with_dynamic_output_size;

use super::{fp_info::EcResponseFpInfo, get_protocol_info::EcResponseGetProtocolInfo, CrosEcCmd};

#[repr(C)]
#[derive(Pod, Zeroable, Clone, Copy)]
struct EcParamsFpFrame {
/// The offset contains the template index or FP_FRAME_INDEX_RAW_IMAGE
/// in the high nibble, and the real offset within the frame in
/// FP_FRAME_OFFSET_MASK.
offset: u32,
size: u32,
}

const FP_FRAME_INDEX_RAW_IMAGE: u32 = 0;

/// This can be changed. `3` is what the ChromiumOS ectool uses.
const MAX_ATTEMPTS: usize = 3;

pub enum DownloadType {
/// (aka `FP_FRAME_INDEX_SIMPLE_IMAGE`) for the a single grayscale image
SimpleImage,
/// (aka `FP_FRAME_INDEX_RAW_IMAGE`) for the full vendor raw finger image.
RawImage,
Template(usize),
}

/// Downloads a frame buffer from the FPMCU.
/// The downloaded data might be either the finger image or a finger template.
pub fn fp_download<File: AsRawFd>(
file: &mut File,
fp_info: &EcResponseFpInfo,
protocol_info: &EcResponseGetProtocolInfo,
download_type: &DownloadType,
) -> Vec<u8> {
let (size, index) = match download_type {
DownloadType::SimpleImage => (fp_info.get_simple_image_size(), FP_FRAME_INDEX_RAW_IMAGE),
DownloadType::RawImage => (fp_info.frame_size as usize, FP_FRAME_INDEX_RAW_IMAGE),
DownloadType::Template(template_index) => {
(fp_info.template_size as usize, *template_index as u32 + 1)
}
};
// The template may be (and probably is) bigger than the max output size, so we need to download it in chunks
let number_of_chunks = size.div_ceil(protocol_info.max_ec_output_size());
let mut chunks = Vec::<Vec<u8>>::with_capacity(number_of_chunks);
for chunk_index in 0..number_of_chunks {
let bytes_read = chunk_index * protocol_info.max_ec_output_size();
let remaining_bytes = size - bytes_read;
let current_chunk_size = remaining_bytes.min(protocol_info.max_ec_output_size());
let mut attempt = 0;
loop {
let result = ec_command_with_dynamic_output_size(
CrosEcCmd::FpFrame,
0,
bytes_of(&EcParamsFpFrame {
offset: (index << 28) + (bytes_read as u32),
size: current_chunk_size as u32,
}),
current_chunk_size,
file.as_raw_fd(),
);
if let Ok(chunk) = result {
chunks.push(chunk);
break;
} else {
attempt += 1;
if attempt == MAX_ATTEMPTS {
panic!("Could not successfully get the fp frame in {MAX_ATTEMPTS} attempts");
}
// Using micros and not millis to be more like original `usleep(100000)` from ChromiumOS's ectool
sleep(Duration::from_micros(100_000));
}
}
}
chunks.concat()
}

/// A safe wrapper around the actual template so you don't try to upload arbitrary data
pub struct FpTemplate {
vec: Vec<u8>,
}

impl From<FpTemplate> for Vec<u8> {
fn from(value: FpTemplate) -> Self {
value.vec
}
}

impl FpTemplate {
pub fn buffer(&self) -> &Vec<u8> {
&self.vec
}

/// Make sure your buffer is actually a compatible fp template
/// # Safety
/// Make sure you're uploading a template from the same FPMCU with the same version and the same seed set.
pub unsafe fn from_vec_unchecked(vec: Vec<u8>) -> Self {
Fixed Show fixed Hide fixed
FpTemplate { vec }
}
}

pub fn fp_download_template<File: AsRawFd>(
file: &mut File,
fp_info: &EcResponseFpInfo,
protocol_info: &EcResponseGetProtocolInfo,
index: usize,
) -> FpTemplate {
FpTemplate {
vec: fp_download(file, fp_info, protocol_info, &DownloadType::Template(index)),
}
}
23 changes: 23 additions & 0 deletions crosec/src/commands/fp_get_encryption_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::commands::CrosEcCmd;
use crate::ec_command::ec_command_bytemuck;
use crate::EcCmdResult;
use bytemuck::{Pod, Zeroable};
use std::os::fd::AsRawFd;

#[repr(u32)]
pub enum FpEncryptionStatus {
SeedSet = 0b1,
}

#[derive(Pod, Zeroable, Copy, Clone)]
#[repr(C)]
pub struct EcResponseFpGetEncryptionStatus {
pub valid_flags: u32,
pub status: u32,
}

pub fn fp_get_encryption_status<File: AsRawFd>(
file: &mut File,
) -> EcCmdResult<EcResponseFpGetEncryptionStatus> {
ec_command_bytemuck(CrosEcCmd::FpGetEncryptionStatus, 0, &(), file.as_raw_fd())
}
Loading
Loading