Skip to main content

viva_u3v/
bootstrap.rs

1//! USB3 Vision bootstrap register maps.
2//!
3//! The device exposes a hierarchy of register maps that describe its
4//! capabilities and provide addresses for higher-level structures:
5//!
6//! ```text
7//! ABRM (addr 0x0000, GenCP standard)
8//!   ├─ manufacturer / model / serial strings
9//!   ├─ ManifestTable address → GenICam XML location
10//!   └─ SBRM address → technology-specific registers
11//!         ├─ max transfer sizes
12//!         ├─ SIRM address → streaming config
13//!         └─ EIRM address → event config
14//! ```
15
16use bytes::Buf;
17
18use crate::U3vError;
19use crate::control::ControlChannel;
20use crate::usb::UsbTransfer;
21
22// ---------------------------------------------------------------------------
23// ABRM — Application Bootstrap Register Map (GenCP standard, at addr 0x0000)
24// ---------------------------------------------------------------------------
25
26/// ABRM register offsets (GenCP §5.3).
27mod abrm_reg {
28    pub const GENCP_VERSION: u64 = 0x0000;
29    pub const MANUFACTURER_NAME: u64 = 0x0048;
30    pub const MODEL_NAME: u64 = 0x0088;
31    pub const FAMILY_NAME: u64 = 0x00C8;
32    pub const DEVICE_VERSION: u64 = 0x0108;
33    pub const SERIAL_NUMBER: u64 = 0x01A8;
34    pub const USER_DEFINED_NAME: u64 = 0x01E8;
35    pub const MANIFEST_TABLE_ADDR: u64 = 0x0228;
36    pub const SBRM_ADDRESS: u64 = 0x0230;
37    pub const DEVICE_CAPABILITY: u64 = 0x0238;
38
39    /// Maximum length of a string register (64 bytes including NUL).
40    pub const STRING_LEN: usize = 64;
41}
42
43/// Application Bootstrap Register Map — GenCP standard registers at address 0.
44#[derive(Debug, Clone)]
45pub struct Abrm {
46    pub gencp_version: u32,
47    pub manufacturer_name: String,
48    pub model_name: String,
49    pub family_name: String,
50    pub device_version: String,
51    pub serial_number: String,
52    pub user_defined_name: String,
53    pub manifest_table_address: u64,
54    pub sbrm_address: u64,
55    pub device_capability: u64,
56}
57
58impl Abrm {
59    /// Read the ABRM from the device.
60    pub fn read_from<T: UsbTransfer>(control: &mut ControlChannel<T>) -> Result<Self, U3vError> {
61        let read_string = |ctrl: &mut ControlChannel<T>, addr: u64| -> Result<String, U3vError> {
62            let raw = ctrl.read_mem(addr, abrm_reg::STRING_LEN)?;
63            Ok(parse_nul_string(&raw))
64        };
65
66        let gencp_ver_bytes = control.read_mem(abrm_reg::GENCP_VERSION, 4)?;
67        let gencp_version = read_u32_be(&gencp_ver_bytes);
68
69        let manufacturer_name = read_string(control, abrm_reg::MANUFACTURER_NAME)?;
70        let model_name = read_string(control, abrm_reg::MODEL_NAME)?;
71        let family_name = read_string(control, abrm_reg::FAMILY_NAME)?;
72        let device_version = read_string(control, abrm_reg::DEVICE_VERSION)?;
73        let serial_number = read_string(control, abrm_reg::SERIAL_NUMBER)?;
74        let user_defined_name = read_string(control, abrm_reg::USER_DEFINED_NAME)?;
75
76        let manifest_bytes = control.read_mem(abrm_reg::MANIFEST_TABLE_ADDR, 8)?;
77        let manifest_table_address = read_u64_be(&manifest_bytes);
78
79        let sbrm_bytes = control.read_mem(abrm_reg::SBRM_ADDRESS, 8)?;
80        let sbrm_address = read_u64_be(&sbrm_bytes);
81
82        let cap_bytes = control.read_mem(abrm_reg::DEVICE_CAPABILITY, 8)?;
83        let device_capability = read_u64_be(&cap_bytes);
84
85        Ok(Self {
86            gencp_version,
87            manufacturer_name,
88            model_name,
89            family_name,
90            device_version,
91            serial_number,
92            user_defined_name,
93            manifest_table_address,
94            sbrm_address,
95            device_capability,
96        })
97    }
98}
99
100// ---------------------------------------------------------------------------
101// SBRM — Serial (technology-specific) Bootstrap Register Map
102// ---------------------------------------------------------------------------
103
104/// SBRM register offsets relative to the SBRM base address.
105mod sbrm_reg {
106    pub const U3V_VERSION: u64 = 0x0000;
107    pub const MAX_CMD_TRANSFER: u64 = 0x0004;
108    pub const MAX_ACK_TRANSFER: u64 = 0x0008;
109    pub const NUM_STREAM_CHANNELS: u64 = 0x000C;
110    pub const SIRM_ADDRESS: u64 = 0x0010;
111    pub const SIRM_LENGTH: u64 = 0x0018;
112    pub const EIRM_ADDRESS: u64 = 0x001C;
113    pub const EIRM_LENGTH: u64 = 0x0024;
114}
115
116/// Technology-specific Bootstrap Register Map for USB3 Vision.
117#[derive(Debug, Clone)]
118pub struct Sbrm {
119    pub base: u64,
120    pub u3v_version: u32,
121    pub max_cmd_transfer: u32,
122    pub max_ack_transfer: u32,
123    pub num_stream_channels: u32,
124    pub sirm_address: u64,
125    pub sirm_length: u32,
126    pub eirm_address: u64,
127    pub eirm_length: u32,
128}
129
130impl Sbrm {
131    /// Read the SBRM from the device at the given base address (from ABRM).
132    pub fn read_from<T: UsbTransfer>(
133        control: &mut ControlChannel<T>,
134        base: u64,
135    ) -> Result<Self, U3vError> {
136        let read_u32 = |ctrl: &mut ControlChannel<T>, off: u64| -> Result<u32, U3vError> {
137            let bytes = ctrl.read_mem(base + off, 4)?;
138            Ok(read_u32_be(&bytes))
139        };
140        let read_u64 = |ctrl: &mut ControlChannel<T>, off: u64| -> Result<u64, U3vError> {
141            let bytes = ctrl.read_mem(base + off, 8)?;
142            Ok(read_u64_be(&bytes))
143        };
144
145        Ok(Self {
146            base,
147            u3v_version: read_u32(control, sbrm_reg::U3V_VERSION)?,
148            max_cmd_transfer: read_u32(control, sbrm_reg::MAX_CMD_TRANSFER)?,
149            max_ack_transfer: read_u32(control, sbrm_reg::MAX_ACK_TRANSFER)?,
150            num_stream_channels: read_u32(control, sbrm_reg::NUM_STREAM_CHANNELS)?,
151            sirm_address: read_u64(control, sbrm_reg::SIRM_ADDRESS)?,
152            sirm_length: read_u32(control, sbrm_reg::SIRM_LENGTH)?,
153            eirm_address: read_u64(control, sbrm_reg::EIRM_ADDRESS)?,
154            eirm_length: read_u32(control, sbrm_reg::EIRM_LENGTH)?,
155        })
156    }
157}
158
159// ---------------------------------------------------------------------------
160// SIRM — Streaming Interface Register Map
161// ---------------------------------------------------------------------------
162
163/// SIRM register offsets relative to the SIRM base address.
164mod sirm_reg {
165    pub const SIRM_INFO: u64 = 0x0000;
166    pub const SIRM_CONTROL: u64 = 0x0004;
167    pub const REQ_PAYLOAD_SIZE: u64 = 0x0008;
168    pub const REQ_LEADER_SIZE: u64 = 0x0010;
169    pub const REQ_TRAILER_SIZE: u64 = 0x0014;
170    pub const MAX_LEADER_SIZE: u64 = 0x0018;
171    pub const MAX_TRAILER_SIZE: u64 = 0x001C;
172    pub const PAYLOAD_SIZE: u64 = 0x0020;
173    pub const PAYLOAD_COUNT: u64 = 0x0028;
174    pub const TRANSFER1_SIZE: u64 = 0x002C;
175    pub const TRANSFER2_SIZE: u64 = 0x0030;
176    pub const MAX_PAYLOAD_TRANSFER: u64 = 0x0034;
177}
178
179/// Streaming Interface Register Map — per-stream-channel configuration.
180#[derive(Debug, Clone)]
181pub struct Sirm {
182    pub base: u64,
183    pub info: u32,
184    pub control: u32,
185    pub req_payload_size: u64,
186    pub req_leader_size: u32,
187    pub req_trailer_size: u32,
188    pub max_leader_size: u32,
189    pub max_trailer_size: u32,
190    pub payload_size: u64,
191    pub payload_count: u32,
192    pub transfer1_size: u32,
193    pub transfer2_size: u32,
194    pub max_payload_transfer: u32,
195}
196
197impl Sirm {
198    /// Read the SIRM from the device at the given base address (from SBRM).
199    pub fn read_from<T: UsbTransfer>(
200        control: &mut ControlChannel<T>,
201        base: u64,
202    ) -> Result<Self, U3vError> {
203        let read_u32 = |ctrl: &mut ControlChannel<T>, off: u64| -> Result<u32, U3vError> {
204            let bytes = ctrl.read_mem(base + off, 4)?;
205            Ok(read_u32_be(&bytes))
206        };
207        let read_u64 = |ctrl: &mut ControlChannel<T>, off: u64| -> Result<u64, U3vError> {
208            let bytes = ctrl.read_mem(base + off, 8)?;
209            Ok(read_u64_be(&bytes))
210        };
211
212        Ok(Self {
213            base,
214            info: read_u32(control, sirm_reg::SIRM_INFO)?,
215            control: read_u32(control, sirm_reg::SIRM_CONTROL)?,
216            req_payload_size: read_u64(control, sirm_reg::REQ_PAYLOAD_SIZE)?,
217            req_leader_size: read_u32(control, sirm_reg::REQ_LEADER_SIZE)?,
218            req_trailer_size: read_u32(control, sirm_reg::REQ_TRAILER_SIZE)?,
219            max_leader_size: read_u32(control, sirm_reg::MAX_LEADER_SIZE)?,
220            max_trailer_size: read_u32(control, sirm_reg::MAX_TRAILER_SIZE)?,
221            payload_size: read_u64(control, sirm_reg::PAYLOAD_SIZE)?,
222            payload_count: read_u32(control, sirm_reg::PAYLOAD_COUNT)?,
223            transfer1_size: read_u32(control, sirm_reg::TRANSFER1_SIZE)?,
224            transfer2_size: read_u32(control, sirm_reg::TRANSFER2_SIZE)?,
225            max_payload_transfer: read_u32(control, sirm_reg::MAX_PAYLOAD_TRANSFER)?,
226        })
227    }
228}
229
230impl Sirm {
231    /// Enable streaming by setting bit 0 of the SIRM control register.
232    pub fn enable<T: UsbTransfer>(&self, control: &mut ControlChannel<T>) -> Result<(), U3vError> {
233        let val = self.control | 0x0000_0001;
234        control.write_mem(self.base + sirm_reg::SIRM_CONTROL, &val.to_be_bytes())
235    }
236
237    /// Disable streaming by clearing bit 0 of the SIRM control register.
238    pub fn disable<T: UsbTransfer>(&self, control: &mut ControlChannel<T>) -> Result<(), U3vError> {
239        let val = self.control & !0x0000_0001;
240        control.write_mem(self.base + sirm_reg::SIRM_CONTROL, &val.to_be_bytes())
241    }
242
243    /// Write the requested payload, leader, and trailer sizes to the SIRM.
244    pub fn configure<T: UsbTransfer>(
245        &self,
246        control: &mut ControlChannel<T>,
247        payload_size: u64,
248        leader_size: u32,
249        trailer_size: u32,
250    ) -> Result<(), U3vError> {
251        control.write_mem(
252            self.base + sirm_reg::REQ_PAYLOAD_SIZE,
253            &payload_size.to_be_bytes(),
254        )?;
255        control.write_mem(
256            self.base + sirm_reg::REQ_LEADER_SIZE,
257            &leader_size.to_be_bytes(),
258        )?;
259        control.write_mem(
260            self.base + sirm_reg::REQ_TRAILER_SIZE,
261            &trailer_size.to_be_bytes(),
262        )?;
263        Ok(())
264    }
265}
266
267// ---------------------------------------------------------------------------
268// Manifest table entry (for locating GenICam XML)
269// ---------------------------------------------------------------------------
270
271/// A single entry in the ABRM ManifestTable.
272#[derive(Debug, Clone)]
273pub struct ManifestEntry {
274    pub file_address: u64,
275    pub file_size: u64,
276}
277
278impl ManifestEntry {
279    /// Read the first manifest entry from the manifest table at the given
280    /// address (from ABRM). Only the first entry is read; multi-entry
281    /// tables are uncommon in practice.
282    pub fn read_first<T: UsbTransfer>(
283        control: &mut ControlChannel<T>,
284        table_address: u64,
285    ) -> Result<Self, U3vError> {
286        // Manifest table header: entry count (u32) at offset 0, then entries.
287        // Each entry: 8-byte file info + 8-byte address + 8-byte size.
288        let header = control.read_mem(table_address, 4)?;
289        let count = read_u32_be(&header);
290        if count == 0 {
291            return Err(U3vError::Protocol("manifest table is empty".into()));
292        }
293
294        // First entry starts at offset 8 (after 4-byte count + 4 reserved).
295        let entry_offset = table_address + 8;
296        let entry_data = control.read_mem(entry_offset, 24)?;
297        // Entry layout: [8 bytes file info] [8 bytes address] [8 bytes size]
298        let file_address = read_u64_be(&entry_data[8..16]);
299        let file_size = read_u64_be(&entry_data[16..24]);
300
301        Ok(Self {
302            file_address,
303            file_size,
304        })
305    }
306}
307
308// ---------------------------------------------------------------------------
309// Helpers
310// ---------------------------------------------------------------------------
311
312fn read_u32_be(data: &[u8]) -> u32 {
313    let mut buf = &data[..4];
314    buf.get_u32()
315}
316
317fn read_u64_be(data: &[u8]) -> u64 {
318    let mut buf = &data[..8];
319    buf.get_u64()
320}
321
322fn parse_nul_string(data: &[u8]) -> String {
323    let end = data.iter().position(|&b| b == 0).unwrap_or(data.len());
324    String::from_utf8_lossy(&data[..end]).into_owned()
325}
326
327// ---------------------------------------------------------------------------
328// Tests
329// ---------------------------------------------------------------------------
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334    use crate::usb::MockUsbTransfer;
335    use bytes::{BufMut, BytesMut};
336    use std::sync::Arc;
337
338    const EP_OUT: u8 = 0x01;
339    const EP_IN: u8 = 0x81;
340    const ACK_PREFIX_LE: u32 = 0x4356_3341;
341    const PREFIX_SIZE: usize = 12;
342
343    /// Build a successful ack carrying `payload`.
344    fn success_ack(request_id: u16, payload: &[u8]) -> Vec<u8> {
345        let mut buf = BytesMut::with_capacity(PREFIX_SIZE + payload.len());
346        buf.put_u32_le(ACK_PREFIX_LE);
347        buf.put_u16_le(0x0000); // Success
348        buf.put_u16_le(0x0085); // ReadMem ack opcode
349        buf.put_u16_le(payload.len() as u16);
350        buf.put_u16_le(request_id);
351        buf.extend_from_slice(payload);
352        buf.to_vec()
353    }
354
355    /// Helper: enqueue a sequence of ack responses for sequential read_mem calls.
356    /// Returns the next request_id after all enqueued acks.
357    fn enqueue_read_responses(
358        mock: &MockUsbTransfer,
359        ep_in: u8,
360        start_req_id: u16,
361        payloads: &[Vec<u8>],
362    ) -> u16 {
363        let mut req_id = start_req_id;
364        for payload in payloads {
365            mock.enqueue_read(ep_in, success_ack(req_id, payload));
366            req_id = req_id.wrapping_add(1);
367        }
368        req_id
369    }
370
371    #[test]
372    fn parse_nul_terminated_string() {
373        let mut data = vec![0u8; 64];
374        let s = b"TestCamera";
375        data[..s.len()].copy_from_slice(s);
376        assert_eq!(parse_nul_string(&data), "TestCamera");
377    }
378
379    #[test]
380    fn parse_string_no_nul() {
381        let data = b"FullBuffer64Chars";
382        assert_eq!(parse_nul_string(data), "FullBuffer64Chars");
383    }
384
385    #[test]
386    fn sbrm_read_from_mock() {
387        let mock = Arc::new(MockUsbTransfer::new());
388        let mut ch = ControlChannel::new(Arc::clone(&mock), EP_IN, EP_OUT, 1024, 1024);
389
390        let sbrm_base: u64 = 0x1_0000;
391        let mut req_id: u16 = 0;
392
393        // SBRM fields in read order: u3v_version, max_cmd, max_ack,
394        // num_streams, sirm_addr, sirm_len, eirm_addr, eirm_len
395        let payloads: Vec<Vec<u8>> = vec![
396            0x0001_0000u32.to_be_bytes().to_vec(), // u3v_version
397            1024u32.to_be_bytes().to_vec(),        // max_cmd_transfer
398            1024u32.to_be_bytes().to_vec(),        // max_ack_transfer
399            1u32.to_be_bytes().to_vec(),           // num_stream_channels
400            0x0002_0000u64.to_be_bytes().to_vec(), // sirm_address
401            256u32.to_be_bytes().to_vec(),         // sirm_length
402            0x0003_0000u64.to_be_bytes().to_vec(), // eirm_address
403            64u32.to_be_bytes().to_vec(),          // eirm_length
404        ];
405        req_id = enqueue_read_responses(&mock, EP_IN, req_id, &payloads);
406        let _ = req_id;
407
408        let sbrm = Sbrm::read_from(&mut ch, sbrm_base).unwrap();
409        assert_eq!(sbrm.u3v_version, 0x0001_0000);
410        assert_eq!(sbrm.max_cmd_transfer, 1024);
411        assert_eq!(sbrm.max_ack_transfer, 1024);
412        assert_eq!(sbrm.num_stream_channels, 1);
413        assert_eq!(sbrm.sirm_address, 0x0002_0000);
414        assert_eq!(sbrm.sirm_length, 256);
415        assert_eq!(sbrm.eirm_address, 0x0003_0000);
416        assert_eq!(sbrm.eirm_length, 64);
417    }
418
419    #[test]
420    fn manifest_entry_read_first() {
421        let mock = Arc::new(MockUsbTransfer::new());
422        let mut ch = ControlChannel::new(Arc::clone(&mock), EP_IN, EP_OUT, 1024, 1024);
423
424        let table_addr: u64 = 0x5000;
425        // Header: count = 1
426        mock.enqueue_read(EP_IN, success_ack(0, &1u32.to_be_bytes()));
427        // Entry: [8 bytes info][8 bytes address][8 bytes size]
428        let mut entry = BytesMut::with_capacity(24);
429        entry.put_u64(0); // file info (version, schema, etc.)
430        entry.put_u64(0x0010_0000); // file_address
431        entry.put_u64(4096); // file_size
432        mock.enqueue_read(EP_IN, success_ack(1, &entry));
433
434        let manifest = ManifestEntry::read_first(&mut ch, table_addr).unwrap();
435        assert_eq!(manifest.file_address, 0x0010_0000);
436        assert_eq!(manifest.file_size, 4096);
437    }
438}