opentitanlib/rescue/
usbdfu.rs1use anyhow::{Result, bail};
6use std::cell::{Cell, Ref, RefCell};
7use std::time::Duration;
8
9use crate::app::{TransportWrapper, UartRx};
10use crate::rescue::dfu::*;
11use crate::rescue::{EntryMode, Rescue, RescueError, RescueMode, RescueParams};
12use crate::util::usb::UsbBackend;
13
14pub struct UsbDfu {
15 usb: RefCell<Option<UsbBackend>>,
16 interface: Cell<u8>,
17 params: RescueParams,
18 reset_delay: Duration,
19 enter_delay: Duration,
20}
21
22impl UsbDfu {
23 const CLASS: u8 = 254;
24 const SUBCLASS: u8 = 1;
25 const PROTOCOL: u8 = 2;
26 pub fn new(params: RescueParams) -> Self {
27 UsbDfu {
28 usb: RefCell::new(None),
29 interface: Cell::default(),
30 params,
31 reset_delay: Duration::from_millis(50),
32 enter_delay: Duration::from_secs(20),
33 }
34 }
35
36 fn device(&self) -> Ref<'_, UsbBackend> {
37 let device = self.usb.borrow();
38 Ref::map(device, |d| d.as_ref().expect("device handle"))
39 }
40}
41
42impl Rescue for UsbDfu {
43 fn enter(&self, transport: &TransportWrapper, mode: EntryMode) -> Result<()> {
44 log::info!(
45 "Setting {:?}({}) to trigger rescue mode.",
46 self.params.trigger,
47 self.params.value
48 );
49 self.params.set_trigger(transport, true)?;
50
51 match mode {
52 EntryMode::Reset => transport.reset_with_delay(UartRx::Keep, self.reset_delay)?,
53 EntryMode::Reboot => self.reboot()?,
54 EntryMode::None => {}
55 }
56
57 let device = UsbBackend::from_interface_with_timeout(
58 Self::CLASS,
59 Self::SUBCLASS,
60 Self::PROTOCOL,
61 self.params.usb_serial.as_deref(),
62 self.enter_delay,
63 );
64 log::info!("Rescue triggered; clearing trigger condition.");
65 self.params.set_trigger(transport, false)?;
66 let device = device?;
67
68 let config = device.active_config_descriptor()?;
69 for intf in config.interfaces() {
70 for desc in intf.descriptors() {
71 if desc.class_code() == Self::CLASS
72 && desc.sub_class_code() == Self::SUBCLASS
73 && desc.protocol_code() == Self::PROTOCOL
74 {
75 device.claim_interface(intf.number())?;
76 self.interface.set(intf.number());
77 break;
78 }
79 }
80 }
81 self.usb.replace(Some(device));
82 Ok(())
83 }
84
85 fn set_mode(&self, mode: RescueMode) -> Result<()> {
86 let setting = match mode {
87 RescueMode::Rescue => 0,
90 RescueMode::RescueB => 1,
91 RescueMode::DeviceId => 2,
92 RescueMode::BootLog => 3,
93 RescueMode::BootSvcReq => 4,
94 RescueMode::BootSvcRsp => 4,
95 RescueMode::OwnerBlock => 5,
96 RescueMode::GetOwnerPage0 => 5,
97 _ => bail!(RescueError::BadMode(format!(
98 "mode {mode:?} not supported by DFU"
99 ))),
100 };
101
102 let device = self.device();
103 log::info!("Mode {mode} is AltSetting {setting}");
104 device.set_alternate_setting(self.interface.get(), setting)?;
105 Ok(())
106 }
107
108 fn set_speed(&self, _speed: u32) -> Result<u32> {
109 log::warn!("set_speed is not implemented for DFU");
110 Ok(0)
111 }
112
113 fn reboot(&self) -> Result<()> {
114 let usb = self.device();
115 usb.release_interface(self.interface.get())?;
116 match usb.reset() {
117 Ok(_) => {}
118 Err(e) => log::warn!("USB reset: {e}"),
119 }
120 Ok(())
121 }
122
123 fn send(&self, data: &[u8]) -> Result<()> {
124 for chunk in data.chunks(2048) {
125 let _ = self.download(chunk)?;
126 let status = loop {
127 let status = self.get_status()?;
128 match status.state() {
129 DfuState::DnLoadIdle | DfuState::Error => {
130 break status;
131 }
132 _ => {
133 std::thread::sleep(Duration::from_millis(status.poll_timeout() as u64));
134 }
135 }
136 };
137 status.status()?;
138 }
139 let _ = self.download(&[])?;
141 let status = self.get_status()?;
142 log::warn!("State after DFU download: {}", status.state());
143 Ok(())
144 }
145
146 fn recv(&self) -> Result<Vec<u8>> {
147 let mut data = vec![0u8; 2048];
148 self.upload(&mut data)?;
163 let status = self.get_status()?;
164 log::warn!("State after DFU upload: {}", status.state());
165 Ok(data)
166 }
167}
168
169impl DfuOperations for UsbDfu {
170 fn write_control(
171 &self,
172 request_type: u8,
173 request: u8,
174 value: u16,
175 index: u16,
176 data: &[u8],
177 ) -> Result<usize> {
178 let usb = self.device();
179 usb.write_control(request_type, request, value, index, data)
180 }
181
182 fn read_control(
183 &self,
184 request_type: u8,
185 request: u8,
186 value: u16,
187 index: u16,
188 data: &mut [u8],
189 ) -> Result<usize> {
190 let usb = self.device();
191 usb.read_control(request_type, request, value, index, data)
192 }
193
194 fn get_interface(&self) -> u8 {
195 self.interface.get()
196 }
197}