protocol.rs (7814B)
1 //! High-level scarlett2 operations built on a [`Transport`]. 2 //! 3 //! These are the primitives the whole feature set rides on. Most controls 4 //! (phantom power, air, pad, inst/line, mutes, monitor level, …) are just 5 //! [`Scarlett::get_data`] / [`Scarlett::set_data`] at a per-feature offset, then 6 //! an [`Scarlett::activate`]. The matrix mixer / routing / metering use their own 7 //! opcodes and are layered on top in `matrix.rs`. 8 9 use crate::packet::Response; 10 use crate::transport::{Transport, TransportError}; 11 12 /// scarlett2 command opcodes (from the kernel driver, confirmed by the spike). 13 pub mod op { 14 pub const INIT_1: u32 = 0x0000_0000; 15 pub const INIT_2: u32 = 0x0000_0002; 16 pub const REBOOT: u32 = 0x0000_0003; 17 pub const GET_METER: u32 = 0x0000_1001; 18 pub const GET_MIX: u32 = 0x0000_2001; 19 pub const SET_MIX: u32 = 0x0000_2002; 20 pub const GET_MUX: u32 = 0x0000_3001; 21 pub const SET_MUX: u32 = 0x0000_3002; 22 pub const GET_SYNC: u32 = 0x0000_6004; 23 pub const GET_DATA: u32 = 0x0080_0000; 24 pub const SET_DATA: u32 = 0x0080_0001; 25 pub const DATA_CMD: u32 = 0x0080_0002; 26 } 27 28 /// `activate` argument that persists the current settings to the device's flash 29 /// (standalone mode). 30 pub const CONFIG_SAVE: u32 = 6; 31 32 /// A connected (or mocked) Scarlett, speaking the scarlett2 protocol. 33 pub struct Scarlett<T: Transport> { 34 transport: T, 35 firmware: u32, 36 } 37 38 impl<T: Transport> Scarlett<T> { 39 pub fn new(transport: T) -> Self { 40 Self { transport, firmware: 0 } 41 } 42 43 /// Firmware version reported by [`Scarlett::init`] (0 until init runs). 44 pub fn firmware(&self) -> u32 { 45 self.firmware 46 } 47 48 /// Issue one raw scarlett2 command (used by the matrix/mux/meter ops in 49 /// `matrix.rs`). Crate-internal so the wire stays encapsulated. 50 pub(crate) fn command( 51 &mut self, 52 cmd: u32, 53 payload: &[u8], 54 resp_cap: usize, 55 ) -> Result<Response, TransportError> { 56 self.transport.command(cmd, payload, resp_cap) 57 } 58 59 /// Run the INIT handshake: a raw priming read, then INIT_1 and INIT_2 each 60 /// with the sequence counter reset to 1. 61 /// 62 /// INIT_2 (which carries the firmware version at byte 8) is **best-effort**: 63 /// on macOS its response is normally signalled on the interrupt endpoint, 64 /// which we can't claim while FocusriteControlServer holds it, so the IN read 65 /// times out. The Phase-0 spike confirmed every other command (GET_SYNC, 66 /// GET_DATA, …) works regardless, so a timed-out INIT_2 must not abort init — 67 /// we just report firmware 0. INIT_1 must still succeed. 68 pub fn init(&mut self) -> Result<u32, TransportError> { 69 let _ = self.transport.raw_in(0, 24); // step 0 (best-effort) 70 71 self.transport.set_seq(1); 72 self.transport.command(op::INIT_1, &[], 0)?; 73 74 self.transport.set_seq(1); 75 self.firmware = match self.transport.command(op::INIT_2, &[], 32) { 76 Ok(resp) => resp 77 .payload 78 .get(8..12) 79 .map(|s| u32::from_le_bytes(s.try_into().unwrap())) 80 .unwrap_or(0), 81 Err(_) => 0, // firmware unknown; not fatal 82 }; 83 Ok(self.firmware) 84 } 85 86 /// Read `size` bytes from the device data space at `offset`. 87 pub fn get_data(&mut self, offset: u32, size: u32) -> Result<Vec<u8>, TransportError> { 88 let mut payload = Vec::with_capacity(8); 89 payload.extend_from_slice(&offset.to_le_bytes()); 90 payload.extend_from_slice(&size.to_le_bytes()); 91 Ok(self 92 .transport 93 .command(op::GET_DATA, &payload, size as usize)? 94 .payload) 95 } 96 97 /// Write a `size`-byte (1, 2, or 4) little-endian `value` at `offset`. 98 /// Remember to [`Scarlett::activate`] afterwards to apply it. 99 pub fn set_data(&mut self, offset: u32, size: u32, value: u32) -> Result<(), TransportError> { 100 let size = size.clamp(1, 4); 101 let mut payload = Vec::with_capacity(8 + size as usize); 102 payload.extend_from_slice(&offset.to_le_bytes()); 103 payload.extend_from_slice(&size.to_le_bytes()); 104 payload.extend_from_slice(&value.to_le_bytes()[..size as usize]); 105 self.transport.command(op::SET_DATA, &payload, 0)?; 106 Ok(()) 107 } 108 109 /// Apply previously uploaded `set_data` changes. `activate` is the per-item 110 /// activation value (or [`CONFIG_SAVE`] to persist to flash). 111 pub fn activate(&mut self, activate: u32) -> Result<(), TransportError> { 112 self.transport 113 .command(op::DATA_CMD, &activate.to_le_bytes(), 0)?; 114 Ok(()) 115 } 116 117 /// Persist the current configuration to the device's flash (standalone mode). 118 pub fn save_to_flash(&mut self) -> Result<(), TransportError> { 119 self.activate(CONFIG_SAVE) 120 } 121 122 /// Clock-sync lock status. The device returns a 4-byte value; non-zero = locked. 123 pub fn get_sync(&mut self) -> Result<bool, TransportError> { 124 let resp = self.transport.command(op::GET_SYNC, &[], 4)?; 125 Ok(resp.payload.iter().any(|&b| b != 0)) 126 } 127 128 /// Consume the wrapper and get the transport back (e.g. to re-issue init). 129 pub fn into_transport(self) -> T { 130 self.transport 131 } 132 } 133 134 #[cfg(test)] 135 mod tests { 136 use super::*; 137 use crate::transport::mock::MockTransport; 138 139 #[test] 140 fn init_resets_seq_and_extracts_firmware() { 141 let mut m = MockTransport::new(); 142 m.push_response(op::INIT_1, &[]); 143 let mut p = vec![0u8; 12]; 144 p[8..12].copy_from_slice(&0x1234u32.to_le_bytes()); 145 m.push_response(op::INIT_2, &p); 146 147 let mut dev = Scarlett::new(m); 148 let fw = dev.init().unwrap(); 149 assert_eq!(fw, 0x1234); 150 assert_eq!(dev.firmware(), 0x1234); 151 152 let m = dev.into_transport(); 153 assert_eq!(m.sent[0].0, op::INIT_1); 154 assert_eq!(m.sent[1].0, op::INIT_2); 155 } 156 157 #[test] 158 fn get_data_builds_offset_size_payload() { 159 let mut m = MockTransport::new(); 160 m.push_response(op::GET_DATA, &[0xaa, 0xbb]); 161 let mut dev = Scarlett::new(m); 162 163 let data = dev.get_data(0x40, 2).unwrap(); 164 assert_eq!(data, vec![0xaa, 0xbb]); 165 166 let m = dev.into_transport(); 167 let (cmd, payload) = &m.sent[0]; 168 assert_eq!(*cmd, op::GET_DATA); 169 assert_eq!(payload, &vec![0x40, 0, 0, 0, 0x02, 0, 0, 0]); 170 } 171 172 #[test] 173 fn set_data_packs_value_to_declared_width() { 174 let mut m = MockTransport::new(); 175 m.push_response(op::SET_DATA, &[]); 176 let mut dev = Scarlett::new(m); 177 178 dev.set_data(0x9c, 1, 1).unwrap(); 179 180 let m = dev.into_transport(); 181 let (cmd, payload) = &m.sent[0]; 182 assert_eq!(*cmd, op::SET_DATA); 183 assert_eq!(payload, &vec![0x9c, 0, 0, 0, 0x01, 0, 0, 0, 0x01]); 184 } 185 186 #[test] 187 fn activate_and_save_send_data_cmd() { 188 let mut m = MockTransport::new(); 189 m.push_response(op::DATA_CMD, &[]); 190 m.push_response(op::DATA_CMD, &[]); 191 let mut dev = Scarlett::new(m); 192 193 dev.activate(0x05).unwrap(); 194 dev.save_to_flash().unwrap(); 195 196 let m = dev.into_transport(); 197 assert_eq!(m.sent[0], (op::DATA_CMD, 0x05u32.to_le_bytes().to_vec())); 198 assert_eq!(m.sent[1], (op::DATA_CMD, CONFIG_SAVE.to_le_bytes().to_vec())); 199 } 200 201 #[test] 202 fn get_sync_reads_lock_state() { 203 let mut m = MockTransport::new(); 204 m.push_response(op::GET_SYNC, &[0x01, 0, 0, 0]); // locked (real spike bytes) 205 let mut dev = Scarlett::new(m); 206 assert!(dev.get_sync().unwrap()); 207 208 let mut m2 = MockTransport::new(); 209 m2.push_response(op::GET_SYNC, &[0, 0, 0, 0]); // unlocked 210 let mut dev2 = Scarlett::new(m2); 211 assert!(!dev2.get_sync().unwrap()); 212 } 213 }