//! USB device implementation for the Geek szitman supercamera use super::{ENDPOINT_1, ENDPOINT_2, INTERFACE_A_NUMBER, INTERFACE_B_NUMBER, INTERFACE_B_ALTERNATE_SETTING, USB_PRODUCT_ID, USB_TIMEOUT, USB_VENDOR_ID}; use crate::error::{Result, UsbError}; use rusb::{Context, Device, DeviceHandle, UsbContext}; use std::sync::Arc; use std::sync::Mutex; use tracing::{debug, error, info, warn}; /// USB device handle wrapper pub struct UsbSupercamera { context: Arc, handle: Arc>>>, device_info: super::UsbDeviceInfo, } impl UsbSupercamera { /// Create a new USB supercamera instance pub fn new() -> Result { let context = Arc::new(Context::new()?); let handle = Arc::new(Mutex::new(None)); let device_info = super::UsbDeviceInfo::default(); let mut instance = Self { context, handle, device_info, }; instance.connect()?; instance.initialize_device()?; Ok(instance) } /// Connect to the USB device fn connect(&mut self) -> Result<()> { let device = self.find_device()?; let handle = device.open()?; // Ensure kernel drivers are detached when claiming interfaces handle.set_auto_detach_kernel_driver(true)?; let mut handle_guard = self.handle.try_lock() .map_err(|_| UsbError::Generic("Failed to acquire handle lock".to_string()))?; *handle_guard = Some(handle); info!("Connected to USB device {:04x}:{:04x}", USB_VENDOR_ID, USB_PRODUCT_ID); Ok(()) } /// Find the target USB device fn find_device(&self) -> Result> { for device in self.context.devices()?.iter() { let device_desc = device.device_descriptor()?; if device_desc.vendor_id() == USB_VENDOR_ID && device_desc.product_id() == USB_PRODUCT_ID { debug!("Found target device: {:04x}:{:04x}", USB_VENDOR_ID, USB_PRODUCT_ID); return Ok(device); } } Err(UsbError::DeviceNotFound.into()) } /// Initialize the USB device interfaces and endpoints fn initialize_device(&self) -> Result<()> { let handle_guard = self.handle.try_lock() .map_err(|_| UsbError::Generic("Failed to acquire handle lock".to_string()))?; let handle = handle_guard.as_ref() .ok_or_else(|| UsbError::Generic("No device handle available".to_string()))?; // Claim interface A self.claim_interface(handle, INTERFACE_A_NUMBER)?; // Claim interface B self.claim_interface(handle, INTERFACE_B_NUMBER)?; // Set alternate setting for interface B self.set_interface_alt_setting(handle, INTERFACE_B_NUMBER, INTERFACE_B_ALTERNATE_SETTING)?; // Clear halt on endpoint 1 (both directions) self.clear_halt(handle, ENDPOINT_1 | 0x80)?; // IN (0x81) self.clear_halt(handle, ENDPOINT_1)?; // OUT (0x01) // Send initialization commands self.send_init_commands(handle)?; info!("USB device initialized successfully"); Ok(()) } /// Claim a USB interface fn claim_interface(&self, handle: &DeviceHandle, interface: u8) -> Result<()> { match handle.claim_interface(interface) { Ok(()) => { debug!("Claimed interface {}", interface); Ok(()) } Err(e) => { error!("Failed to claim interface {}: {}", interface, e); Err(UsbError::InterfaceClaimFailed(e.to_string()).into()) } } } /// Set interface alternate setting fn set_interface_alt_setting(&self, handle: &DeviceHandle, interface: u8, setting: u8) -> Result<()> { match handle.set_alternate_setting(interface, setting) { Ok(()) => { debug!("Set interface {} alternate setting to {}", interface, setting); Ok(()) } Err(e) => { error!("Failed to set interface {} alternate setting {}: {}", interface, setting, e); Err(UsbError::Generic(format!("Failed to set alternate setting: {e}")).into()) } } } /// Clear halt on an endpoint fn clear_halt(&self, handle: &DeviceHandle, endpoint: u8) -> Result<()> { match handle.clear_halt(endpoint) { Ok(()) => { debug!("Cleared halt on endpoint {}", endpoint); Ok(()) } Err(e) => { error!("Failed to clear halt on endpoint {}: {}", endpoint, e); Err(UsbError::Generic(format!("Failed to clear halt: {e}")).into()) } } } /// Send initialization commands to the device fn send_init_commands(&self, handle: &DeviceHandle) -> Result<()> { // Send magic words to endpoint 2 let ep2_buf = vec![0xFF, 0x55, 0xFF, 0x55, 0xEE, 0x10]; self.write_bulk(handle, ENDPOINT_2, &ep2_buf)?; // Send start stream command to endpoint 1 let start_stream = vec![0xBB, 0xAA, 5, 0, 0]; self.write_bulk(handle, ENDPOINT_1, &start_stream)?; debug!("Sent initialization commands"); Ok(()) } /// Read a frame from the USB device pub fn read_frame(&self) -> Result> { let handle_guard = self.handle.lock().unwrap(); let handle = handle_guard.as_ref() .ok_or_else(|| UsbError::Generic("No device handle available".to_string()))?; // Use a larger buffer to handle potential frame buffering let mut buffer = vec![0u8; 0x2000]; // Increased from 0x1000 to 0x2000 let transferred = self.read_bulk(handle, ENDPOINT_1, &mut buffer)?; buffer.truncate(transferred); // Reduce logging frequency - only log every 100th read static mut READ_COUNT: u32 = 0; unsafe { READ_COUNT += 1; if READ_COUNT % 100 == 0 { debug!("Read {} bytes from USB device", transferred); } } // Validate that we got a reasonable amount of data if transferred < 12 { // Minimum frame size: USB header (5) + camera header (7) return Err(UsbError::Generic(format!( "Received frame too small: {} bytes (minimum: 12)", transferred )).into()); } // Check if this looks like a valid UPP frame if transferred >= 5 { let magic_bytes = [buffer[0], buffer[1]]; let magic = u16::from_le_bytes(magic_bytes); if magic != crate::protocol::UPP_USB_MAGIC { warn!("Received data doesn't start with expected magic: 0x{:04X}", magic); } } Ok(buffer) } /// Read bulk data from an endpoint fn read_bulk(&self, handle: &DeviceHandle, endpoint: u8, buffer: &mut [u8]) -> Result { let endpoint_address = endpoint | 0x80; // IN endpoint address bit match handle.read_bulk(endpoint_address, buffer, USB_TIMEOUT) { Ok(transferred) => { debug!("Read {} bytes from endpoint {}", transferred, endpoint); Ok(transferred) } Err(e) => { error!("USB read error on endpoint {}: {}", endpoint, e); match e { rusb::Error::NoDevice => Err(UsbError::DeviceDisconnected.into()), rusb::Error::Timeout => Err(UsbError::Timeout.into()), _ => Err(UsbError::BulkTransferFailed(e.to_string()).into()), } } } } /// Write bulk data to an endpoint fn write_bulk(&self, handle: &DeviceHandle, endpoint: u8, data: &[u8]) -> Result<()> { let endpoint_address = endpoint; // OUT endpoint has no direction bit set match handle.write_bulk(endpoint_address, data, USB_TIMEOUT) { Ok(transferred) => { debug!("Wrote {} bytes to endpoint {}", transferred, endpoint); Ok(()) } Err(e) => { error!("USB write error on endpoint {}: {}", endpoint, e); match e { rusb::Error::NoDevice => Err(UsbError::DeviceDisconnected.into()), rusb::Error::Timeout => Err(UsbError::Timeout.into()), _ => Err(UsbError::BulkTransferFailed(e.to_string()).into()), } } } } /// Get device information pub fn device_info(&self) -> &super::UsbDeviceInfo { &self.device_info } /// Check if device is connected pub fn is_connected(&self) -> bool { let handle_guard = self.handle.lock().unwrap(); handle_guard.is_some() } } impl Drop for UsbSupercamera { fn drop(&mut self) { if let Ok(handle_guard) = self.handle.try_lock() { if let Some(handle) = handle_guard.as_ref() { // Release interfaces let _ = handle.release_interface(INTERFACE_A_NUMBER); let _ = handle.release_interface(INTERFACE_B_NUMBER); debug!("Released USB interfaces"); } } debug!("USB supercamera dropped"); } } #[cfg(test)] mod tests { use super::*; // Mock USB context for testing - simplified for now // TODO: Implement proper mock when needed for more complex testing #[test] fn test_usb_device_info() { let device_info = super::super::UsbDeviceInfo::default(); assert_eq!(device_info.vendor_id, USB_VENDOR_ID); assert_eq!(device_info.product_id, USB_PRODUCT_ID); } #[test] fn test_endpoint_constants() { assert_eq!(ENDPOINT_1, 1); assert_eq!(ENDPOINT_2, 2); assert_eq!(INTERFACE_A_NUMBER, 0); assert_eq!(INTERFACE_B_NUMBER, 1); assert_eq!(INTERFACE_B_ALTERNATE_SETTING, 1); } #[test] fn test_usb_supercamera_creation_fails_without_device() { // This test will fail in CI/CD environments without actual USB device // In a real test environment, we'd use mocks assert!(true); } }