sylveos

Toy Operating System
Log | Files | Refs

main.rs (4010B)


      1 use clap::Parser;
      2 use indicatif::ProgressStyle;
      3 use tokio::io::{self, AsyncReadExt, AsyncWriteExt, ReadHalf, Stdout};
      4 use tokio_serial::{DataBits, FlowControl, Parity, SerialPortBuilderExt, SerialStream, StopBits};
      5 use tracing::{info, level_filters::LevelFilter, warn};
      6 use tracing_indicatif::{
      7     IndicatifLayer,
      8     filter::{IndicatifFilter, hide_indicatif_span_fields},
      9 };
     10 use tracing_subscriber::{
     11     EnvFilter, Layer, fmt::format::DefaultFields, layer::SubscriberExt, util::SubscriberInitExt,
     12 };
     13 
     14 use std::{path::PathBuf, process::exit, time::Duration};
     15 
     16 mod bootload;
     17 mod sylveos;
     18 mod tcp;
     19 
     20 #[derive(Parser, Debug)]
     21 #[command(about)]
     22 struct Args {
     23     #[arg(short, long, default_value_t = 921600)]
     24     baud: u32,
     25     #[arg(short, long, value_hint = clap::ValueHint::FilePath, default_value = "/dev/ttyUSB0")]
     26     device: String,
     27     #[arg(short, long, default_value_t = 10.0)]
     28     timeout: f64,
     29     #[arg(short, long, default_value = "0x8000", value_parser = hex_value, value_name = "ADDRESS")]
     30     arm_base: u32,
     31     #[arg(long, default_value_t = false)]
     32     tcp: bool,
     33     #[arg(long, default_value_t = false)]
     34     sylveos: bool,
     35     #[arg(value_hint = clap::ValueHint::DirPath)]
     36     file: PathBuf,
     37 }
     38 
     39 fn hex_value(s: &str) -> Result<u32, String> {
     40     let trimmed = if let Some(trimmed) = s.strip_prefix("0x") {
     41         trimmed
     42     } else {
     43         s
     44     };
     45 
     46     u32::from_str_radix(trimmed, 16).map_err(|e| e.to_string())
     47 }
     48 
     49 async fn serial_to_stdout(
     50     serial: &mut ReadHalf<SerialStream>,
     51     stdout: &mut Stdout,
     52 ) -> io::Result<()> {
     53     loop {
     54         let byte = serial.read_u8().await?;
     55 
     56         if byte == 0 {
     57             info!("pi exited");
     58             exit(0);
     59         }
     60 
     61         stdout.write_u8(byte).await?;
     62         stdout.flush().await?;
     63     }
     64 }
     65 
     66 async fn default_action(port: SerialStream) -> anyhow::Result<()> {
     67     let (mut port_read, mut port_write) = io::split(port);
     68     let mut stdin = io::stdin();
     69     let mut stdout = io::stdout();
     70 
     71     tokio::select!(
     72         _ = io::copy(&mut stdin, &mut port_write) => {
     73             warn!("stdin disconnected, exiting");
     74             exit(0);
     75         },
     76         _ = serial_to_stdout(&mut port_read, &mut stdout) => {
     77             warn!("serial port disconnected, exiting");
     78             exit(0);
     79         },
     80     );
     81 }
     82 
     83 #[tokio::main]
     84 async fn main() -> anyhow::Result<()> {
     85     let indicatif_layer = IndicatifLayer::new()
     86         .with_span_field_formatter(hide_indicatif_span_fields(DefaultFields::new()))
     87         .with_progress_style(ProgressStyle::with_template(
     88             "{span_child_prefix}{span_name}{{{span_fields}}} {wide_msg} {elapsed}",
     89         )?)
     90         .with_span_child_prefix_symbol("↳ ")
     91         .with_span_child_prefix_indent(" ");
     92 
     93     tracing_subscriber::registry()
     94         .with(
     95             EnvFilter::builder()
     96                 .with_default_directive(LevelFilter::INFO.into())
     97                 .from_env_lossy(),
     98         )
     99         .with(
    100             tracing_subscriber::fmt::layer()
    101                 .with_writer(indicatif_layer.get_stderr_writer())
    102                 .with_target(false),
    103         )
    104         .with(indicatif_layer.with_filter(IndicatifFilter::new(false)))
    105         .init();
    106 
    107     let args = Args::parse();
    108 
    109     if !PathBuf::from(&args.device).exists() {
    110         anyhow::bail!("serial device not found");
    111     }
    112 
    113     if !args.file.exists() {
    114         anyhow::bail!("binary not found");
    115     }
    116 
    117     let mut port = tokio_serial::new(&args.device, args.baud)
    118         .data_bits(DataBits::Eight)
    119         .flow_control(FlowControl::None)
    120         .parity(Parity::None)
    121         .stop_bits(StopBits::One)
    122         .timeout(Duration::from_secs_f64(args.timeout))
    123         .open_native_async()?;
    124 
    125     bootload::boot(&mut port, &args.file, args.arm_base).await?;
    126 
    127     if args.tcp {
    128         let (port_read, port_write) = io::split(port);
    129         tcp::serve(port_read, port_write).await?;
    130     } else if args.sylveos {
    131         sylveos::start(port).await?;
    132     } else {
    133         default_action(port).await?;
    134     }
    135 
    136     Ok(())
    137 }