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}
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<'_, UsbBackend> {
35 let device = self.usb.borrow();
36 Ref::map(device, |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 = UsbBackend::from_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_config_descriptor()?;
67 for intf in config.interfaces() {
68 for desc in intf.descriptors() {
69 if desc.class_code() == Self::CLASS
70 && desc.sub_class_code() == Self::SUBCLASS
71 && desc.protocol_code() == Self::PROTOCOL
72 {
73 device.claim_interface(intf.number())?;
74 self.interface.set(intf.number());
75 break;
76 }
77 }
78 }
79 self.usb.replace(Some(device));
80 Ok(())
81 }
82
83 fn set_mode(&self, mode: RescueMode) -> Result<()> {
84 let setting = match mode {
85 RescueMode::Rescue => 0,
88 RescueMode::RescueB => 1,
89 RescueMode::DeviceId => 2,
90 RescueMode::BootLog => 3,
91 RescueMode::BootSvcReq => 4,
92 RescueMode::BootSvcRsp => 4,
93 RescueMode::OwnerBlock => 5,
94 RescueMode::GetOwnerPage0 => 5,
95 _ => bail!(RescueError::BadMode(format!(
96 "mode {mode:?} not supported by DFU"
97 ))),
98 };
99
100 let device = self.device();
101 log::info!("Mode {mode} is AltSetting {setting}");
102 device.set_alternate_setting(self.interface.get(), setting)?;
103 Ok(())
104 }
105
106 fn set_speed(&self, _speed: u32) -> Result<u32> {
107 log::warn!("set_speed is not implemented for DFU");
108 Ok(0)
109 }
110
111 fn reboot(&self) -> Result<()> {
112 let usb = self.device();
113 usb.release_interface(self.interface.get())?;
114 match usb.reset() {
115 Ok(_) => {}
116 Err(e) => log::warn!("USB reset: {e}"),
117 }
118 Ok(())
119 }
120
121 fn send(&self, data: &[u8]) -> Result<()> {
122 for chunk in data.chunks(2048) {
123 let _ = self.download(chunk)?;
124 let status = loop {
125 let status = self.get_status()?;
126 match status.state() {
127 DfuState::DnLoadIdle | DfuState::Error => {
128 break status;
129 }
130 _ => {
131 std::thread::sleep(Duration::from_millis(status.poll_timeout() as u64));
132 }
133 }
134 };
135 status.status()?;
136 }
137 let _ = self.download(&[])?;
139 let status = self.get_status()?;
140 log::warn!("State after DFU download: {}", status.state());
141 Ok(())
142 }
143
144 fn recv(&self) -> Result<Vec<u8>> {
145 let mut data = vec![0u8; 2048];
146 self.upload(&mut data)?;
161 let status = self.get_status()?;
162 log::warn!("State after DFU upload: {}", status.state());
163 Ok(data)
164 }
165}
166
167impl DfuOperations for UsbDfu {
168 fn write_control(
169 &self,
170 request_type: u8,
171 request: u8,
172 value: u16,
173 index: u16,
174 data: &[u8],
175 ) -> Result<usize> {
176 let usb = self.device();
177 usb.write_control(request_type, request, value, index, data)
178 }
179
180 fn read_control(
181 &self,
182 request_type: u8,
183 request: u8,
184 value: u16,
185 index: u16,
186 data: &mut [u8],
187 ) -> Result<usize> {
188 let usb = self.device();
189 usb.read_control(request_type, request, value, index, data)
190 }
191
192 fn get_interface(&self) -> u8 {
193 self.interface.get()
194 }
195}