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::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 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 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 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}