1use functional_descriptor::FunctionalDescriptor;
2
3use super::*;
4
5const REQUEST_TYPE: u8 = 0b00100001;
6const DFU_DNLOAD: u8 = 1;
7
8#[must_use]
10pub struct Start<'dfu> {
11 pub(crate) descriptor: &'dfu FunctionalDescriptor,
12 pub(crate) end_pos: u32,
13 pub(crate) protocol: ProtocolData<'dfu>,
14}
15
16impl<'dfu> ChainedCommand for Start<'dfu> {
17 type Arg = get_status::GetStatusMessage;
18 type Into = Result<DownloadLoop<'dfu>, Error>;
19
20 fn chain(
21 self,
22 get_status::GetStatusMessage {
23 status: _,
24 poll_timeout: _,
25 state,
26 index: _,
27 }: Self::Arg,
28 ) -> Self::Into {
29 log::trace!("Starting download process");
30 if state == State::DfuIdle {
32 let (block_num, copied_pos) = match self.protocol {
33 ProtocolData::Dfu => (0, 0),
34 ProtocolData::Dfuse(d) => (2, d.address),
35 };
36 Ok(DownloadLoop {
37 descriptor: self.descriptor,
38 end_pos: self.end_pos,
39 copied_pos,
40 protocol: self.protocol,
41 block_num,
42 eof: false,
43 })
44 } else {
45 Err(Error::InvalidState {
46 got: state,
47 expected: State::DfuIdle,
48 })
49 }
50 }
51}
52
53#[derive(Debug, Copy, Clone)]
54pub(crate) struct DfuseProtocolData<'dfu> {
55 pub address: u32,
56 pub erased_pos: u32,
57 pub address_set: bool,
58 pub memory_layout: &'dfu memory_layout::mem,
59}
60
61#[derive(Debug, Copy, Clone)]
62pub(crate) enum ProtocolData<'dfu> {
63 Dfu,
64 Dfuse(DfuseProtocolData<'dfu>),
65}
66
67#[must_use]
69pub struct DownloadLoop<'dfu> {
70 descriptor: &'dfu FunctionalDescriptor,
71 protocol: ProtocolData<'dfu>,
72 end_pos: u32,
73 copied_pos: u32,
74 block_num: u16,
75 eof: bool,
76}
77
78impl<'dfu> DownloadLoop<'dfu> {
79 pub fn next(self) -> Step<'dfu> {
81 if self.eof {
82 log::trace!("Download loop ended");
83
84 log::trace!("Device will detach? {}", self.descriptor.will_detach);
87 return if !self.descriptor.manifestation_tolerant && !self.descriptor.will_detach {
88 Step::UsbReset
89 } else {
90 Step::Break
91 };
92 }
93
94 match self.protocol {
95 ProtocolData::Dfuse(d) if d.erased_pos < self.end_pos => {
96 log::trace!("Download loop: erase page");
97 log::trace!("Erased position: {}", d.erased_pos);
98 log::trace!("End position: {}", self.end_pos);
99 Step::Erase(ErasePage {
100 descriptor: self.descriptor,
101 end_pos: self.end_pos,
102 copied_pos: self.copied_pos,
103 protocol: d,
104 block_num: self.block_num,
105 })
106 }
107 ProtocolData::Dfuse(d) if !d.address_set => {
108 log::trace!("Download loop: set address");
109 Step::SetAddress(SetAddress {
110 descriptor: self.descriptor,
111 end_pos: self.end_pos,
112 copied_pos: self.copied_pos,
113 protocol: d,
114 block_num: self.block_num,
115 })
116 }
117 _ => {
118 log::trace!("Download loop: download chunk");
119 Step::DownloadChunk(DownloadChunk {
120 descriptor: self.descriptor,
121 end_pos: self.end_pos,
122 copied_pos: self.copied_pos,
123 block_num: self.block_num,
124 protocol: self.protocol,
125 })
126 }
127 }
128 }
129}
130
131#[allow(missing_docs)]
133pub enum Step<'dfu> {
134 Break,
135 UsbReset,
136 Erase(ErasePage<'dfu>),
137 SetAddress(SetAddress<'dfu>),
138 DownloadChunk(DownloadChunk<'dfu>),
139}
140
141#[must_use]
143pub struct ErasePage<'dfu> {
144 descriptor: &'dfu FunctionalDescriptor,
145 end_pos: u32,
146 copied_pos: u32,
147 protocol: DfuseProtocolData<'dfu>,
148 block_num: u16,
149}
150
151impl<'dfu> ErasePage<'dfu> {
152 pub fn erase(
154 self,
155 ) -> Result<
156 (
157 get_status::WaitState<DownloadLoop<'dfu>>,
158 UsbWriteControl<[u8; 5]>,
159 ),
160 crate::Error,
161 > {
162 let (&page_size, rest_memory_layout) = self
163 .protocol
164 .memory_layout
165 .split_first()
166 .ok_or(Error::NoSpaceLeft)?;
167 log::trace!("Rest of memory layout: {:?}", rest_memory_layout);
168 log::trace!("Page size: {:?}", page_size);
169
170 let next_protocol = ProtocolData::Dfuse(DfuseProtocolData {
171 erased_pos: self
172 .protocol
173 .erased_pos
174 .checked_add(page_size)
175 .ok_or(Error::EraseLimitReached)?,
176 memory_layout: rest_memory_layout,
177 ..self.protocol
178 });
179 let next = get_status::WaitState::new(
180 State::DfuDnbusy,
181 State::DfuDnloadIdle,
182 DownloadLoop {
183 descriptor: self.descriptor,
184 protocol: next_protocol,
185 end_pos: self.end_pos,
186 copied_pos: self.copied_pos,
187 block_num: self.block_num,
188 eof: false,
189 },
190 );
191
192 let control = UsbWriteControl {
193 request_type: REQUEST_TYPE,
194 request: DFU_DNLOAD,
195 value: 0,
196 buffer: <[u8; 5]>::from(DownloadCommandErase(self.protocol.erased_pos)),
197 };
198
199 Ok((next, control))
200 }
201}
202
203#[must_use]
205pub struct SetAddress<'dfu> {
206 descriptor: &'dfu FunctionalDescriptor,
207 end_pos: u32,
208 copied_pos: u32,
209 protocol: DfuseProtocolData<'dfu>,
210 block_num: u16,
211}
212
213impl<'dfu> SetAddress<'dfu> {
214 pub fn set_address(
216 self,
217 ) -> (
218 get_status::WaitState<DownloadLoop<'dfu>>,
219 UsbWriteControl<[u8; 5]>,
220 ) {
221 let next_protocol = ProtocolData::Dfuse(DfuseProtocolData {
222 address_set: true,
223 ..self.protocol
224 });
225
226 let next = get_status::WaitState::new(
227 State::DfuDnbusy,
228 State::DfuDnloadIdle,
229 DownloadLoop {
230 descriptor: self.descriptor,
231 end_pos: self.end_pos,
232 copied_pos: self.copied_pos,
233 protocol: next_protocol,
234 block_num: self.block_num,
235 eof: false,
236 },
237 );
238 let control = UsbWriteControl::new(
239 REQUEST_TYPE,
240 DFU_DNLOAD,
241 0,
242 <[u8; 5]>::from(DownloadCommandSetAddress(self.copied_pos)),
243 );
244
245 (next, control)
246 }
247}
248
249#[must_use]
251pub struct DownloadChunk<'dfu> {
252 descriptor: &'dfu FunctionalDescriptor,
253 end_pos: u32,
254 copied_pos: u32,
255 block_num: u16,
256 protocol: ProtocolData<'dfu>,
257}
258
259impl<'dfu> DownloadChunk<'dfu> {
260 pub fn download<'data>(
262 self,
263 bytes: &'data [u8],
264 ) -> Result<
265 (
266 get_status::WaitState<DownloadLoop<'dfu>>,
267 UsbWriteControl<&'data [u8]>,
268 ),
269 crate::Error,
270 > {
271 let transfer_size = self.descriptor.transfer_size as u32;
272 log::trace!("Transfer size: {}", transfer_size);
273 let len = u32::try_from(bytes.len())
274 .map_err(|_| Error::BufferTooBig {
275 got: bytes.len(),
276 expected: u32::MAX as usize,
277 })?
278 .min(transfer_size);
279 log::trace!("Chunk length: {}", len);
280 log::trace!("Copied position: {}", self.copied_pos);
281 log::trace!("Block number: {}", self.block_num);
282
283 let (intermediate, next_state) = if bytes.is_empty() {
284 if self.descriptor.manifestation_tolerant {
285 (State::DfuManifest, State::DfuIdle)
286 } else {
287 (State::DfuManifest, State::DfuManifest)
288 }
289 } else {
290 (State::DfuDnbusy, State::DfuDnloadIdle)
291 };
292
293 let next = get_status::WaitState::new(
294 intermediate,
295 next_state,
296 DownloadLoop {
297 descriptor: self.descriptor,
298 end_pos: self.end_pos,
299 copied_pos: self
300 .copied_pos
301 .checked_add(len)
302 .ok_or(Error::MaximumTransferSizeExceeded)?,
303 protocol: self.protocol,
304 block_num: self.block_num.wrapping_add(1),
305 eof: bytes.is_empty(),
306 },
307 );
308 let control = UsbWriteControl {
309 request_type: REQUEST_TYPE,
310 request: DFU_DNLOAD,
311 value: self.block_num,
312 buffer: &bytes[..len as usize],
313 };
314
315 Ok((next, control))
316 }
317}
318
319#[derive(Debug, Clone, Copy)]
321pub struct DownloadCommandErase(u32);
322
323impl From<DownloadCommandErase> for [u8; 5] {
324 fn from(command: DownloadCommandErase) -> Self {
325 let mut buffer = [0; 5];
326 buffer[0] = 0x41;
327 buffer[1..].copy_from_slice(&command.0.to_le_bytes());
328 buffer
329 }
330}
331
332#[derive(Debug, Clone, Copy)]
334pub struct DownloadCommandSetAddress(u32);
335
336impl From<DownloadCommandSetAddress> for [u8; 5] {
337 fn from(command: DownloadCommandSetAddress) -> Self {
338 let mut buffer = [0; 5];
339 buffer[0] = 0x21;
340 buffer[1..].copy_from_slice(&command.0.to_le_bytes());
341 buffer
342 }
343}