Skip to main content

km003c_lib/
device.rs

1//! KM003C USB device communication
2//!
3//! # KM003C USB Interfaces
4//!
5//! The KM003C provides multiple USB interfaces for communication.
6//! All interfaces support the same protocol commands (4-byte packets).
7//!
8//! ## Interface 0: Vendor Specific (Primary Protocol)
9//! - **Transfer Type**: Bulk
10//! - **Endpoints**: 0x01 OUT, 0x81 IN
11//! - **Max Packet Size**: 64 bytes
12//! - **Throughput**: ~200 KB/s (official spec)
13//! - **Latency**: ~0.6 ms (measured)
14//! - **Linux Driver**: `powerz` (hwmon)
15//! - **Use Case**: Best performance, same as kernel driver
16//! - **Note**: Requires detaching kernel driver on Linux
17//!
18//! ## Interface 1+2: CDC (Virtual Serial Port)
19//! - **Transfer Type**: Bulk (Interface 2) + Interrupt (Interface 1)
20//! - **Endpoints**: 0x02 OUT, 0x82 IN (data), 0x83 IN (control)
21//! - **Throughput**: ~200 KB/s (official spec)
22//! - **Linux Driver**: `cdc_acm`
23//! - **Use Case**: Serial port compatibility
24//! - **Note**: Not currently implemented in this library
25//!
26//! ## Interface 3: HID (Human Interface Device)
27//! - **Transfer Type**: Interrupt
28//! - **Endpoints**: 0x05 OUT, 0x85 IN
29//! - **Max Packet Size**: 64 bytes
30//! - **Throughput**: ~60 KB/s (official spec)
31//! - **Latency**: ~3.8 ms (measured)
32//! - **Linux Driver**: `usbhid`
33//! - **Use Case**: Most compatible, no driver installation needed
34//! - **Note**: Works on all platforms without custom drivers
35//!
36//! ## Performance Comparison
37//! Based on real device measurements:
38//! - **Interface 0 (Bulk)**: 0.6 ms latency - **6x faster** ⚡
39//! - **Interface 3 (Interrupt)**: 3.8 ms latency - most compatible
40//!
41//! ## Recommendation
42//! - Use **Interface 0** for performance-critical applications (same as kernel driver)
43//! - Use **Interface 3** for maximum compatibility across platforms
44
45use crate::adc::AdcDataSimple;
46use crate::adcqueue::GraphSampleRate;
47use crate::auth::{DeviceInfo, HardwareId};
48use crate::error::KMError;
49use crate::message::Packet;
50use crate::packet::{Attribute, AttributeSet, PacketType, RawPacket};
51use crate::pd::{PdEventStream, PdStatus};
52use bytes::Bytes;
53use nusb::Interface;
54use nusb::io::{EndpointRead, EndpointWrite};
55use nusb::transfer::{Bulk, Interrupt};
56use std::collections::VecDeque;
57use std::fmt;
58use std::time::Duration;
59use tokio::io::{AsyncReadExt, AsyncWriteExt};
60use tokio::time::timeout;
61use tracing::{debug, info, trace, warn};
62
63/// Device state populated by initialization
64///
65/// Contains all information gathered during device init:
66/// - Device info (model, versions, serials)
67/// - Hardware ID (used for authentication)
68/// - Authentication state
69#[derive(Debug, Clone)]
70pub struct DeviceState {
71    /// Device information (model, firmware, serials, etc.)
72    pub info: DeviceInfo,
73    /// Hardware ID used for authentication
74    pub hardware_id: HardwareId,
75    /// Authentication level (0 = not authenticated, 1+ = authenticated)
76    pub auth_level: u8,
77    /// Whether AdcQueue streaming is enabled
78    pub adcqueue_enabled: bool,
79}
80
81impl DeviceState {
82    /// Check if device is authenticated
83    pub fn is_authenticated(&self) -> bool {
84        self.auth_level > 0
85    }
86
87    /// Get device model name
88    pub fn model(&self) -> &str {
89        &self.info.model
90    }
91
92    /// Get firmware version
93    pub fn firmware_version(&self) -> &str {
94        &self.info.fw_version
95    }
96}
97
98impl fmt::Display for DeviceState {
99    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100        const W: usize = 58; // inner width between │ and │
101
102        // Helper to create a content line with label and value
103        fn line(label: &str, value: &str) -> String {
104            let content = format!(" {}: {}", label, value);
105            format!("│{:<W$}│", content, W = W)
106        }
107
108        let sep = format!("├{:─<W$}┤", "", W = W);
109
110        writeln!(f, "┌{:─<W$}┐", "", W = W)?;
111        writeln!(f, "│{:^W$}│", "POWER-Z KM003C Device Information", W = W)?;
112        writeln!(f, "{}", sep)?;
113        writeln!(f, "{}", line("Model", &self.info.model))?;
114        writeln!(f, "{}", line("Hardware Version", &self.info.hw_version))?;
115        writeln!(f, "{}", line("Mfg Date", &self.info.mfg_date))?;
116        writeln!(f, "{}", sep)?;
117        writeln!(f, "{}", line("Firmware Version", &self.info.fw_version))?;
118        writeln!(f, "{}", line("Firmware Date", &self.info.fw_date))?;
119        writeln!(f, "{}", sep)?;
120        writeln!(f, "{}", line("Serial ID", &self.info.serial_id))?;
121        writeln!(f, "{}", line("UUID", &self.info.uuid))?;
122        writeln!(f, "{}", line("Hardware ID", &format!("{}", self.hardware_id)))?;
123        writeln!(f, "{}", sep)?;
124        writeln!(f, "{}", line("Auth Level", &format!("{}", self.auth_level)))?;
125        writeln!(f, "{}", line("AdcQueue Enabled", &format!("{}", self.adcqueue_enabled)))?;
126        write!(f, "└{:─<W$}┘", "", W = W)
127    }
128}
129
130/// USB device identification constants
131pub const VID: u16 = 0x5FC9; // ChargerLAB vendor ID
132pub const PID: u16 = 0x0063; // KM003C product ID
133
134/// Interface 0 (Vendor Specific): Bulk transfers, fastest (~0.6ms)
135pub const INTERFACE_VENDOR: u8 = 0;
136pub const ENDPOINT_OUT_VENDOR: u8 = 0x01;
137pub const ENDPOINT_IN_VENDOR: u8 = 0x81;
138
139/// Interface 3 (HID): Interrupt transfers, most compatible (~3.8ms)
140pub const INTERFACE_HID: u8 = 3;
141pub const ENDPOINT_OUT_HID: u8 = 0x05;
142pub const ENDPOINT_IN_HID: u8 = 0x85;
143
144// Default timeout for USB operations
145const DEFAULT_TIMEOUT: Duration = Duration::from_secs(2);
146const BULK_TRANSFER_SIZE: usize = 2048;
147const INTERRUPT_TRANSFER_SIZE: usize = 64;
148const MAX_PENDING_RESPONSES: usize = 256;
149const AES_BLOCK_SIZE: usize = 16;
150
151fn parse_framed_response(bytes: &[u8]) -> Option<RawPacket> {
152    RawPacket::try_from(Bytes::copy_from_slice(bytes)).ok()
153}
154
155fn response_matches(bytes: &[u8], id: u8, packet_type: PacketType) -> bool {
156    parse_framed_response(bytes).is_some_and(|packet| packet.id() == id && packet.packet_type() == packet_type)
157}
158
159fn response_type_matches(bytes: &[u8], packet_type: PacketType) -> bool {
160    parse_framed_response(bytes).is_some_and(|packet| packet.packet_type() == packet_type)
161}
162
163fn control_response_matches(bytes: &[u8], id: u8) -> bool {
164    matches!(
165        parse_framed_response(bytes),
166        Some(RawPacket::Ctrl { header, .. }) if header.id() == id
167    )
168}
169
170fn memory_confirmation_matches(bytes: &[u8], id: u8) -> bool {
171    parse_framed_response(bytes).is_some_and(|packet| {
172        packet.id() == id
173            && matches!(
174                packet.packet_type(),
175                PacketType::MemoryRead | PacketType::Rejected | PacketType::NotReadable
176            )
177    })
178}
179
180fn memory_response_size(requested_size: u32) -> usize {
181    (requested_size as usize).div_ceil(AES_BLOCK_SIZE) * AES_BLOCK_SIZE
182}
183
184/// Transfer type for USB communication
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum TransferType {
187    Bulk,
188    Interrupt,
189}
190
191/// Connection mode determined by interface type
192///
193/// - **Basic**: HID interface - ADC/PD polling only, no initialization needed
194/// - **Full**: Vendor interface - all features including AdcQueue streaming
195#[derive(Debug, Clone)]
196pub enum ConnectionMode {
197    /// Basic mode (HID interface) - ADC/PD polling only
198    Basic,
199    /// Full mode (Vendor interface) - all features, includes device state
200    Full(DeviceState),
201}
202
203/// Configuration for KM003C device communication
204///
205/// Specifies which USB interface to use. The interface determines the connection mode:
206/// - **Vendor** (Interface 0): Full mode - all features including AdcQueue
207/// - **HID** (Interface 3): Basic mode - ADC/PD polling only
208///
209/// # Examples
210///
211/// ```no_run
212/// use km003c_lib::{KM003C, DeviceConfig};
213///
214/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
215/// // Full mode with vendor interface (fastest, all features)
216/// let device = KM003C::new(DeviceConfig::vendor()).await?;
217///
218/// // Basic mode with HID interface (most compatible, ADC/PD only)
219/// let device = KM003C::new(DeviceConfig::hid()).await?;
220///
221/// // With options
222/// let device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
223/// # Ok(())
224/// # }
225/// ```
226#[derive(Debug, Clone, Copy)]
227pub struct DeviceConfig {
228    /// USB interface number (0 or 3)
229    interface: u8,
230    /// OUT endpoint address
231    endpoint_out: u8,
232    /// IN endpoint address
233    endpoint_in: u8,
234    /// Transfer type (Bulk or Interrupt)
235    transfer_type: TransferType,
236    /// Skip initial USB reset
237    skip_reset: bool,
238}
239
240impl DeviceConfig {
241    /// Vendor interface (Interface 0) - Full mode with all features
242    ///
243    /// This is the **fastest option** with ~0.6ms latency (6x faster than HID).
244    /// Enables all features including AdcQueue streaming.
245    ///
246    /// **Performance**:
247    /// - Latency: ~600 µs per request
248    /// - Throughput: ~200 KB/s
249    ///
250    /// **Features**: Full mode - ADC, PD, AdcQueue, device info, authentication
251    ///
252    /// **Note**: On Linux, requires detaching the `powerz` kernel driver,
253    /// which this library handles automatically.
254    pub fn vendor() -> Self {
255        Self {
256            interface: INTERFACE_VENDOR,
257            endpoint_out: ENDPOINT_OUT_VENDOR,
258            endpoint_in: ENDPOINT_IN_VENDOR,
259            transfer_type: TransferType::Bulk,
260            skip_reset: false,
261        }
262    }
263
264    /// HID interface (Interface 3) - Basic mode for ADC/PD polling
265    ///
266    /// This is the **most compatible option** that works on all platforms
267    /// without custom drivers. Only supports basic ADC and PD polling.
268    ///
269    /// **Performance**:
270    /// - Latency: ~3.8 ms per request
271    /// - Throughput: ~60 KB/s
272    ///
273    /// **Features**: Basic mode - ADC and PD polling only (no AdcQueue, no device info)
274    ///
275    /// **Advantages**:
276    /// - Works without driver installation on Windows/Mac/Linux
277    /// - Uses standard HID protocol
278    /// - No permission issues
279    pub fn hid() -> Self {
280        Self {
281            interface: INTERFACE_HID,
282            endpoint_out: ENDPOINT_OUT_HID,
283            endpoint_in: ENDPOINT_IN_HID,
284            transfer_type: TransferType::Interrupt,
285            skip_reset: false,
286        }
287    }
288
289    /// Skip USB reset during connection
290    ///
291    /// Some systems (particularly MacOS) may have issues with USB reset.
292    /// Use this if device connection fails with reset errors.
293    pub fn skip_reset(mut self) -> Self {
294        self.skip_reset = true;
295        self
296    }
297
298    /// Check if this config uses vendor interface (full mode)
299    pub fn is_vendor(&self) -> bool {
300        self.interface == INTERFACE_VENDOR
301    }
302
303    /// Check if this config uses HID interface (basic mode)
304    pub fn is_hid(&self) -> bool {
305        self.interface == INTERFACE_HID
306    }
307}
308
309/// Endpoint reader wrapper to handle both Bulk and Interrupt types
310enum EndpointReaderType {
311    Bulk(EndpointRead<Bulk>),
312    Interrupt(EndpointRead<Interrupt>),
313}
314
315/// Endpoint writer wrapper to handle both Bulk and Interrupt types
316enum EndpointWriterType {
317    Bulk(EndpointWrite<Bulk>),
318    Interrupt(EndpointWrite<Interrupt>),
319}
320
321pub struct KM003C {
322    /// Kept alive for RAII - dropping this releases the USB interface claim
323    #[allow(dead_code)]
324    interface: Interface,
325    transaction_id: u8,
326    reader: EndpointReaderType,
327    writer: EndpointWriterType,
328    pending_responses: VecDeque<Vec<u8>>,
329    /// Rate currently selected with StartGraph, used to decode rate-dependent fields.
330    graph_sample_rate: Option<GraphSampleRate>,
331    /// Connection mode: Basic (HID) or Full (Vendor with device state)
332    mode: ConnectionMode,
333}
334
335impl KM003C {
336    /// Connect to device with the given configuration
337    ///
338    /// The config determines the connection mode:
339    /// - **`DeviceConfig::vendor()`**: Full mode - runs init sequence, all features enabled
340    /// - **`DeviceConfig::hid()`**: Basic mode - no init, ADC/PD polling only
341    ///
342    /// # Examples
343    ///
344    /// ```no_run
345    /// use km003c_lib::{KM003C, DeviceConfig};
346    ///
347    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
348    /// // Full mode (vendor interface) - all features
349    /// let device = KM003C::new(DeviceConfig::vendor()).await?;
350    /// println!("Model: {}", device.state().unwrap().model());
351    /// println!("AdcQueue enabled: {}", device.adcqueue_enabled());
352    ///
353    /// // Basic mode (HID interface) - ADC/PD polling only
354    /// let mut device = KM003C::new(DeviceConfig::hid()).await?;
355    /// let adc = device.request_adc_data().await?;
356    ///
357    /// // With options
358    /// let device = KM003C::new(DeviceConfig::vendor().skip_reset()).await?;
359    /// # Ok(())
360    /// # }
361    /// ```
362    pub async fn new(config: DeviceConfig) -> Result<Self, KMError> {
363        let mut device = Self::connect(config).await?;
364
365        if config.is_vendor() {
366            // Full mode: run init sequence
367            device.run_init().await?;
368        }
369        // HID mode: stays in Basic mode, no init needed
370
371        Ok(device)
372    }
373
374    /// Internal: Connect to USB device without initialization
375    async fn connect(config: DeviceConfig) -> Result<Self, KMError> {
376        info!("Searching for POWER-Z KM003C...");
377        let device_info = nusb::list_devices()
378            .await?
379            .find(|d| d.vendor_id() == VID && d.product_id() == PID)
380            .ok_or(KMError::DeviceNotFound)?;
381
382        info!(
383            "Found device on bus {} addr {}",
384            device_info.bus_id(),
385            device_info.device_address()
386        );
387
388        let mut device = device_info.open().await?;
389
390        // Optionally reset device (skip on MacOS if having issues)
391        if !config.skip_reset {
392            info!("Resetting device...");
393            device.reset().await?;
394            // CRITICAL: Device needs 1.5 seconds to fully initialize after reset
395            // (validated through protocol research - 100ms is insufficient for AdcQueue)
396            tokio::time::sleep(Duration::from_millis(1500)).await;
397            // Re-enumerate and reopen after reset (old handle may be invalid).
398            let device_info = nusb::list_devices()
399                .await?
400                .find(|d| d.vendor_id() == VID && d.product_id() == PID)
401                .ok_or(KMError::DeviceNotFound)?;
402            device = device_info.open().await?;
403        } else {
404            debug!("Skipping USB reset (skip_reset=true)");
405        }
406
407        // Detach kernel drivers from ALL interfaces
408        // All 4 interfaces have kernel drivers on Linux:
409        //   Interface 0: powerz (hwmon)
410        //   Interface 1+2: cdc_acm (serial)
411        //   Interface 3: usbhid (HID generic)
412        // Detaching all prevents re-binding and ensures clean access
413        for interface_num in 0..4 {
414            if let Err(e) = device.detach_kernel_driver(interface_num) {
415                // Ignore errors - driver may already be detached
416                trace!("Could not detach interface {}: {}", interface_num, e);
417            } else {
418                debug!("Detached kernel driver from interface {}", interface_num);
419            }
420        }
421
422        // Claim the configured interface
423        let interface = device.claim_interface(config.interface).await?;
424        info!("Interface {} claimed successfully", config.interface);
425
426        // Create persistent endpoints based on transfer type
427        // Using 4 concurrent transfers for better throughput
428        // A bulk response can span multiple 2048-byte transfers. receive_raw()
429        // joins transfers until the device terminates the response with a short packet.
430        let (reader, writer) = match config.transfer_type {
431            TransferType::Bulk => {
432                let ep_in = interface.endpoint::<Bulk, _>(config.endpoint_in)?;
433                let ep_out = interface.endpoint::<Bulk, _>(config.endpoint_out)?;
434                (
435                    EndpointReaderType::Bulk(ep_in.reader(BULK_TRANSFER_SIZE).with_num_transfers(4)),
436                    EndpointWriterType::Bulk(ep_out.writer(INTERRUPT_TRANSFER_SIZE).with_num_transfers(4)),
437                )
438            }
439            TransferType::Interrupt => {
440                let ep_in = interface.endpoint::<Interrupt, _>(config.endpoint_in)?;
441                let ep_out = interface.endpoint::<Interrupt, _>(config.endpoint_out)?;
442                (
443                    EndpointReaderType::Interrupt(ep_in.reader(INTERRUPT_TRANSFER_SIZE).with_num_transfers(4)),
444                    EndpointWriterType::Interrupt(ep_out.writer(INTERRUPT_TRANSFER_SIZE).with_num_transfers(4)),
445                )
446            }
447        };
448
449        let km003c = Self {
450            interface,
451            transaction_id: 0,
452            reader,
453            writer,
454            pending_responses: VecDeque::new(),
455            graph_sample_rate: None,
456            mode: ConnectionMode::Basic,
457        };
458
459        info!("USB connection established");
460        Ok(km003c)
461    }
462
463    /// Get the next transaction ID (internal use)
464    fn next_transaction_id(&mut self) -> u8 {
465        let id = self.transaction_id;
466        self.transaction_id = self.transaction_id.wrapping_add(1);
467        id
468    }
469
470    /// Get the next transaction ID (for auth module and external use)
471    pub fn next_tid(&mut self) -> u8 {
472        self.next_transaction_id()
473    }
474
475    /// Set the transaction ID counter (for protocol research/sync purposes)
476    pub fn set_transaction_id(&mut self, id: u8) {
477        self.transaction_id = id;
478    }
479
480    /// Send a high-level packet to the device
481    pub async fn send(&mut self, packet: Packet) -> Result<(), KMError> {
482        self.send_tracked(packet).await.map(|_| ())
483    }
484
485    /// Send a high-level packet and return the transaction ID placed on the wire.
486    async fn send_tracked(&mut self, packet: Packet) -> Result<u8, KMError> {
487        use crate::auth;
488
489        let id = self.next_transaction_id();
490
491        // Special handling for auth packets that need custom wire format
492        // (MemoryRead and StreamingAuth use a different header layout than standard packets)
493        match &packet {
494            Packet::MemoryRead { address, size } => {
495                let raw = auth::build_memory_read_packet(*address, *size, id);
496                self.send_raw(&raw).await?;
497                return Ok(id);
498            }
499            Packet::StreamingAuth { hardware_id } => {
500                let raw = auth::build_streaming_auth_packet(hardware_id, id);
501                self.send_raw(&raw).await?;
502                return Ok(id);
503            }
504            _ => {}
505        }
506
507        let raw_packet = packet.to_raw_packet(id)?;
508        self.send_raw_packet(raw_packet).await?;
509        Ok(id)
510    }
511
512    /// Send a raw packet to the device
513    async fn send_raw_packet(&mut self, packet: RawPacket) -> Result<(), KMError> {
514        let (reserved_flag, has_logical_packets) = match &packet {
515            RawPacket::Ctrl { header, .. } => (header.reserved_flag(), false),
516            RawPacket::SimpleData { header, .. } => (header.reserved_flag(), false),
517            RawPacket::Data {
518                header,
519                logical_packets,
520            } => (header.reserved_flag(), !logical_packets.is_empty()),
521        };
522
523        debug!(
524            "Sending packet: type={:?}, reserved_flag={}, has_logical_packets={}, id={}",
525            packet.packet_type(),
526            reserved_flag,
527            has_logical_packets,
528            packet.id(),
529        );
530
531        let message_bytes = Bytes::from(packet);
532        let message = message_bytes.to_vec();
533        trace!("TX [{} bytes]: {:02x?}", message.len(), message);
534
535        // Use the persistent writer
536        match &mut self.writer {
537            EndpointWriterType::Bulk(writer) => {
538                timeout(DEFAULT_TIMEOUT, writer.write_all(&message)).await??;
539                timeout(DEFAULT_TIMEOUT, writer.flush_end_async()).await??;
540            }
541            EndpointWriterType::Interrupt(writer) => {
542                timeout(DEFAULT_TIMEOUT, writer.write_all(&message)).await??;
543                timeout(DEFAULT_TIMEOUT, writer.flush_end_async()).await??;
544            }
545        }
546
547        debug!("Sent successfully");
548        Ok(())
549    }
550
551    /// Send raw bytes to the device (for protocol research/testing)
552    pub async fn send_raw(&mut self, data: &[u8]) -> Result<(), KMError> {
553        trace!("TX [{} bytes]: {:02x?}", data.len(), data);
554        match &mut self.writer {
555            EndpointWriterType::Bulk(writer) => {
556                timeout(DEFAULT_TIMEOUT, writer.write_all(data)).await??;
557                timeout(DEFAULT_TIMEOUT, writer.flush_end_async()).await??;
558            }
559            EndpointWriterType::Interrupt(writer) => {
560                timeout(DEFAULT_TIMEOUT, writer.write_all(data)).await??;
561                timeout(DEFAULT_TIMEOUT, writer.flush_end_async()).await??;
562            }
563        }
564        Ok(())
565    }
566
567    /// Read one complete response directly from the USB endpoint.
568    async fn read_raw_from_usb(&mut self) -> Result<Vec<u8>, KMError> {
569        let mut buffer = Vec::new();
570        let bytes_read = match &mut self.reader {
571            EndpointReaderType::Bulk(reader) => {
572                // Bulk messages are delimited by a short USB packet. Reading through
573                // this adapter preserves one complete protocol response even when it
574                // is larger than either the caller's buffer or one 2048-byte transfer.
575                let mut message = reader.until_short_packet();
576                let bytes_read = timeout(DEFAULT_TIMEOUT, message.read_to_end(&mut buffer)).await??;
577                message
578                    .consume_end()
579                    .map_err(|_| KMError::Protocol("Bulk response ended without a short packet".to_string()))?;
580                bytes_read
581            }
582            EndpointReaderType::Interrupt(reader) => {
583                buffer.resize(INTERRUPT_TRANSFER_SIZE, 0);
584                let bytes_read = timeout(DEFAULT_TIMEOUT, reader.read(&mut buffer)).await??;
585                buffer.truncate(bytes_read);
586                bytes_read
587            }
588        };
589        trace!("RX [{} bytes]: {:02x?}", bytes_read, buffer);
590        Ok(buffer)
591    }
592
593    fn queue_pending_response(&mut self, response: Vec<u8>) {
594        if self.pending_responses.len() == MAX_PENDING_RESPONSES {
595            warn!("Dropping oldest unmatched response because the pending queue is full");
596            self.pending_responses.pop_front();
597        }
598        self.pending_responses.push_back(response);
599    }
600
601    async fn receive_matching_raw<F>(&mut self, mut predicate: F) -> Result<Vec<u8>, KMError>
602    where
603        F: FnMut(&[u8]) -> bool,
604    {
605        if let Some(index) = self.pending_responses.iter().position(|response| predicate(response)) {
606            return Ok(self
607                .pending_responses
608                .remove(index)
609                .expect("pending response index is valid"));
610        }
611
612        let deadline = tokio::time::Instant::now() + DEFAULT_TIMEOUT;
613        loop {
614            let remaining = deadline.saturating_duration_since(tokio::time::Instant::now());
615            if remaining.is_zero() {
616                return Err(KMError::Protocol(
617                    "Timed out waiting for a correlated response".to_string(),
618                ));
619            }
620
621            let response = timeout(remaining, self.read_raw_from_usb()).await??;
622            if predicate(&response) {
623                return Ok(response);
624            }
625
626            if let Some(packet) = parse_framed_response(&response) {
627                debug!(
628                    "Queueing unmatched response: type={:?}, id={}, len={}",
629                    packet.packet_type(),
630                    packet.id(),
631                    response.len()
632                );
633            } else {
634                debug!("Queueing unframed response: len={}", response.len());
635            }
636            self.queue_pending_response(response);
637        }
638    }
639
640    /// Receive raw bytes from the device (for protocol research/testing).
641    /// Previously received out-of-order responses are returned first.
642    pub async fn receive_raw(&mut self) -> Result<Vec<u8>, KMError> {
643        if let Some(response) = self.pending_responses.pop_front() {
644            return Ok(response);
645        }
646        self.read_raw_from_usb().await
647    }
648
649    fn parse_response(raw_bytes: Vec<u8>, graph_rate: Option<GraphSampleRate>) -> Result<Packet, KMError> {
650        if raw_bytes.is_empty() {
651            return Err(KMError::Protocol("Received 0 bytes".to_string()));
652        }
653
654        let raw_packet = RawPacket::try_from(Bytes::from(raw_bytes))?;
655        if let Some(rate) = graph_rate {
656            Packet::from_raw_with_graph_rate(raw_packet, rate)
657        } else {
658            Packet::try_from(raw_packet)
659        }
660    }
661
662    /// Receive a high-level packet from the device
663    pub async fn receive(&mut self) -> Result<Packet, KMError> {
664        let raw_bytes = self.receive_raw().await?;
665        Self::parse_response(raw_bytes, self.graph_sample_rate)
666    }
667
668    async fn receive_control_response(&mut self, id: u8) -> Result<Packet, KMError> {
669        let raw_bytes = self
670            .receive_matching_raw(|bytes| control_response_matches(bytes, id))
671            .await?;
672        Self::parse_response(raw_bytes, self.graph_sample_rate)
673    }
674
675    async fn expect_accept(&mut self, id: u8, command: &str) -> Result<(), KMError> {
676        match self.receive_control_response(id).await? {
677            Packet::Accept { .. } => Ok(()),
678            Packet::Reject { .. } => Err(KMError::Protocol(format!("{command} was rejected by the device"))),
679            Packet::NotReadable { .. } => Err(KMError::Protocol(format!("{command} is not readable"))),
680            other => Err(KMError::Protocol(format!(
681                "Expected Accept for {command}, got {other:?}"
682            ))),
683        }
684    }
685
686    /// Receive encrypted MemoryRead response data
687    ///
688    /// After sending a MemoryRead request and receiving the confirmation (0xC4),
689    /// the actual data comes as raw encrypted AES blocks. This method
690    /// receives and decrypts that data.
691    ///
692    /// Response size is rounded up to 16-byte boundary (AES block size):
693    /// - 12-byte request → 16-byte response
694    /// - 64-byte request → 64-byte response (4 blocks)
695    pub async fn receive_memory_read_data(&mut self) -> Result<Packet, KMError> {
696        let raw_bytes = self.receive_raw().await?;
697
698        // Response must be multiple of 16 bytes (AES block size)
699        if raw_bytes.is_empty() || !raw_bytes.len().is_multiple_of(AES_BLOCK_SIZE) {
700            return Err(KMError::Protocol(format!(
701                "Expected encrypted data (multiple of 16 bytes), got {} bytes",
702                raw_bytes.len()
703            )));
704        }
705
706        // Decrypt all blocks with MEMORY_READ_KEY
707        let decrypted = crate::auth::aes_ecb_decrypt_blocks(&raw_bytes, crate::auth::MEMORY_READ_KEY)?;
708
709        Ok(Packet::MemoryReadResponse { data: decrypted })
710    }
711
712    async fn receive_memory_read_data_exact(&mut self, requested_size: u32) -> Result<Vec<u8>, KMError> {
713        let expected_size = memory_response_size(requested_size);
714        let raw_bytes = self.read_raw_from_usb().await?;
715        if raw_bytes.len() != expected_size {
716            return Err(KMError::Protocol(format!(
717                "Expected {expected_size} encrypted memory bytes, got {}",
718                raw_bytes.len()
719            )));
720        }
721
722        crate::auth::aes_ecb_decrypt_blocks(&raw_bytes, crate::auth::MEMORY_READ_KEY)
723    }
724
725    /// Request data with a specific attribute set
726    pub async fn request_data(&mut self, mask: AttributeSet) -> Result<Packet, KMError> {
727        let id = self
728            .send_tracked(Packet::GetData {
729                attribute_mask: mask.raw(),
730            })
731            .await?;
732        let raw_bytes = self
733            .receive_matching_raw(|bytes| response_matches(bytes, id, PacketType::PutData))
734            .await?;
735        let raw_packet = RawPacket::try_from(Bytes::from(raw_bytes))?;
736        raw_packet.validate_correlation(mask.raw())?;
737        if let Some(rate) = self.graph_sample_rate {
738            Packet::from_raw_with_graph_rate(raw_packet, rate)
739        } else {
740            Packet::try_from(raw_packet)
741        }
742    }
743
744    /// Request ADC data only
745    pub async fn request_adc_data(&mut self) -> Result<AdcDataSimple, KMError> {
746        let packet = self.request_data(AttributeSet::single(Attribute::Adc)).await?;
747        packet
748            .get_adc()
749            .cloned()
750            .ok_or_else(|| KMError::Protocol("No ADC data in response".to_string()))
751    }
752
753    /// Request PD data (returns full packet as it can contain PdStatus OR PdEventStream)
754    ///
755    /// The response depends on the device state:
756    /// - If payload is 12 bytes: returns PdStatus (use packet.get_pd_status())
757    /// - If payload > 12 bytes: returns PdEventStream (use packet.get_pd_events())
758    ///
759    /// Use `request_adc_with_pd()` to get ADC + PdStatus together (68 bytes).
760    pub async fn request_pd_data(&mut self) -> Result<Packet, KMError> {
761        self.request_data(AttributeSet::single(Attribute::PdPacket)).await
762    }
763
764    /// Request both ADC and PD data in a single request
765    ///
766    /// This typically returns ADC + PdStatus (68 bytes total).
767    /// Use packet.get_adc() and packet.get_pd_status() to extract data.
768    pub async fn request_adc_with_pd(&mut self) -> Result<Packet, KMError> {
769        let mask = AttributeSet::single(Attribute::Adc).with(Attribute::PdPacket);
770        self.request_data(mask).await
771    }
772
773    /// Helper: Try to get PD status from a packet
774    ///
775    /// Returns Some if the packet contains PdStatus (12-byte payload),
776    /// None otherwise. Useful after request_pd_data() or request_adc_with_pd().
777    pub fn extract_pd_status(packet: &Packet) -> Option<&PdStatus> {
778        packet.get_pd_status()
779    }
780
781    /// Helper: Try to get PD event stream from a packet
782    ///
783    /// Returns Some if the packet contains PdEventStream (>12 bytes),
784    /// None otherwise. Useful after request_pd_data().
785    pub fn extract_pd_events(packet: &Packet) -> Option<&PdEventStream> {
786        packet.get_pd_events()
787    }
788
789    /// Enable PD monitor/sniffer
790    ///
791    /// Sends the EnablePdMonitor command (0x10) to start capturing PD events.
792    /// This may be required before `request_pd_data()` returns PD events,
793    /// especially in Basic mode (HID interface).
794    ///
795    /// Returns Ok(()) on Accept, error otherwise.
796    pub async fn enable_pd_monitor(&mut self) -> Result<(), KMError> {
797        let id = self.send_tracked(Packet::EnablePdMonitor).await?;
798        self.expect_accept(id, "EnablePdMonitor").await
799    }
800
801    /// Disable PD monitor/sniffer
802    ///
803    /// Sends the DisablePdMonitor command (0x11) to stop capturing PD events.
804    ///
805    /// Returns Ok(()) on Accept, error otherwise.
806    pub async fn disable_pd_monitor(&mut self) -> Result<(), KMError> {
807        let id = self.send_tracked(Packet::DisablePdMonitor).await?;
808        self.expect_accept(id, "DisablePdMonitor").await
809    }
810
811    /// Internal: Run initialization sequence for vendor interface (Full mode)
812    ///
813    /// Performs the full initialization sequence:
814    /// 1. Connect to device
815    /// 2. Read DeviceInfo (0x420)
816    /// 3. Read FirmwareInfo (0x4420)
817    /// 4. Read Calibration (0x3000C00)
818    /// 5. Read HardwareID (0x40010450)
819    /// 6. StreamingAuth (authenticate for AdcQueue)
820    ///
821    /// After successful init, mode is set to Full(DeviceState).
822    async fn run_init(&mut self) -> Result<(), KMError> {
823        use crate::auth::{HARDWARE_ID_ADDRESS, HARDWARE_ID_SIZE};
824
825        // 1. Connect (with retries - sometimes device responds with Disconnect on first try)
826        const MAX_CONNECT_RETRIES: u8 = 3;
827        let mut last_error = None;
828        for attempt in 1..=MAX_CONNECT_RETRIES {
829            match self.send_tracked(Packet::Connect).await {
830                Err(err) => last_error = Some(format!("Connect send failed: {err:?}")),
831                Ok(id) => match self.expect_accept(id, "Connect").await {
832                    Ok(()) => {
833                        last_error = None;
834                        break;
835                    }
836                    Err(err) => last_error = Some(format!("Connect receive failed: {err:?}")),
837                },
838            }
839            if attempt < MAX_CONNECT_RETRIES {
840                debug!("Connect attempt {} failed, retrying...", attempt);
841                tokio::time::sleep(Duration::from_millis(100)).await;
842            }
843        }
844        if let Some(err) = last_error {
845            return Err(KMError::Protocol(err));
846        }
847
848        let info = self.read_device_info_blocks().await;
849        let mut hardware_id_bytes = [0u8; HARDWARE_ID_SIZE];
850
851        // 5. Read HardwareID
852        let hardware_id = if let Ok(data) = self
853            .read_memory_block(HARDWARE_ID_ADDRESS, HARDWARE_ID_SIZE as u32)
854            .await
855        {
856            if data.len() >= HARDWARE_ID_SIZE {
857                hardware_id_bytes.copy_from_slice(&data[..HARDWARE_ID_SIZE]);
858            }
859            HardwareId::from_bytes(hardware_id_bytes)
860        } else {
861            return Err(KMError::Protocol(
862                "Failed to read HardwareID - required for authentication".to_string(),
863            ));
864        };
865
866        // 6. StreamingAuth
867        self.send_tracked(Packet::StreamingAuth {
868            hardware_id: hardware_id.clone(),
869        })
870        .await?;
871        let auth_response = self
872            // StreamingAuth is the documented exception to normal transaction
873            // correlation: captured device responses always carry ID 0.
874            .receive_matching_raw(|bytes| response_type_matches(bytes, PacketType::StreamingAuth))
875            .await?;
876
877        let auth = match Self::parse_response(auth_response, self.graph_sample_rate)? {
878            Packet::StreamingAuthResponse(result) => result,
879            other => {
880                return Err(KMError::Protocol(format!(
881                    "Expected StreamingAuthResponse, got {:?}",
882                    other
883                )));
884            }
885        };
886
887        // Store device state in Full mode
888        let state = DeviceState {
889            info,
890            hardware_id,
891            auth_level: auth.auth_level,
892            adcqueue_enabled: auth.adcqueue_enabled(),
893        };
894
895        info!("Device initialized: {} (auth_level={})", state.model(), auth.auth_level);
896
897        self.mode = ConnectionMode::Full(state);
898
899        Ok(())
900    }
901
902    /// Get the current connection mode
903    ///
904    /// - `ConnectionMode::Basic` - HID interface, ADC/PD polling only
905    /// - `ConnectionMode::Full(state)` - Vendor interface, all features
906    pub fn mode(&self) -> &ConnectionMode {
907        &self.mode
908    }
909
910    /// Check if device is in full mode (vendor interface)
911    pub fn is_full_mode(&self) -> bool {
912        matches!(self.mode, ConnectionMode::Full(_))
913    }
914
915    /// Check if device is in basic mode (HID interface)
916    pub fn is_basic_mode(&self) -> bool {
917        matches!(self.mode, ConnectionMode::Basic)
918    }
919
920    /// Get device state (only available in Full mode)
921    ///
922    /// Returns `Some` when connected via vendor interface (Full mode).
923    /// Returns `None` when connected via HID interface (Basic mode).
924    pub fn state(&self) -> Option<&DeviceState> {
925        match &self.mode {
926            ConnectionMode::Full(state) => Some(state),
927            ConnectionMode::Basic => None,
928        }
929    }
930
931    /// Check if device is authenticated (Full mode only)
932    ///
933    /// Returns `false` in Basic mode or if authentication failed.
934    pub fn is_authenticated(&self) -> bool {
935        self.state().map(|s| s.is_authenticated()).unwrap_or(false)
936    }
937
938    /// Check if AdcQueue streaming is enabled (Full mode only)
939    ///
940    /// Returns `false` in Basic mode or if not authenticated for streaming.
941    pub fn adcqueue_enabled(&self) -> bool {
942        self.state().map(|s| s.adcqueue_enabled).unwrap_or(false)
943    }
944
945    /// Read an encrypted memory block from the device
946    ///
947    /// Sends a MemoryRead request, receives the confirmation, then receives
948    /// and decrypts the actual data. Returns the decrypted bytes.
949    ///
950    /// Response size is rounded up to 16-byte boundary (AES block size).
951    pub async fn read_memory_block(&mut self, address: u32, size: u32) -> Result<Vec<u8>, KMError> {
952        let id = self.send_tracked(Packet::MemoryRead { address, size }).await?;
953        let confirmation = self
954            .receive_matching_raw(|bytes| memory_confirmation_matches(bytes, id))
955            .await?;
956
957        match Self::parse_response(confirmation, self.graph_sample_rate)? {
958            Packet::Accept { .. } => self.receive_memory_read_data_exact(size).await,
959            Packet::Reject { .. } => Err(KMError::Protocol(format!("MemoryRead at 0x{address:08X} was rejected"))),
960            Packet::NotReadable { .. } => Err(KMError::Protocol(format!(
961                "Memory address 0x{address:08X} is not readable"
962            ))),
963            other => Err(KMError::Protocol(format!(
964                "Expected MemoryRead confirmation, got {other:?}"
965            ))),
966        }
967    }
968
969    /// Read device information from memory
970    ///
971    /// Reads and parses:
972    /// - DeviceInfo1 (0x420): model, hw_version, mfg_date
973    /// - FirmwareInfo (0x4420): fw_version, fw_date
974    /// - CalibrationData (0x3000C00): serial_id, uuid
975    ///
976    /// **Note:** Requires Connect to be sent first. Only works in Full mode.
977    /// When using `DeviceConfig::vendor()`, this info is already available via `state()`.
978    ///
979    /// # Example
980    /// ```no_run
981    /// # use km003c_lib::{KM003C, DeviceConfig};
982    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
983    /// // In Full mode, device info is already available
984    /// let device = KM003C::new(DeviceConfig::vendor()).await?;
985    /// println!("Model: {}", device.state().unwrap().model());
986    /// # Ok(())
987    /// # }
988    /// ```
989    pub async fn get_device_info(&mut self) -> Result<crate::auth::DeviceInfo, KMError> {
990        Ok(self.read_device_info_blocks().await)
991    }
992
993    /// Internal: Read DeviceInfo, FirmwareInfo, and CalibrationData blocks
994    ///
995    /// Used by both `run_init()` and `get_device_info()`. Non-critical reads
996    /// are logged as warnings on failure but don't prevent initialization.
997    async fn read_device_info_blocks(&mut self) -> crate::auth::DeviceInfo {
998        use crate::auth::{
999            CALIBRATION_ADDRESS, DEVICE_INFO_ADDRESS, DeviceInfo, FIRMWARE_INFO_ADDRESS, INFO_BLOCK_SIZE,
1000        };
1001        use tracing::warn;
1002
1003        let mut info = DeviceInfo::default();
1004
1005        match self
1006            .read_memory_block(DEVICE_INFO_ADDRESS, INFO_BLOCK_SIZE as u32)
1007            .await
1008        {
1009            Ok(data) => info.parse_device_info(&data),
1010            Err(e) => warn!("Failed to read DeviceInfo at 0x{:08X}: {}", DEVICE_INFO_ADDRESS, e),
1011        }
1012
1013        match self
1014            .read_memory_block(FIRMWARE_INFO_ADDRESS, INFO_BLOCK_SIZE as u32)
1015            .await
1016        {
1017            Ok(data) => info.parse_firmware_info(&data),
1018            Err(e) => warn!("Failed to read FirmwareInfo at 0x{:08X}: {}", FIRMWARE_INFO_ADDRESS, e),
1019        }
1020
1021        match self
1022            .read_memory_block(CALIBRATION_ADDRESS, INFO_BLOCK_SIZE as u32)
1023            .await
1024        {
1025            Ok(data) => info.parse_calibration(&data),
1026            Err(e) => warn!("Failed to read CalibrationData at 0x{:08X}: {}", CALIBRATION_ADDRESS, e),
1027        }
1028
1029        info
1030    }
1031
1032    /// Start AdcQueue graph/streaming mode with specified sample rate
1033    ///
1034    /// # Arguments
1035    /// * `rate` - Sample rate (use GraphSampleRate enum)
1036    ///
1037    /// **Requires Full mode** (vendor interface). Will return error in Basic mode.
1038    ///
1039    /// Rate values:
1040    /// - `GraphSampleRate::Sps2` = 2 samples per second
1041    /// - `GraphSampleRate::Sps10` = 10 SPS
1042    /// - `GraphSampleRate::Sps50` = 50 SPS
1043    /// - `GraphSampleRate::Sps1000` = 1000 SPS
1044    ///
1045    /// After calling this, poll with `request_data(AttributeSet::single(Attribute::AdcQueue))`
1046    /// to receive buffered samples.
1047    ///
1048    /// # Example
1049    /// ```no_run
1050    /// # use km003c_lib::{KM003C, DeviceConfig, GraphSampleRate};
1051    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
1052    /// let mut device = KM003C::new(DeviceConfig::vendor()).await?;
1053    ///
1054    /// // Start 1000 SPS streaming
1055    /// device.start_graph_mode(GraphSampleRate::Sps1000).await?;
1056    ///
1057    /// // Poll for data...
1058    ///
1059    /// // Stop when done
1060    /// device.stop_graph_mode().await?;
1061    /// # Ok(())
1062    /// # }
1063    /// ```
1064    pub async fn start_graph_mode(&mut self, rate: GraphSampleRate) -> Result<(), KMError> {
1065        if self.is_basic_mode() {
1066            return Err(KMError::Protocol(
1067                "AdcQueue streaming requires Full mode (vendor interface)".to_string(),
1068            ));
1069        }
1070
1071        // Device expects rate index directly: 0=2SPS, 1=10SPS, 2=50SPS, 3=1000SPS
1072        let id = self
1073            .send_tracked(Packet::StartGraph {
1074                rate_index: rate as u16,
1075            })
1076            .await?;
1077        self.expect_accept(id, "StartGraph").await?;
1078        self.graph_sample_rate = Some(rate);
1079        Ok(())
1080    }
1081
1082    /// Stop AdcQueue graph/streaming mode
1083    ///
1084    /// **Requires Full mode** (vendor interface). Will return error in Basic mode.
1085    ///
1086    /// Returns device to normal ADC polling mode.
1087    pub async fn stop_graph_mode(&mut self) -> Result<(), KMError> {
1088        if self.is_basic_mode() {
1089            return Err(KMError::Protocol(
1090                "AdcQueue streaming requires Full mode (vendor interface)".to_string(),
1091            ));
1092        }
1093
1094        let id = self.send_tracked(Packet::StopGraph).await?;
1095        self.expect_accept(id, "StopGraph").await?;
1096        self.graph_sample_rate = None;
1097        Ok(())
1098    }
1099}
1100
1101#[cfg(test)]
1102mod tests {
1103    use super::*;
1104
1105    #[test]
1106    fn response_matching_uses_the_typed_header_parser() {
1107        assert!(response_matches(&[0xc1, 7, 0, 0], 7, PacketType::PutData));
1108    }
1109
1110    #[test]
1111    fn response_matching_requires_type_and_transaction_id() {
1112        assert!(!response_matches(&[0xc1, 8, 0, 0], 7, PacketType::PutData));
1113        assert!(!response_matches(&[0xc0, 7, 0, 0], 7, PacketType::PutData));
1114        assert!(!response_matches(&[0xc1], 7, PacketType::PutData));
1115    }
1116
1117    #[test]
1118    fn response_type_matching_supports_fixed_id_protocol_exceptions() {
1119        assert!(response_type_matches(&[0x4c, 0, 3, 2], PacketType::StreamingAuth));
1120    }
1121
1122    #[test]
1123    fn memory_response_size_rounds_to_complete_aes_blocks() {
1124        assert_eq!(memory_response_size(1), 16);
1125        assert_eq!(memory_response_size(12), 16);
1126        assert_eq!(memory_response_size(16), 16);
1127        assert_eq!(memory_response_size(17), 32);
1128        assert_eq!(memory_response_size(64), 64);
1129    }
1130}