//! USB transfer handling for the Geek szitman supercamera use crate::error::Result; use rusb::{Direction, TransferType}; use std::time::Duration; use tracing::{error, trace}; /// USB transfer error types #[derive(Debug, thiserror::Error)] pub enum UsbTransferError { /// Transfer timeout #[error("Transfer timeout after {:?}", duration)] Timeout { duration: Duration }, /// Transfer failed #[error("Transfer failed: {}", reason)] Failed { reason: String }, /// Invalid endpoint #[error("Invalid endpoint: {}", endpoint)] InvalidEndpoint { endpoint: u8 }, /// Buffer too small #[error("Buffer too small: required {}, provided {}", required, provided)] BufferTooSmall { required: usize, provided: usize }, } /// USB transfer configuration #[derive(Debug, Clone)] pub struct UsbTransferConfig { pub timeout: Duration, pub retry_count: u32, pub buffer_size: usize, } impl Default for UsbTransferConfig { fn default() -> Self { Self { timeout: Duration::from_millis(1000), retry_count: 3, buffer_size: 0x1000, } } } /// USB transfer result #[derive(Debug)] pub struct UsbTransferResult { pub bytes_transferred: usize, pub endpoint: u8, pub direction: Direction, pub transfer_type: TransferType, } impl UsbTransferResult { /// Create a new transfer result pub fn new( bytes_transferred: usize, endpoint: u8, direction: Direction, transfer_type: TransferType, ) -> Self { Self { bytes_transferred, endpoint, direction, transfer_type, } } /// Check if the transfer was successful pub fn is_successful(&self) -> bool { self.bytes_transferred > 0 } /// Get the effective endpoint address pub fn endpoint_address(&self) -> u8 { match self.direction { Direction::In => self.endpoint | 0x80, Direction::Out => self.endpoint, } } } /// USB transfer statistics #[derive(Debug, Default)] pub struct UsbTransferStats { pub total_transfers: u64, pub successful_transfers: u64, pub failed_transfers: u64, pub total_bytes_transferred: u64, pub average_transfer_size: f64, } impl UsbTransferStats { /// Update statistics with a transfer result pub fn update(&mut self, result: &UsbTransferResult) { self.total_transfers += 1; if result.is_successful() { self.successful_transfers += 1; self.total_bytes_transferred += result.bytes_transferred as u64; } else { self.failed_transfers += 1; } // Update average transfer size if self.successful_transfers > 0 { self.average_transfer_size = self.total_bytes_transferred as f64 / self.successful_transfers as f64; } } /// Get success rate as a percentage pub fn success_rate(&self) -> f64 { if self.total_transfers == 0 { 0.0 } else { (self.successful_transfers as f64 / self.total_transfers as f64) * 100.0 } } /// Reset statistics pub fn reset(&mut self) { *self = Self::default(); } } /// USB transfer manager pub struct UsbTransferManager { config: UsbTransferConfig, stats: UsbTransferStats, } impl UsbTransferManager { /// Create a new transfer manager pub fn new(config: UsbTransferConfig) -> Self { Self { config, stats: UsbTransferStats::default(), } } /// Create a transfer manager with default configuration pub fn new_default() -> Self { Self::new(UsbTransferConfig::default()) } /// Get current statistics pub fn stats(&self) -> &UsbTransferStats { &self.stats } /// Get mutable reference to statistics pub fn stats_mut(&mut self) -> &mut UsbTransferStats { &mut self.stats } /// Reset statistics pub fn reset_stats(&mut self) { self.stats.reset(); } /// Validate endpoint configuration pub fn validate_endpoint( &self, endpoint: u8, direction: Direction, transfer_type: TransferType, ) -> Result<()> { if endpoint > 15 { return Err(UsbTransferError::InvalidEndpoint { endpoint }.into()); } // Validate endpoint address based on direction let endpoint_address = match direction { Direction::In => endpoint | 0x80, Direction::Out => endpoint, }; trace!( "Validated endpoint: 0x{:02X} ({:?}, {:?})", endpoint_address, direction, transfer_type ); Ok(()) } /// Validate buffer size pub fn validate_buffer(&self, buffer_size: usize) -> Result<()> { if buffer_size < self.config.buffer_size { return Err(UsbTransferError::BufferTooSmall { required: self.config.buffer_size, provided: buffer_size, } .into()); } Ok(()) } /// Get transfer configuration pub fn config(&self) -> &UsbTransferConfig { &self.config } /// Update transfer configuration pub fn update_config(&mut self, config: UsbTransferConfig) { self.config = config; } } impl Default for UsbTransferManager { fn default() -> Self { Self::new_default() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_usb_transfer_config_default() { let config = UsbTransferConfig::default(); assert_eq!(config.timeout, Duration::from_millis(1000)); assert_eq!(config.retry_count, 3); assert_eq!(config.buffer_size, 0x1000); } #[test] fn test_usb_transfer_result() { let result = UsbTransferResult::new(1024, 1, Direction::In, TransferType::Bulk); assert_eq!(result.bytes_transferred, 1024); assert_eq!(result.endpoint, 1); assert!(result.is_successful()); assert_eq!(result.endpoint_address(), 0x81); } #[test] fn test_usb_transfer_stats() { let mut stats = UsbTransferStats::default(); let result = UsbTransferResult::new(1024, 1, Direction::In, TransferType::Bulk); stats.update(&result); assert_eq!(stats.total_transfers, 1); assert_eq!(stats.successful_transfers, 1); assert_eq!(stats.failed_transfers, 0); assert_eq!(stats.total_bytes_transferred, 1024); assert_eq!(stats.success_rate(), 100.0); } #[test] fn test_usb_transfer_manager() { let manager = UsbTransferManager::new_default(); assert_eq!(manager.config().timeout, Duration::from_millis(1000)); assert_eq!(manager.stats().total_transfers, 0); } #[test] fn test_endpoint_validation() { let manager = UsbTransferManager::new_default(); // Valid endpoint assert!(manager .validate_endpoint(1, Direction::In, TransferType::Bulk) .is_ok()); // Invalid endpoint assert!(manager .validate_endpoint(16, Direction::In, TransferType::Bulk) .is_err()); } #[test] fn test_buffer_validation() { let manager = UsbTransferManager::new_default(); // Valid buffer size assert!(manager.validate_buffer(0x1000).is_ok()); // Invalid buffer size assert!(manager.validate_buffer(0x800).is_err()); } }