Skip to main content

dfu_core/
synchronous.rs

1use super::*;
2use std::convert::TryFrom;
3use std::io::Cursor;
4use std::prelude::v1::*;
5
6struct Buffer<R: std::io::Read> {
7    reader: R,
8    buf: Box<[u8]>,
9    level: usize,
10}
11
12impl<R: std::io::Read> Buffer<R> {
13    fn new(size: usize, reader: R) -> Self {
14        Self {
15            reader,
16            buf: vec![0; size].into_boxed_slice(),
17            level: 0,
18        }
19    }
20
21    fn fill_buf(&mut self) -> Result<&[u8], std::io::Error> {
22        while self.level < self.buf.len() {
23            let dst = &mut self.buf[self.level..];
24            let r = self.reader.read(dst)?;
25            if r == 0 {
26                break;
27            } else {
28                self.level += r;
29            }
30        }
31        Ok(&self.buf[0..self.level])
32    }
33
34    fn consume(&mut self, amt: usize) {
35        if amt >= self.level {
36            self.level = 0;
37        } else {
38            self.buf.copy_within(amt..self.level, 0);
39            self.level -= amt;
40        }
41    }
42}
43
44/// Generic synchronous implementation of DFU.
45#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
46pub struct DfuSync<IO, E>
47where
48    IO: DfuIo<Read = usize, Write = usize, Reset = (), Error = E>,
49    E: From<std::io::Error> + From<Error>,
50{
51    io: IO,
52    dfu: DfuSansIo,
53    buffer: Vec<u8>,
54    progress: Option<Box<dyn FnMut(usize)>>,
55}
56
57impl<IO, E> DfuSync<IO, E>
58where
59    IO: DfuIo<Read = usize, Write = usize, Reset = (), Error = E>,
60    E: From<std::io::Error> + From<Error>,
61{
62    /// Create a new instance of a generic synchronous implementation of DFU.
63    pub fn new(io: IO) -> Self {
64        let transfer_size = io.functional_descriptor().transfer_size as usize;
65        let descriptor = *io.functional_descriptor();
66
67        Self {
68            io,
69            dfu: DfuSansIo::new(descriptor),
70            buffer: vec![0x00; transfer_size],
71            progress: None,
72        }
73    }
74
75    /// Override the address onto which the firmware is downloaded.
76    ///
77    /// This address is only used if the device uses the DfuSe protocol.
78    pub fn override_address(&mut self, address: u32) -> &mut Self {
79        self.dfu.set_address(address);
80        self
81    }
82
83    /// Use this closure to show progress.
84    pub fn with_progress(&mut self, progress: impl FnMut(usize) + 'static) -> &mut Self {
85        self.progress = Some(Box::new(progress));
86        self
87    }
88
89    /// Consume the object and return its [`DfuIo`]
90    pub fn into_inner(self) -> IO {
91        self.io
92    }
93}
94
95impl<IO, E> DfuSync<IO, E>
96where
97    IO: DfuIo<Read = usize, Write = usize, Reset = (), Error = E>,
98    E: From<std::io::Error> + From<Error>,
99{
100    /// Download a firmware into the device from a slice.
101    ///
102    /// Returns `Some(Self)` if the device stayed on the bus (manifestation tolerant, no USB reset
103    /// occurred) or `None` if a USB reset was performed.
104    pub fn download_from_slice(self, slice: &[u8]) -> Result<Option<Self>, IO::Error> {
105        let length = slice.len();
106        let cursor = Cursor::new(slice);
107        self.download(
108            cursor,
109            u32::try_from(length).map_err(|_| Error::OutOfCapabilities)?,
110        )
111    }
112
113    /// Download a firmware into the device from a reader.
114    ///
115    /// Returns `Some(Self)` if the device stayed on the bus (manifestation tolerant, no USB reset
116    /// occurred) or `None` if a USB reset was performed.
117    pub fn download<R: std::io::Read>(
118        mut self,
119        reader: R,
120        length: u32,
121    ) -> Result<Option<Self>, IO::Error> {
122        let transfer_size = self.io.functional_descriptor().transfer_size as usize;
123        let mut reader = Buffer::new(transfer_size, reader);
124        let buffer = reader.fill_buf()?;
125        if buffer.is_empty() {
126            return Ok(Some(self));
127        }
128
129        macro_rules! wait_status {
130            ($cmd:expr) => {{
131                let mut cmd = $cmd;
132                loop {
133                    cmd = match cmd.next() {
134                        get_status::Step::Break(cmd) => break cmd,
135                        get_status::Step::Wait(cmd, poll_timeout) => {
136                            std::thread::sleep(std::time::Duration::from_millis(poll_timeout));
137                            let (cmd, mut control) = cmd.get_status(&mut self.buffer);
138                            let n = control.execute(&self.io)?;
139                            cmd.chain(&self.buffer[..n as usize])??
140                        }
141                    };
142                }
143            }};
144        }
145
146        let cmd = self.dfu.download(self.io.protocol(), length)?;
147        let (cmd, mut control) = cmd.get_status(&mut self.buffer);
148        let n = control.execute(&self.io)?;
149        let (cmd, control) = cmd.chain(&self.buffer[..n])?;
150        if let Some(control) = control {
151            control.execute(&self.io)?;
152        }
153        let (cmd, mut control) = cmd.get_status(&mut self.buffer);
154        let n = control.execute(&self.io)?;
155        let mut download_loop = cmd.chain(&self.buffer[..n])??;
156
157        loop {
158            download_loop = match download_loop.next() {
159                download::Step::Break => break Ok(Some(self)),
160                download::Step::Erase(cmd) => {
161                    let (cmd, control) = cmd.erase()?;
162                    control.execute(&self.io)?;
163                    wait_status!(cmd)
164                }
165                download::Step::SetAddress(cmd) => {
166                    let (cmd, control) = cmd.set_address();
167                    control.execute(&self.io)?;
168                    wait_status!(cmd)
169                }
170                download::Step::DownloadChunk(cmd) => {
171                    let chunk = reader.fill_buf()?;
172                    let (cmd, control) = cmd.download(chunk)?;
173                    let n = control.execute(&self.io)?;
174                    reader.consume(n);
175                    if let Some(progress) = self.progress.as_mut() {
176                        progress(n);
177                    }
178                    wait_status!(cmd)
179                }
180                download::Step::UsbReset => {
181                    log::trace!("Device reset");
182                    self.io.usb_reset()?;
183                    break Ok(None);
184                }
185            }
186        }
187    }
188
189    /// Download a firmware into the device.
190    ///
191    /// The length is inferred from the reader. Returns `Some(Self)` if the device stayed on the
192    /// bus (manifestation tolerant, no USB reset occurred) or `None` if a USB reset was performed.
193    pub fn download_all<R: std::io::Read + std::io::Seek>(
194        self,
195        mut reader: R,
196    ) -> Result<Option<Self>, IO::Error> {
197        let length = u32::try_from(reader.seek(std::io::SeekFrom::End(0))?)
198            .map_err(|_| Error::MaximumTransferSizeExceeded)?;
199        reader.seek(std::io::SeekFrom::Start(0))?;
200        self.download(reader, length)
201    }
202
203    /// Send a Detach request to the device
204    pub fn detach(&self) -> Result<(), IO::Error> {
205        self.dfu.detach().execute(&self.io)?;
206        Ok(())
207    }
208
209    /// Reset the USB device
210    pub fn usb_reset(self) -> Result<IO::Reset, IO::Error> {
211        self.io.usb_reset()
212    }
213
214    /// Returns whether the device will detach if requested
215    pub fn will_detach(&self) -> bool {
216        self.io.functional_descriptor().will_detach
217    }
218
219    /// Returns whether the device is manifestation tolerant
220    pub fn manifestation_tolerant(&self) -> bool {
221        self.io.functional_descriptor().manifestation_tolerant
222    }
223}