opentitanlib/rescue/
usbdfu.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use anyhow::{Result, bail};
6use std::cell::{Cell, Ref, RefCell};
7use std::time::Duration;
8
9use crate::app::{TransportWrapper, UartRx};
10use crate::io::usb::UsbDevice;
11use crate::rescue::dfu::*;
12use crate::rescue::{EntryMode, Rescue, RescueError, RescueMode, RescueParams};
13
14pub struct UsbDfu {
15    usb: RefCell<Option<Box<dyn UsbDevice>>>,
16    interface: Cell<u8>,
17    params: RescueParams,
18    reset_delay: Duration,
19}
20
21impl UsbDfu {
22    const CLASS: u8 = 254;
23    const SUBCLASS: u8 = 1;
24    const PROTOCOL: u8 = 2;
25    pub fn new(params: RescueParams) -> Self {
26        UsbDfu {
27            usb: RefCell::new(None),
28            interface: Cell::default(),
29            params,
30            reset_delay: Duration::from_millis(50),
31        }
32    }
33
34    fn device(&self) -> Ref<'_, dyn UsbDevice> {
35        let usb = self.usb.borrow();
36        Ref::map(usb, |d| &**d.as_ref().expect("device handle"))
37    }
38}
39
40impl Rescue for UsbDfu {
41    fn enter(&self, transport: &TransportWrapper, mode: EntryMode) -> Result<()> {
42        log::info!(
43            "Setting {:?}({}) to trigger rescue mode.",
44            self.params.trigger,
45            self.params.value
46        );
47        self.params.set_trigger(transport, true)?;
48
49        match mode {
50            EntryMode::Reset => transport.reset_with_delay(UartRx::Keep, self.reset_delay)?,
51            EntryMode::Reboot => self.reboot()?,
52            EntryMode::None => {}
53        }
54
55        let device = transport.usb()?.device_by_interface_with_timeout(
56            Self::CLASS,
57            Self::SUBCLASS,
58            Self::PROTOCOL,
59            self.params.usb_serial.as_deref(),
60            self.params.enter_delay,
61        );
62        log::info!("Rescue triggered; clearing trigger condition.");
63        self.params.set_trigger(transport, false)?;
64        let device = device?;
65
66        let config = device.active_configuration()?;
67        for intf in config.interface_alt_settings() {
68            let desc = intf.descriptor()?;
69            if desc.class == Self::CLASS
70                && desc.subclass == Self::SUBCLASS
71                && desc.protocol == Self::PROTOCOL
72            {
73                device.claim_interface(desc.intf_num)?;
74                self.interface.set(desc.intf_num);
75                break;
76            }
77        }
78        self.usb.replace(Some(device));
79        Ok(())
80    }
81
82    fn set_mode(&self, mode: RescueMode) -> Result<()> {
83        let setting = match mode {
84            // FIXME: the RescueMode to AltSetting values either need to be permanently fixed, or
85            // the alt interfaces need to describe themselves via a string descriptor.
86            RescueMode::Rescue => 0,
87            RescueMode::RescueB => 1,
88            RescueMode::DeviceId => 2,
89            RescueMode::BootLog => 3,
90            RescueMode::BootSvcReq => 4,
91            RescueMode::BootSvcRsp => 4,
92            RescueMode::OwnerBlock => 5,
93            RescueMode::GetOwnerPage0 => 5,
94            _ => bail!(RescueError::BadMode(format!(
95                "mode {mode:?} not supported by DFU"
96            ))),
97        };
98
99        let device = self.device();
100        log::info!("Mode {mode} is AltSetting {setting}");
101        device.set_alternate_setting(self.interface.get(), setting)?;
102        Ok(())
103    }
104
105    fn set_speed(&self, _speed: u32) -> Result<u32> {
106        log::warn!("set_speed is not implemented for DFU");
107        Ok(0)
108    }
109
110    fn reboot(&self) -> Result<()> {
111        let usb = self.device();
112        usb.release_interface(self.interface.get())?;
113        match usb.reset() {
114            Ok(_) => {}
115            Err(e) => log::warn!("USB reset: {e}"),
116        }
117        Ok(())
118    }
119
120    fn send(&self, data: &[u8]) -> Result<()> {
121        for chunk in data.chunks(2048) {
122            let _ = self.download(chunk)?;
123            let status = loop {
124                let status = self.get_status()?;
125                match status.state() {
126                    DfuState::DnLoadIdle | DfuState::Error => {
127                        break status;
128                    }
129                    _ => {
130                        std::thread::sleep(Duration::from_millis(status.poll_timeout() as u64));
131                    }
132                }
133            };
134            status.status()?;
135        }
136        // Send a zero-length chunk to signal the end.
137        let _ = self.download(&[])?;
138        let status = self.get_status()?;
139        log::warn!("State after DFU download: {}", status.state());
140        Ok(())
141    }
142
143    fn recv(&self) -> Result<Vec<u8>> {
144        let mut data = vec![0u8; 2048];
145        /*
146         * FIXME: what am I supposed to do here?
147         * The spec seems to indicate that I should keep performing `upload` until I get back a
148         * short or zero length packet.
149        let mut offset = 0;
150        loop {
151            log::info!("upload at {offset}");
152            let length = self.upload(&mut data[offset..])?;
153            if length == 0 || length < data.len() - offset {
154                break;
155            }
156            offset += length;
157        }
158        */
159        self.upload(&mut data)?;
160        let status = self.get_status()?;
161        log::warn!("State after DFU upload: {}", status.state());
162        Ok(data)
163    }
164}
165
166impl DfuOperations for UsbDfu {
167    fn write_control(
168        &self,
169        request_type: u8,
170        request: u8,
171        value: u16,
172        index: u16,
173        data: &[u8],
174    ) -> Result<usize> {
175        let usb = self.device();
176        usb.write_control(request_type, request, value, index, data)
177    }
178
179    fn read_control(
180        &self,
181        request_type: u8,
182        request: u8,
183        value: u16,
184        index: u16,
185        data: &mut [u8],
186    ) -> Result<usize> {
187        let usb = self.device();
188        usb.read_control(request_type, request, value, index, data)
189    }
190
191    fn get_interface(&self) -> u8 {
192        self.interface.get()
193    }
194}