Skip to main content

crazyflie_lib/subsystems/memory/
deckmem.rs

1use std::{sync::Arc, time::Duration};
2
3use crate::{
4    Error, Result,
5    subsystems::memory::{MemoryBackend, memory_types},
6};
7use memory_types::{FromMemoryBackend, MemoryType};
8use tokio::{sync::Mutex, time::sleep};
9
10const DECKMEM_VERSION_REQUIREMENT: u8 = 3;
11
12// Bit field 1 masks (0x0000)
13const IS_VALID_MASK: u8 = 0x01;
14const IS_STARTED_MASK: u8 = 0x02;
15const SUPPORTS_READ_MASK: u8 = 0x04;
16const SUPPORTS_WRITE_MASK: u8 = 0x08;
17const SUPPORTS_UPGRADE_MASK: u8 = 0x10;
18const UPGRADE_REQUIRED_MASK: u8 = 0x20;
19const BOOTLOADER_ACTIVE_MASK: u8 = 0x40;
20
21// Bit field 2 masks (0x0001)
22const CAN_RESET_TO_FIRMWARE_MASK: u8 = 0x01;
23const CAN_RESET_TO_BOOTLOADER_MASK: u8 = 0x02;
24
25const DECKMEM_MAX_SECTIONS: usize = 8;
26const DECKMEM_INFO_OFFSET: usize = 1;
27const DECKMEM_INFO_SIZE: usize = 0x20;
28const DECKMEM_CMD_OFFSET: usize = 0x1000;
29const DECKMEM_CMD_SIZE: usize = 0x20;
30const DECKMEM_CMD_NEW_FW_SIZE_OFFSET: usize = 0x0;
31const DECKMEM_CMD_BITS_OFFSET: usize = 0x4;
32
33const DECKMEM_CMD_RST_TO_FIRMWARE: u8 = 0x01;
34const DECKMEM_CMD_RST_TO_BOOTLOADER: u8 = 0x02;
35
36/// Describes the content of a Crazyflie deck memory used to access the deck firmware and bootloaders
37#[derive(Debug)]
38pub struct DeckMemory {
39    /// Thread-safe reference to the underlying memory backend
40    memory: Arc<Mutex<MemoryBackend>>,
41    /// The memory sections available in the deck memory (each one corresponds to the primary
42    /// or secondary memory of a deck)
43    sections: Vec<DeckMemorySection>,
44}
45
46impl FromMemoryBackend for DeckMemory {
47    async fn from_memory_backend(memory: MemoryBackend) -> Result<Self> {
48        if memory.memory_type == MemoryType::DeckMemory {
49            Ok(DeckMemory::new(memory).await?)
50        } else {
51            Err(Error::MemoryError("Wrong type of memory!".to_owned()))
52        }
53    }
54
55    async fn initialize_memory_backend(_memory: MemoryBackend) -> Result<Self> {
56        Err(Error::MemoryError(
57            "Memory does not support initializing".to_owned(),
58        ))
59    }
60
61    fn close_memory(mut self) -> MemoryBackend {
62        // Drop all sections to release their Arc references
63        self.sections.clear();
64
65        // Return backend
66        Arc::try_unwrap(self.memory)
67            .map_err(|_arc| {
68                Error::MemoryError(format!("Multiple references to memory"))
69            })
70            .map(|mutex| mutex.into_inner())
71            .expect("Multiple reference to sections still held")
72    }
73}
74
75#[derive(Debug)]
76/// Represents a memory section for a deck in the Crazyflie system.
77///
78/// This structure contains information about a deck's memory configuration,
79/// including its capabilities (read, write, upgrade) and memory layout (addresses
80/// for base, command, and info).
81pub struct DeckMemorySection {
82    /// Whether the deck supports read operations
83    supports_read: bool,
84    /// Whether the deck supports write operations
85    supports_write: bool,
86    /// Whether the deck supports firmware upgrades
87    supports_upgrade: bool,
88    /// Whether the deck can be reset to run firmware
89    can_reset_to_firmware: bool,
90    /// Whether the deck can be reset to bootloader mode
91    can_reset_to_bootloader: bool,
92    /// Optional hash value required for firmware validation
93    required_hash: Option<u32>,
94    /// Optional expected length of the firmware
95    required_length: Option<u32>,
96    /// The base memory address for this deck section
97    base_address: usize,
98    /// The memory address used for sending commands
99    command_address: usize,
100    /// The memory address containing deck information
101    info_address: usize,
102    /// The name identifier of the deck
103    name: String,
104    /// Thread-safe reference to the underlying memory backend
105    memory: Arc<Mutex<MemoryBackend>>,
106}
107
108impl DeckMemorySection {
109    async fn from_bytes(
110        memory: Arc<Mutex<MemoryBackend>>,
111        info_offset: usize,
112        command_address: usize,
113    ) -> Result<Option<Self>> {
114        let data = memory
115            .lock()
116            .await
117            .read::<fn(usize, usize)>(info_offset, DECKMEM_INFO_SIZE, None)
118            .await?;
119
120        // Validate minimum data length for parsing so we don't panic later
121        if data.len() < DECKMEM_INFO_SIZE {
122            return Ok(None);
123        }
124
125        // Only cache data which is not changed between restarts of the Crazyflie
126
127        let bit_field_1 = data[0];
128        let is_valid = (bit_field_1 & IS_VALID_MASK) != 0;
129        let supports_read = (bit_field_1 & SUPPORTS_READ_MASK) != 0;
130        let supports_write = (bit_field_1 & SUPPORTS_WRITE_MASK) != 0;
131        let supports_upgrade = (bit_field_1 & SUPPORTS_UPGRADE_MASK) != 0;
132
133        let bit_field_2 = data[1];
134        let can_reset_to_firmware = (bit_field_2 & CAN_RESET_TO_FIRMWARE_MASK) != 0;
135        let can_reset_to_bootloader = (bit_field_2 & CAN_RESET_TO_BOOTLOADER_MASK) != 0;
136
137        let required_hash = u32::from_le_bytes([data[2], data[3], data[4], data[5]]);
138        let required_length = u32::from_le_bytes([data[6], data[7], data[8], data[9]]);
139        let base_address = u32::from_le_bytes([data[10], data[11], data[12], data[13]]);
140
141        // Parse name (offset 0x000E / 14, up to 19 bytes including null terminator, zero terminated)
142        let name_bytes = &data[14..32];
143        let name = name_bytes
144            .iter()
145            .take_while(|&&b| b != 0)
146            .copied()
147            .collect::<Vec<u8>>();
148        let name = String::from_utf8_lossy(&name).to_string();
149
150        if is_valid {
151            Ok(Some(DeckMemorySection {
152                supports_read,
153                supports_write,
154                supports_upgrade,
155                can_reset_to_firmware,
156                can_reset_to_bootloader,
157                required_hash: match required_hash {
158                    0 => None,
159                    v => Some(v),
160                },
161                required_length: match required_length {
162                    0 => None,
163                    v => Some(v),
164                },
165                base_address: base_address as usize,
166                command_address: command_address,
167                info_address: info_offset,
168                name,
169                memory,
170            }))
171        } else {
172            Ok(None)
173        }
174    }
175
176    async fn read_info_byte(&self) -> Result<u8> {
177        let data = self.memory
178            .lock()
179            .await
180            .read::<fn(usize, usize)>(self.info_address, 1, None)
181            .await?;
182        Ok(data[0])
183    }
184
185    /// Returns whether the deck has been started.
186    pub async fn is_started(&self) -> Result<bool> {
187        let data = self.read_info_byte().await?;
188        Ok((data & IS_STARTED_MASK) != 0)
189    }
190
191    /// Returns whether this deck supports read operations.
192    pub fn supports_read(&self) -> bool {
193        self.supports_read
194    }
195
196    /// Returns whether this deck supports write operations.
197    pub fn supports_write(&self) -> bool {
198        self.supports_write
199    }
200
201    /// Returns whether this deck supports firmware upgrades.
202    pub fn supports_upgrade(&self) -> bool {
203        self.supports_upgrade
204    }
205
206    /// Returns whether a firmware upgrade is required for this deck.
207    pub async fn upgrade_required(&self) -> Result<bool> {
208        let data = self.read_info_byte().await?;
209        Ok((data & UPGRADE_REQUIRED_MASK) != 0)
210    }
211
212    /// Returns whether the bootloader is currently active on this deck.
213    pub async fn bootloader_active(&self) -> Result<bool> {
214        let data = self.read_info_byte().await?;
215        Ok((data & BOOTLOADER_ACTIVE_MASK) != 0)
216    }
217
218    /// Returns whether this deck can be reset to firmware mode.
219    pub fn can_reset_to_firmware(&self) -> bool {
220        self.can_reset_to_firmware
221    }
222
223    /// Returns whether this deck can be reset to bootloader mode.
224    pub fn can_reset_to_bootloader(&self) -> bool {
225        self.can_reset_to_bootloader
226    }
227
228    /// Returns the required hash for firmware verification, if any.
229    pub fn required_hash(&self) -> Option<u32> {
230        self.required_hash
231    }
232
233    /// Returns the required firmware length, if any.
234    pub fn required_length(&self) -> Option<u32> {
235        self.required_length
236    }
237
238    /// Returns the name of this memory section.
239    pub fn name(&self) -> &str {
240        &self.name
241    }
242
243    /// Flash a complete firmware binary to this deck section.
244    ///
245    /// Writes the firmware size to the deck's command section, then writes the
246    /// firmware bytes to the section's base address. The size step is required
247    /// by some deck flashers (e.g. AI-deck ESP and GAP8) and silently ignored
248    /// by others; bundling it into this single call ensures it is never
249    /// skipped.
250    ///
251    /// # Errors
252    /// Returns an error if the section does not support firmware upgrade or
253    /// if any underlying write fails.
254    pub async fn flash_firmware(&self, data: &[u8]) -> Result<()> {
255        if !self.supports_upgrade {
256            return Err(Error::MemoryError(
257                "Section does not support firmware upgrade".to_owned(),
258            ));
259        }
260
261        let size = u32::try_from(data.len()).map_err(|_| {
262            Error::MemoryError(format!(
263                "Firmware too large: {} bytes exceeds u32::MAX",
264                data.len()
265            ))
266        })?;
267        self.write_new_firmware_size(size).await?;
268        self.write(0, data).await
269    }
270
271    /// Flash a complete firmware binary to this deck section, reporting
272    /// progress on the data write.
273    ///
274    /// The callback is invoked with `(bytes_written_so_far, total_bytes)`
275    /// during the data phase. The size-write phase is a fixed 4 bytes and is
276    /// not reported.
277    ///
278    /// # Errors
279    /// Returns an error if the section does not support firmware upgrade or
280    /// if any underlying write fails.
281    pub async fn flash_firmware_with_progress<F>(
282        &self,
283        data: &[u8],
284        progress_callback: F,
285    ) -> Result<()>
286    where
287        F: FnMut(usize, usize),
288    {
289        if !self.supports_upgrade {
290            return Err(Error::MemoryError(
291                "Section does not support firmware upgrade".to_owned(),
292            ));
293        }
294
295        let size = u32::try_from(data.len()).map_err(|_| {
296            Error::MemoryError(format!(
297                "Firmware too large: {} bytes exceeds u32::MAX",
298                data.len()
299            ))
300        })?;
301        self.write_new_firmware_size(size).await?;
302        self.write_with_progress(0, data, progress_callback).await
303    }
304
305    async fn write_new_firmware_size(&self, size: u32) -> Result<()> {
306        self.memory
307            .lock()
308            .await
309            .write::<fn(usize, usize)>(
310                self.command_address + DECKMEM_CMD_NEW_FW_SIZE_OFFSET,
311                &size.to_le_bytes(),
312                None,
313            )
314            .await
315    }
316
317    /// Reset the MCU connected to this memory section into bootloader mode.
318    ///
319    /// # Returns
320    /// A `Result` indicating success or failure of the reset operation
321    /// # Errors
322    /// Returns an `Error` if the section does not support resetting to bootloader
323    /// or if the reset operation fails
324    pub async fn reset_to_bootloader(&self) -> Result<()> {
325        if !self.can_reset_to_bootloader {
326            return Err(Error::MemoryError(
327                "Section cannot reset to bootloader".to_owned(),
328            ));
329        }
330
331        // Write to specific address to trigger reset
332        self.memory
333            .lock()
334            .await
335            .write::<fn(usize, usize)>(self.command_address + DECKMEM_CMD_BITS_OFFSET, &[DECKMEM_CMD_RST_TO_BOOTLOADER], None)
336            .await?;
337
338        // Sleep for 10 ms to allow the reset to complete
339        sleep(Duration::from_millis(10)).await;
340
341        Ok(())
342    }
343
344    /// Reset the MCU connected to this memory section into firmware mode.
345    ///
346    /// # Returns
347    /// A `Result` indicating success or failure of the reset operation
348    /// # Errors
349    /// Returns an `Error` if the section does not support resetting to firmware
350    /// or if the reset operation fails
351    pub async fn reset_to_firmware(&self) -> Result<()> {
352        if !self.can_reset_to_firmware {
353            return Err(Error::MemoryError(
354                "Section cannot reset to firmware".to_owned(),
355            ));
356        }
357
358        // Write to specific address to trigger reset
359        self.memory
360            .lock()
361            .await
362            .write::<fn(usize, usize)>(self.command_address + DECKMEM_CMD_BITS_OFFSET, &[DECKMEM_CMD_RST_TO_FIRMWARE], None)
363            .await?;
364
365        // Sleep for 10 ms to allow the reset to complete
366        sleep(Duration::from_millis(10)).await;
367
368        Ok(())
369    }
370
371    /// Write data to the memory section at the specified address.
372    ///
373    /// # Arguments
374    /// * `address` - The address within the memory section to write to.
375    /// * `data` - The data to write.
376    ///
377    /// # Returns
378    /// A `Result` indicating success or failure of the write operation.
379    /// # Errors
380    /// Returns an `Error` if the section does not support writing or if the write operation fails.
381    async fn write(&self, address: usize, data: &[u8]) -> Result<()> {
382        if !self.supports_write {
383            return Err(Error::MemoryError(
384                "Section does not support write".to_owned(),
385            ));
386        }
387
388        self.memory
389            .lock()
390            .await
391            .write::<fn(usize, usize)>(self.base_address + address, data, None)
392            .await
393    }
394
395    /// Write data to the memory section at the specified address with progress reporting.
396    ///
397    /// # Arguments
398    /// * `address` - The address within the memory section to write to.
399    /// * `data` - The data to write.
400    /// * `progress_callback` - A callback function that takes two usize arguments:
401    ///   the number of bytes written so far and the total number of bytes to write.
402    ///
403    /// # Returns
404    /// A `Result` indicating success or failure of the write operation.
405    /// # Errors
406    /// Returns an `Error` if the section does not support writing or if the write operation fails.
407    async fn write_with_progress<F>(
408        &self,
409        address: usize,
410        data: &[u8],
411        progress_callback: F,
412    ) -> Result<()>
413    where
414        F: FnMut(usize, usize),
415    {
416        if !self.supports_write {
417            return Err(Error::MemoryError(
418                "Section does not support write".to_owned(),
419            ));
420        }
421
422        self.memory
423            .lock()
424            .await
425            .write(self.base_address + address, data, Some(progress_callback))
426            .await
427    }
428
429    /// Read data from the memory section at the specified address.
430    ///
431    /// # Arguments
432    /// * `address` - The address within the memory section to read from.
433    /// * `length` - The number of bytes to read.
434    /// # Returns
435    /// A `Result` containing a vector of bytes read from the memory section or an `Error` if the operation fails.
436    pub async fn read(&self, address: usize, length: usize) -> Result<Vec<u8>> {
437        if !self.supports_read {
438            return Err(Error::MemoryError(
439                "Section does not support read".to_owned(),
440            ));
441        }
442
443        self
444            .memory
445            .lock()
446            .await
447            .read::<fn(usize, usize)>(self.base_address + address, length, None)
448            .await
449    }
450
451    /// Read data from the memory section at the specified address with progress reporting.
452    ///
453    /// # Arguments
454    /// * `address` - The address within the memory section to read from.
455    /// * `length` - The number of bytes to read.
456    /// * `progress_callback` - A callback function that takes two usize arguments:
457    ///   the number of bytes read so far and the total number of bytes to read.
458    /// # Returns
459    /// A `Result` containing a vector of bytes read from the memory section or an `Error` if the operation fails.
460    pub async fn read_with_progress<F>(
461        &self,
462        address: usize,
463        length: usize,
464        progress_callback: F,
465    ) -> Result<Vec<u8>>
466    where
467        F: FnMut(usize, usize),
468    {
469        if !self.supports_read {
470            return Err(Error::MemoryError(
471                "Section does not support read".to_owned(),
472            ));
473        }
474
475        self.memory
476            .lock()
477            .await
478            .read(self.base_address + address, length, Some(progress_callback))
479            .await
480    }
481}
482
483impl DeckMemory {
484    pub(crate) async fn new(memory: MemoryBackend) -> Result<Self> {
485        let sharable_memory = Arc::new(Mutex::new(memory));
486
487        let info = sharable_memory
488            .lock()
489            .await
490            .read::<fn(usize, usize)>(0, 1, None)
491            .await?;
492
493        // Parse version byte
494        let version = info[0];
495        if version != DECKMEM_VERSION_REQUIREMENT {
496            return Err(Error::MemoryError(format!(
497                "Unsupported deck memory version: {}",
498                version
499            )));
500        }
501
502        let mut sections: Vec<DeckMemorySection> = Vec::new();
503        for i in 0..DECKMEM_MAX_SECTIONS {
504            let info_base = DECKMEM_INFO_OFFSET + i * DECKMEM_INFO_SIZE;
505            let cmd_base = DECKMEM_CMD_OFFSET + i * DECKMEM_CMD_SIZE;
506            if let Some(section) =
507                DeckMemorySection::from_bytes(sharable_memory.clone(), info_base, cmd_base).await?
508            {
509                sections.push(section);
510            }
511        }
512
513        Ok(DeckMemory {
514            memory: sharable_memory,
515            sections,
516        })
517    }
518
519    /// Get all memory sections available in this deck memory.
520    /// # Returns
521    /// A slice of `DeckMemorySection` representing all available sections.
522    pub fn sections(&self) -> &[DeckMemorySection] {
523        &self.sections
524    }
525
526    /// Get a memory section by name.
527    /// # Arguments
528    /// * `name` - The name of the memory section to retrieve.
529    /// # Returns
530    /// An `Option` containing a reference to the `DeckMemorySection` if found, or `None` if not found.
531    pub fn section(&self, name: &str) -> Option<&DeckMemorySection> {
532        self.sections.iter().find(|s| s.name == name)
533    }
534}