Skip to content

Commit

Permalink
wait_event function
Browse files Browse the repository at this point in the history
The function can wait for any event and detect the type of the event. It has in depth Rust-safe types for the fingerprint events.
  • Loading branch information
ChocolateLoverRaj committed Jun 6, 2024
1 parent 06efbf5 commit cb213aa
Show file tree
Hide file tree
Showing 5 changed files with 275 additions and 0 deletions.
1 change: 1 addition & 0 deletions crosec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub mod ec_command;
pub mod get_number_of_fans;
pub mod read_mem_any;
pub mod read_mem_string;
pub mod wait_event;

#[derive(FromPrimitive, Debug, Copy, Clone)]
pub enum EcResponseStatus {
Expand Down
82 changes: 82 additions & 0 deletions crosec/src/wait_event/event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
use std::{
io::{self, Read},
mem::size_of,
};

use bytemuck::{from_bytes, Pod, Zeroable};
use num_derive::FromPrimitive;
use strum_macros::{EnumString, IntoStaticStr};

use crate::wait_event::fingerprint::EcMkbpEventFingerprint;

#[derive(Debug, Clone, Copy, Pod, Zeroable)]
#[repr(C, packed)]
pub struct EcResponseMotionSenseFifoInfo {
size: u16,
count: u16,
timestamp: u32,
total_lost: u16,
lost: [u16; 0],
}

#[derive(Debug)]
#[repr(u8)]
pub enum EcMkbpEvent {
KeyMatrix([u8; 13]),
HostEvent(u32),
HostEvent64(u64),
SensorFifo(EcResponseMotionSenseFifoInfo),
Buttons(u32),
Switches(u32),
Fingerprint(EcMkbpEventFingerprint),
Sysrq(u32),
CecEvent(u32),
}

#[derive(Debug, IntoStaticStr, EnumString, FromPrimitive, Clone, Copy)]
#[repr(u8)]
pub enum EcMkbpEventType {
KeyMatrix,
HostEvent,
SensorFifo,
Buttons,
Switches,
Fingerprint,
Sysrq,
HostEvent64,
CecEvent,
CecMessage,
DpAltModeEntered,
OnlineCalibration,
Pchg,
}
impl EcMkbpEventType {
fn data_size(&self) -> usize {
match self {
Self::KeyMatrix => size_of::<[u8; 13]>(),
Self::HostEvent => size_of::<u32>(),
Self::SensorFifo => size_of::<EcResponseMotionSenseFifoInfo>(),
Self::Buttons => size_of::<u32>(),
Self::Switches => size_of::<u32>(),
Self::Fingerprint => size_of::<u32>(),
Self::Sysrq => size_of::<u32>(),
Self::HostEvent64 => size_of::<u64>(),
Self::CecEvent => size_of::<u32>(),
_ => 0,
}
}

pub(crate) fn read<T: Read>(&self, stream: &mut T) -> io::Result<EcMkbpEvent> {
let mut event = vec![Default::default(); size_of::<Self>() + self.data_size()];
stream.read_exact(&mut event)?;
debug_assert_eq!(event[0], *self as u8);
event.remove(0);
let data = event;
Ok(match self {
EcMkbpEventType::Fingerprint => {
EcMkbpEvent::Fingerprint(from_bytes::<EcMkbpEventFingerprint>(&data).to_owned())
}
event_type => panic!("{event_type:#?} from_bytes not implemented yet"),
})
}
}
125 changes: 125 additions & 0 deletions crosec/src/wait_event/fingerprint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use std::fmt::Debug;

use bytemuck::{Pod, Zeroable};

#[derive(Debug)]
pub enum EcMkbpEventFingerprintEnrollError {
LowQuality,
Immobile,
LowCoverage,
Internal,
}

#[derive(Debug)]
pub struct EcMkbpEventFingerprintEnroll {
pub percentage: u8,
pub error: Option<EcMkbpEventFingerprintEnrollError>,
}

#[derive(Debug)]
pub struct EcMkbpEventFingerprintMatch {
pub index: usize,
/// If `Some`, his means that the fingerprint matched an existing template and the existing template was updated to more accurately match future fingerprints.
/// `None` if `EC_MKBP_FP_ERR_MATCH_YES`.
/// `Some(Ok)` if `EC_MKBP_FP_ERR_MATCH_YES_UPDATED`.
/// `Some(Err)` if `EC_MKBP_FP_ERR_MATCH_YES_UPDATE_FAILED`.
// TODO: Find the CrOS EC documentation for this and add the link here
pub update: Option<Result<(), ()>>,
}

#[derive(Debug)]
pub enum EcMkbpEventFingerprintNoMatchError {
/// `EC_MKBP_FP_ERR_MATCH_NO_INTERNAL` - Probably means there was an internal error.
Internal,
/// `EC_MKBP_FP_ERR_MATCH_NO_TEMPLATES` - This either means there are no templates, or something's wrong with the templates. Idk which one.
Templates,
/// `EC_MKBP_FP_ERR_MATCH_NO_LOW_QUALITY` - My guess is this might happen if the sensor or finger is dirty
LowQuality,
/// `EC_MKBP_FP_ERR_MATCH_NO_LOW_COVERAGE` - My guess is this might happen if only a small part of a finger is sensed
LowCoverage,
}

#[derive(Debug)]
pub enum EcMkbpEventFingerprintMatchResult {
Match(EcMkbpEventFingerprintMatch),
NoMatch(Result<(), EcMkbpEventFingerprintNoMatchError>),
}

#[derive(Debug)]
pub enum EcMkbpEventFingerprintRust {
/// Contains the enroll progress, as a percentage
Enroll(EcMkbpEventFingerprintEnroll),
Match(EcMkbpEventFingerprintMatchResult),
FingerDown,
FingerUp,
ImageReady,
}

const EC_MKBP_EVENT_FINGERPRINT_ERROR_MASK: u32 = 0x0000000F;

#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct EcMkbpEventFingerprint {
fp_events: u32,
}
impl EcMkbpEventFingerprint {
/// Get a Rust-friendly format. Uses CPU to call and format uses more memory.
pub fn rust(&self) -> EcMkbpEventFingerprintRust {
match self.fp_events {
fp_events if fp_events & (1 << 27) != 0 => {
EcMkbpEventFingerprintRust::Enroll(EcMkbpEventFingerprintEnroll {
percentage: ((self.fp_events & 0x00000FF0) >> 4).try_into().unwrap(),
error: match self.fp_events & EC_MKBP_EVENT_FINGERPRINT_ERROR_MASK {
0 => None,
1 => Some(EcMkbpEventFingerprintEnrollError::LowQuality),
2 => Some(EcMkbpEventFingerprintEnrollError::Immobile),
3 => Some(EcMkbpEventFingerprintEnrollError::LowCoverage),
5 => Some(EcMkbpEventFingerprintEnrollError::Internal),
unknown_error => panic!("Unknown error: {unknown_error}"),
},
})
}
fp_events if fp_events & (1 << 28) != 0 => EcMkbpEventFingerprintRust::Match({
let code = self.fp_events & EC_MKBP_EVENT_FINGERPRINT_ERROR_MASK;
let get_match_index = || ((self.fp_events & 0x0000F000) >> 12) as usize;
match code {
0 => EcMkbpEventFingerprintMatchResult::NoMatch(Ok(())),
6 => EcMkbpEventFingerprintMatchResult::NoMatch(Err(
EcMkbpEventFingerprintNoMatchError::Internal,
)),
7 => EcMkbpEventFingerprintMatchResult::NoMatch(Err(
EcMkbpEventFingerprintNoMatchError::Templates,
)),
2 => EcMkbpEventFingerprintMatchResult::NoMatch(Err(
EcMkbpEventFingerprintNoMatchError::LowQuality,
)),
4 => EcMkbpEventFingerprintMatchResult::NoMatch(Err(
EcMkbpEventFingerprintNoMatchError::LowCoverage,
)),
1 => EcMkbpEventFingerprintMatchResult::Match(EcMkbpEventFingerprintMatch {
index: get_match_index(),
update: None,
}),
3 => EcMkbpEventFingerprintMatchResult::Match(EcMkbpEventFingerprintMatch {
index: get_match_index(),
update: Some(Ok(())),
}),
5 => EcMkbpEventFingerprintMatchResult::Match(EcMkbpEventFingerprintMatch {
index: get_match_index(),
update: Some(Err(())),
}),
code => panic!("Unknown error code: {code} ({code:#b})"),
}
}),
fp_events if fp_events & (1 << 29) != 0 => EcMkbpEventFingerprintRust::FingerDown,
fp_events if fp_events & (1 << 30) != 0 => EcMkbpEventFingerprintRust::FingerUp,
fp_events if fp_events & (1 << 31) != 0 => EcMkbpEventFingerprintRust::ImageReady,
fp_events => panic!("Unknown FP event: {fp_events} ({fp_events:#b})"),
}
}
}
impl Debug for EcMkbpEventFingerprint {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.rust().fmt(f)
}
}
50 changes: 50 additions & 0 deletions crosec/src/wait_event/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use std::{fs::File, os::fd::AsRawFd};

