Skip to content

Commit

Permalink
Make usb optional in host board for CI in github (#127)
Browse files Browse the repository at this point in the history
  • Loading branch information
ia0 authored Apr 26, 2023
1 parent 6fa2157 commit 953fedb
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 60 deletions.
8 changes: 5 additions & 3 deletions crates/runner-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ ccm = { version = "0.5.0", features = ["std"] }
env_logger = "0.10.0"
rand = "0.8.5"
tokio = { version = "1.27.0", features = ["full"] }
usb-device = "0.2.9"
usbd-serial = "0.1.1"
usbip-device = "0.1.5"
usb-device = { version = "0.2.9", optional = true }
usbd-serial = { version = "0.1.1", optional = true }
usbip-device = { version = "0.1.5", optional = true }
wasefire-applet-api = { path = "../api", features = ["host"] }
wasefire-board-api = { path = "../board", features = ["std"] }
wasefire-interpreter = { path = "../interpreter" }
Expand All @@ -26,4 +26,6 @@ wasefire-store = { path = "../store", features = ["std"] }

[features]
debug = ["wasefire-logger/log", "wasefire-scheduler/log"]
default = ["usb"]
release = []
usb = ["dep:usb-device", "dep:usbd-serial", "dep:usbip-device"]
22 changes: 11 additions & 11 deletions crates/runner-host/src/board.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ mod crypto;
mod led;
mod rng;
pub mod timer;
mod usb;
#[cfg(feature = "usb")]
pub mod usb;

use std::sync::{Arc, Mutex};

use tokio::sync::mpsc::{Receiver, Sender};
use usb_device::prelude::UsbDevice;
use usbip_device::UsbIpBus;
use wasefire_board_api::usb::serial::Serial;
use wasefire_board_api::{Api, Event};
use wasefire_store::FileStorage;

Expand All @@ -35,8 +33,8 @@ pub struct State {
pub button: bool, // whether interrupts are enabled
pub led: bool,
pub timers: Timers,
pub serial: Serial<'static, UsbIpBus>,
pub usb_dev: UsbDevice<'static, UsbIpBus>,
#[cfg(feature = "usb")]
pub usb: usb::Usb,
pub storage: Option<FileStorage>,
}

Expand Down Expand Up @@ -88,14 +86,16 @@ impl Api for Board {
self
}

#[cfg(feature = "usb")]
type Usb<'a> = &'a mut Self;
#[cfg(feature = "usb")]
fn usb(&mut self) -> Self::Usb<'_> {
self
}
}

impl State {
pub fn poll(&mut self) -> bool {
self.usb_dev.poll(&mut [self.serial.port()])
#[cfg(not(feature = "usb"))]
type Usb<'a> = !;
#[cfg(not(feature = "usb"))]
fn usb(&mut self) -> Self::Usb<'_> {
unreachable!()
}
}
67 changes: 65 additions & 2 deletions crates/runner-host/src/board/usb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::process::{Child, Command};
use std::sync::{Arc, Mutex};
use std::time::Duration;

use board::usb::serial::{HasSerial, WithSerial};
use usb_device::class_prelude::UsbBusAllocator;
use usb_device::prelude::{UsbDevice, UsbDeviceBuilder, UsbVidPid};
use usb_device::UsbError;
use usbd_serial::{SerialPort, USB_CLASS_CDC};
use usbip_device::UsbIpBus;
use wasefire_board_api as board;
use wasefire_board_api::usb::serial::Serial;

use crate::board::Board;
use crate::board::{Board, State};

