1#![no_std]
3#![warn(missing_docs)]
4#![allow(clippy::type_complexity)]
5#![cfg_attr(docsrs, feature(doc_cfg))]
6
7#[cfg(any(feature = "std", test))]
8#[macro_use]
9extern crate std;
10
11#[cfg(feature = "async")]
13#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
14pub mod asynchronous;
15pub mod detach;
17pub mod download;
19pub mod functional_descriptor;
21pub mod get_status;
23pub mod memory_layout;
25#[cfg(any(feature = "std", test))]
27#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
28pub mod sync;
29
30use core::convert::TryFrom;
31
32use displaydoc::Display;
33use functional_descriptor::FunctionalDescriptor;
34#[cfg(any(feature = "std", test))]
35use thiserror::Error;
36
37#[derive(Debug, Display)]
38#[cfg_attr(any(feature = "std", test), derive(Error))]
39#[allow(missing_docs)]
40#[non_exhaustive]
41pub enum Error {
42 OutOfCapabilities,
44 InvalidState { got: State, expected: State },
46 BufferTooBig { got: usize, expected: usize },
48 MaximumTransferSizeExceeded,
50 EraseLimitReached,
52 NoSpaceLeft,
54 UnrecognizedStatusCode(u8),
56 UnrecognizedStateCode(u8),
58 ResponseTooShort { got: usize, expected: usize },
60 StatusError(Status),
62 StateError(State),
64 UnknownProtocol,
66 InvalidInterfaceString,
68 #[cfg(any(feature = "std", test))]
70 MemoryLayout(memory_layout::Error),
71 InvalidAddress,
73}
74
75pub trait DfuIo {
77 type Read;
79 type Write;
81 type Reset;
83 type Error: From<Error>;
85 type MemoryLayout: AsRef<memory_layout::mem>;
87
88 fn read_control(
90 &self,
91 request_type: u8,
92 request: u8,
93 value: u16,
94 buffer: &mut [u8],
95 ) -> Result<Self::Read, Self::Error>;
96
97 fn write_control(
99 &self,
100 request_type: u8,
101 request: u8,
102 value: u16,
103 buffer: &[u8],
104 ) -> Result<Self::Write, Self::Error>;
105
106 fn usb_reset(&self) -> Result<Self::Reset, Self::Error>;
108
109 fn protocol(&self) -> &DfuProtocol<Self::MemoryLayout>;
111
112 fn functional_descriptor(&self) -> &functional_descriptor::FunctionalDescriptor;
114}
115
116pub enum DfuProtocol<M> {
118 Dfu,
120 Dfuse {
122 address: u32,
124 memory_layout: M,
126 },
127}
128
129#[cfg(any(feature = "std", test))]
130impl DfuProtocol<memory_layout::MemoryLayout> {
131 pub fn new(interface_string: &str, version: (u8, u8)) -> Result<Self, Error> {
133 match version {
134 (0x1, 0x10) => Ok(DfuProtocol::Dfu),
135 (0x1, 0x1a) => {
136 let (rest, memory_layout) = interface_string
137 .rsplit_once('/')
138 .ok_or(Error::InvalidInterfaceString)?;
139 let memory_layout = memory_layout::MemoryLayout::try_from(memory_layout)
140 .map_err(Error::MemoryLayout)?;
141 let (_rest, address) =
142 rest.rsplit_once('/').ok_or(Error::InvalidInterfaceString)?;
143 let address = address
144 .strip_prefix("0x")
145 .and_then(|s| u32::from_str_radix(s, 16).ok())
146 .ok_or(Error::InvalidAddress)?;
147 Ok(DfuProtocol::Dfuse {
148 address,
149 memory_layout,
150 })
151 }
152 _ => Err(Error::UnknownProtocol),
153 }
154 }
155}
156
157pub struct DfuSansIo {
159 descriptor: FunctionalDescriptor,
160 override_address: Option<u32>,
161}
162
163impl DfuSansIo {
164 pub fn new(descriptor: FunctionalDescriptor) -> Self {
166 Self {
167 descriptor,
168 override_address: None,
169 }
170 }
171
172 pub fn download<'a, Layout>(
174 &'a self,
175 protocol: &'a DfuProtocol<Layout>,
176 length: u32,
177 ) -> Result<
178 get_status::GetStatus<get_status::ClearStatus<get_status::GetStatus<download::Start<'a>>>>,
179 Error,
180 >
181 where
182 Layout: AsRef<memory_layout::mem>,
183 {
184 let (protocol, end_pos) = match protocol {
185 DfuProtocol::Dfu => (download::ProtocolData::Dfu, length),
186 DfuProtocol::Dfuse {
187 address,
188 memory_layout,
189 ..
190 } => {
191 let address = self.override_address.unwrap_or(*address);
192 (
193 download::ProtocolData::Dfuse(download::DfuseProtocolData {
194 address,
195 erased_pos: address,
196 address_set: false,
197 memory_layout: memory_layout.as_ref(),
198 }),
199 address.checked_add(length).ok_or(Error::NoSpaceLeft)?,
200 )
201 }
202 };
203
204 Ok(get_status::GetStatus {
205 chained_command: get_status::ClearStatus {
206 chained_command: get_status::GetStatus {
207 chained_command: download::Start {
208 descriptor: &self.descriptor,
209 protocol,
210 end_pos,
211 },
212 },
213 },
214 })
215 }
216
217 pub fn detach(&self) -> UsbWriteControl<[u8; 0]> {
219 const REQUEST_TYPE: u8 = 0b00100001;
220 const DFU_DETACH: u8 = 0;
221 UsbWriteControl::new(REQUEST_TYPE, DFU_DETACH, 1000, [])
222 }
223
224 pub fn set_address(&mut self, address: u32) {
228 self.override_address = Some(address);
229 }
230}
231
232#[derive(Debug, Clone, Copy, Eq, PartialEq, Display)]
236pub enum Status {
237 Ok,
239 ErrTarget,
241 ErrFile,
243 ErrWrite,
245 ErrErase,
247 ErrCheckErased,
249 ErrProg,
251 ErrVerify,
253 ErrAddress,
255 ErrNotdone,
257 ErrFirmware,
259 ErrVendor,
261 ErrUsbr,
263 ErrPor,
265 ErrUnknown,
267 ErrStalledpkt,
269 Other(u8),
271}
272
273impl From<u8> for Status {
274 fn from(state: u8) -> Self {
275 match state {
276 0x00 => Status::Ok,
277 0x01 => Status::ErrTarget,
278 0x02 => Status::ErrFile,
279 0x03 => Status::ErrWrite,
280 0x04 => Status::ErrErase,
281 0x05 => Status::ErrCheckErased,
282 0x06 => Status::ErrProg,
283 0x07 => Status::ErrVerify,
284 0x08 => Status::ErrAddress,
285 0x09 => Status::ErrNotdone,
286 0x0a => Status::ErrFirmware,
287 0x0b => Status::ErrVendor,
288 0x0c => Status::ErrUsbr,
289 0x0d => Status::ErrPor,
290 0x0e => Status::ErrUnknown,
291 0x0f => Status::ErrStalledpkt,
292 other => Status::Other(other),
293 }
294 }
295}
296
297impl From<Status> for u8 {
298 fn from(state: Status) -> Self {
299 match state {
300 Status::Ok => 0x00,
301 Status::ErrTarget => 0x01,
302 Status::ErrFile => 0x02,
303 Status::ErrWrite => 0x03,
304 Status::ErrErase => 0x04,
305 Status::ErrCheckErased => 0x05,
306 Status::ErrProg => 0x06,
307 Status::ErrVerify => 0x07,
308 Status::ErrAddress => 0x08,
309 Status::ErrNotdone => 0x09,
310 Status::ErrFirmware => 0x0a,
311 Status::ErrVendor => 0x0b,
312 Status::ErrUsbr => 0x0c,
313 Status::ErrPor => 0x0d,
314 Status::ErrUnknown => 0x0e,
315 Status::ErrStalledpkt => 0x0f,
316 Status::Other(other) => other,
317 }
318 }
319}
320
321#[derive(Debug, Clone, Copy, Eq, PartialEq, Display)]
325pub enum State {
326 AppIdle,
328 AppDetach,
330 DfuIdle,
332 DfuDnloadSync,
334 DfuDnbusy,
336 DfuDnloadIdle,
338 DfuManifestSync,
340 DfuManifest,
342 DfuManifestWaitReset,
344 DfuUploadIdle,
346 DfuError,
348 Other(u8),
350}
351
352impl From<u8> for State {
353 fn from(state: u8) -> Self {
354 match state {
355 0 => State::AppIdle,
356 1 => State::AppDetach,
357 2 => State::DfuIdle,
358 3 => State::DfuDnloadSync,
359 4 => State::DfuDnbusy,
360 5 => State::DfuDnloadIdle,
361 6 => State::DfuManifestSync,
362 7 => State::DfuManifest,
363 8 => State::DfuManifestWaitReset,
364 9 => State::DfuUploadIdle,
365 10 => State::DfuError,
366 other => State::Other(other),
367 }
368 }
369}
370
371impl From<State> for u8 {
372 fn from(state: State) -> Self {
373 match state {
374 State::AppIdle => 0,
375 State::AppDetach => 1,
376 State::DfuIdle => 2,
377 State::DfuDnloadSync => 3,
378 State::DfuDnbusy => 4,
379 State::DfuDnloadIdle => 5,
380 State::DfuManifestSync => 6,
381 State::DfuManifest => 7,
382 State::DfuManifestWaitReset => 8,
383 State::DfuUploadIdle => 9,
384 State::DfuError => 10,
385 State::Other(other) => other,
386 }
387 }
388}
389
390impl State {
391 fn for_status(self) -> Self {
396 match self {
397 State::DfuManifestSync => State::DfuManifest,
398 State::DfuDnloadSync => State::DfuDnbusy,
399 _ => self,
400 }
401 }
402}
403
404pub trait ChainedCommand {
406 type Arg;
408 type Into;
410
411 fn chain(self, arg: Self::Arg) -> Self::Into;
413}
414
415#[must_use]
417pub struct UsbWriteControl<D> {
418 request_type: u8,
419 request: u8,
420 value: u16,
421 buffer: D,
422}
423
424impl<D> UsbWriteControl<D>
425where
426 D: AsRef<[u8]>,
427{
428 fn new(request_type: u8, request: u8, value: u16, buffer: D) -> Self {
429 Self {
430 request_type,
431 request,
432 value,
433 buffer,
434 }
435 }
436
437 pub fn execute<IO: DfuIo>(&self, io: &IO) -> Result<IO::Write, IO::Error> {
439 io.write_control(
440 self.request_type,
441 self.request,
442 self.value,
443 self.buffer.as_ref(),
444 )
445 }
446}
447
448#[must_use]
450pub struct UsbReadControl<'a> {
451 request_type: u8,
452 request: u8,
453 value: u16,
454 buffer: &'a mut [u8],
455}
456
457impl<'a> UsbReadControl<'a> {
458 fn new(request_type: u8, request: u8, value: u16, buffer: &'a mut [u8]) -> Self {
459 Self {
460 request_type,
461 request,
462 value,
463 buffer,
464 }
465 }
466
467 pub fn execute<IO: DfuIo>(&mut self, io: &IO) -> Result<IO::Read, IO::Error> {
469 io.read_control(self.request_type, self.request, self.value, self.buffer)
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::*;
476
477 const _: [&dyn DfuIo<Read = (), Write = (), Reset = (), MemoryLayout = (), Error = Error>; 0] =
479 [];
480}