1use anyhow::{Result, bail};
6use std::cell::RefCell;
7use std::rc::Rc;
8use std::time::{Duration, Instant};
9use zerocopy::{Immutable, IntoBytes};
10
11use crate::app::{TransportWrapper, UartRx};
12use crate::chip::rom_error::RomError;
13use crate::io::spi::Target;
14use crate::rescue::dfu::*;
15use crate::rescue::{EntryMode, Rescue, RescueError, RescueMode, RescueParams};
16use crate::spiflash::SpiFlash;
17use crate::spiflash::sfdp::Sdfu;
18
19#[repr(C)]
20#[derive(Default, Debug, Immutable, IntoBytes)]
21struct SetupData {
22 request_type: u8,
23 request: u8,
24 value: u16,
25 index: u16,
26 length: u16,
27}
28
29pub struct SpiDfu {
30 spi: Rc<dyn Target>,
31 flash: RefCell<SpiFlash>,
32 sdfu: RefCell<Sdfu>,
33 params: RescueParams,
34 reset_delay: Duration,
35 enter_delay: Duration,
36}
37
38impl SpiDfu {
39 const SET_INTERFACE: u8 = 0x0b;
40
41 pub fn new(spi: Rc<dyn Target>, params: RescueParams) -> Self {
42 SpiDfu {
43 spi,
44 flash: RefCell::default(),
45 sdfu: RefCell::default(),
46 params,
47 reset_delay: Duration::from_millis(50),
48 enter_delay: Duration::from_secs(5),
49 }
50 }
51
52 fn wait_for_device(spi: &dyn Target, timeout: Duration) -> Result<SpiFlash> {
53 let deadline = Instant::now() + timeout;
54 loop {
55 match SpiFlash::from_spi(spi) {
56 Ok(flash) => return Ok(flash),
57 Err(e) => {
58 if Instant::now() < deadline {
59 std::thread::sleep(Duration::from_millis(100));
60 } else {
61 return Err(e);
62 }
63 }
64 }
65 }
66 }
67}
68
69impl Rescue for SpiDfu {
70 fn enter(&self, transport: &TransportWrapper, mode: EntryMode) -> Result<()> {
71 log::info!(
72 "Setting {:?}({}) to trigger rescue mode.",
73 self.params.trigger,
74 self.params.value
75 );
76 self.params.set_trigger(transport, true)?;
77 match mode {
78 EntryMode::Reset => transport.reset_with_delay(UartRx::Keep, self.reset_delay)?,
79 EntryMode::Reboot => {
80 self.reboot()?;
81 std::thread::sleep(Duration::from_millis(100));
84 }
85 EntryMode::None => {}
86 }
87
88 let flash = Self::wait_for_device(&*self.spi, self.enter_delay);
89 log::info!("Rescue triggered; clearing trigger condition.");
90 self.params.set_trigger(transport, false)?;
91 let mut flash = flash?;
92 log::info!("Flash = {:?}", flash.sfdp);
93 if let Some(sdfu) = flash.sfdp.as_ref().and_then(|sfdp| sfdp.sdfu.as_ref()) {
94 self.sdfu.replace(sdfu.clone());
95 } else {
96 return Err(RescueError::NotFound(
97 "Could not find SDFU parameters in the SDFP table".into(),
98 )
99 .into());
100 }
101 flash.set_address_mode_auto(&*self.spi)?;
102 self.flash.replace(flash);
103 Ok(())
104 }
105
106 fn set_mode(&self, mode: RescueMode) -> Result<()> {
107 let setting = match mode {
108 RescueMode::Rescue => RescueMode::Rescue,
111 RescueMode::RescueB => RescueMode::RescueB,
112 RescueMode::DeviceId => RescueMode::DeviceId,
113 RescueMode::BootLog => RescueMode::BootLog,
114 RescueMode::BootSvcReq => RescueMode::BootSvcRsp,
115 RescueMode::BootSvcRsp => RescueMode::BootSvcRsp,
116 RescueMode::OwnerBlock => RescueMode::GetOwnerPage0,
117 RescueMode::GetOwnerPage0 => RescueMode::GetOwnerPage0,
118 _ => bail!(RescueError::BadMode(format!(
119 "mode {mode:?} not supported by DFU"
120 ))),
121 };
122
123 log::info!("Mode {mode} is AltSetting {setting}");
124 let setting = u32::from(setting);
125 self.write_control(
127 DfuRequestType::Vendor.into(),
128 Self::SET_INTERFACE,
129 (setting >> 16) as u16,
130 setting as u16,
131 &[],
132 )?;
133 Ok(())
134 }
135
136 fn set_speed(&self, _speed: u32) -> Result<u32> {
137 log::warn!("set_speed is not implemented for DFU");
138 Ok(0)
139 }
140
141 fn reboot(&self) -> Result<()> {
142 SpiFlash::chip_reset(&*self.spi)?;
143 Ok(())
144 }
145
146 fn send(&self, data: &[u8]) -> Result<()> {
147 let sdfu = self.sdfu.borrow();
148 for chunk in data.chunks(sdfu.dfu_size as usize) {
149 let _ = self.download(chunk)?;
150 let status = loop {
151 let status = self.get_status()?;
152 match status.state() {
153 DfuState::DnLoadIdle | DfuState::Error => {
154 break status;
155 }
156 _ => {
157 std::thread::sleep(Duration::from_millis(status.poll_timeout() as u64));
158 }
159 }
160 };
161 status.status()?;
162 }
163 let _ = self.download(&[])?;
165 let status = self.get_status()?;
166 log::warn!("State after DFU download: {}", status.state());
167 Ok(())
168 }
169
170 fn recv(&self) -> Result<Vec<u8>> {
171 let sdfu = self.sdfu.borrow();
172 let mut data = vec![0u8; sdfu.dfu_size as usize];
173 self.upload(&mut data)?;
188 let status = self.get_status()?;
189 log::warn!("State after DFU upload: {}", status.state());
190 Ok(data)
191 }
192}
193
194impl DfuOperations for SpiDfu {
195 fn get_interface(&self) -> u8 {
196 0
197 }
198
199 fn write_control(
206 &self,
207 request_type: u8,
208 request: u8,
209 value: u16,
210 index: u16,
211 data: &[u8],
212 ) -> Result<usize> {
213 let setup = SetupData {
214 request_type,
215 request,
216 value,
217 index,
218 length: data.len().try_into()?,
219 };
220 let flash = self.flash.borrow();
221 let sdfu = self.sdfu.borrow();
222 flash.program(&*self.spi, sdfu.mailbox_address, setup.as_bytes())?;
223
224 let mut result = [0u8; 4];
225 flash.read(&*self.spi, sdfu.mailbox_address, &mut result)?;
226 Result::<(), RomError>::from(RomError(u32::from_le_bytes(result)))?;
227
228 flash.program(&*self.spi, 0, data)?;
229 Ok(data.len())
230 }
231
232 fn read_control(
239 &self,
240 request_type: u8,
241 request: u8,
242 value: u16,
243 index: u16,
244 data: &mut [u8],
245 ) -> Result<usize> {
246 let setup = SetupData {
247 request_type,
248 request,
249 value,
250 index,
251 length: data.len().try_into()?,
252 };
253 let flash = self.flash.borrow();
254 let sdfu = self.sdfu.borrow();
255 flash.program(&*self.spi, sdfu.mailbox_address, setup.as_bytes())?;
256
257 let mut result = [0u8; 4];
258 flash.read(&*self.spi, sdfu.mailbox_address, &mut result)?;
259 Result::<(), RomError>::from(RomError(u32::from_le_bytes(result)))?;
260
261 flash.read(&*self.spi, 0, data)?;
262 Ok(data.len())
263 }
264}