opentitanlib/rescue/
dfu.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 crate::with_unknown;
6use anyhow::Result;
7use zerocopy::{FromBytes, Immutable, IntoBytes};
8
9with_unknown! {
10    #[derive(FromBytes, Immutable, IntoBytes)]
11    pub enum DfuState: u8 [default = Self::AppIdle] {
12      AppIdle = 0,
13      AppDetach= 1,
14      Idle = 2,
15      DnLoadSync = 3,
16      DnLoadBusy = 4,
17      DnLoadIdle = 5,
18      ManifestSync = 6,
19      Manifest = 7,
20      ManifestWaitReset = 8,
21      UpLoadIdle = 9,
22      Error = 10,
23    }
24}
25
26with_unknown! {
27    #[derive(FromBytes, Immutable, IntoBytes)]
28    pub enum DfuError: u8 [default = Self::Ok] {
29      Ok = 0,
30      Target = 1,
31      File = 2,
32      Write = 3,
33      Erase = 4,
34      CheckErased = 5,
35      Prog = 6,
36      Verify = 7,
37      Address = 8,
38      NotDone = 9,
39      Firmware = 10,
40      Vendor = 11,
41      UsbReset = 12,
42      PowerOnReset = 13,
43      Unknown = 14,
44      StalledPkt = 15,
45    }
46}
47
48with_unknown! {
49    pub enum DfuRequest: u8 {
50      Detach = 0,
51      DnLoad = 1,
52      UpLoad = 2,
53      GetStatus = 3,
54      ClrStatus = 4,
55      GetState = 5,
56      Abort = 6,
57      BusReset = 7,
58    }
59}
60
61#[derive(Clone, Copy)]
62#[repr(u8)]
63pub enum DfuRequestType {
64    Out = 0x21,    // direction=out, type=class, recipient=interface.
65    In = 0xA1,     // direction=in, type=class, recipient=interface.
66    Vendor = 0x40, // direction=out, type=vendor, recipient=device.
67}
68
69impl From<DfuRequestType> for u8 {
70    fn from(val: DfuRequestType) -> Self {
71        val as u8
72    }
73}
74
75#[derive(Clone, FromBytes, Immutable, IntoBytes, Default)]
76#[repr(C)]
77pub struct DfuStatus {
78    status: DfuError,
79    poll_timeout: [u8; 3],
80    state: DfuState,
81    string: u8,
82}
83
84impl std::error::Error for DfuError {}
85
86impl DfuStatus {
87    pub fn status(&self) -> std::result::Result<(), DfuError> {
88        match self.status {
89            DfuError::Ok => Ok(()),
90            e => Err(e),
91        }
92    }
93    pub fn poll_timeout(&self) -> u32 {
94        u32::from_le_bytes([
95            self.poll_timeout[0],
96            self.poll_timeout[1],
97            self.poll_timeout[2],
98            0,
99        ])
100    }
101    pub fn state(&self) -> DfuState {
102        self.state
103    }
104    pub fn string(&self) -> u8 {
105        self.string
106    }
107}
108
109pub trait DfuOperations {
110    fn write_control(
111        &self,
112        request_type: u8,
113        request: u8,
114        value: u16,
115        index: u16,
116        data: &[u8],
117    ) -> Result<usize>;
118
119    fn read_control(
120        &self,
121        request_type: u8,
122        request: u8,
123        value: u16,
124        index: u16,
125        data: &mut [u8],
126    ) -> Result<usize>;
127
128    fn get_interface(&self) -> u8;
129
130    /// Download (send) a block to the target device.
131    fn download(&self, data: &[u8]) -> Result<usize> {
132        self.write_control(
133            DfuRequestType::Out.into(),
134            DfuRequest::DnLoad.into(),
135            /*wValue=*/ 0,
136            /*wIndex=*/ self.get_interface() as u16,
137            data,
138        )
139    }
140
141    /// Upload (receive) a block from the target device.
142    fn upload(&self, data: &mut [u8]) -> Result<usize> {
143        self.read_control(
144            DfuRequestType::In.into(),
145            DfuRequest::UpLoad.into(),
146            /*wValue=*/ 0,
147            /*wIndex=*/ self.get_interface() as u16,
148            data,
149        )
150    }
151
152    /// Get the current DFU state.
153    fn get_state(&self) -> Result<DfuState> {
154        let mut buffer = [0u8];
155        self.read_control(
156            DfuRequestType::In.into(),
157            DfuRequest::GetState.into(),
158            /*wValue=*/ 0,
159            /*wIndex=*/ self.get_interface() as u16,
160            &mut buffer,
161        )?;
162        Ok(DfuState(buffer[0]))
163    }
164
165    /// Get the DFU status.
166    fn get_status(&self) -> Result<DfuStatus> {
167        let mut status = DfuStatus::default();
168        self.read_control(
169            DfuRequestType::In.into(),
170            DfuRequest::GetStatus.into(),
171            /*wValue=*/ 0,
172            /*wIndex=*/ self.get_interface() as u16,
173            status.as_mut_bytes(),
174        )?;
175        Ok(status)
176    }
177
178    /// Clear the DFU status.
179    fn clear_status(&self) -> Result<()> {
180        self.write_control(
181            DfuRequestType::Out.into(),
182            DfuRequest::ClrStatus.into(),
183            /*wValue=*/ 0,
184            /*wIndex=*/ self.get_interface() as u16,
185            &[],
186        )?;
187        Ok(())
188    }
189
190    /// Abort a DFU operation.
191    fn abort(&self) -> Result<()> {
192        self.write_control(
193            DfuRequestType::Out.into(),
194            DfuRequest::Abort.into(),
195            /*wValue=*/ 0,
196            /*wIndex=*/ self.get_interface() as u16,
197            &[],
198        )?;
199        Ok(())
200    }
201}