1use 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#[derive(Debug, Clone)]
70pub struct DeviceState {
71 pub info: DeviceInfo,
73 pub hardware_id: HardwareId,
75 pub auth_level: u8,
77 pub adcqueue_enabled: bool,
79}
80
81impl DeviceState {
82 pub fn is_authenticated(&self) -> bool {
84 self.auth_level > 0
85 }
86
87 pub fn model(&self) -> &str {
89 &self.info.model
90 }
91
92 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; 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
130pub const VID: u16 = 0x5FC9; pub const PID: u16 = 0x0063; pub const INTERFACE_VENDOR: u8 = 0;
136pub const ENDPOINT_OUT_VENDOR: u8 = 0x01;
137pub const ENDPOINT_IN_VENDOR: u8 = 0x81;
138
139pub const INTERFACE_HID: u8 = 3;
141pub const ENDPOINT_OUT_HID: u8 = 0x05;
142pub const ENDPOINT_IN_HID: u8 = 0x85;
143
144const 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
186pub enum TransferType {
187 Bulk,
188 Interrupt,
189}
190
191#[derive(Debug, Clone)]
196pub enum ConnectionMode {
197 Basic,
199 Full(DeviceState),
201}
202
203#[derive(Debug, Clone, Copy)]
227pub struct DeviceConfig {
228 interface: u8,
230 endpoint_out: u8,
232 endpoint_in: u8,
234 transfer_type: TransferType,
236 skip_reset: bool,
238}
239
240impl DeviceConfig {
241 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 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 pub fn skip_reset(mut self) -> Self {
294 self.skip_reset = true;
295 self
296 }
297
298 pub fn is_vendor(&self) -> bool {
300 self.interface == INTERFACE_VENDOR
301 }
302
303 pub fn is_hid(&self) -> bool {
305 self.interface == INTERFACE_HID
306 }
307}
308
309enum EndpointReaderType {
311 Bulk(EndpointRead<Bulk>),
312 Interrupt(EndpointRead<Interrupt>),
313}
314
315enum EndpointWriterType {
317 Bulk(EndpointWrite<Bulk>),
318 Interrupt(EndpointWrite<Interrupt>),
319}
320
321pub struct KM003C {
322 #[allow(dead_code)]
324 interface: Interface,
325 transaction_id: u8,
326 reader: EndpointReaderType,
327 writer: EndpointWriterType,
328 pending_responses: VecDeque<Vec<u8>>,
329 graph_sample_rate: Option<GraphSampleRate>,
331 mode: ConnectionMode,
333}
334
335impl KM003C {
336 pub async fn new(config: DeviceConfig) -> Result<Self, KMError> {
363 let mut device = Self::connect(config).await?;
364
365 if config.is_vendor() {
366 device.run_init().await?;
368 }
369 Ok(device)
372 }
373
374 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 if !config.skip_reset {
392 info!("Resetting device...");
393 device.reset().await?;
394 tokio::time::sleep(Duration::from_millis(1500)).await;
397 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 for interface_num in 0..4 {
414 if let Err(e) = device.detach_kernel_driver(interface_num) {
415 trace!("Could not detach interface {}: {}", interface_num, e);
417 } else {
418 debug!("Detached kernel driver from interface {}", interface_num);
419 }
420 }
421
422 let interface = device.claim_interface(config.interface).await?;
424 info!("Interface {} claimed successfully", config.interface);
425
426 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 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 pub fn next_tid(&mut self) -> u8 {
472 self.next_transaction_id()
473 }
474
475 pub fn set_transaction_id(&mut self, id: u8) {
477 self.transaction_id = id;
478 }
479
480 pub async fn send(&mut self, packet: Packet) -> Result<(), KMError> {
482 self.send_tracked(packet).await.map(|_| ())
483 }
484
485 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 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 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 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 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 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 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 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 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 pub async fn receive_memory_read_data(&mut self) -> Result<Packet, KMError> {
696 let raw_bytes = self.receive_raw().await?;
697
698 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 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 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 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 pub async fn request_pd_data(&mut self) -> Result<Packet, KMError> {
761 self.request_data(AttributeSet::single(Attribute::PdPacket)).await
762 }
763
764 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 pub fn extract_pd_status(packet: &Packet) -> Option<&PdStatus> {
778 packet.get_pd_status()
779 }
780
781 pub fn extract_pd_events(packet: &Packet) -> Option<&PdEventStream> {
786 packet.get_pd_events()
787 }
788
789 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 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 async fn run_init(&mut self) -> Result<(), KMError> {
823 use crate::auth::{HARDWARE_ID_ADDRESS, HARDWARE_ID_SIZE};
824
825 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 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 self.send_tracked(Packet::StreamingAuth {
868 hardware_id: hardware_id.clone(),
869 })
870 .await?;
871 let auth_response = self
872 .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 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 pub fn mode(&self) -> &ConnectionMode {
907 &self.mode
908 }
909
910 pub fn is_full_mode(&self) -> bool {
912 matches!(self.mode, ConnectionMode::Full(_))
913 }
914
915 pub fn is_basic_mode(&self) -> bool {
917 matches!(self.mode, ConnectionMode::Basic)
918 }
919
920 pub fn state(&self) -> Option<&DeviceState> {
925 match &self.mode {
926 ConnectionMode::Full(state) => Some(state),
927 ConnectionMode::Basic => None,
928 }
929 }
930
931 pub fn is_authenticated(&self) -> bool {
935 self.state().map(|s| s.is_authenticated()).unwrap_or(false)
936 }
937
938 pub fn adcqueue_enabled(&self) -> bool {
942 self.state().map(|s| s.adcqueue_enabled).unwrap_or(false)
943 }
944
945 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 pub async fn get_device_info(&mut self) -> Result<crate::auth::DeviceInfo, KMError> {
990 Ok(self.read_device_info_blocks().await)
991 }
992
993 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 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 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 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}