From faa29b1854e0f45be1fd7cfb9984aa9a3c347726 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Fri, 10 Jan 2020 20:02:19 +1100 Subject: [PATCH] Add a really basic interface to xhcid. --- Cargo.lock | 40 ++++- initfs.toml | 6 + xhcid/.gitignore | 1 + xhcid/Cargo.toml | 5 +- xhcid/config.toml | 12 +- xhcid/src/main.rs | 11 +- xhcid/src/xhci/mod.rs | 59 +++++--- xhcid/src/xhci/port.rs | 44 +++--- xhcid/src/xhci/scheme.rs | 311 ++++++++++++++++++++++++++++++++++++++- 9 files changed, 434 insertions(+), 55 deletions(-) create mode 100644 xhcid/.gitignore diff --git a/Cargo.lock b/Cargo.lock index 23f2855227..ab822c01a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,6 +264,11 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "ixgbed" version = "1.0.0" @@ -858,6 +863,11 @@ dependencies = [ "stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ryu" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "safemem" version = "0.3.1" @@ -939,6 +949,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" @@ -950,6 +963,16 @@ dependencies = [ "syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "serde_json" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "slab" version = "0.4.2" @@ -960,6 +983,14 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "smallvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "spin" version = "0.4.10" @@ -1346,10 +1377,13 @@ dependencies = [ name = "xhcid" version = "0.1.0" dependencies = [ - "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_event 0.1.0 (git+https://gitlab.redox-os.org/redox-os/event.git)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1382,6 +1416,7 @@ dependencies = [ "checksum hyper-rustls 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "71f7b2e5858ab9e19771dc361159f09ee5031734a6f7471fe0947db0238d92b7" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" @@ -1440,6 +1475,7 @@ dependencies = [ "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustls 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "942b71057b31981152970d57399c25f72e27a6ee0d207a669d8304cabf44705b" "checksum rusttype 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "30047cc747a78ae042bf2cd65c79f83c3485d90107535b532d6e8f60e2c89cb1" +"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" "checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" @@ -1452,8 +1488,10 @@ dependencies = [ "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "7fe5626ac617da2f2d9c48af5515a21d5a480dbd151e01bb1c355e26a3e68113" "checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" +"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" +"checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba" diff --git a/initfs.toml b/initfs.toml index a6eb2dddd6..6430657f3e 100644 --- a/initfs.toml +++ b/initfs.toml @@ -36,3 +36,9 @@ class = 8 vendor = 33006 device = 51966 command = ["vboxd", "$NAME", "$BAR0", "$BAR1", "$IRQ"] +[[drivers]] +name = "XHCI" +class = 12 +subclass = 3 +interface = 48 +command = ["xhcid", "$NAME", "$BAR0", "$IRQ"] diff --git a/xhcid/.gitignore b/xhcid/.gitignore new file mode 100644 index 0000000000..ea8c4bf7f3 --- /dev/null +++ b/xhcid/.gitignore @@ -0,0 +1 @@ +/target diff --git a/xhcid/Cargo.toml b/xhcid/Cargo.toml index 9d8b2e7bd1..6a1907d8c4 100644 --- a/xhcid/Cargo.toml +++ b/xhcid/Cargo.toml @@ -4,8 +4,11 @@ version = "0.1.0" edition = "2018" [dependencies] -bitflags = "0.7" +bitflags = "1" plain = "0.2" spin = "0.4" redox_event = { git = "https://gitlab.redox-os.org/redox-os/event.git" } redox_syscall = "0.1" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +smallvec = { version = "1", features = ["serde"] } diff --git a/xhcid/config.toml b/xhcid/config.toml index 74db47d8e1..6654d5b598 100644 --- a/xhcid/config.toml +++ b/xhcid/config.toml @@ -1,6 +1,6 @@ -# [[drivers]] -# name = "XHCI" -# class = 12 -# subclass = 3 -# interface = 48 -# command = ["xhcid", "$NAME", "$BAR0", "$IRQ"] +[[drivers]] +name = "XHCI" +class = 12 +subclass = 3 +interface = 48 +command = ["xhcid", "$NAME", "$BAR0", "$IRQ"] diff --git a/xhcid/src/main.rs b/xhcid/src/main.rs index 4fa3e5ea3a..c97a0f382f 100644 --- a/xhcid/src/main.rs +++ b/xhcid/src/main.rs @@ -6,7 +6,7 @@ extern crate syscall; use event::{Event, EventQueue}; use std::cell::RefCell; -use std::env; +use std::{io, env}; use std::fs::File; use std::io::{Result, Read, Write}; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; @@ -37,7 +37,7 @@ fn main() { // Daemonize if unsafe { syscall::clone(0).unwrap() } == 0 { - let socket_fd = syscall::open(":usb", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("xhcid: failed to create usb scheme"); + let socket_fd = syscall::open(format!(":usb/{}-{}-{}", name, bar_str, irq_str), syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("xhcid: failed to create usb scheme"); let socket = Arc::new(RefCell::new(unsafe { File::from_raw_fd(socket_fd as RawFd) })); let mut irq_file = File::open(format!("irq:{}", irq)).expect("xhcid: failed to open IRQ file"); @@ -87,8 +87,11 @@ fn main() { event_queue.add(socket_fd, move |_| -> Result> { loop { let mut packet = Packet::default(); - if socket_packet.borrow_mut().read(&mut packet)? == 0 { - break; + match socket_packet.borrow_mut().read(&mut packet) { + Ok(0) => break, + Err(err) if err.kind() == io::ErrorKind::WouldBlock => break, + Ok(_) => (), + Err(err) => return Err(err), } let a = packet.a; diff --git a/xhcid/src/xhci/mod.rs b/xhcid/src/xhci/mod.rs index 64bd94e539..1a3255f137 100644 --- a/xhcid/src/xhci/mod.rs +++ b/xhcid/src/xhci/mod.rs @@ -1,5 +1,6 @@ use plain::Plain; use std::{mem, slice}; +use std::collections::BTreeMap; use syscall::error::Result; use syscall::io::{Dma, Io}; use crate::usb; @@ -112,6 +113,16 @@ pub struct Xhci { run: &'static mut RuntimeRegs, dev_ctx: DeviceContextList, cmd: CommandRing, + + handles: BTreeMap, + next_handle: usize, + port_states: BTreeMap, +} + +struct PortState { + slot: u8, + input_context: Dma, + ring: Ring, } impl Xhci { @@ -178,6 +189,9 @@ impl Xhci { run: run, dev_ctx: DeviceContextList::new(max_slots)?, cmd: CommandRing::new()?, + handles: BTreeMap::new(), + next_handle: 0, + port_states: BTreeMap::new(), }; xhci.init(max_slots); @@ -237,6 +251,24 @@ impl Xhci { println!(" - XHCI initialized"); } + pub fn enable_port_slot(cmd: &mut CommandRing, dbs: &mut [Doorbell]) -> u8 { + let (cmd, cycle, event) = cmd.next(); + + cmd.enable_slot(0, cycle); + + dbs[0].write(0); + + while event.data.read() == 0 { + println!(" - Waiting for event"); + } + let slot = (event.control.read() >> 24) as u8; + + cmd.reserved(false); + event.reserved(false); + + slot + } + pub fn probe(&mut self) -> Result<()> { for (i, port) in self.ports.iter().enumerate() { let data = port.read(); @@ -245,32 +277,18 @@ impl Xhci { let flags = port.flags(); println!(" + XHCI Port {}: {:X}, State {}, Speed {}, Flags {:?}", i, data, state, speed, flags); - if flags.contains(port::PORT_CCS) { + if flags.contains(port::PortFlags::PORT_CCS) { //TODO: Link TRB when running to the end of the ring buffer println!(" - Enable slot"); - let slot; - { - let (cmd, cycle, event) = self.cmd.next(); + self.run.ints[0].erdp.write(self.cmd.erdp()); // TODO: ? - cmd.enable_slot(0, cycle); - - self.dbs[0].write(0); - - while event.data.read() == 0 { - println!(" - Waiting for event"); - } - slot = (event.control.read() >> 24) as u8; - - cmd.reserved(false); - event.reserved(false); - } - - self.run.ints[0].erdp.write(self.cmd.erdp()); + let slot = Self::enable_port_slot(&mut self.cmd, &mut self.dbs); println!(" - Slot {}", slot); + // transfer ring? let mut ring = Ring::new(true)?; let mut input = Dma::::zeroed()?; @@ -360,6 +378,11 @@ impl Xhci { } } } + self.port_states.insert(i, PortState { + slot, + input_context: input, + ring, + }); } } diff --git a/xhcid/src/xhci/port.rs b/xhcid/src/xhci/port.rs index 50f23c9f18..0f62d66140 100644 --- a/xhcid/src/xhci/port.rs +++ b/xhcid/src/xhci/port.rs @@ -1,28 +1,28 @@ use syscall::io::{Io, Mmio}; bitflags! { - pub flags PortFlags: u32 { - const PORT_CCS = 1 << 0, - const PORT_PED = 1 << 1, - const PORT_OCA = 1 << 3, - const PORT_PR = 1 << 4, - const PORT_PP = 1 << 9, - const PORT_PIC_AMB = 1 << 14, - const PORT_PIC_GRN = 1 << 15, - const PORT_LWS = 1 << 16, - const PORT_CSC = 1 << 17, - const PORT_PEC = 1 << 18, - const PORT_WRC = 1 << 19, - const PORT_OCC = 1 << 20, - const PORT_PRC = 1 << 21, - const PORT_PLC = 1 << 22, - const PORT_CEC = 1 << 23, - const PORT_CAS = 1 << 24, - const PORT_WCE = 1 << 25, - const PORT_WDE = 1 << 26, - const PORT_WOE = 1 << 27, - const PORT_DR = 1 << 30, - const PORT_WPR = 1 << 31, + pub struct PortFlags: u32 { + const PORT_CCS = 1 << 0; + const PORT_PED = 1 << 1; + const PORT_OCA = 1 << 3; + const PORT_PR = 1 << 4; + const PORT_PP = 1 << 9; + const PORT_PIC_AMB = 1 << 14; + const PORT_PIC_GRN = 1 << 15; + const PORT_LWS = 1 << 16; + const PORT_CSC = 1 << 17; + const PORT_PEC = 1 << 18; + const PORT_WRC = 1 << 19; + const PORT_OCC = 1 << 20; + const PORT_PRC = 1 << 21; + const PORT_PLC = 1 << 22; + const PORT_CEC = 1 << 23; + const PORT_CAS = 1 << 24; + const PORT_WCE = 1 << 25; + const PORT_WDE = 1 << 26; + const PORT_WOE = 1 << 27; + const PORT_DR = 1 << 30; + const PORT_WPR = 1 << 31; } } diff --git a/xhcid/src/xhci/scheme.rs b/xhcid/src/xhci/scheme.rs index e1531c7dd1..4e8816c34b 100644 --- a/xhcid/src/xhci/scheme.rs +++ b/xhcid/src/xhci/scheme.rs @@ -1,4 +1,309 @@ -use super::Xhci; -use syscall::scheme::SchemeMut; +use std::{cmp, mem, str}; +use std::io::prelude::*; -impl SchemeMut for Xhci {} +use plain::Plain; +use serde::Serialize; +use smallvec::SmallVec; + +use syscall::io::Io; +use syscall::scheme::SchemeMut; +use syscall::{ + EACCES, EBADF, EINVAL, EISDIR, ENOENT, + O_CREAT, O_DIRECTORY, O_STAT, ENOSYS, ENOTDIR, + MODE_DIR, MODE_FILE, + SEEK_CUR, SEEK_END, SEEK_SET, + Stat, + Error, Result +}; + +use super::{Device, Xhci}; +use super::{port, usb}; + +pub enum Handle { + TopLevel(usize, Vec), // offset, contents (ports) + Port(usize, usize, Vec), // port, offset, contents + PortDesc(usize, usize, Vec), // port, offset, contents +} + +#[derive(Serialize)] +struct PortDesc { + // I have no idea whether this number is useful to the API users. + slot: u8, + dev_desc: DevDescJson, +} + +#[derive(Serialize)] +struct DevDescJson { + // TODO: length? + kind: u8, + usb: u16, + class: u8, + sub_class: u8, + protocol: u8, + packet_size: u8, + vendor: u16, + product: u16, + release: u16, + manufacturer_str: Option, + product_str: Option, + serial_str: Option, + config_descs: SmallVec<[ConfDescJson; 1]>, +} + +#[derive(Serialize)] +struct ConfDescJson { + // TODO: length? + kind: u8, + // TODO: total_length? + // TODO: configuration_value? + configuration: Option, + attributes: u8, + max_power: u8, + interface_descs: SmallVec<[IfDescJson; 1]>, +} + +#[derive(Serialize)] +struct EndpDescJson { + kind: u8, + address: u8, + attributes: u8, + max_packet_size: u16, + interval: u8, +} + +#[derive(Serialize)] +struct IfDescJson { + // TODO: length? + kind: u8, + number: u8, + alternate_setting: u8, + class: u8, + sub_class: u8, + protocol: u8, + interface_str: Option, + endpoints: SmallVec<[EndpDescJson; 2]>, +} + +impl Xhci { + fn write_port_desc(&mut self, port_id: usize, contents: &mut Vec) -> Result<()> { + let port = self.ports.get(port_id).ok_or(Error::new(ENOENT))?; + if !port.flags().contains(port::PortFlags::PORT_CCS) { + return Err(Error::new(ENOENT)); + } + + let st = self.port_states.get_mut(&port_id).unwrap(); + + // TODO: Should the descriptors be stored in PortState? + + self.run.ints[0].erdp.write(self.cmd.erdp()); + + let mut dev = Device { + ring: &mut st.ring, + cmd: &mut self.cmd, + db: &mut self.dbs[st.slot as usize], + int: &mut self.run.ints[0], + }; + + let raw_dd = dev.get_device()?; + + let (manufacturer_str, product_str, serial_str) = ( + if raw_dd.manufacturer_str > 0 { + Some(dev.get_string(raw_dd.manufacturer_str)?) + } else { None }, + if raw_dd.product_str > 0 { + Some(dev.get_string(raw_dd.product_str)?) + } else { None }, + if raw_dd.serial_str > 0 { + Some(dev.get_string(raw_dd.serial_str)?) + } else { None }, + ); + + let config_descs = (0..raw_dd.configurations).map(|index| -> Result<_> { + let (desc, data) = dev.get_config(index)?; + + let extra_length = desc.total_length as usize - mem::size_of::(); + + let mut i = 0; + + let interface_descs = (0..desc.interfaces).filter_map(|_| -> Option> { + let mut idesc = usb::InterfaceDescriptor::default(); + if i < extra_length && i < data.len() && idesc.copy_from_bytes(&data[i..extra_length]).is_ok() { + i += mem::size_of_val(&idesc); + + let endpoints = (0..idesc.endpoints).filter_map(|_| { + let mut edesc = usb::EndpointDescriptor::default(); + if i < extra_length && i < data.len() && edesc.copy_from_bytes(&data[i..extra_length]).is_ok() { + Some(EndpDescJson { + address: edesc.address, + attributes: edesc.attributes, + interval: edesc.interval, + kind: edesc.kind, + max_packet_size: edesc.max_packet_size, + }) + } else { None } + }).collect(); + + Some(Ok(IfDescJson { + kind: idesc.kind, + number: idesc.number, + alternate_setting: idesc.alternate_setting, + class: idesc.class, + sub_class: idesc.sub_class, + protocol: idesc.protocol, + interface_str: match if idesc.interface_str > 0 { Some(dev.get_string(idesc.interface_str)) } else { None } { + Some(Err(err)) => return Some(Err(err)), + Some(Ok(ok)) => Some(ok), + None => None, + }, + endpoints, + })) + } else { + None + } + }).collect::>>()?; + + Ok(ConfDescJson { + kind: desc.kind, + configuration: if desc.configuration_str > 0 { Some(dev.get_string(desc.configuration_str)?) } else { None }, + attributes: desc.attributes, + max_power: desc.max_power, + interface_descs, + }) + }).collect::>>()?; + + let dev_desc = DevDescJson { + kind: raw_dd.kind, + usb: raw_dd.usb, + class: raw_dd.class, + sub_class: raw_dd.sub_class, + protocol: raw_dd.protocol, + packet_size: raw_dd.packet_size, + vendor: raw_dd.vendor, + product: raw_dd.product, + release: raw_dd.release, + manufacturer_str, + product_str, + serial_str, + config_descs, + }; + + let desc = PortDesc { + slot: st.slot, + dev_desc, + }; + + serde_json::to_writer_pretty(contents, &desc).unwrap(); + + Ok(()) + } +} + +impl SchemeMut for Xhci { + fn open(&mut self, path: &[u8], flags: usize, uid: u32, _gid: u32) -> Result { + if uid != 0 { return Err(Error::new(EACCES)) } + if flags & O_CREAT != 0 { return Err(Error::new(EINVAL) ) } + + let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?.trim_start_matches('/'); + + if path_str.is_empty() { + if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 { + let mut contents = Vec::new(); + + for (index, _) in self.ports.iter().enumerate().filter(|(_, port)| port.flags().contains(port::PortFlags::PORT_CCS)) { + write!(contents, "port{}\n", index).unwrap(); + } + + let fd = self.next_handle; + self.handles.insert(fd, Handle::TopLevel(0, contents)); + self.next_handle += 1; + Ok(fd) + } else { + return Err(Error::new(EISDIR)); + } + } else if path_str.starts_with("port") { + let slash_idx = path_str.chars().position(|c| c == '/'); + + let num_str = &path_str[4..slash_idx.unwrap_or(path_str.len())]; + let num = num_str.parse::().or(Err(Error::new(ENOENT)))?; + + if slash_idx.is_some() && slash_idx.unwrap() + 1 < path_str.len() && path_str[slash_idx.unwrap() + 1..].starts_with("descriptor") { + if flags & O_DIRECTORY != 0 { + return Err(Error::new(ENOTDIR)); + } + + let mut contents = Vec::new(); + self.write_port_desc(num, &mut contents)?; + + let fd = self.next_handle; + self.handles.insert(fd, Handle::PortDesc(num, 0, contents)); + self.next_handle += 1; + return Ok(fd); + } + + if flags & O_DIRECTORY != 0 || flags & O_STAT != 0 { + let mut contents = Vec::new(); + + write!(contents, "descriptor\n").unwrap(); + + let fd = self.next_handle; + self.handles.insert(fd, Handle::Port(num, 0, contents)); + self.next_handle += 1; + Ok(fd) + } else { + return Err(Error::new(EISDIR)); + } + } else { + return Err(Error::new(ENOSYS)); + } + } + + fn fstat(&mut self, id: usize, stat: &mut Stat) -> Result { + match self.handles.get(&id).ok_or(Error::new(EBADF))? { + Handle::TopLevel(_, ref buf) | Handle::Port(_, _, ref buf) => { + // TODO: Known size perhaps? + stat.st_mode = MODE_DIR; + stat.st_size = buf.len() as u64; + Ok(0) + } + Handle::PortDesc(_, _, ref buf) => { + stat.st_mode = MODE_FILE; + stat.st_size = buf.len() as u64; + Ok(0) + } + } + } + + fn seek(&mut self, fd: usize, pos: usize, whence: usize) -> Result { + match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + Handle::TopLevel(ref mut offset, ref buf) | Handle::Port(_, ref mut offset, ref buf) | Handle::PortDesc(_, ref mut offset, ref buf) => { + *offset = match whence { + SEEK_SET => cmp::max(0, cmp::min(pos, buf.len())), + SEEK_CUR => cmp::max(0, cmp::min(*offset + pos, buf.len())), + SEEK_END => cmp::max(0, cmp::min(buf.len() + pos, buf.len())), + _ => return Err(Error::new(EINVAL)), + }; + Ok(*offset) + } + } + } + + fn read(&mut self, fd: usize, buf: &mut [u8]) -> Result { + match self.handles.get_mut(&fd).ok_or(Error::new(EBADF))? { + Handle::TopLevel(ref mut offset, ref src_buf) | Handle::Port(_, ref mut offset, ref src_buf) | Handle::PortDesc(_, ref mut offset, ref src_buf) => { + let max_bytes_to_read = cmp::min(src_buf.len(), buf.len()); + let bytes_to_read = cmp::max(max_bytes_to_read, *offset) - *offset; + + buf[..bytes_to_read].copy_from_slice(&src_buf[..bytes_to_read]); + *offset += bytes_to_read; + + Ok(bytes_to_read) + } + } + } + fn close(&mut self, fd: usize) -> Result { + if self.handles.remove(&fd).is_none() { + return Err(Error::new(EBADF)); + } + Ok(0) + } +}