summaryrefslogtreecommitdiff
path: root/src/usb/transfer.rs
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2026-02-08 12:44:10 +0100
committerDawid Rycerz <dawid@rycerz.xyz>2026-02-08 12:44:10 +0100
commit0c20fb86633104744dbccf30ad732296694fff1b (patch)
tree02ffb8494086960b4a84decf3bdc2c8c61bfc4f6 /src/usb/transfer.rs
Initial pipewiremain
Diffstat (limited to 'src/usb/transfer.rs')
-rw-r--r--src/usb/transfer.rs287
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());
+ }
+}