Skip to main content

KM003C

Struct KM003C 

Source
pub struct KM003C { /* private fields */ }

Implementations§

Source§

impl KM003C

Source

pub async fn new(config: DeviceConfig) -> Result<Self, KMError>

Connect to device with the given configuration

The config determines the connection mode:

  • DeviceConfig::vendor(): Full mode - runs init sequence, all features enabled
  • DeviceConfig::hid(): Basic mode - no init, ADC/PD polling only
§Examples
use km003c_lib::{KM003C, DeviceConfig};

// Full mode (vendor interface) - all features
let device = KM003C::new(DeviceConfig::vendor()).await?;
println!("Model: {}", device.state().unwrap().model());
println!("AdcQueue enabled: {}", device.adcqueue_enabled());

// Basic mode (HID interface) - ADC/PD polling only
let mut device = KM003C::new(DeviceConfig::hid()).await?;
let adc = device.request_adc_data().await?;

// With options
let device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
Examples found in repository?
examples/settings.rs (line 7)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let settings = device.request_settings().await?;
9
10    println!("Device name: {}", settings.device_name().unwrap_or("<invalid UTF-8>"));
11    println!("Language selection: {}", settings.language_selection());
12    println!("Uncalibrated: {}", settings.is_uncalibrated());
13    println!("Brightness: {}%", settings.brightness().get::<percent>());
14    println!(
15        "Sample interval: {} us",
16        settings.sample_interval().get::<microsecond>()
17    );
18    println!("Screen orientation: {}", settings.screen_orientation());
19    println!("Mtools device mode: {}", settings.mtools_device_mode());
20    println!("Selected main page: {}", settings.selected_main_page());
21
22    Ok(())
23}
More examples
Hide additional examples
examples/calibration_auth.rs (line 7)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let initial_level = device.state().map(|state| state.auth_level).unwrap_or(0);
9    println!("Initial authentication level: {initial_level}");
10
11    let result = device.authenticate_calibration().await?;
12    println!(
13        "Calibration authentication level: {} (attribute 0x{:04x})",
14        result.auth_level, result.attribute
15    );
16
17    device.start_graph_mode(GraphSampleRate::Sps50).await?;
18    tokio::time::sleep(Duration::from_millis(200)).await;
19    let response = device.request_data(AttributeSet::single(Attribute::AdcQueue)).await;
20    device.stop_graph_mode().await?;
21
22    let response = response?;
23    let samples = response.get_adc_queue().map(|queue| queue.samples.len()).unwrap_or(0);
24    println!("AdcQueue samples received after level-2 auth: {samples}");
25
26    Ok(())
27}
Source

pub fn next_tid(&mut self) -> u8

Get the next transaction ID (for auth module and external use)

Source

pub fn set_transaction_id(&mut self, id: u8)

Set the transaction ID counter (for protocol research/sync purposes)

Source

pub async fn send(&mut self, packet: Packet) -> Result<(), KMError>

Send a high-level packet without waiting for or correlating its response.

This is a low-level protocol-research API. Prefer command-specific methods such as Self::request_data and Self::read_memory_block for normal use.

Source

pub async fn send_raw(&mut self, data: &[u8]) -> Result<(), KMError>

Send raw bytes to the device (for protocol research/testing)

Source

pub async fn receive_raw(&mut self) -> Result<Vec<u8>, KMError>

Receive raw bytes without transaction correlation.

This is a low-level protocol-research API. Previously received out-of-order responses are returned first.

Source

pub async fn receive(&mut self) -> Result<Packet, KMError>

Receive the next framed high-level packet without transaction correlation.

Unframed transfers such as encrypted MemoryRead data cannot be parsed by this method. Use Self::read_memory_block for device memory reads.

Source

pub async fn request_data( &mut self, mask: AttributeSet, ) -> Result<Packet, KMError>

Request data with a specific attribute set

