elf2flash_core/boards/
mod.rs

1mod circuit_playground_bluefruit;
2mod rp2040;
3mod rp2350;
4
5pub use circuit_playground_bluefruit::CircuitPlaygroundBluefruit;
6pub use rp2040::RP2040;
7pub use rp2350::RP2350;
8use thiserror::Error;
9
10/// This is a helper struct, which allows you to iterate over every board defined
11pub struct BoardIter {
12    inner: std::vec::IntoIter<Box<dyn BoardInfo>>,
13}
14
15impl BoardIter {
16    /// Creates a new BoardIter
17    pub fn new() -> Self {
18        Self {
19            inner: vec![
20                Box::new(RP2040::default()) as Box<dyn BoardInfo>,
21                Box::new(RP2350::default()),
22                Box::new(CircuitPlaygroundBluefruit::default()),
23            ]
24            .into_iter(),
25        }
26    }
27
28    pub fn find_by_name(name: &str) -> Option<Box<dyn BoardInfo>> {
29        for board in Self::new() {
30            if board.board_name().eq_ignore_ascii_case(name) {
31                return Some(board);
32            }
33        }
34        None
35    }
36}
37
38impl Iterator for BoardIter {
39    type Item = Box<dyn BoardInfo>;
40    fn next(&mut self) -> Option<Self::Item> {
41        self.inner.next()
42    }
43}
44
45/// This is the version of the firmware on the usb device
46#[allow(unused)]
47#[derive(Debug, Clone)]
48pub struct UsbVersion(pub u8, pub u8, pub u8);
49
50/// This is the usb device information from the usb device. It is possible to generate this information with something like
51/// rusb
52#[allow(unused)]
53#[derive(Debug, Clone)]
54pub struct UsbDevice {
55    pub bus_number: u8,
56    pub address: u8,
57    pub vendor_id: u16,
58    pub product_id: u16,
59    pub version: UsbVersion,
60}
61
62/// This trait helps by allowing for definitions of multiple different boards.
63pub trait BoardInfo {
64    /// Check if the board is connected to the specified UsbDevice
65    fn is_device_board(&self, device: &UsbDevice) -> bool;
66
67    /// Returns the proper family id to use for the uf2 device
68    fn family_id(&self) -> u32;
69
70    /// Optional, just sent to a sensible default of 256, as long as it is less than 512 - 32 it should be okay, but boards very, and so does the bootloader firmware
71    fn page_size(&self) -> u32 {
72        256
73    }
74
75    /// Optional, with a default erase size of 4096
76    fn flash_sector_erase_size(&self) -> u64 {
77        4096
78    }
79
80    /// Get the board's name
81    fn board_name(&self) -> String;
82}
83
84/// A builder for the CustomBoard struct, which can be passed into the elf2uf2 function
85#[derive(Debug, Clone)]
86pub struct CustomBoardBuilder {
87    vendor_id: Option<u16>,
88    product_id: Option<u16>,
89    family_id: Option<u32>,
90    board_name: Option<String>,
91    page_size: Option<u32>,
92    flash_sector_erase_size: Option<u64>,
93}
94
95impl CustomBoardBuilder {
96    pub fn new() -> Self {
97        Self {
98            vendor_id: None,
99            product_id: None,
100            family_id: None,
101            board_name: None,
102            page_size: None,
103            flash_sector_erase_size: None,
104        }
105    }
106
107    pub fn vendor_id(mut self, vendor_id: u16) -> Self {
108        self.vendor_id = Some(vendor_id);
109        self
110    }
111
112    pub fn product_id(mut self, product_id: u16) -> Self {
113        self.product_id = Some(product_id);
114        self
115    }
116
117    pub fn family_id(mut self, family_id: u32) -> Self {
118        self.family_id = Some(family_id);
119        self
120    }
121
122    pub fn board_name<S: Into<String>>(mut self, board_name: S) -> Self {
123        self.board_name = Some(board_name.into());
124        self
125    }
126
127    pub fn page_size(mut self, page_size: u32) -> Self {
128        self.page_size = Some(page_size);
129        self
130    }
131
132    pub fn flash_sector_erase_size(mut self, size: u64) -> Self {
133        self.flash_sector_erase_size = Some(size);
134        self
135    }
136
137    pub fn build(self) -> Result<CustomBoard, CustomBoardBuildError> {
138        Ok(CustomBoard {
139            vendor_id: self.vendor_id,
140            product_id: self.product_id,
141            family_id: self
142                .family_id
143                .ok_or(CustomBoardBuildError::FamilyIdRequired)?,
144            board_name: self.board_name,
145            page_size: self.page_size,
146            flash_sector_erase_size: self.flash_sector_erase_size,
147        })
148    }
149}
150
151#[derive(Error, Debug)]
152pub enum CustomBoardBuildError {
153    #[error("family_id is required")]
154    FamilyIdRequired,
155}
156
157/// A struct, which can be passed into the elf2uf2 function, this can be constructed via the CustomBoardBuilder struct.
158#[derive(Debug, Clone)]
159pub struct CustomBoard {
160    vendor_id: Option<u16>,
161    product_id: Option<u16>,
162    family_id: u32,
163    board_name: Option<String>,
164    page_size: Option<u32>,
165    flash_sector_erase_size: Option<u64>,
166}
167
168impl BoardInfo for CustomBoard {
169    fn is_device_board(&self, device: &UsbDevice) -> bool {
170        if let Some(vendor_id) = self.vendor_id
171            && device.vendor_id != vendor_id
172        {
173            return false;
174        }
175
176        if let Some(vendor_id) = self.product_id
177            && device.product_id != vendor_id
178        {
179            return false;
180        }
181
182        true
183    }
184
185    fn family_id(&self) -> u32 {
186        self.family_id
187    }
188
189    fn board_name(&self) -> String {
190        self.board_name.clone().unwrap_or("custom".to_string())
191    }
192
193    fn page_size(&self) -> u32 {
194        self.page_size.unwrap_or(256)
195    }
196
197    fn flash_sector_erase_size(&self) -> u64 {
198        self.flash_sector_erase_size.unwrap_or(4096)
199    }
200}