diff options
Diffstat (limited to 'src/usb/transfer.rs')
| -rw-r--r-- | src/usb/transfer.rs | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/src/usb/transfer.rs b/src/usb/transfer.rs new file mode 100644 index 0000000..51d31ce --- /dev/null +++ b/src/usb/transfer.rs @@ -0,0 +1,287 @@ +//! 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()); + } +} |