impl board::usb::Api for &mut Board {
type Serial<'a> = WithSerial<&'a mut Board>
Expand All @@ -31,6 +39,61 @@ impl HasSerial for &mut Board {
type UsbBus = UsbIpBus;

fn with_serial<R>(&mut self, f: impl FnOnce(&mut Serial<Self::UsbBus>) -> R) -> R {
f(&mut self.state.lock().unwrap().serial)
f(&mut self.state.lock().unwrap().usb.serial)
}
}

pub struct Usb {
pub serial: Serial<'static, UsbIpBus>,
pub usb_dev: UsbDevice<'static, UsbIpBus>,
}

impl Default for Usb {
fn default() -> Self {
let usb_bus = Box::leak(Box::new(UsbBusAllocator::new(UsbIpBus::new())));
let serial = Serial::new(SerialPort::new(usb_bus));
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
.product("Serial port")
.device_class(USB_CLASS_CDC)
.build();
Self { serial, usb_dev }
}
}

impl Usb {
pub fn init(state: Arc<Mutex<State>>) {
assert_eq!(spawn(&["sudo", "modprobe", "vhci-hcd"]).wait().unwrap().code().unwrap(), 0);
let mut usbip = spawn(&["sudo", "usbip", "attach", "-r", "localhost", "-b", "1-1"]);
loop {
state.lock().unwrap().usb.poll();
match usbip.try_wait().unwrap() {
None => continue,
Some(e) => assert_eq!(e.code().unwrap(), 0),
}
break;
}
tokio::spawn({
async move {
loop {
tokio::time::sleep(Duration::from_millis(1)).await;
let mut state = state.lock().unwrap();
let polled = state.usb.poll()
&& !matches!(
state.usb.serial.port().read(&mut []),
Err(UsbError::WouldBlock)
);
let State { sender, usb: Usb { serial, .. }, .. } = &mut *state;
serial.tick(polled, |event| drop(sender.try_send(event.into())));
}
}
});
}

pub fn poll(&mut self) -> bool {
self.usb_dev.poll(&mut [self.serial.port()])
}
}

fn spawn(cmd: &[&str]) -> Child {
Command::new(cmd[0]).args(&cmd[1 ..]).spawn().unwrap()
}
49 changes: 5 additions & 44 deletions crates/runner-host/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,20 @@
// limitations under the License.

#![feature(core_intrinsics)]
#![feature(never_type)]
#![feature(try_blocks)]

use std::io::BufRead;
use std::path::Path;
use std::process::{Child, Command};
use std::sync::{Arc, Mutex};
use std::time::Duration;

use anyhow::Result;
use tokio::runtime::Handle;
use tokio::sync::mpsc::channel;
use usb_device::class_prelude::UsbBusAllocator;
use usb_device::prelude::{UsbDeviceBuilder, UsbVidPid};
use usb_device::UsbError;
use usbd_serial::{SerialPort, USB_CLASS_CDC};
use usbip_device::UsbIpBus;
use wasefire_board_api::usb::serial::Serial;
use wasefire_scheduler::Scheduler;
use wasefire_store::{FileOptions, FileStorage};

use crate::board::timer::Timers;
use crate::board::State;

mod board;

Expand All @@ -43,12 +35,6 @@ async fn main() -> Result<()> {
env_logger::init();
// TODO: Should be a flag controlled by xtask (value is duplicated there).
const STORAGE: &str = "../../target/storage.bin";
let usb_bus = Box::leak(Box::new(UsbBusAllocator::new(UsbIpBus::new())));
let serial = Serial::new(SerialPort::new(usb_bus));
let usb_dev = UsbDeviceBuilder::new(usb_bus, UsbVidPid(0x16c0, 0x27dd))
.product("Serial port")
.device_class(USB_CLASS_CDC)
.build();
let options = FileOptions { word_size: 4, page_size: 4096, num_pages: 16 };
let storage = Some(FileStorage::new(Path::new(STORAGE), options).unwrap());
let (sender, receiver) = channel(10);
Expand All @@ -57,33 +43,12 @@ async fn main() -> Result<()> {
button: false,
led: false,
timers: Timers::default(),
serial,
usb_dev,
#[cfg(feature = "usb")]
usb: board::usb::Usb::default(),
storage,
}));
assert_eq!(spawn(&["sudo", "modprobe", "vhci-hcd"]).wait().unwrap().code().unwrap(), 0);
let mut usbip = spawn(&["sudo", "usbip", "attach", "-r", "localhost", "-b", "1-1"]);
loop {
state.lock().unwrap().poll();
match usbip.try_wait().unwrap() {
None => continue,
Some(e) => assert_eq!(e.code().unwrap(), 0),
}
break;
}
tokio::spawn({
let state = state.clone();
async move {
loop {
tokio::time::sleep(Duration::from_millis(1)).await;
let mut state = state.lock().unwrap();
let polled = state.poll()
&& !matches!(state.serial.port().read(&mut []), Err(UsbError::WouldBlock));
let State { sender, serial, .. } = &mut *state;
serial.tick(polled, |event| drop(sender.try_send(event.into())));
}
}
});
#[cfg(feature = "usb")]
board::usb::Usb::init(state.clone());
tokio::spawn({
let state = state.clone();
async move {
Expand All @@ -105,7 +70,3 @@ async fn main() -> Result<()> {
println!("Running.");
Handle::current().spawn_blocking(|| Scheduler::run(board::Board { receiver, state })).await?
}

fn spawn(cmd: &[&str]) -> Child {
Command::new(cmd[0]).args(&cmd[1 ..]).spawn().unwrap()
}
1 change: 1 addition & 0 deletions crates/runner-host/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ set -ex
touch ../../target/applet.wasm
cargo check --features=debug
cargo check --features=release
cargo check --no-default-features --features=debug
cargo fmt -- --check
cargo clippy --features=debug -- --deny=warnings
cargo test --features=debug
14 changes: 14 additions & 0 deletions crates/xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,14 @@ struct RunnerOptions {
/// Runner name.
name: String,

/// Cargo no-default-features.
#[clap(long)]
no_default_features: bool,

/// Cargo features.
#[clap(long)]
features: Vec<String>,

/// Optimization level (0, 1, 2, 3, s, z).
#[clap(long, short = 'O', default_value_t)]
opt_level: OptLevel,
Expand Down Expand Up @@ -398,6 +406,12 @@ impl RunnerOptions {
} else {
cargo.arg("--features=debug");
}
if self.no_default_features {
cargo.arg("--no-default-features");
}
for features in &self.features {
cargo.arg(format!("--features={features}"));
}
if let Some(log) = &self.log {
cargo.env(self.log_env(), log);
}
Expand Down

0 comments on commit 953fedb

Please sign in to comment.