Refactor XHCI driver

This commit is contained in:
Jeremy Soller
2017-07-27 21:01:42 -06:00
parent 14e1b556ab
commit b4209a6a9c
11 changed files with 272 additions and 304 deletions
+1 -3
View File
@@ -1,5 +1,3 @@
#![feature(core_intrinsics)]
#[macro_use]
extern crate bitflags;
extern crate syscall;
@@ -27,7 +25,7 @@ fn main() {
match Xhci::new(address) {
Ok(mut xhci) => {
xhci.init();
xhci.probe();
},
Err(err) => {
println!("xhcid: error: {}", err);
+15
View File
@@ -0,0 +1,15 @@
use syscall::io::Mmio;
#[repr(packed)]
pub struct CapabilityRegs {
pub len: Mmio<u8>,
_rsvd: Mmio<u8>,
pub hci_ver: Mmio<u16>,
pub hcs_params1: Mmio<u32>,
pub hcs_params2: Mmio<u32>,
pub hcs_params3: Mmio<u32>,
pub hcc_params1: Mmio<u32>,
pub db_offset: Mmio<u32>,
pub rts_offset: Mmio<u32>,
pub hcc_params2: Mmio<u32>
}
+23
View File
@@ -0,0 +1,23 @@
use syscall::error::Result;
use syscall::io::Dma;
use super::event::EventRing;
use super::trb::Trb;
pub struct CommandRing {
pub trbs: Dma<[Trb; 256]>,
pub events: EventRing,
}
impl CommandRing {
pub fn new() -> Result<CommandRing> {
Ok(CommandRing {
trbs: Dma::zeroed()?,
events: EventRing::new()?,
})
}
pub fn crcr(&self) -> u64 {
self.trbs.physical() as u64 | 1
}
}
+56
View File
@@ -0,0 +1,56 @@
use syscall::error::Result;
use syscall::io::Dma;
#[repr(packed)]
pub struct SlotContext {
inner: [u8; 32]
}
#[repr(packed)]
pub struct EndpointContext {
inner: [u8; 32]
}
#[repr(packed)]
pub struct DeviceContext {
pub slot: SlotContext,
pub endpoints: [EndpointContext; 15]
}
#[repr(packed)]
pub struct InputContext {
pub drop_context: Mmio<u32>,
pub add_context: Mmio<u32>,
_rsvd: [Mmio<u32>; 5],
pub control: Mmio<u32>,
pub device: DeviceContext,
}
pub struct DeviceList {
pub dcbaa: Dma<[u64; 256]>,
pub contexts: Vec<Dma<DeviceContext>>,
}
impl DeviceList {
pub fn new(max_slots: u8) -> Result<DeviceList> {
let mut dcbaa = Dma::<[u64; 256]>::zeroed()?;
let mut contexts = vec![];
// Create device context buffers for each slot
for i in 0..max_slots as usize {
println!(" - Setup dev ctx {}", i);
let context: Dma<DeviceContext> = Dma::zeroed()?;
dcbaa[i] = context.physical() as u64;
contexts.push(context);
}
Ok(DeviceList {
dcbaa: dcbaa,
contexts: contexts
})
}
pub fn dcbaap(&self) -> u64 {
self.dcbaa.physical() as u64
}
}
+14
View File
@@ -0,0 +1,14 @@
use syscall::io::{Io, Mmio};
#[repr(packed)]
pub struct Doorbell(Mmio<u32>);
impl Doorbell {
pub fn read(&self) -> u32 {
self.0.read()
}
pub fn write(&mut self, data: u32) {
self.0.write(data);
}
}
+1
View File
@@ -3,6 +3,7 @@ use syscall::io::{Dma, Io, Mmio};
use super::trb::Trb;
#[repr(packed)]
pub struct EventRingSte {
pub address: Mmio<u64>,
pub size: Mmio<u16>,
+77 -199
View File
@@ -1,154 +1,42 @@
use std::slice;
use syscall::error::Result;
use syscall::io::{Dma, Mmio, Io};
use syscall::io::Io;
mod capability;
mod command;
mod device;
mod doorbell;
mod event;
mod operational;
mod port;
mod runtime;
mod trb;
use self::event::*;
use self::trb::*;
#[repr(packed)]
pub struct XhciCap {
len: Mmio<u8>,
_rsvd: Mmio<u8>,
hci_ver: Mmio<u16>,
hcs_params1: Mmio<u32>,
hcs_params2: Mmio<u32>,
hcs_params3: Mmio<u32>,
hcc_params1: Mmio<u32>,
db_offset: Mmio<u32>,
rts_offset: Mmio<u32>,
hcc_params2: Mmio<u32>
}
#[repr(packed)]
pub struct XhciOp {
usb_cmd: Mmio<u32>,
usb_sts: Mmio<u32>,
page_size: Mmio<u32>,
_rsvd: [Mmio<u32>; 2],
dn_ctrl: Mmio<u32>,
crcr: Mmio<u64>,
_rsvd2: [Mmio<u32>; 4],
dcbaap: Mmio<u64>,
config: Mmio<u32>,
}
pub struct XhciInterrupter {
iman: Mmio<u32>,
imod: Mmio<u32>,
erstsz: Mmio<u32>,
_rsvd: Mmio<u32>,
erstba: Mmio<u64>,
erdp: Mmio<u64>,
}
pub struct XhciRun {
mfindex: Mmio<u32>,
_rsvd: [Mmio<u32>; 7],
ints: [XhciInterrupter; 1024],
}
bitflags! {
flags XhciPortFlags: 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,
}
}
#[repr(packed)]
pub struct XhciPort {
portsc : Mmio<u32>,
portpmsc : Mmio<u32>,
portli : Mmio<u32>,
porthlpmc : Mmio<u32>,
}
impl XhciPort {
fn read(&self) -> u32 {
self.portsc.read()
}
fn state(&self) -> u32 {
(self.read() & (0b1111 << 5)) >> 5
}
fn speed(&self) -> u32 {
(self.read() & (0b1111 << 10)) >> 10
}
fn flags(&self) -> XhciPortFlags {
XhciPortFlags::from_bits_truncate(self.read())
}
}
pub struct XhciDoorbell(Mmio<u32>);
impl XhciDoorbell {
fn read(&self) -> u32 {
self.0.read()
}
fn write(&mut self, data: u32) {
self.0.write(data);
}
}
#[repr(packed)]
pub struct XhciSlotContext {
inner: [u8; 32]
}
#[repr(packed)]
pub struct XhciEndpointContext {
inner: [u8; 32]
}
#[repr(packed)]
pub struct XhciDeviceContext {
slot: XhciSlotContext,
endpoints: [XhciEndpointContext; 15]
}
use self::capability::CapabilityRegs;
use self::command::CommandRing;
use self::device::DeviceList;
use self::doorbell::Doorbell;
use self::operational::OperationalRegs;
use self::port::Port;
use self::runtime::RuntimeRegs;
pub struct Xhci {
cap: &'static mut XhciCap,
op: &'static mut XhciOp,
ports: &'static mut [XhciPort],
dbs: &'static mut [XhciDoorbell],
run: &'static mut XhciRun,
dev_baa: Dma<[u64; 256]>,
dev_ctxs: Vec<Dma<XhciDeviceContext>>,
cmds: Dma<[Trb; 256]>,
events: [EventRing; 1],
cap: &'static mut CapabilityRegs,
op: &'static mut OperationalRegs,
ports: &'static mut [Port],
dbs: &'static mut [Doorbell],
run: &'static mut RuntimeRegs,
devices: DeviceList,
cmd: CommandRing,
}
impl Xhci {
pub fn new(address: usize) -> Result<Xhci> {
let cap = unsafe { &mut *(address as *mut XhciCap) };
let cap = unsafe { &mut *(address as *mut CapabilityRegs) };
println!(" - CAP {:X}", address);
let op_base = address + cap.len.read() as usize;
let op = unsafe { &mut *(op_base as *mut XhciOp) };
let op = unsafe { &mut *(op_base as *mut OperationalRegs) };
println!(" - OP {:X}", op_base);
let max_slots;
@@ -174,27 +62,22 @@ impl Xhci {
println!(" - Read max slots");
// Read maximum slots and ports
let hcs_params1 = cap.hcs_params1.read();
max_slots = hcs_params1 & 0xFF;
max_ports = (hcs_params1 & 0xFF000000) >> 24;
max_slots = (hcs_params1 & 0xFF) as u8;
max_ports = ((hcs_params1 & 0xFF000000) >> 24) as u8;
println!(" - Max Slots: {}, Max Ports {}", max_slots, max_ports);
println!(" - Set enabled slots");
// Set enabled slots
op.config.write(max_slots);
println!(" - Enabled Slots: {}", op.config.read() & 0xFF);
}
let port_base = op_base + 0x400;
let ports = unsafe { slice::from_raw_parts_mut(port_base as *mut XhciPort, max_ports as usize) };
let ports = unsafe { slice::from_raw_parts_mut(port_base as *mut Port, max_ports as usize) };
println!(" - PORT {:X}", port_base);
let db_base = address + cap.db_offset.read() as usize;
let dbs = unsafe { slice::from_raw_parts_mut(db_base as *mut XhciDoorbell, 256) };
let dbs = unsafe { slice::from_raw_parts_mut(db_base as *mut Doorbell, 256) };
println!(" - DOORBELL {:X}", db_base);
let run_base = address + cap.rts_offset.read() as usize;
let run = unsafe { &mut *(run_base as *mut XhciRun) };
let run = unsafe { &mut *(run_base as *mut RuntimeRegs) };
println!(" - RUNTIME {:X}", run_base);
let mut xhci = Xhci {
@@ -203,58 +86,53 @@ impl Xhci {
ports: ports,
dbs: dbs,
run: run,
dev_baa: Dma::zeroed()?,
dev_ctxs: Vec::new(),
cmds: Dma::zeroed()?,
events: [
EventRing::new()?
],
devices: DeviceList::new(max_slots)?,
cmd: CommandRing::new()?,
};
{
// Create device context buffers for each slot
for i in 0..max_slots as usize {
println!(" - Setup dev ctx {}", i);
let dev_ctx: Dma<XhciDeviceContext> = Dma::zeroed()?;
xhci.dev_baa[i] = dev_ctx.physical() as u64;
xhci.dev_ctxs.push(dev_ctx);
}
println!(" - Write DCBAAP");
// Set device context address array pointer
xhci.op.dcbaap.write(xhci.dev_baa.physical() as u64);
println!(" - Write CRCR");
// Set command ring control register
xhci.op.crcr.write(xhci.cmds.physical() as u64 | 1);
println!(" - Write ERST");
// Set event ring segment table registers
xhci.run.ints[0].erstsz.write(1);
xhci.run.ints[0].erstba.write(xhci.events[0].ste.physical() as u64);
xhci.run.ints[0].erdp.write(xhci.events[0].trbs.physical() as u64);
println!(" - Start");
// Set run/stop to 1
xhci.op.usb_cmd.writef(1, true);
println!(" - Wait for running");
// Wait until controller is running
while xhci.op.usb_sts.readf(1) {
println!(" - Waiting for XHCI running");
}
println!(" - Ring doorbell");
// Ring command doorbell
xhci.dbs[0].write(0);
println!(" - XHCI initialized");
}
xhci.init(max_slots);
Ok(xhci)
}
pub fn init(&mut self) {
pub fn init(&mut self, max_slots: u8) {
println!(" - Set enabled slots");
// Set enabled slots
self.op.config.write(max_slots as u32);
println!(" - Enabled Slots: {}", self.op.config.read() & 0xFF);
println!(" - Write DCBAAP");
// Set device context address array pointer
self.op.dcbaap.write(self.devices.dcbaap());
println!(" - Write CRCR");
// Set command ring control register
self.op.crcr.write(self.cmd.crcr());
println!(" - Write ERST");
// Set event ring segment table registers
self.run.ints[0].erstsz.write(1);
self.run.ints[0].erstba.write(self.cmd.events.ste.physical() as u64);
self.run.ints[0].erdp.write(self.cmd.events.trbs.physical() as u64);
println!(" - Start");
// Set run/stop to 1
self.op.usb_cmd.writef(1, true);
println!(" - Wait for running");
// Wait until controller is running
while self.op.usb_sts.readf(1) {
println!(" - Waiting for XHCI running");
}
println!(" - Ring doorbell");
// Ring command doorbell
self.dbs[0].write(0);
println!(" - XHCI initialized");
}
pub fn probe(&mut self) {
for (i, port) in self.ports.iter().enumerate() {
let data = port.read();
let state = port.state();
@@ -265,12 +143,12 @@ impl Xhci {
println!(" - Running Enable Slot command");
self.cmds[0].enable_slot(0, true);
self.cmd.trbs[0].enable_slot(0, true);
println!(" - Command");
println!(" - data: {:X}", self.cmds[0].data.read());
println!(" - status: {:X}", self.cmds[0].status.read());
println!(" - control: {:X}", self.cmds[0].control.read());
println!(" - data: {:X}", self.cmd.trbs[0].data.read());
println!(" - status: {:X}", self.cmd.trbs[0].status.read());
println!(" - control: {:X}", self.cmd.trbs[0].control.read());
self.dbs[0].write(0);
@@ -280,8 +158,8 @@ impl Xhci {
}
println!(" - Response");
println!(" - data: {:X}", self.events[0].trbs[0].data.read());
println!(" - status: {:X}", self.events[0].trbs[0].status.read());
println!(" - control: {:X}", self.events[0].trbs[0].control.read());
println!(" - data: {:X}", self.cmd.events.trbs[0].data.read());
println!(" - status: {:X}", self.cmd.events.trbs[0].status.read());
println!(" - control: {:X}", self.cmd.events.trbs[0].control.read());
}
}
+14
View File
@@ -0,0 +1,14 @@
use syscall::io::Mmio;
#[repr(packed)]
pub struct OperationalRegs {
pub usb_cmd: Mmio<u32>,
pub usb_sts: Mmio<u32>,
pub page_size: Mmio<u32>,
_rsvd: [Mmio<u32>; 2],
pub dn_ctrl: Mmio<u32>,
pub crcr: Mmio<u64>,
_rsvd2: [Mmio<u32>; 4],
pub dcbaap: Mmio<u64>,
pub config: Mmio<u32>,
}
+53
View File
@@ -0,0 +1,53 @@
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,
}
}
#[repr(packed)]
pub struct Port {
pub portsc : Mmio<u32>,
pub portpmsc : Mmio<u32>,
pub portli : Mmio<u32>,
pub porthlpmc : Mmio<u32>,
}
impl Port {
pub fn read(&self) -> u32 {
self.portsc.read()
}
pub fn state(&self) -> u32 {
(self.read() & (0b1111 << 5)) >> 5
}
pub fn speed(&self) -> u32 {
(self.read() & (0b1111 << 10)) >> 10
}
pub fn flags(&self) -> PortFlags {
PortFlags::from_bits_truncate(self.read())
}
}
+18
View File
@@ -0,0 +1,18 @@
use syscall::io::Mmio;
#[repr(packed)]
pub struct Interrupter {
pub iman: Mmio<u32>,
pub imod: Mmio<u32>,
pub erstsz: Mmio<u32>,
_rsvd: Mmio<u32>,
pub erstba: Mmio<u64>,
pub erdp: Mmio<u64>,
}
#[repr(packed)]
pub struct RuntimeRegs {
pub mfindex: Mmio<u32>,
_rsvd: [Mmio<u32>; 7],
pub ints: [Interrupter; 1024],
}
-102
View File
@@ -1,102 +0,0 @@
use std::cell::RefCell;
use std::ops::{Deref, DerefMut};
use std::rc::Weak;
use syscall::error::{Error, Result, EBADF, EINVAL, EPIPE};
use syscall::flag::{F_GETFL, F_SETFL, O_ACCMODE};
use pty::Pty;
use resource::Resource;
/// Read side of a pipe
#[derive(Clone)]
pub struct PtyTermios {
pty: Weak<RefCell<Pty>>,
flags: usize,
}
impl PtyTermios {
pub fn new(pty: Weak<RefCell<Pty>>, flags: usize) -> Self {
PtyTermios {
pty: pty,
flags: flags,
}
}
}
impl Resource for PtyTermios {
fn boxed_clone(&self) -> Box<Resource> {
Box::new(self.clone())
}
fn pty(&self) -> Weak<RefCell<Pty>> {
self.pty.clone()
}
fn flags(&self) -> usize {
self.flags
}
fn path(&self, buf: &mut [u8]) -> Result<usize> {
if let Some(pty_lock) = self.pty.upgrade() {
pty_lock.borrow_mut().path(buf)
} else {
Err(Error::new(EPIPE))
}
}
fn read(&self, buf: &mut [u8]) -> Result<usize> {
if let Some(pty_lock) = self.pty.upgrade() {
let pty = pty_lock.borrow();
let termios: &[u8] = pty.termios.deref();
let mut i = 0;
while i < buf.len() && i < termios.len() {
buf[i] = termios[i];
i += 1;
}
Ok(i)
} else {
Ok(0)
}
}
fn write(&self, buf: &[u8]) -> Result<usize> {
if let Some(pty_lock) = self.pty.upgrade() {
let mut pty = pty_lock.borrow_mut();
let termios: &mut [u8] = pty.termios.deref_mut();
let mut i = 0;
while i < buf.len() && i < termios.len() {
termios[i] = buf[i];
i += 1;
}
Ok(i)
} else {
Err(Error::new(EPIPE))
}
}
fn sync(&self) -> Result<usize> {
Ok(0)
}
fn fcntl(&mut self, cmd: usize, arg: usize) -> Result<usize> {
match cmd {
F_GETFL => Ok(self.flags),
F_SETFL => {
self.flags = (self.flags & O_ACCMODE) | (arg & ! O_ACCMODE);
Ok(0)
},
_ => Err(Error::new(EINVAL))
}
}
fn fevent(&self) -> Result<()> {
Err(Error::new(EBADF))
}
fn fevent_count(&self) -> Option<usize> {
None
}
}