fastboot_protocol/
protocol.rs1use 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_hex(hex: &str) -> Result<u32, ParseIntError> {
14 let hex = hex.strip_prefix("0x").unwrap_or("invalid");
17 u32::from_str_radix(hex, 16)
18}
19
20pub fn parse_u64_hex(hex: &str) -> Result<u64, ParseIntError> {
22 let hex = hex.strip_prefix("0x").unwrap_or("invalid");
25 u64::from_str_radix(hex, 16)
26}
27
28#[derive(Debug)]
30pub enum FastBootCommand<S> {
31 GetVar(S),
33 Download(u32),
35 Verify(u32),
37 Flash(S),
39 Erase(S),
41 Boot,
43 Continue,
45 Reboot,
47 RebootBootloader,
49 Powerdown,
51}
52
53impl<S: Display> Display for FastBootCommand<S> {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 FastBootCommand::GetVar(var) => write!(f, "getvar:{var}"),
57 FastBootCommand::Download(size) => write!(f, "download:{size:08x}"),
58 FastBootCommand::Verify(part) => write!(f, "verity:{part}"),
59 FastBootCommand::Flash(part) => write!(f, "flash:{part}"),
60 FastBootCommand::Erase(part) => write!(f, "erase:{part}"),
61 FastBootCommand::Boot => write!(f, "boot"),
62 FastBootCommand::Continue => write!(f, "continue"),
63 FastBootCommand::Reboot => write!(f, "reboot"),
64 FastBootCommand::RebootBootloader => write!(f, "reboot-bootloader"),
65 FastBootCommand::Powerdown => write!(f, "powerdown"),
66 }
67 }
68}
69
70#[derive(Error, Debug, PartialEq, Eq)]
72pub enum FastBootResponseParseError {
73 #[error("Unknown response type")]
75 UnknownReply,
76 #[error("Couldn't parse response type")]
78 ParseType,
79 #[error("Couldn't parse response payload")]
81 ParsePayload,
82 #[error("Couldn't parse DATA length")]
84 DataLength,
85}
86
87#[derive(Debug, PartialEq, Eq)]
89pub enum FastBootResponse {
90 Okay(String),
92 Info(String),
94 Text(String),
96 Fail(String),
98 Data(u32),
100}
101
102impl<'a> FastBootResponse {
103 fn from_parts(resp: &str, data: &'a str) -> Result<Self, FastBootResponseParseError> {
104 trace!("Parsing Response: {} {}", resp, data);
105 match resp {
106 "OKAY" => Ok(Self::Okay(data.into())),
107 "INFO" => Ok(Self::Info(data.into())),
108 "TEXT" => Ok(Self::Text(data.into())),
109 "FAIL" => Ok(Self::Fail(data.into())),
110 "DATA" => {
111 let offset = u32::from_str_radix(data, 16)
112 .or(Err(FastBootResponseParseError::DataLength))?;
113 Ok(Self::Data(offset))
114 }
115 _ => Err(FastBootResponseParseError::UnknownReply),
116 }
117 }
118
119 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, FastBootResponseParseError> {
121 if bytes.len() < 4 {
122 Err(FastBootResponseParseError::UnknownReply)
123 } else {
124 let resp =
125 std::str::from_utf8(&bytes[0..4]).or(Err(FastBootResponseParseError::ParseType))?;
126 let data = std::str::from_utf8(bytes_slice_null(&bytes[4..]))
127 .or(Err(FastBootResponseParseError::ParsePayload))?;
128
129 Self::from_parts(resp, data)
130 }
131 }
132}
133
134#[cfg(test)]
135mod test {
136 use super::*;
137
138 #[test]
139 fn parse_valid_u32_hex() {
140 let hex = parse_u32_hex("0x123456").unwrap();
141 assert_eq!(0x123456, hex);
142
143 let hex = parse_u32_hex("0x0012abcd").unwrap();
144 assert_eq!(0x12abcd, hex);
145 }
146
147 #[test]
148 fn parse_valid_u64_hex() {
149 let hex = parse_u64_hex("0x123456").unwrap();
150 assert_eq!(0x123456, hex);
151
152 let hex = parse_u64_hex("0x0012abcd").unwrap();
153 assert_eq!(0x12abcd, hex);
154
155 let hex = parse_u64_hex("0x0000000134b72400").unwrap();
156 assert_eq!(0x134b72400, hex);
157 }
158
159 #[test]
160 fn parse_invalid_u32_hex() {
161 parse_u32_hex("123456").unwrap_err();
162 }
163
164 #[test]
165 fn response_parse_ok() {
166 let r = FastBootResponse::from_bytes(b"OKAYtest").unwrap();
167 assert_eq!(r, FastBootResponse::Okay("test".to_string()));
168 }
169
170 #[test]
171 fn response_parse_ok_with_null() {
172 let r = FastBootResponse::from_bytes(b"OKAYtest\0foo").unwrap();
173 assert_eq!(r, FastBootResponse::Okay("test".to_string()));
174 }
175
176 #[test]
177 fn response_parse_fail() {
178 let r = FastBootResponse::from_bytes(b"FAILtest").unwrap();
179 assert_eq!(r, FastBootResponse::Fail("test".to_string()));
180 }
181
182 #[test]
183 fn response_parse_fail_with_null() {
184 let r = FastBootResponse::from_bytes(b"FAILtest\0foo").unwrap();
185 assert_eq!(r, FastBootResponse::Fail("test".to_string()));
186 }
187
188 #[test]
189 fn response_parse_info() {
190 let r = FastBootResponse::from_bytes(b"INFOtest").unwrap();
191 assert_eq!(r, FastBootResponse::Info("test".to_string()));
192 }
193
194 #[test]
195 fn response_parse_info_with_null() {
196 let r = FastBootResponse::from_bytes(b"INFOtest\0foo").unwrap();
197 assert_eq!(r, FastBootResponse::Info("test".to_string()));
198 }
199
200 #[test]
201 fn response_parse_text() {
202 let r = FastBootResponse::from_bytes(b"TEXTtest").unwrap();
203 assert_eq!(r, FastBootResponse::Text("test".to_string()));
204 }
205
206 #[test]
207 fn response_parse_text_with_null() {
208 let r = FastBootResponse::from_bytes(b"TEXTtest\0foo").unwrap();
209 assert_eq!(r, FastBootResponse::Text("test".to_string()));
210 }
211
212 #[test]
213 fn response_parse_data() {
214 let r = FastBootResponse::from_bytes(b"DATA00123456").unwrap();
215 assert_eq!(r, FastBootResponse::Data(0x123456));
216 }
217
218 #[test]
219 fn response_parse_data_with_null() {
220 let r = FastBootResponse::from_bytes(b"DATA00123456\0foo").unwrap();
221 assert_eq!(r, FastBootResponse::Data(0x123456));
222 }
223
224 #[test]
225 fn response_parse_invalid() {
226 let e = FastBootResponse::from_bytes(b"UNKN").unwrap_err();
227 assert_eq!(e, FastBootResponseParseError::UnknownReply);
228 }
229
230 #[test]
231 fn response_parse_too_short() {
232 let e = FastBootResponse::from_bytes(b"UN").unwrap_err();
233 assert_eq!(e, FastBootResponseParseError::UnknownReply);
234 }
235}