waverave_hackrf/
debug.rs

1/*!
2Debug operations for the HackRF.
3
4The HackRF exposes a number of internal debugging operations through the
5[`Debug`][struct@Debug] struct, allowing for direct read & write of most
6peripheral ICs. This is also how it can be reprogrammed without entering the DFU
7mode.
8
9The way to do this with a HackRF is by calling [`HackRf::debug`] with an open
10peripheral, like so:
11
12```no_run
13
14# use anyhow::Result;
15# #[tokio::main]
16# async fn main() -> Result<()> {
17
18use waverave_hackrf::debug::*;
19
20let mut hackrf = waverave_hackrf::open_hackrf()?;
21// Mutably borrow - make sure no other operations are in progress.
22let mut debug = hackrf.debug();
23
24// Just grab the internal state of the M0 processor and dump it
25let state = debug.get_m0_state().await?;
26dbg!(&state);
27
28# Ok(())
29# }
30```
31
32 */
33use std::ops::Range;
34
35use nusb::transfer::{ControlIn, ControlOut, ControlType, Recipient};
36
37use crate::{ControlRequest, Error, HackRf, TransceiverMode};
38
39/// Internal state data of the Cortex M0 sub-processor.
40///
41/// This can be retrieved with [`Debug::get_m0_state`].
42///
43/// The best use of this struct is that it will show any shortfalls that
44/// occurred during the last TX/RX/Sweep operation, i.e. a TX underrun or RX
45/// overrun.
46///
47#[repr(C)]
48#[derive(Clone, Copy, Debug, bytemuck::Zeroable, bytemuck::Pod)]
49pub struct M0State {
50    /// Requested Mode. Possible values are: 0(IDLE), 1(WAIT), 2(RX),
51    /// 3(TX_START), 4(TX_RUN)
52    pub requested_mode: u16,
53    /// Request flag, 0 means request is completed, any other value means
54    /// request is pending
55    pub request_flag: u16,
56    /// Active mode. Possible values are: 0(IDLE), 1(WAIT), 2(RX), 3(TX_START),
57    /// 4(TX_RUN)
58    pub active_mode: u32,
59    /// Number of bytes transferred by M0
60    pub m0_count: u32,
61    /// Number of bytes transferred by M4
62    pub m4_count: u32,
63    /// Number of shortfalls
64    pub num_shortfalls: u32,
65    /// Longest shortfall in bytes
66    pub longest_shortfall: u32,
67    /// Shortfall limit in bytes
68    pub shortfall_limit: u32,
69    /// Threshold `m0_count` value in bytes for next mode change
70    pub threshold: u32,
71    /// Mode which will be switched to when threshold is reached.
72    pub next_mode: u32,
73    /// Error, if any, that caused M0 to revert to IDLE mode. Possible values
74    /// are: 0 (NONE), 1 (RX_TIMEOUT), 2 (TX_TIMEOUT), or 3 (MISSED_DEADLINE)
75    pub error: u32,
76}
77
78impl M0State {
79    fn le_convert(&mut self) {
80        self.requested_mode = self.requested_mode.to_le();
81        self.request_flag = self.request_flag.to_le();
82        self.active_mode = self.active_mode.to_le();
83        self.m0_count = self.m0_count.to_le();
84        self.m4_count = self.m4_count.to_le();
85        self.num_shortfalls = self.num_shortfalls.to_le();
86        self.longest_shortfall = self.longest_shortfall.to_le();
87        self.shortfall_limit = self.shortfall_limit.to_le();
88        self.threshold = self.threshold.to_le();
89        self.next_mode = self.next_mode.to_le();
90        self.error = self.error.to_le();
91    }
92}
93
94/// Debug operations for the HackRF, including programming operations.
95///
96/// Borrows the interface while doing operations.
97///
98/// The way to get to this struct is by calling [`HackRf::debug`] with an open
99/// peripheral, like so:
100///
101/// ```no_run
102///
103/// # use anyhow::Result;
104/// # #[tokio::main]
105/// # async fn main() -> Result<()> {
106///
107/// use waverave_hackrf::debug::*;
108///
109/// let mut hackrf = waverave_hackrf::open_hackrf()?;
110/// // Mutably borrow - make sure no other operations are in progress.
111/// let mut debug = hackrf.debug();
112///
113/// // Just grab the internal state of the M0 processor and dump it
114/// let state = debug.get_m0_state().await?;
115/// dbg!(&state);
116///
117/// # Ok(())
118/// # }
119/// ```
120pub struct Debug<'a> {
121    inner: &'a mut HackRf,
122}
123
124impl<'a> Debug<'a> {
125    pub(crate) fn new(inner: &'a mut HackRf) -> Debug<'a> {
126        Self { inner }
127    }
128
129    /// Get the internal state of the M0 code of the LPC43xx MCU.
130    ///
131    /// Requires API version 0x0106 or higher.
132    pub async fn get_m0_state(&self) -> Result<M0State, Error> {
133        self.inner.api_check(0x0106)?;
134        let mut v: M0State = self.inner.read_struct(ControlRequest::GetM0State).await?;
135        v.le_convert();
136        Ok(v)
137    }
138
139    /// Access the attached SPI flash.
140    ///
141    /// See [`SpiFlash`] for what to do with it.
142    pub fn spi_flash(&self) -> SpiFlash<'_> {
143        SpiFlash { inner: self.inner }
144    }
145
146    /// Update the XC2C64A-7VQ100C CPLD with a new bitstream.
147    ///
148    /// After every transfer completes, an optional callback will be invoked
149    /// with the number of bytes transferred as the first argument, and the
150    /// total number of bytes to be transferred as the second argument.
151    pub async fn cpld_write<F>(&mut self, data: &[u8], mut callback: Option<F>) -> Result<(), Error>
152    where
153        F: FnMut(usize, usize),
154    {
155        const CHUNK_SIZE: usize = 512;
156        self.inner
157            .set_transceiver_mode(TransceiverMode::CpldUpdate)
158            .await?;
159        let mut sent = 0;
160        let total = data.len();
161        let queue = &mut self.inner.tx.queue;
162        for chunk in data.chunks(CHUNK_SIZE) {
163            let mut buf = if queue.pending() != 0 {
164                let resp = queue.next_complete().await.into_result()?;
165                sent += resp.actual_length();
166                if let Some(ref mut c) = callback {
167                    c(sent, total);
168                }
169                resp.reuse()
170            } else {
171                Vec::with_capacity(CHUNK_SIZE)
172            };
173            buf.copy_from_slice(chunk);
174            queue.submit(buf);
175        }
176        while queue.pending() != 0 {
177            let resp = queue.next_complete().await.into_result()?;
178            sent += resp.actual_length();
179            if let Some(ref mut c) = callback {
180                c(sent, total);
181            }
182        }
183        self.inner
184            .set_transceiver_mode(TransceiverMode::Off)
185            .await?;
186        Ok(())
187    }
188
189    /// Get the checksum of the CPLD bitstream.
190    ///
191    /// Requires API version 0x0103 or higher.
192    pub async fn cpld_checksum(&self) -> Result<u32, Error> {
193        self.inner.api_check(0x0103)?;
194        let ret = self
195            .inner
196            .read_bytes(ControlRequest::CpldChecksum, 4)
197            .await?;
198        let ret: [u8; 4] = ret.as_slice().try_into().map_err(|_| Error::ReturnData)?;
199        Ok(u32::from_le_bytes(ret))
200    }
201
202    /// Read a register from the SI5351C.
203    pub async fn si5351c_read(&self, register: u8) -> Result<u8, Error> {
204        self.inner
205            .read_u8(ControlRequest::Si5351cRead, register as u16)
206            .await
207    }
208
209    /// Write a register to the SI5351C.
210    pub async fn si5351c_write(&self, register: u8, value: u8) -> Result<(), Error> {
211        self.inner
212            .write_u8(ControlRequest::Si5351cWrite, register as u16, value)
213            .await
214    }
215
216    /// Read a register from the RFFC5071.
217    pub async fn rffc5071_read(&self, register: u8) -> Result<u16, Error> {
218        if register >= 31 {
219            return Err(Error::AddressRange {
220                range: Range { start: 0, end: 31 },
221                addr: register as u32,
222            });
223        }
224
225        self.inner
226            .read_u16(ControlRequest::Rffc5071Read, register as u16)
227            .await
228    }
229
230    /// Write a register to the RFFC5071.
231    pub async fn rffc5071_write(&self, register: u8, value: u16) -> Result<(), Error> {
232        if register >= 31 {
233            return Err(Error::AddressRange {
234                range: Range { start: 0, end: 31 },
235                addr: register as u32,
236            });
237        }
238
239        self.inner
240            .write_u16(ControlRequest::Rffc5071Write, register as u16, value)
241            .await
242    }
243
244    /// Read a register from the MAX2837.
245    pub async fn max2837_read(&self, register: u8) -> Result<u16, Error> {
246        if register >= 32 {
247            return Err(Error::AddressRange {
248                range: Range { start: 0, end: 32 },
249                addr: register as u32,
250            });
251        }
252
253        self.inner
254            .read_u16(ControlRequest::Max2837Read, register as u16)
255            .await
256    }
257
258    /// Write a register to the MAX2837.
259    pub async fn max2837_write(&self, register: u8, value: u16) -> Result<(), Error> {
260        if register >= 32 {
261            return Err(Error::AddressRange {
262                range: Range { start: 0, end: 32 },
263                addr: register as u32,
264            });
265        }
266
267        if value >= 0x400 {
268            return Err(Error::ValueRange {
269                range: Range {
270                    start: 0,
271                    end: 0x400,
272                },
273                val: value as u32,
274            });
275        }
276
277        self.inner
278            .write_u16(ControlRequest::Max2837Write, register as u16, value)
279            .await
280    }
281}
282
283/// Accessor for the W25Q80BV SPI Flash in the HackRF.
284///
285/// ⚠️ WARNING: This allows for directly manipulating the SPI flash, which is a
286/// great way to brick your HackRF and require [recovering through DFU mode][dfu].
287///
288/// The general write procedure is to [erase][SpiFlash::erase] the flash,
289/// [write][SpiFlash::write] all bytes to it starting from address 0, then
290/// verify by [reading][SpiFlash::read] the flash to verify a successful write.
291/// As long as you don't depower it after a flash operation, a failed write
292/// attempt can potentially be repeated, as it appears the HackRF only accesses
293/// flash during initial boot and configuration.
294///
295/// [dfu]: https://hackrf.readthedocs.io/en/latest/updating_firmware.html#only-if-necessary-recovering-the-spi-flash-firmware
296pub struct SpiFlash<'a> {
297    inner: &'a HackRf,
298}
299
300impl SpiFlash<'_> {
301    /// Erase the entire flash memory.
302    ///
303    /// Should be immediately followed by writing a new image, or the HackRF
304    /// will be soft-bricked (but recoverable by DFU).
305    pub async fn erase(&self) -> Result<(), Error> {
306        self.inner
307            .write_u8(ControlRequest::SpiflashErase, 0, 0)
308            .await
309    }
310
311    /// Write firmware to the flash memory.
312    ///
313    /// Should only be used for firmware image writing, and needs to be
314    /// preceeded by an erase command before doing a write sequence.
315    ///
316    /// Writes can be up to the max size of the memory; this command will split
317    /// them into sub-commands if needed.
318    pub async fn write(&self, addr: u32, data: &[u8]) -> Result<(), Error> {
319        const END_ADDR: u32 = 0x100000;
320        if addr >= END_ADDR {
321            return Err(Error::AddressRange {
322                range: Range {
323                    start: 0,
324                    end: END_ADDR,
325                },
326                addr,
327            });
328        }
329
330        if (data.len() + addr as usize) > (END_ADDR as usize) {
331            let end = END_ADDR - addr;
332            return Err(Error::ValueRange {
333                range: Range { start: 0, end },
334                val: data.len() as u32,
335            });
336        }
337
338        let mut addr = addr;
339        let mut data = data;
340        let mut chunk: &[u8];
341        while !data.is_empty() {
342            // Split so that all writes are within a 256-byte page.
343            let len = (0x100 - ((addr & 0xff) as usize)).min(data.len());
344            (chunk, data) = data.split_at(len);
345            self.inner
346                .interface
347                .control_out(ControlOut {
348                    control_type: ControlType::Vendor,
349                    recipient: Recipient::Device,
350                    request: ControlRequest::SpiflashWrite as u8,
351                    value: (addr >> 16) as u16,
352                    index: (addr & 0xFFFF) as u16,
353                    data: chunk,
354                })
355                .await
356                .into_result()?;
357            addr += len as u32;
358        }
359        Ok(())
360    }
361
362    /// Read from the flash memory.
363    ///
364    /// This should only be used for firmware verification.
365    ///
366    /// Reads can be up to the max size of the memory; this command will split
367    /// them into sub-commands if needed.
368    pub async fn read(&self, addr: u32, len: usize) -> Result<Vec<u8>, Error> {
369        const END_ADDR: u32 = 0x10_0000;
370        if addr >= END_ADDR {
371            return Err(Error::AddressRange {
372                range: Range {
373                    start: 0,
374                    end: END_ADDR,
375                },
376                addr,
377            });
378        }
379
380        if (len + addr as usize) > (END_ADDR as usize) {
381            let end = END_ADDR - addr;
382            return Err(Error::ValueRange {
383                range: Range { start: 0, end },
384                val: len as u32,
385            });
386        }
387
388        let mut addr = addr;
389        let mut data = Vec::with_capacity(len);
390        while data.len() < len {
391            // Read from one 256-byte page at a time, dividing up as needed.
392            let block_len = (0x100 - ((addr & 0xff) as usize)).min(len - data.len());
393            let resp = self
394                .inner
395                .interface
396                .control_in(ControlIn {
397                    control_type: ControlType::Vendor,
398                    recipient: Recipient::Device,
399                    request: ControlRequest::SpiflashRead as u8,
400                    value: (addr >> 16) as u16,
401                    index: (addr & 0xFFFF) as u16,
402                    length: block_len as u16,
403                })
404                .await
405                .into_result()?;
406            data.extend_from_slice(&resp);
407            addr += resp.len() as u32;
408        }
409        Ok(data)
410    }
411
412    /// Get the status registers of the W25Q80BV flash memory.
413    ///
414    /// Requires API version 0x0103 or higher.
415    pub async fn status(&self) -> Result<[u8; 2], Error> {
416        self.inner.api_check(0x0103)?;
417        let val = self
418            .inner
419            .read_u16(ControlRequest::SpiflashStatus, 0)
420            .await?;
421        Ok(val.to_le_bytes())
422    }
423
424    /// Clear the status registers of the W25Q80BV flash memory.
425    pub async fn clear_status(&self) -> Result<(), Error> {
426        self.inner.api_check(0x0103)?;
427        self.inner
428            .write_u16(ControlRequest::SpiflashClearStatus, 0, 0)
429            .await
430    }
431}