1use colored::Colorize;
2use serde::{Deserialize, Serialize};
3use std::io::{stdout, Write};
4use std::mem::size_of;
5use std::sync::Arc;
6use tokio::sync::Mutex;
7
8#[cfg(feature = "ssh")]
9pub mod ssh;
10
11pub const CDBA_SERVER_BIN_NAME: &str = "cdba-server";
12
13#[repr(u8)]
14#[derive(Debug, PartialEq)]
15#[non_exhaustive]
16pub enum Sk8brdMsgs {
17 MsgSelectBoard = 1,
18 MsgConsole,
19 MsgHardReset,
20 MsgPowerOn,
21 MsgPowerOff,
22 MsgFastbootPresent,
23 MsgFastbootDownload,
24 MsgFastbootBoot,
25 MsgStatusUpdate,
26 MsgVbusOn,
27 MsgVbusOff,
28 MsgFastbootReboot,
29 MsgSendBreak,
30 MsgListDevices,
31 MsgBoardInfo,
32 MsgFastbootContinue,
33}
34
35impl TryFrom<u8> for Sk8brdMsgs {
36 type Error = String;
37
38 fn try_from(value: u8) -> Result<Self, Self::Error> {
39 match value {
40 1 => Ok(Sk8brdMsgs::MsgSelectBoard),
41 2 => Ok(Sk8brdMsgs::MsgConsole),
42 3 => Ok(Sk8brdMsgs::MsgHardReset),
43 4 => Ok(Sk8brdMsgs::MsgPowerOn),
44 5 => Ok(Sk8brdMsgs::MsgPowerOff),
45 6 => Ok(Sk8brdMsgs::MsgFastbootPresent),
46 7 => Ok(Sk8brdMsgs::MsgFastbootDownload),
47 8 => Ok(Sk8brdMsgs::MsgFastbootBoot),
48 9 => Ok(Sk8brdMsgs::MsgStatusUpdate),
49 10 => Ok(Sk8brdMsgs::MsgVbusOn),
50 11 => Ok(Sk8brdMsgs::MsgVbusOff),
51 12 => Ok(Sk8brdMsgs::MsgFastbootReboot),
52 13 => Ok(Sk8brdMsgs::MsgSendBreak),
53 14 => Ok(Sk8brdMsgs::MsgListDevices),
54 15 => Ok(Sk8brdMsgs::MsgBoardInfo),
55 16 => Ok(Sk8brdMsgs::MsgFastbootContinue),
56 _ => Err(format!("Unknown msg package {value}")),
57 }
58 }
59}
60
61#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
62#[repr(C)]
63#[repr(packed(1))]
64pub struct Sk8brdMsg {
65 pub r#type: u8,
66 pub len: u16,
67}
68pub const MSG_HDR_SIZE: usize = size_of::<Sk8brdMsg>();
69
70pub async fn send_msg(
71 write_sink: &mut Arc<Mutex<impl Write>>,
72 r#type: Sk8brdMsgs,
73 buf: &[u8],
74) -> anyhow::Result<()> {
75 let mut write_sink = write_sink.lock().await;
77
78 let len = buf.len();
79 let hdr = [r#type as u8, (len & 0xff) as u8, ((len >> 8) & 0xff) as u8];
80
81 write_sink.write_all(&hdr)?;
82 write_sink.write_all(buf)?;
83 Ok(())
84}
85
86pub async fn send_ack(
87 write_sink: &mut Arc<Mutex<impl Write>>,
88 r#type: Sk8brdMsgs,
89) -> anyhow::Result<()> {
90 send_msg(write_sink, r#type, &[]).await
91}
92
93pub fn parse_recv_msg(buf: &[u8]) -> Sk8brdMsg {
94 let msg: Sk8brdMsg = Sk8brdMsg {
95 r#type: buf[0],
96 len: (buf[2] as u16) << 8 | buf[1] as u16,
97 };
98
99 msg
102}
103
104pub async fn console_print(buf: &[u8]) {
105 print!("{}", String::from_utf8_lossy(buf));
106 stdout().flush().unwrap();
107}
108
109#[allow(clippy::explicit_write)]
110pub async fn send_image(
111 write_sink: &mut Arc<Mutex<impl Write>>,
112 buf: &[u8],
113 quit: &Arc<Mutex<bool>>,
114) -> anyhow::Result<()> {
115 let mut last_percent_done: usize = 0;
116 let mut bytes_sent = 0;
117
118 for chunk in buf.chunks(2048) {
119 let percent_done = 100 * bytes_sent / buf.len();
120
121 if *quit.lock().await {
122 return Ok(());
123 }
124
125 if percent_done != last_percent_done {
126 let s = format!("Sending image: {}%\r", percent_done);
127 print!("{}", s.green());
128 stdout().flush()?;
129 }
130
131 send_msg(write_sink, Sk8brdMsgs::MsgFastbootDownload, chunk).await?;
132
133 bytes_sent += chunk.len();
134 last_percent_done = percent_done;
135
136 if bytes_sent == buf.len() {
137 print!("\r{}\r", " ".repeat(80));
138 print!("{}\r\n", String::from("Image sent!").green());
139 stdout().flush()?;
140 }
141 }
142
143 send_ack(write_sink, Sk8brdMsgs::MsgFastbootDownload).await
144}
145
146pub async fn select_brd(write_sink: &mut Arc<Mutex<impl Write>>, name: &str) -> anyhow::Result<()> {
147 send_msg(write_sink, Sk8brdMsgs::MsgSelectBoard, name.as_bytes()).await
148}
149
150pub async fn send_break(write_sink: &mut Arc<Mutex<impl Write>>) -> anyhow::Result<()> {
151 send_ack(write_sink, Sk8brdMsgs::MsgSendBreak).await
152}
153
154pub async fn send_console(
155 write_sink: &mut Arc<Mutex<impl Write>>,
156 buf: &[u8],
157) -> anyhow::Result<()> {
158 send_msg(write_sink, Sk8brdMsgs::MsgConsole, buf).await
159}
160
161#[allow(clippy::explicit_write)]
162pub fn print_string_msg(buf: &[u8]) {
163 if buf.is_empty() {
164 return;
165 }
166
167 println!("{}\r", String::from_utf8_lossy(buf));
168 stdout().flush().unwrap();
169}
170
171#[macro_export]
172macro_rules! todo {
173 ($s: expr) => {{
174 let val = format!($s);
175 println!("{val}\r");
176 stdout().flush()?;
177 }};
178}