1use core::cmp::min;
37
38use embassy_futures::select::{Either, select};
39use embassy_stm32::gpio::Input;
40use embassy_stm32::sdmmc::sd::{Addressable, CmdBlock, DataBlock, StorageDevice};
41use embassy_stm32::sdmmc::{Error, Sdmmc};
42use embassy_stm32::time::mhz;
43use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
44use embassy_sync::signal::Signal;
45
46use veloxity_core::errors;
47use veloxity_core::packets::{self, PARAM_PACKET_SIZE, ParamPacket};
48use veloxity_core::params::Params;
49use veloxity_core::params::storage::{
50 ROSFLIGHT_C_PARAM_STORAGE_SIZE, decode_rosflight_c_params, encode_rosflight_c_params,
51};
52
53const BLOCK_SIZE: usize = 512;
54const CHECKSUM_SIZE: usize = size_of::<u32>();
55const MAX_BLOCKS: usize = 16;
56const MAX_PAYLOAD_SIZE: usize = MAX_BLOCKS * BLOCK_SIZE - CHECKSUM_SIZE;
57const _: () = assert!(ROSFLIGHT_C_PARAM_STORAGE_SIZE == PARAM_PACKET_SIZE);
58
59pub static SD_WRITE_SIGNAL: Signal<
60 CriticalSectionRawMutex,
61 Result<packets::ParamPacket, errors::SensorError>,
62> = Signal::<CriticalSectionRawMutex, Result<packets::ParamPacket, errors::SensorError>>::new();
63
64pub static SD_READ_SIGNAL: Signal<
65 CriticalSectionRawMutex,
66 Result<packets::ParamPacket, errors::SensorError>,
67> = Signal::<CriticalSectionRawMutex, Result<packets::ParamPacket, errors::SensorError>>::new();
68
69pub static SD_READ_REQUEST_SIGNAL: Signal<CriticalSectionRawMutex, u64> =
70 Signal::<CriticalSectionRawMutex, u64>::new();
71
72#[derive(Debug)]
74pub enum SdCardError {
75 Device(Error),
77 ImageTooLarge,
79 CardTooSmall,
81 ChecksumMismatch,
83}
84
85impl From<Error> for SdCardError {
86 fn from(error: Error) -> Self {
87 Self::Device(error)
88 }
89}
90
91pub fn encode_params(params: &Params) -> Option<ParamPacket> {
93 let mut packet = ParamPacket::default();
94 encode_rosflight_c_params(params, &mut packet.values).then_some(packet)
95}
96
97pub fn decode_params(packet: &ParamPacket) -> Option<Params> {
100 if packet.header.status != 1 {
101 return None;
102 }
103 decode_rosflight_c_params(&packet.values)
104}
105
106pub struct SdCard {
107 pub sdmmc: Sdmmc<'static>,
108 pub detect: Input<'static>,
109}
110
111impl SdCard {
112 pub async fn read(&mut self, destination: &mut [u8]) -> Result<(), SdCardError> {
114 validate_image_size(destination.len())?;
115
116 let mut cmd_block = CmdBlock::new();
117 let mut storage =
118 StorageDevice::new_sd_card(&mut self.sdmmc, &mut cmd_block, mhz(4)).await?;
119 validate_card_size(&storage, destination.len())?;
120
121 let checksum = read_image(&mut storage, destination).await?;
122 if checksum != rosflight_crc32(destination) {
123 return Err(SdCardError::ChecksumMismatch);
124 }
125
126 Ok(())
127 }
128
129 pub async fn write(&mut self, source: &[u8]) -> Result<(), SdCardError> {
131 validate_image_size(source.len())?;
132
133 let mut cmd_block = CmdBlock::new();
134 let mut storage =
135 StorageDevice::new_sd_card(&mut self.sdmmc, &mut cmd_block, mhz(4)).await?;
136 validate_card_size(&storage, source.len())?;
137
138 write_image(&mut storage, source, rosflight_crc32(source)).await
139 }
140
141 async fn run(&mut self) {
142 loop {
143 match select(SD_READ_REQUEST_SIGNAL.wait(), SD_WRITE_SIGNAL.wait()).await {
144 Either::First(request_id) => {
145 let mut packet = packets::ParamPacket {
146 header: packets::RosflightPacketHeader {
147 timestamp: request_id,
148 status: 0,
149 },
150 values: [0u8; packets::PARAM_PACKET_SIZE],
151 };
152 packet.header.status = u16::from(self.read(&mut packet.values).await.is_ok());
153 SD_READ_SIGNAL.signal(Ok(packet));
154 }
155 Either::Second(Ok(mut packet)) => {
156 packet.header.status = u16::from(self.write(&packet.values).await.is_ok());
157 SD_READ_SIGNAL.signal(Ok(packet));
158 }
159 Either::Second(Err(error)) => SD_READ_SIGNAL.signal(Err(error)),
160 }
161 }
162 }
163}
164
165fn image_blocks(image_size: usize) -> usize {
166 (image_size + CHECKSUM_SIZE).div_ceil(BLOCK_SIZE)
167}
168
169fn validate_image_size(image_size: usize) -> Result<(), SdCardError> {
170 if image_size > MAX_PAYLOAD_SIZE {
171 Err(SdCardError::ImageTooLarge)
172 } else {
173 Ok(())
174 }
175}
176
177fn validate_card_size(
178 storage: &StorageDevice<'_, '_, impl Addressable>,
179 image_size: usize,
180) -> Result<(), SdCardError> {
181 let available_blocks = storage.card().size() as usize / BLOCK_SIZE;
182 if image_blocks(image_size) > available_blocks {
183 Err(SdCardError::CardTooSmall)
184 } else {
185 Ok(())
186 }
187}
188
189async fn read_image(
190 storage: &mut StorageDevice<'_, '_, impl Addressable>,
191 destination: &mut [u8],
192) -> Result<u32, SdCardError> {
193 let image_size = destination.len();
194 let mut stored_checksum = [0u8; CHECKSUM_SIZE];
195
196 for block_index in 0..image_blocks(image_size) {
197 let mut block = DataBlock::new();
198 storage.read_block(block_index as u32, &mut block).await?;
199
200 let block_start = block_index * BLOCK_SIZE;
201 let payload_end = min(image_size.saturating_sub(block_start), BLOCK_SIZE);
202 if payload_end != 0 {
203 destination[block_start..block_start + payload_end]
204 .copy_from_slice(&block[..payload_end]);
205 }
206
207 copy_checksum_overlap(&block, block_start, image_size, &mut stored_checksum);
208 }
209
210 Ok(u32::from_le_bytes(stored_checksum))
211}
212
213async fn write_image(
214 storage: &mut StorageDevice<'_, '_, impl Addressable>,
215 source: &[u8],
216 checksum: u32,
217) -> Result<(), SdCardError> {
218 let image_size = source.len();
219 let checksum = checksum.to_le_bytes();
220
221 for block_index in 0..image_blocks(image_size) {
222 let mut block = DataBlock::new();
223 let block_start = block_index * BLOCK_SIZE;
224 let payload_end = min(image_size.saturating_sub(block_start), BLOCK_SIZE);
225 if payload_end != 0 {
226 block[..payload_end].copy_from_slice(&source[block_start..block_start + payload_end]);
227 }
228
229 write_checksum_overlap(&mut block, block_start, image_size, &checksum);
230 storage.write_block(block_index as u32, &block).await?;
231 }
232
233 Ok(())
234}
235
236fn copy_checksum_overlap(
237 block: &[u8; BLOCK_SIZE],
238 block_start: usize,
239 image_size: usize,
240 checksum: &mut [u8; CHECKSUM_SIZE],
241) {
242 let checksum_start = image_size;
243 let checksum_end = image_size + CHECKSUM_SIZE;
244 let block_end = block_start + BLOCK_SIZE;
245 let overlap_start = block_start.max(checksum_start);
246 let overlap_end = block_end.min(checksum_end);
247
248 if overlap_start < overlap_end {
249 checksum[overlap_start - checksum_start..overlap_end - checksum_start]
250 .copy_from_slice(&block[overlap_start - block_start..overlap_end - block_start]);
251 }
252}
253
254fn write_checksum_overlap(
255 block: &mut [u8; BLOCK_SIZE],
256 block_start: usize,
257 image_size: usize,
258 checksum: &[u8; CHECKSUM_SIZE],
259) {
260 let checksum_start = image_size;
261 let checksum_end = image_size + CHECKSUM_SIZE;
262 let block_end = block_start + BLOCK_SIZE;
263 let overlap_start = block_start.max(checksum_start);
264 let overlap_end = block_end.min(checksum_end);
265
266 if overlap_start < overlap_end {
267 block[overlap_start - block_start..overlap_end - block_start].copy_from_slice(
268 &checksum[overlap_start - checksum_start..overlap_end - checksum_start],
269 );
270 }
271}
272
273fn rosflight_crc32(bytes: &[u8]) -> u32 {
276 let mut crc = u32::MAX;
277
278 for byte in bytes {
279 crc ^= u32::from(*byte) << 24;
280 for _ in 0..8 {
281 crc = if crc & 0x8000_0000 != 0 {
282 (crc << 1) ^ 0x04C1_1DB7
283 } else {
284 crc << 1
285 };
286 }
287 }
288
289 crc
290}
291
292#[embassy_executor::task]
293pub async fn task(mut sd_card: SdCard) {
294 sd_card.run().await;
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn crc_matches_stm32_default_crc_for_standard_vector() {
303 assert_eq!(rosflight_crc32(b"123456789"), 0x0376_E6E7);
304 }
305
306 #[test]
307 fn block_count_includes_checksum() {
308 assert_eq!(image_blocks(0), 1);
309 assert_eq!(image_blocks(508), 1);
310 assert_eq!(image_blocks(509), 2);
311 assert_eq!(image_blocks(2048), 5);
312 }
313}