summaryrefslogtreecommitdiff
path: root/src/usb/device.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/usb/device.rs')
-rw-r--r--src/usb/device.rs287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/usb/device.rs b/src/usb/device.rs
new file mode 100644
index 0000000..b285e81
--- /dev/null
+++ b/src/usb/device.rs
@@ -0,0 +1,287 @@
+//! 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<Context>,
+ handle: Arc<Mutex<Option<DeviceHandle<Context>>>>,
+ device_info: super::UsbDeviceInfo,
+}
+
+impl UsbSupercamera {
+ /// Create a new USB supercamera instance
+ pub fn new() -> Result<Self> {
+ 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<Device<Context>> {
+ 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<Context>, 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<Context>, 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<Context>, 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<Context>) -> 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<Vec<u8>> {
+ 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<Context>, endpoint: u8, buffer: &mut [u8]) -> Result<usize> {
+ 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<Context>, 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);
+ }
+}