Examples found in repository?
examples/calibration_auth.rs (line 19)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let initial_level = device.state().map(|state| state.auth_level).unwrap_or(0);
9    println!("Initial authentication level: {initial_level}");
10
11    let result = device.authenticate_calibration().await?;
12    println!(
13        "Calibration authentication level: {} (attribute 0x{:04x})",
14        result.auth_level, result.attribute
15    );
16
17    device.start_graph_mode(GraphSampleRate::Sps50).await?;
18    tokio::time::sleep(Duration::from_millis(200)).await;
19    let response = device.request_data(AttributeSet::single(Attribute::AdcQueue)).await;
20    device.stop_graph_mode().await?;
21
22    let response = response?;
23    let samples = response.get_adc_queue().map(|queue| queue.samples.len()).unwrap_or(0);
24    println!("AdcQueue samples received after level-2 auth: {samples}");
25
26    Ok(())
27}
Source

pub async fn request_adc_data(&mut self) -> Result<AdcDataSimple, KMError>

Request ADC data only

Source

pub async fn request_settings(&mut self) -> Result<Settings, KMError>

Request and parse the two read-only settings blocks.

Examples found in repository?
examples/settings.rs (line 8)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let settings = device.request_settings().await?;
9
10    println!("Device name: {}", settings.device_name().unwrap_or("<invalid UTF-8>"));
11    println!("Language selection: {}", settings.language_selection());
12    println!("Uncalibrated: {}", settings.is_uncalibrated());
13    println!("Brightness: {}%", settings.brightness().get::<percent>());
14    println!(
15        "Sample interval: {} us",
16        settings.sample_interval().get::<microsecond>()
17    );
18    println!("Screen orientation: {}", settings.screen_orientation());
19    println!("Mtools device mode: {}", settings.mtools_device_mode());
20    println!("Selected main page: {}", settings.selected_main_page());
21
22    Ok(())
23}
Source

pub async fn request_pd_trace(&mut self) -> Result<PdTrace, KMError>

Request and drain the internal USB PD state-machine trace queues.

Source

pub async fn request_log_metadata( &mut self, ) -> Result<Vec<LogMetadata>, KMError>

Request metadata for the offline log currently selected on the device.

Returns an empty vector when no offline log is available.

Source

pub async fn download_offline_log( &mut self, metadata: LogMetadata, ) -> Result<OfflineLog, KMError>

Download the offline samples described by previously requested metadata.

Source

pub async fn request_pd_data(&mut self) -> Result<Packet, KMError>

Request PD data (returns full packet as it can contain PdStatus OR PdEventStream)

The response depends on the device state:

  • If payload is 12 bytes: returns PdStatus (use packet.get_pd_status())
  • If payload > 12 bytes: returns PdEventStream (use packet.get_pd_events())

Use request_adc_with_pd() to get ADC + PdStatus together (68 bytes).

Source

pub async fn request_adc_with_pd(&mut self) -> Result<Packet, KMError>

Request both ADC and PD data in a single request

This typically returns ADC + PdStatus (68 bytes total). Use packet.get_adc() and packet.get_pd_status() to extract data.

Source

pub fn extract_pd_status(packet: &Packet) -> Option<&PdStatus>

Helper: Try to get PD status from a packet

Returns Some if the packet contains PdStatus (12-byte payload), None otherwise. Useful after request_pd_data() or request_adc_with_pd().

Source

pub fn extract_pd_events(packet: &Packet) -> Option<&PdEventStream>

Helper: Try to get PD event stream from a packet

Returns Some if the packet contains PdEventStream (>12 bytes), None otherwise. Useful after request_pd_data().

Source

pub async fn enable_pd_monitor(&mut self) -> Result<(), KMError>

Enable PD monitor/sniffer

Sends the EnablePdMonitor command (0x10) to start capturing PD events. This may be required before request_pd_data() returns PD events, especially in Basic mode (HID interface).