use event::{EcMkbpEvent, EcMkbpEventType};
use nix::{
libc::{ioctl, poll, pollfd},
request_code_none,
};

use crate::CROS_EC_IOC_MAGIC;

pub mod event;
pub mod fingerprint;

const POLL_IN: i16 = 0x001;

#[derive(Debug)]
pub enum PollData {
EventHappened(EcMkbpEvent),
Timeout,
SomethingElseHappened(i16),
}

pub fn wait_event(
file: &mut File,
event_type: EcMkbpEventType,
timeout: i32,
) -> Result<PollData, i32> {
let mask = 1 << event_type as u8;
unsafe {
ioctl(
file.as_raw_fd(),
request_code_none!(CROS_EC_IOC_MAGIC, 2),
mask,
)
};
let mut fds = pollfd {
fd: file.as_raw_fd(),
events: POLL_IN,
revents: Default::default(),
};
let result = unsafe { poll(&mut fds, 1, timeout) };
match result {
0 => Ok(PollData::Timeout),
1 => match fds.revents {
POLL_IN => Ok(PollData::EventHappened(event_type.read(file).unwrap())),
events => Ok(PollData::SomethingElseHappened(events)),
},
result => Err(result),
}
}
17 changes: 17 additions & 0 deletions ectool/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crosec::commands::fp_mode::{fp_mode, FpMode};
use crosec::commands::fp_set_seed::{fp_set_seed, FP_CONTEXT_TPM_BYTES};
use crosec::commands::fp_stats::fp_stats;
use crosec::commands::get_protocol_info::get_protocol_info;
use crosec::wait_event::{event::EcMkbpEventType, wait_event};
use num_traits::cast::FromPrimitive;

use crosec::battery::battery;
Expand Down Expand Up @@ -101,6 +102,12 @@ enum Commands {
FpMode {
mode: Vec<String>,
},
WaitEvent {
event_type: String,
/// Timeout in milliseconds
timeout: i32,
device: Option<Device>,
},
}

#[derive(Subcommand)]
Expand Down Expand Up @@ -321,6 +328,16 @@ fn main() -> Result<()> {
let display = FpMode::display(mode);
println!("FP mode: {display}");
}
Commands::WaitEvent {
event_type,
device,
timeout,
} => {
let mut file = File::open(device.unwrap_or_default().get_path())?;
let result =
wait_event(&mut file, EcMkbpEventType::from_str(&event_type)?, timeout).unwrap();
println!("{result:#?}");
}
}

Ok(())
Expand Down

0 comments on commit cb213aa

Please sign in to comment.