sk8brd/
lib.rs

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    // Make sure we're not trying to send two messages at once
76    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    // println!("{:?}", msg);
100
101    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}