1use std::{fmt::Display, num::ParseIntError};
2use thiserror::Error;
3use tracing::trace;
4
5fn bytes_slice_null(bytes: &[u8]) -> &[u8] {
6 match bytes.iter().position(|&b| b == 0x00) {
7 Some(pos) => &bytes[..pos],
8 None => bytes,
9 }
10}
11
12pub fn parse_u32(s: &str) -> Result<u32, ParseIntError> {
14 if s.starts_with("0x") {
15 parse_u32_hex(s)
16 } else {
17 s.parse()
18 }
19}
20
21pub fn parse_u32_hex(hex: &str) -> Result<u32, ParseIntError> {
23 let hex = hex.strip_prefix("0x").unwrap_or("invalid");
26 u32::from_str_radix(hex, 16)
27}
28
29pub fn parse_u64_hex(hex: &str) -> Result<u64, ParseIntError> {
31 let hex = hex.strip_prefix("0x").unwrap_or("invalid");
34 u64::from_str_radix(hex, 16)
35}
36
37#[derive(Debug)]
39pub enum FastBootCommand<S> {
40 GetVar(S),
42 Download(u32),
44 Verify(u32),
46 Flash(S),
48 Erase(S),
50 Boot,
52 Continue,
54 Reboot,
56 RebootBootloader,
58 RebootTo(S),
60 Powerdown,
62}
63
64impl<S: Display> Display for FastBootCommand<S> {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 match self {
67 FastBootCommand::GetVar(var) => write!(f, "getvar:{var}"),
68 FastBootCommand::Download(size) => write!(f, "download:{size:08x}"),
69 FastBootCommand::Verify(part) => write!(f, "verity:{part}"),
70 FastBootCommand::Flash(part) => write!(f, "flash:{part}"),
71 FastBootCommand::Erase(part) => write!(f, "erase:{part}"),
72 FastBootCommand::Boot => write!(f, "boot"),
73 FastBootCommand::Continue => write!(f, "continue"),
74 FastBootCommand::Reboot => write!(f, "reboot"),
75 FastBootCommand::RebootBootloader => write!(f, "reboot-bootloader"),
76 FastBootCommand::RebootTo(mode) => write!(f, "reboot-{mode}"),
77 FastBootCommand::Powerdown => write!(f, "powerdown"),
78 }
79 }
80}
81
82#[derive(Error, Debug, PartialEq, Eq)]
84pub enum FastBootResponseParseError {
85 #[error("Unknown response type")]
87 UnknownReply,
88 #[error("Couldn't parse response type")]
90 ParseType,
91 #[error("Couldn't parse response payload")]
93 ParsePayload,
94 #[error("Couldn't parse DATA length")]
96 DataLength,
97}
98
99#[derive(Debug, PartialEq, Eq)]
101pub enum FastBootResponse {
102 Okay(String),
104 Info(String),
106 Text(String),
108 Fail(String),
110 Data(u32),
112}
113
114impl<'a> FastBootResponse {
115 fn from_parts(resp: &str, data: &'a str) -> Result<Self, FastBootResponseParseError> {
116 trace!("Parsing Response: {} {}", resp, data);
117 match resp {
118 "OKAY" => Ok(Self::Okay(data.into())),
119 "INFO" => Ok(Self::Info(data.into())),
120 "TEXT" => Ok(Self::Text(data.into())),
121 "FAIL" => Ok(Self::Fail(data.into())),
122 "DATA" => {
123 let offset = u32::from_str_radix(data, 16)
124 .or(Err(FastBootResponseParseError::DataLength))?;
125 Ok(Self::Data(offset))
126 }
127 _ => Err(FastBootResponseParseError::UnknownReply),
128 }
129 }
130
131 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, FastBootResponseParseError> {
133 if bytes.len() < 4 {
134 Err(FastBootResponseParseError::UnknownReply)
135 } else {
136 let resp =
137 std::str::from_utf8(&bytes[0..4]).or(Err(FastBootResponseParseError::ParseType))?;
138 let data = std::str::from_utf8(bytes_slice_null(&bytes[4..]))
139 .or(Err(FastBootResponseParseError::ParsePayload))?;
140
141 Self::from_parts(resp, data)
142 }
143 }
144}
145
146#[cfg(test)]
147mod test {
148 use super::*;
149
150 #[test]
151 fn parse_valid_u32() {
152 let hex = parse_u32("0x123456").unwrap();
153 assert_eq!(0x123456, hex);
154
155 let hex = parse_u32("0012345").unwrap();
156 assert_eq!(12345, hex);
157 }
158
159 #[test]
160 fn parse_valid_u32_hex() {
161 let hex = parse_u32_hex("0x123456").unwrap();
162 assert_eq!(0x123456, hex);
163
164 let hex = parse_u32_hex("0x0012abcd").unwrap();
165 assert_eq!(0x12abcd, hex);
166 }
167
168 #[test]
169 fn parse_valid_u64_hex() {
170 let hex = parse_u64_hex("0x123456").unwrap();
171 assert_eq!(0x123456, hex);
172
173 let hex = parse_u64_hex("0x0012abcd").unwrap();
174 assert_eq!(0x12abcd, hex);
175
176 let hex = parse_u64_hex("0x0000000134b72400").unwrap();
177 assert_eq!(0x134b72400, hex);
178 }
179
180 #[test]
181 fn parse_invalid_u32() {
182 parse_u32("12abcd").unwrap_err();
183 parse_u32("hello").unwrap_err();
184 }
185
186 #[test]
187 fn parse_invalid_u32_hex() {
188 parse_u32_hex("123456").unwrap_err();
189 }
190
191 #[test]
192 fn response_parse_ok() {
193 let r = FastBootResponse::from_bytes(b"OKAYtest").unwrap();
194 assert_eq!(r, FastBootResponse::Okay("test".to_string()));
195 }
196
197 #[test]
198 fn response_parse_ok_with_null() {
199 let r = FastBootResponse::from_bytes(b"OKAYtest\0foo").unwrap();
200 assert_eq!(r, FastBootResponse::Okay("test".to_string()));
201 }
202
203 #[test]
204 fn response_parse_fail() {
205 let r = FastBootResponse::from_bytes(b"FAILtest").unwrap();
206 assert_eq!(r, FastBootResponse::Fail("test".to_string()));
207 }
208
209 #[test]
210 fn response_parse_fail_with_null() {
211 let r = FastBootResponse::from_bytes(b"FAILtest\0foo").unwrap();
212 assert_eq!(r, FastBootResponse::Fail("test".to_string()));
213 }
214
215 #[test]
216 fn response_parse_info() {
217 let r = FastBootResponse::from_bytes(b"INFOtest").unwrap();
218 assert_eq!(r, FastBootResponse::Info("test".to_string()));
219 }
220
221 #[test]
222 fn response_parse_info_with_null() {
223 let r = FastBootResponse::from_bytes(b"INFOtest\0foo").unwrap();
224 assert_eq!(r, FastBootResponse::Info("test".to_string()));
225 }
226
227 #[test]
228 fn response_parse_text() {
229 let r = FastBootResponse::from_bytes(b"TEXTtest").unwrap();
230 assert_eq!(r, FastBootResponse::Text("test".to_string()));
231 }
232
233 #[test]
234 fn response_parse_text_with_null() {
235 let r = FastBootResponse::from_bytes(b"TEXTtest\0foo").unwrap();
236 assert_eq!(r, FastBootResponse::Text("test".to_string()));
237 }
238
239 #[test]
240 fn response_parse_data() {
241 let r = FastBootResponse::from_bytes(b"DATA00123456").unwrap();
242 assert_eq!(r, FastBootResponse::Data(0x123456));
243 }
244
245 #[test]
246 fn response_parse_data_with_null() {
247 let r = FastBootResponse::from_bytes(b"DATA00123456\0foo").unwrap();
248 assert_eq!(r, FastBootResponse::Data(0x123456));
249 }
250
251 #[test]
252 fn response_parse_invalid() {
253 let e = FastBootResponse::from_bytes(b"UNKN").unwrap_err();
254 assert_eq!(e, FastBootResponseParseError::UnknownReply);
255 }
256
257 #[test]
258 fn response_parse_too_short() {
259 let e = FastBootResponse::from_bytes(b"UN").unwrap_err();
260 assert_eq!(e, FastBootResponseParseError::UnknownReply);
261 }
262}