1use crate::{cdb, layout, Identity, INQUIRY_LEN};
20
21const FIELD_MAX: usize = 0xFF_FFFF;
23
24#[derive(Debug)]
26pub enum Data<'a> {
27 None,
29 In(&'a mut [u8]),
31 Out(&'a [u8]),
33}
34
35pub trait Transport {
37 type Error: core::fmt::Debug;
39
40 fn exec(&mut self, cdb: &[u8], data: Data<'_>) -> Result<usize, Self::Error>;
43
44 fn sense(&self) -> Option<(u8, u8, u8)>;
46}
47
48#[derive(Debug)]
50#[non_exhaustive]
51pub enum Error<E> {
52 Transport(E),
54 Short {
56 expected: usize,
58 actual: usize,
60 },
61 Oversize(usize),
63}
64
65#[cfg(feature = "std")]
66impl<E: core::fmt::Debug> std::error::Error for Error<E> {}
67
68impl<E: core::fmt::Debug> core::fmt::Display for Error<E> {
69 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70 match self {
71 Self::Transport(e) => write!(f, "transport error: {e:?}"),
72 Self::Short { expected, actual } => {
73 write!(f, "short transfer: expected {expected} bytes, got {actual}")
74 }
75 Self::Oversize(n) => write!(f, "{n:#x} exceeds the CDB field"),
76 }
77 }
78}
79
80fn exec<T: Transport>(t: &mut T, cdb: &[u8], data: Data<'_>) -> Result<usize, Error<T::Error>> {
81 t.exec(cdb, data).map_err(Error::Transport)
82}
83
84pub(crate) fn read<T: Transport>(
86 t: &mut T,
87 cdb: &[u8],
88 buf: &mut [u8],
89 min: usize,
90) -> Result<usize, Error<T::Error>> {
91 let n = exec(t, cdb, Data::In(buf))?.min(buf.len());
92 if n < min {
93 return Err(Error::Short {
94 expected: min,
95 actual: n,
96 });
97 }
98 Ok(n)
99}
100
101fn write<T: Transport>(t: &mut T, cdb: &[u8], data: &[u8]) -> Result<(), Error<T::Error>> {
103 let n = exec(t, cdb, Data::Out(data))?;
104 if n < data.len() {
105 return Err(Error::Short {
106 expected: data.len(),
107 actual: n,
108 });
109 }
110 Ok(())
111}
112
113fn field<E>(n: usize, max: usize) -> Result<u32, Error<E>> {
114 if n > max {
115 return Err(Error::Oversize(n));
116 }
117 Ok(n as u32)
118}
119
120pub fn identify<T: Transport>(t: &mut T) -> Result<Identity, Error<T::Error>> {
127 let mut inquiry = [0; cdb::INQUIRY_ALLOC as usize];
128 read(
129 t,
130 &cdb::inquiry(cdb::INQUIRY_ALLOC),
131 &mut inquiry,
132 INQUIRY_LEN,
133 )?;
134 let mut banner = [0; layout::BANNER.len];
135 let banner_len =
136 read_exact(t, layout::BANNER.start as u32, &mut banner).map_or(0, |_| banner.len());
137 let mut descriptor = [0; layout::DESCRIPTOR.len];
138 let descriptor_len = read_exact(t, layout::DESCRIPTOR.start as u32, &mut descriptor)
139 .map_or(0, |_| descriptor.len());
140 Identity::parse(
141 &inquiry,
142 &banner[..banner_len],
143 &descriptor[..descriptor_len],
144 )
145 .ok_or(Error::Short {
146 expected: INQUIRY_LEN,
147 actual: 0,
148 })
149}
150
151fn read_exact<T: Transport>(t: &mut T, off: u32, buf: &mut [u8]) -> Result<(), Error<T::Error>> {
152 let len = field(buf.len(), FIELD_MAX)?;
153 field::<T::Error>(off as usize, FIELD_MAX)?;
154 let n = buf.len();
155 read(t, &cdb::read_memory(off, len), buf, n).map(|_| ())
156}
157
158pub fn read_memory<T: Transport>(
163 t: &mut T,
164 off: u32,
165 buf: &mut [u8],
166) -> Result<(), Error<T::Error>> {
167 read_exact(t, off, buf)
168}
169
170pub fn feature<T: Transport>(
173 t: &mut T,
174 code: u16,
175 buf: &mut [u8],
176) -> Result<usize, Error<T::Error>> {
177 let alloc = field(buf.len(), u16::MAX as usize)? as u16;
178 read(t, &cdb::get_configuration(code, alloc), buf, 12)
179}
180
181pub fn test_unit_ready<T: Transport>(t: &mut T) -> Result<(), Error<T::Error>> {
184 exec(t, &cdb::test_unit_ready(), Data::None).map(|_| ())
185}
186
187pub fn request_sense<T: Transport>(
190 t: &mut T,
191) -> Result<Option<crate::sense::Triplet>, Error<T::Error>> {
192 let mut buf = [0; cdb::REQUEST_SENSE_LEN];
193 let n = read(t, &cdb::request_sense(), &mut buf, 0)?;
194 Ok(crate::sense::parse(&buf[..n]))
195}
196
197pub struct Session<'a, T: Transport> {
203 t: &'a mut T,
204}
205
206impl<T: Transport> core::fmt::Debug for Session<'_, T> {
207 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
208 f.debug_struct("Session").finish_non_exhaustive()
209 }
210}
211
212impl<T: Transport> Session<'_, T> {
213 pub fn write(&mut self, off: u32, data: &[u8]) -> Result<(), Error<T::Error>> {
216 let len = field(data.len(), u16::MAX as usize)? as u16;
217 field::<T::Error>(off as usize, FIELD_MAX)?;
218 write(self.t, &cdb::transfer(off, len), data)
219 }
220
221 pub fn finish(self) -> Result<(), Error<T::Error>> {
225 write(self.t, &cdb::finish(), &[])
226 }
227}
228
229pub fn enter_update<T: Transport>(t: &mut T) -> Result<Session<'_, T>, Error<T::Error>> {
232 enter_update_command(t, cdb::Prepare::ModeOne)
233}
234
235pub(crate) fn enter_update_command<T: Transport>(
236 t: &mut T,
237 prepare: cdb::Prepare,
238) -> Result<Session<'_, T>, Error<T::Error>> {
239 let mut probe = [0; cdb::PROBE_LEN];
240 read(t, &cdb::probe(), &mut probe, cdb::PROBE_LEN)?;
241 test_unit_ready(t)?;
242 write(t, &prepare.command(), &[])?;
243 Ok(Session { t })
244}
245
246pub fn enter_recovery<T: Transport>(t: &mut T) -> Result<Session<'_, T>, Error<T::Error>> {
251 write(t, &cdb::enter_update(), &[])?;
252 Ok(Session { t })
253}
254
255#[cfg(feature = "image")]
259pub(crate) fn enter_recovery_reporting<T: Transport>(
260 t: &mut T,
261 prepare: cdb::Prepare,
262 mut failed: impl FnMut(&Error<T::Error>),
263) -> Session<'_, T> {
264 if let Err(error) = write(t, &prepare.command(), &[]) {
265 failed(&error);
266 }
267 Session { t }
268}
269
270#[cfg(test)]
271#[path = "drive_tests.rs"]
272mod tests;