Skip to content

Commit

Permalink
Add a framework-ish for USB drivers.
Browse files Browse the repository at this point in the history
  • Loading branch information
4lDO2 committed Feb 3, 2020
1 parent 8c15832 commit ae0d43c
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 11 deletions.
15 changes: 15 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ members = [
"vboxd",
"vesad",
"xhcid",
"usbscsid",
]
1 change: 1 addition & 0 deletions usbscsid/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/target
9 changes: 9 additions & 0 deletions usbscsid/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "usbscsid"
version = "0.1.0"
authors = ["4lDO2 <4lDO2@protonmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
13 changes: 13 additions & 0 deletions usbscsid/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use std::env;

fn main() {
let mut args = env::args().skip(1);

const USAGE: &'static str = "usbscsid <scheme> <port> <protocol>";

let scheme = args.next().expect(USAGE);
let port = args.next().expect(USAGE);
let protocol = args.next().expect(USAGE);

println!("USB SCSI driver spawned with scheme `{}`, port {}, protocol {}", scheme, port, protocol);
}
2 changes: 2 additions & 0 deletions xhcid/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ edition = "2018"
[dependencies]
bitflags = "1"
plain = "0.2"
lazy_static = "1.4"
spin = "0.4"
redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" }
redox_syscall = { git = "https://gitlab.redox-os.org/4lDO2/syscall.git", branch = "dma-slices" }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
smallvec = { version = "1", features = ["serde"] }
toml = "0.5"
5 changes: 5 additions & 0 deletions xhcid/drivers.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[[drivers]]
name = "SCSI over USB"
class = 8 # Mass Storage class
subclass = 6 # SCSI transparent command set
command = ["usbscsid", "$SCHEME", "$PORT", "$IF_PROTO"]
2 changes: 1 addition & 1 deletion xhcid/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fn main() {

let address = unsafe { syscall::physmap(bar, 65536, PHYSMAP_WRITE | PHYSMAP_NO_CACHE).expect("xhcid: failed to map address") };
{
let hci = Arc::new(RefCell::new(Xhci::new(address).expect("xhcid: failed to allocate device")));
let hci = Arc::new(RefCell::new(Xhci::new(name, address).expect("xhcid: failed to allocate device")));

hci.borrow_mut().probe().expect("xhcid: failed to probe");

Expand Down
75 changes: 67 additions & 8 deletions xhcid/src/xhci/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use plain::Plain;
use std::{mem, slice, sync::atomic, task};
use std::{mem, slice, process, sync::atomic, task};
use std::collections::BTreeMap;
use std::ffi::OsStr;
use std::pin::Pin;
use std::sync::{Arc, Mutex, Weak, atomic::AtomicBool};
use syscall::error::Result;

use serde::Deserialize;
use syscall::error::{EBADF, EBADMSG, ENOENT, Error, Result};
use syscall::io::{Dma, Io};

use crate::usb;

mod capability;
Expand Down Expand Up @@ -139,6 +142,9 @@ pub struct Xhci {
// IRQs, but it would make sense in case the hub has both isochronous (which trigger interrupts
// reapeatedly with some time in between), bulk, control, etc. I might be wrong though...
irq_state: Arc<IrqState>,

drivers: BTreeMap<usize, process::Child>,
scheme_name: String,
}

struct PortState {
Expand Down Expand Up @@ -167,7 +173,7 @@ impl EndpointState {
}

impl Xhci {
pub fn new(address: usize) -> Result<Xhci> {
pub fn new(scheme_name: String, address: usize) -> Result<Xhci> {
let cap = unsafe { &mut *(address as *mut CapabilityRegs) };
println!(" - CAP {:X}", address);

Expand Down Expand Up @@ -238,6 +244,8 @@ impl Xhci {
triggered: AtomicBool::new(false),
wakers: Mutex::new(Vec::new()),
}),
drivers: BTreeMap::new(),
scheme_name,
};

xhci.init(max_slots);
Expand Down Expand Up @@ -380,15 +388,21 @@ impl Xhci {
}

let dev_desc = Self::get_dev_desc_raw(&mut self.ports, &mut self.run, &mut self.cmd, &mut self.dbs, i, slot, &mut ring)?;

self.port_states.insert(i, PortState {
let mut port_state = PortState {
slot,
input_context: input,
dev_desc,
endpoint_states: std::iter::once((0, EndpointState::Ready(
RingOrStreams::Ring(ring),
))).collect::<BTreeMap<_, _>>(),
});
};

match self.spawn_drivers(i, &mut port_state) {
Ok(()) => (),
Err(err) => println!("Failed to spawn driver for port {}: `{}`", i, err),
}

self.port_states.insert(i, port_state);
}
}

Expand All @@ -405,7 +419,7 @@ impl Xhci {
// FIXME: MSI and MSI-X systems
self.run.ints[0].iman.writef(1, true);

// Wake all futures that await the IRQ.
// Wake all futures awaiting the IRQ.
for waker in self.irq_state.wakers.lock().unwrap().drain(..) {
waker.wake();
}
Expand All @@ -420,6 +434,51 @@ impl Xhci {
state: IrqFutureState::Pending(Arc::downgrade(&self.irq_state))
}
}
fn spawn_drivers(&mut self, port: usize, ps: &mut PortState) -> Result<()> {
// TODO: There should probably be a way to select alternate interfaces, and not just the
// first one.
// TODO: Now that there are some good error crates, I don't think errno.h error codes are
// suitable here.

let ifdesc = &ps.dev_desc.config_descs.first().ok_or(Error::new(EBADF))?.interface_descs.first().ok_or(Error::new(EBADF))?;
let drivers_usercfg: &DriversConfig = &DRIVERS_CONFIG;

if let Some(driver) = drivers_usercfg.drivers.iter().find(|driver| driver.class == ifdesc.class && driver.subclass == ifdesc.sub_class) {
println!("Loading driver \"{}\"", driver.name);
let (command, args) = driver.command.split_first().ok_or(Error::new(EBADMSG))?;

let if_proto = ifdesc.protocol;

let process = process::Command::new(command).args(args.into_iter().map(|arg| arg.replace("$SCHEME", &self.scheme_name).replace("$PORT", &format!("{}", port)).replace("$IF_PROTO", &format!("{}", if_proto))).collect::<Vec<_>>()).stdin(process::Stdio::null()).spawn().or(Err(Error::new(ENOENT)))?;
self.drivers.insert(port, process);
} else {
return Err(Error::new(ENOENT));
}

Ok(())
}
}
#[derive(Deserialize)]
struct DriverConfig {
name: String,
class: u8,
subclass: u8,
command: Vec<String>,
}
#[derive(Deserialize)]
struct DriversConfig {
drivers: Vec<DriverConfig>,
}

use lazy_static::lazy_static;

lazy_static! {
static ref DRIVERS_CONFIG: DriversConfig = {
// TODO: Load this at runtime.
const TOML: &'static [u8] = include_bytes!("../../drivers.toml");

toml::from_slice::<DriversConfig>(TOML).expect("Failed to parse internally embedded config file")
};
}

pub(crate) struct IrqFuture { state: IrqFutureState }
Expand Down
5 changes: 3 additions & 2 deletions xhcid/src/xhci/scheme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub enum Handle {
Port(usize, usize, Vec<u8>), // port, offset, contents
PortDesc(usize, usize, Vec<u8>), // port, offset, contents
PortState(usize, usize), // port, offset
PortReq(usize),
PortReq(usize), // port
Endpoints(usize, usize, Vec<u8>), // port, offset, contents
Endpoint(usize, u8, EndpointHandleTy), // port, endpoint, offset, state
ConfigureEndpoints(usize), // port
Expand All @@ -55,7 +55,8 @@ pub enum Handle {
#[derive(Serialize)]
struct PortDesc(DevDesc);

// Even though these descriptors are originally intended for JSON, they should suffice..
// TODO: Even though these descriptors are originally intended for JSON, they should suffice... for
// now.

#[derive(Clone, Debug, Serialize)]
pub(crate) struct DevDesc {
Expand Down

0 comments on commit ae0d43c

Please sign in to comment.