transport.rs (8952B)
1 //! The command channel to the device. 2 //! 3 //! [`Transport`] is the seam between the protocol logic and the wire: the real 4 //! [`UsbTransport`] drives endpoint 0 with rusb (libusb), and tests use a mock. 5 //! 6 //! rusb is used rather than nusb because on macOS nusb's device-level control 7 //! transfers require claiming the device (or an EP0-bearing interface), and both 8 //! are blocked here: AppleUSBAudio is attached to the audio interfaces, and 9 //! FocusriteControlServer holds the vendor interface. rusb's read_control / 10 //! write_control hit EP0 with no claim — proven on the real 18i20 by the spike. 11 12 use std::time::Duration; 13 14 use rusb::{Direction, Recipient, RequestType, UsbContext}; 15 16 use crate::packet::{self, PacketError, Response, HEADER_LEN}; 17 18 /// Focusrite / Novation USB vendor id. 19 pub const VID: u16 = 0x1235; 20 /// Scarlett 18i20 3rd Gen product id. 21 pub const PID_18I20_G3: u16 = 0x8215; 22 23 /// The scarlett2 control protocol lives on this (vendor-specific, class 0xFF) 24 /// interface — confirmed by the Phase-0 spike. Used as the control-transfer 25 /// `wIndex`. 26 pub const CONTROL_INTERFACE: u16 = 3; 27 28 const REQ_CMD: u8 = 2; // bRequest: host -> device 29 const RESP_CMD: u8 = 3; // bRequest: device -> host 30 const TIMEOUT: Duration = Duration::from_millis(1000); 31 /// The IN read can time out when the device defers a larger response (it normally 32 /// signals readiness on the interrupt EP, which we can't claim on macOS), so we 33 /// retry the read a few times before giving up. 34 const READ_ATTEMPTS: u32 = 4; 35 36 #[derive(Debug)] 37 pub enum TransportError { 38 /// No matching device is connected. 39 NotFound, 40 /// A USB-layer failure (open/transfer); message is from the OS/libusb. 41 Usb(String), 42 /// The response packet was malformed or the device returned an error code. 43 Packet(PacketError), 44 } 45 46 impl core::fmt::Display for TransportError { 47 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 48 match self { 49 TransportError::NotFound => write!(f, "device not found"), 50 TransportError::Usb(m) => write!(f, "usb error: {m}"), 51 TransportError::Packet(e) => write!(f, "protocol error: {e}"), 52 } 53 } 54 } 55 56 impl std::error::Error for TransportError {} 57 58 impl From<PacketError> for TransportError { 59 fn from(e: PacketError) -> Self { 60 TransportError::Packet(e) 61 } 62 } 63 64 /// One scarlett2 round-trip plus the small amount of state (sequence number) the 65 /// protocol requires. 66 pub trait Transport { 67 /// Send `cmd` with `payload` and return the validated response, reading up to 68 /// `resp_cap` payload bytes back. 69 fn command( 70 &mut self, 71 cmd: u32, 72 payload: &[u8], 73 resp_cap: usize, 74 ) -> Result<Response, TransportError>; 75 76 /// Raw control-IN with an arbitrary `bRequest` (the INIT "step 0" priming 77 /// read uses bRequest 0, outside the packet framing). 78 fn raw_in(&mut self, request: u8, len: usize) -> Result<Vec<u8>, TransportError>; 79 80 /// Force the next sequence number (the INIT_1/INIT_2 handshake resets it to 1). 81 fn set_seq(&mut self, seq: u16); 82 } 83 84 /// Live USB transport over endpoint 0 (rusb / libusb). 85 pub struct UsbTransport { 86 handle: rusb::DeviceHandle<rusb::Context>, 87 index: u16, 88 seq: u16, 89 } 90 91 impl UsbTransport { 92 /// Open the first connected Scarlett 18i20 3rd Gen. 93 pub fn open_default() -> Result<Self, TransportError> { 94 Self::open(VID, PID_18I20_G3, CONTROL_INTERFACE) 95 } 96 97 /// Open a specific device + control interface. 98 pub fn open(vid: u16, pid: u16, index: u16) -> Result<Self, TransportError> { 99 let ctx = rusb::Context::new().map_err(|e| TransportError::Usb(e.to_string()))?; 100 let device = ctx 101 .devices() 102 .map_err(|e| TransportError::Usb(e.to_string()))? 103 .iter() 104 .find(|d| { 105 d.device_descriptor() 106 .map(|x| x.vendor_id() == vid && x.product_id() == pid) 107 .unwrap_or(false) 108 }) 109 .ok_or(TransportError::NotFound)?; 110 let handle = device 111 .open() 112 .map_err(|e| TransportError::Usb(format!("{e} (quit Focusrite Control?)")))?; 113 Ok(Self { handle, index, seq: 1 }) 114 } 115 116 fn rt_out() -> u8 { 117 rusb::request_type(Direction::Out, RequestType::Class, Recipient::Interface) 118 } 119 120 fn rt_in() -> u8 { 121 rusb::request_type(Direction::In, RequestType::Class, Recipient::Interface) 122 } 123 } 124 125 impl Transport for UsbTransport { 126 fn command( 127 &mut self, 128 cmd: u32, 129 payload: &[u8], 130 resp_cap: usize, 131 ) -> Result<Response, TransportError> { 132 let seq = self.seq; 133 self.seq = self.seq.wrapping_add(1); 134 135 let req = packet::encode_request(cmd, seq, payload); 136 self.handle 137 .write_control(Self::rt_out(), REQ_CMD, 0, self.index, &req, TIMEOUT) 138 .map_err(|e| TransportError::Usb(e.to_string()))?; 139 140 let mut buf = vec![0u8; HEADER_LEN + resp_cap]; 141 let mut last = None; 142 for _ in 0..READ_ATTEMPTS { 143 match self 144 .handle 145 .read_control(Self::rt_in(), RESP_CMD, 0, self.index, &mut buf, TIMEOUT) 146 { 147 Ok(n) => { 148 buf.truncate(n); 149 return Ok(packet::decode_response(cmd, &buf)?); 150 } 151 Err(rusb::Error::Timeout) => last = Some("read timed out".to_string()), 152 Err(e) => last = Some(e.to_string()), 153 } 154 } 155 Err(TransportError::Usb(last.unwrap_or_else(|| "read failed".into()))) 156 } 157 158 fn raw_in(&mut self, request: u8, len: usize) -> Result<Vec<u8>, TransportError> { 159 let mut buf = vec![0u8; len]; 160 let n = self 161 .handle 162 .read_control(Self::rt_in(), request, 0, self.index, &mut buf, TIMEOUT) 163 .map_err(|e| TransportError::Usb(e.to_string()))?; 164 buf.truncate(n); 165 Ok(buf) 166 } 167 168 fn set_seq(&mut self, seq: u16) { 169 self.seq = seq; 170 } 171 } 172 173 #[cfg(test)] 174 pub(crate) mod mock { 175 use super::*; 176 use std::collections::VecDeque; 177 178 /// Records what the protocol layer sends and replays canned raw response 179 /// buffers, so protocol logic can be tested without hardware. 180 pub struct MockTransport { 181 /// Raw response buffers (header + payload) to return, in order. 182 pub responses: VecDeque<Vec<u8>>, 183 /// Every (cmd, payload) the protocol layer sent. 184 pub sent: Vec<(u32, Vec<u8>)>, 185 pub seq: u16, 186 /// When true, any command with no queued response gets a synthesized 187 /// empty success echo. Handy for write-heavy flows (e.g. apply_preset) 188 /// where only the `sent` log matters. 189 pub auto_echo: bool, 190 } 191 192 impl MockTransport { 193 pub fn new() -> Self { 194 Self { responses: VecDeque::new(), sent: Vec::new(), seq: 1, auto_echo: false } 195 } 196 197 /// A mock that auto-echoes an empty success for every command. 198 pub fn echoing() -> Self { 199 Self { auto_echo: true, ..Self::new() } 200 } 201 202 /// Queue a response with the given cmd echoed and `payload` attached. 203 pub fn push_response(&mut self, cmd: u32, payload: &[u8]) { 204 let mut b = Vec::new(); 205 b.extend_from_slice(&cmd.to_le_bytes()); 206 b.extend_from_slice(&(payload.len() as u16).to_le_bytes()); 207 b.extend_from_slice(&self.seq.to_le_bytes()); 208 b.extend_from_slice(&0u32.to_le_bytes()); // error 209 b.extend_from_slice(&0u32.to_le_bytes()); // pad 210 b.extend_from_slice(payload); 211 self.responses.push_back(b); 212 } 213 } 214 215 impl Transport for MockTransport { 216 fn command( 217 &mut self, 218 cmd: u32, 219 payload: &[u8], 220 _resp_cap: usize, 221 ) -> Result<Response, TransportError> { 222 self.sent.push((cmd, payload.to_vec())); 223 if let Some(raw) = self.responses.pop_front() { 224 return Ok(packet::decode_response(cmd, &raw)?); 225 } 226 if self.auto_echo { 227 // Synthesize an empty matching response. 228 let mut b = Vec::new(); 229 b.extend_from_slice(&cmd.to_le_bytes()); 230 b.extend_from_slice(&0u16.to_le_bytes()); // size 231 b.extend_from_slice(&self.seq.to_le_bytes()); 232 b.extend_from_slice(&0u32.to_le_bytes()); // error 233 b.extend_from_slice(&0u32.to_le_bytes()); // pad 234 return Ok(packet::decode_response(cmd, &b)?); 235 } 236 panic!("MockTransport: no queued response for cmd {cmd:#x}"); 237 } 238 239 fn raw_in(&mut self, _request: u8, _len: usize) -> Result<Vec<u8>, TransportError> { 240 Ok(Vec::new()) 241 } 242 243 fn set_seq(&mut self, seq: u16) { 244 self.seq = seq; 245 } 246 } 247 }