Skip to main content

viva_u3v/
descriptor.rs

1//! USB descriptor parsing for USB3 Vision devices.
2//!
3//! USB3 Vision devices are identified by their Interface Association Descriptor
4//! (IAD) class codes and interface class/subclass values. This module extracts
5//! endpoint addresses and interface numbers from USB descriptors.
6
7/// USB3 Vision interface class code.
8pub const U3V_INTERFACE_CLASS: u8 = 0xEF;
9/// USB3 Vision interface subclass for IAD.
10pub const U3V_INTERFACE_SUBCLASS: u8 = 0x05;
11
12/// U3V control interface subclass.
13pub const U3V_CONTROL_SUBCLASS: u8 = 0x00;
14/// U3V event interface subclass.
15pub const U3V_EVENT_SUBCLASS: u8 = 0x01;
16/// U3V streaming interface subclass.
17pub const U3V_STREAM_SUBCLASS: u8 = 0x02;
18
19/// U3V interface protocol code.
20pub const U3V_INTERFACE_PROTOCOL: u8 = 0x00;
21
22/// Information about a USB3 Vision device's interfaces and endpoints,
23/// extracted from USB descriptors.
24#[derive(Debug, Clone)]
25pub struct U3vInterfaceInfo {
26    /// Interface number for the control channel.
27    pub control_iface: u8,
28    /// Bulk IN endpoint for control acks.
29    pub control_ep_in: u8,
30    /// Bulk OUT endpoint for control commands.
31    pub control_ep_out: u8,
32    /// Interface number for the streaming channel (if present).
33    pub stream_iface: Option<u8>,
34    /// Bulk IN endpoint for stream data (if present).
35    pub stream_ep_in: Option<u8>,
36    /// Interface number for the event channel (if present).
37    pub event_iface: Option<u8>,
38    /// Bulk IN endpoint for events (if present).
39    pub event_ep_in: Option<u8>,
40}
41
42/// Check whether a USB device's interface descriptors indicate a USB3 Vision device
43/// and extract endpoint information.
44///
45/// Returns `None` if no U3V control interface is found.
46#[cfg(feature = "usb")]
47pub fn parse_u3v_interfaces(config: &rusb::ConfigDescriptor) -> Option<U3vInterfaceInfo> {
48    let mut control_iface = None;
49    let mut control_ep_in = None;
50    let mut control_ep_out = None;
51    let mut stream_iface = None;
52    let mut stream_ep_in = None;
53    let mut event_iface = None;
54    let mut event_ep_in = None;
55
56    for interface in config.interfaces() {
57        for desc in interface.descriptors() {
58            if desc.class_code() != U3V_INTERFACE_CLASS {
59                continue;
60            }
61
62            match desc.sub_class_code() {
63                U3V_CONTROL_SUBCLASS => {
64                    control_iface = Some(desc.interface_number());
65                    for ep in desc.endpoint_descriptors() {
66                        if ep.transfer_type() != rusb::TransferType::Bulk {
67                            continue;
68                        }
69                        match ep.direction() {
70                            rusb::Direction::In => control_ep_in = Some(ep.address()),
71                            rusb::Direction::Out => control_ep_out = Some(ep.address()),
72                        }
73                    }
74                }
75                U3V_EVENT_SUBCLASS => {
76                    event_iface = Some(desc.interface_number());
77                    for ep in desc.endpoint_descriptors() {
78                        if ep.transfer_type() == rusb::TransferType::Bulk
79                            && ep.direction() == rusb::Direction::In
80                        {
81                            event_ep_in = Some(ep.address());
82                        }
83                    }
84                }
85                U3V_STREAM_SUBCLASS => {
86                    stream_iface = Some(desc.interface_number());
87                    for ep in desc.endpoint_descriptors() {
88                        if ep.transfer_type() == rusb::TransferType::Bulk
89                            && ep.direction() == rusb::Direction::In
90                        {
91                            stream_ep_in = Some(ep.address());
92                        }
93                    }
94                }
95                _ => {}
96            }
97        }
98    }
99
100    let control_iface = control_iface?;
101    let control_ep_in = control_ep_in?;
102    let control_ep_out = control_ep_out?;
103
104    Some(U3vInterfaceInfo {
105        control_iface,
106        control_ep_in,
107        control_ep_out,
108        stream_iface,
109        stream_ep_in,
110        event_iface,
111        event_ep_in,
112    })
113}
114
115/// Check whether a device descriptor looks like it could be a U3V device.
116///
117/// USB3 Vision uses IAD class `0xEF`, subclass `0x02`, protocol `0x01`
118/// at the device level, but some cameras use `0x00` (per-interface).
119/// This is a coarse filter; full detection requires inspecting interface
120/// descriptors via [`parse_u3v_interfaces`].
121#[cfg(feature = "usb")]
122pub fn is_likely_u3v_device(desc: &rusb::DeviceDescriptor) -> bool {
123    // IAD class at device level
124    if desc.class_code() == 0xEF && desc.sub_class_code() == 0x02 && desc.protocol_code() == 0x01 {
125        return true;
126    }
127    // Per-interface class: device descriptor says 0x00, need to check interfaces
128    desc.class_code() == 0x00
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn u3v_class_constants_are_correct() {
137        assert_eq!(U3V_INTERFACE_CLASS, 0xEF);
138        assert_eq!(U3V_CONTROL_SUBCLASS, 0x00);
139        assert_eq!(U3V_EVENT_SUBCLASS, 0x01);
140        assert_eq!(U3V_STREAM_SUBCLASS, 0x02);
141        assert_eq!(U3V_INTERFACE_PROTOCOL, 0x00);
142        assert_eq!(U3V_INTERFACE_SUBCLASS, 0x05);
143    }
144}