Returns Ok(()) on Accept, error otherwise.

Source

pub async fn disable_pd_monitor(&mut self) -> Result<(), KMError>

Disable PD monitor/sniffer

Sends the DisablePdMonitor command (0x11) to stop capturing PD events.

Returns Ok(()) on Accept, error otherwise.

Source

pub fn mode(&self) -> &ConnectionMode

Get the current connection mode

  • ConnectionMode::Basic - HID interface, ADC/PD polling only
  • ConnectionMode::Full(state) - Vendor interface, all features
Source

pub fn is_full_mode(&self) -> bool

Check if device is in full mode (vendor interface)

Source

pub fn is_basic_mode(&self) -> bool

Check if device is in basic mode (HID interface)

Source

pub fn state(&self) -> Option<&DeviceState>

Get device state (only available in Full mode)

Returns Some when connected via vendor interface (Full mode). Returns None when connected via HID interface (Basic mode).

Examples found in repository?
examples/calibration_auth.rs (line 8)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let initial_level = device.state().map(|state| state.auth_level).unwrap_or(0);
9    println!("Initial authentication level: {initial_level}");
10
11    let result = device.authenticate_calibration().await?;
12    println!(
13        "Calibration authentication level: {} (attribute 0x{:04x})",
14        result.auth_level, result.attribute
15    );
16
17    device.start_graph_mode(GraphSampleRate::Sps50).await?;
18    tokio::time::sleep(Duration::from_millis(200)).await;
19    let response = device.request_data(AttributeSet::single(Attribute::AdcQueue)).await;
20    device.stop_graph_mode().await?;
21
22    let response = response?;
23    let samples = response.get_adc_queue().map(|queue| queue.samples.len()).unwrap_or(0);
24    println!("AdcQueue samples received after level-2 auth: {samples}");
25
26    Ok(())
27}
Source

pub fn is_authenticated(&self) -> bool

Check if device is authenticated (Full mode only)

Returns false in Basic mode or if authentication failed.

Source

pub fn adcqueue_enabled(&self) -> bool

Check if AdcQueue streaming is enabled (Full mode only)

Returns false in Basic mode or if not authenticated for streaming.

Source

pub async fn authenticate_calibration( &mut self, ) -> Result<StreamingAuthResult, KMError>

Authenticate with the device’s calibration credential and enter auth level 2.

KM003C firmware V1.9.9 prefers the first 12 bytes at 0x03000D80. When that record starts with 0xFFFFFFFF, it falls back to 0x03000C00. This method only reads those known addresses and sends a StreamingAuth request; it does not write settings or flash memory.

Examples found in repository?
examples/calibration_auth.rs (line 11)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let initial_level = device.state().map(|state| state.auth_level).unwrap_or(0);
9    println!("Initial authentication level: {initial_level}");
10
11    let result = device.authenticate_calibration().await?;
12    println!(
13        "Calibration authentication level: {} (attribute 0x{:04x})",
14        result.auth_level, result.attribute
15    );
16
17    device.start_graph_mode(GraphSampleRate::Sps50).await?;
18    tokio::time::sleep(Duration::from_millis(200)).await;
19    let response = device.request_data(AttributeSet::single(Attribute::AdcQueue)).await;
20    device.stop_graph_mode().await?;
21
22    let response = response?;
23    let samples = response.get_adc_queue().map(|queue| queue.samples.len()).unwrap_or(0);
24    println!("AdcQueue samples received after level-2 auth: {samples}");
25
26    Ok(())
27}
Source

pub async fn read_memory_block( &mut self, address: u32, size: u32, ) -> Result<Vec<u8>, KMError>

Read an encrypted memory block from the device

Sends a MemoryRead request, receives the confirmation, then receives and decrypts the actual data. Returns exactly size decrypted bytes; AES block padding received from the device is removed.

§Errors

