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

Merged
merged 39 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
39 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
10417b1
fix: use lossy conversion
lleyton Oct 28, 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,058 changes: 1,877 additions & 181 deletions Cargo.lock

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions crosec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,16 @@ 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"

[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())
}
74 changes: 74 additions & 0 deletions crosec/src/commands/charge_control.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
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::fs::File;
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: &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: &mut File) -> EcCmdResult<ChargeControl> {
panic!("Not implemented yet");
}

pub fn set_charge_control(file: &mut File, charge_control: ChargeControl) -> EcCmdResult<()> {
Fixed Show fixed Hide fixed
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(())
}
113 changes: 113 additions & 0 deletions crosec/src/commands/fp_download.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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::{CrosEcCmd, fp_info::EcResponseFpInfo, get_protocol_info::EcResponseGetProtocolInfo};

#[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 Into<Vec<u8>> for FpTemplate {
Fixed Show fixed Hide fixed
fn into(self) -> Vec<u8> {
self.vec
}
}

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

/// Make sure your buffer is actually a compatible fp template
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)),
}
}
21 changes: 21 additions & 0 deletions crosec/src/commands/fp_get_encryption_status.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::os::fd::AsRawFd;
use bytemuck::{Pod, Zeroable};
use crate::commands::CrosEcCmd;
use crate::ec_command::ec_command_bytemuck;
use crate::EcCmdResult;

#[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())
}
52 changes: 52 additions & 0 deletions crosec/src/commands/fp_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use std::os::fd::AsRawFd;

use bytemuck::{Pod, Zeroable};

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

use super::{
CrosEcCmd,
get_cmd_versions::{ec_cmd_get_cmd_versions, V1},
};

#[repr(C, align(4))]
#[derive(Pod, Zeroable, Clone, Copy, Debug)]
pub struct EcResponseFpInfo {
pub vendor_id: u32,
pub product_id: u32,
pub model_id: u32,
pub version: u32,
/// The size of the PGM image, in bytes
pub frame_size: u32,
pub pixel_format: u32,
pub width: u16,
pub height: u16,
pub bpp: u16,
pub errors: u16,
/// The template size, in bytes
pub template_size: u32,
/// The maximum number of templates the FP can store and match at once
pub template_max: u16,
/// The number of templates loaded into the FP
pub template_valid: u16,
/// The first bit (the rightmost) represents template 0, the 2nd bit form the right represents template 1, etc.
/// If the bit is 1, that means that the template has been updated by the FP and the updated version has not been downloaded yet.
pub template_dirty: u32,
/// This version could increase after an update to the FP firmware
pub template_version: u32,
}
impl EcResponseFpInfo {
pub(crate) fn get_simple_image_size(&self) -> usize {
(self.width as usize) * (self.height as usize) * (self.bpp as usize) / 8
}
}

pub fn fp_info<File: AsRawFd>(file: &mut File) -> EcCmdResult<EcResponseFpInfo> {
let fd = file.as_raw_fd();
let versions = ec_cmd_get_cmd_versions(file, CrosEcCmd::FpInfo)?;
if versions & V1 == 0 {
panic!("fp doesn't support V1. Other versions are currently not implemented");
}
let info: EcResponseFpInfo = ec_command_bytemuck(CrosEcCmd::FpInfo, 1, &(), fd)?;
Ok(info)
}
Loading
Loading