The encrypted payload arrives unframed, so a response still in flight from another request would be consumed as ciphertext. AdcQueue streaming must therefore be stopped first; calling this while graph mode is active returns an error instead of corrupting the read.

Source

pub async fn get_device_info(&mut self) -> Result<DeviceInfo, KMError>

Read device information from memory

Reads and parses:

  • DeviceInfo1 (0x420): model, hw_version, mfg_date
  • FirmwareInfo (0x4420): fw_version, fw_date
  • CalibrationData (0x3000C00): serial_id, uuid

Note: Requires Connect to be sent first. Only works in Full mode. When using DeviceConfig::vendor(), this info is already available via state().

§Example
// In Full mode, device info is already available
let device = KM003C::new(DeviceConfig::vendor()).await?;
println!("Model: {}", device.state().unwrap().model());
Source

pub async fn start_graph_mode( &mut self, rate: GraphSampleRate, ) -> Result<(), KMError>

Start AdcQueue graph/streaming mode with specified sample rate

§Arguments
  • rate - Sample rate (use GraphSampleRate enum)

Requires Full mode (vendor interface) and successful StreamingAuth.

Rate values:

  • GraphSampleRate::Sps2 = 2 samples per second
  • GraphSampleRate::Sps10 = 10 SPS
  • GraphSampleRate::Sps50 = 50 SPS
  • GraphSampleRate::Sps1000 = 1000 SPS

After calling this, poll with request_data(AttributeSet::single(Attribute::AdcQueue)) to receive buffered samples.

§Example
let mut device = KM003C::new(DeviceConfig::vendor()).await?;

// Start 1000 SPS streaming
device.start_graph_mode(GraphSampleRate::Sps1000).await?;

// Poll for data...

// Stop when done
device.stop_graph_mode().await?;
Examples found in repository?
examples/calibration_auth.rs (line 17)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let initial_level = device.state().map(|state| state.auth_level).unwrap_or(0);
9    println!("Initial authentication level: {initial_level}");
10
11    let result = device.authenticate_calibration().await?;
12    println!(
13        "Calibration authentication level: {} (attribute 0x{:04x})",
14        result.auth_level, result.attribute
15    );
16
17    device.start_graph_mode(GraphSampleRate::Sps50).await?;
18    tokio::time::sleep(Duration::from_millis(200)).await;
19    let response = device.request_data(AttributeSet::single(Attribute::AdcQueue)).await;
20    device.stop_graph_mode().await?;
21
22    let response = response?;
23    let samples = response.get_adc_queue().map(|queue| queue.samples.len()).unwrap_or(0);
24    println!("AdcQueue samples received after level-2 auth: {samples}");
25
26    Ok(())
27}
Source

pub async fn stop_graph_mode(&mut self) -> Result<(), KMError>

Stop AdcQueue graph/streaming mode

Requires Full mode (vendor interface). Will return error in Basic mode.

Returns device to normal ADC polling mode.

Examples found in repository?
examples/calibration_auth.rs (line 20)
6async fn main() -> Result<(), Box<dyn std::error::Error>> {
7    let mut device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
8    let initial_level = device.state().map(|state| state.auth_level).unwrap_or(0);
9    println!("Initial authentication level: {initial_level}");
10
11    let result = device.authenticate_calibration().await?;
12    println!(
13        "Calibration authentication level: {} (attribute 0x{:04x})",
14        result.auth_level, result.attribute
15    );
16
17    device.start_graph_mode(GraphSampleRate::Sps50).await?;
18    tokio::time::sleep(Duration::from_millis(200)).await;
19    let response = device.request_data(AttributeSet::single(Attribute::AdcQueue)).await;
20    device.stop_graph_mode().await?;
21
22    let response = response?;
23    let samples = response.get_adc_queue().map(|queue| queue.samples.len()).unwrap_or(0);
24    println!("AdcQueue samples received after level-2 auth: {samples}");
25
26    Ok(())
27}

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more