hardware 0.0.9

A no_std bare-metal hardware abstraction layer — all port I/O, memory and swap allocations are guarded at runtime. Do not consider this dependency stable before x.1.x
Documentation
use core::ptr;

pub const CAPLENGTH: usize = 0x00;
pub const HCIVERSION: usize = 0x02;
pub const HCSPARAMS1: usize = 0x04;
pub const HCSPARAMS2: usize = 0x08;
pub const HCSPARAMS3: usize = 0x0C;
pub const HCCPARAMS1: usize = 0x10;
pub const DBOFF: usize = 0x14;
pub const RTSOFF: usize = 0x18;
pub const HCCPARAMS2: usize = 0x1C;

pub const USBCMD: usize = 0x00;
pub const USBSTS: usize = 0x04;
pub const PAGESIZE: usize = 0x08;
pub const DNCTRL: usize = 0x14;
pub const CRCR: usize = 0x18;
pub const DCBAAP: usize = 0x30;
pub const CONFIG: usize = 0x38;

pub const PORTSC_BASE: usize = 0x400;
pub const PORTSC_STRIDE: usize = 0x10;

pub const USBCMD_RUN: u32 = 1 << 0;
pub const USBCMD_HCRST: u32 = 1 << 1;
pub const USBCMD_INTE: u32 = 1 << 2;

pub const USBSTS_HCH: u32 = 1 << 0;
pub const USBSTS_HSE: u32 = 1 << 2;
pub const USBSTS_EINT: u32 = 1 << 3;
pub const USBSTS_PCD: u32 = 1 << 4;
pub const USBSTS_CNR: u32 = 1 << 11;

pub const PORTSC_CCS: u32 = 1 << 0;
pub const PORTSC_PED: u32 = 1 << 1;
pub const PORTSC_PR: u32 = 1 << 4;
pub const PORTSC_PP: u32 = 1 << 9;
pub const PORTSC_SPEED_MASK: u32 = 0xF << 10;
pub const PORTSC_SPEED_SHIFT: u32 = 10;

pub fn read_cap(base: usize, offset: usize) -> u32 {
    unsafe { ptr::read_volatile((base + offset) as *const u32) }
}

pub fn read_op(base: usize, cap_length: usize, offset: usize) -> u32 {
    unsafe { ptr::read_volatile((base + cap_length + offset) as *const u32) }
}

pub fn write_op(base: usize, cap_length: usize, offset: usize, val: u32) {
    unsafe { ptr::write_volatile((base + cap_length + offset) as *mut u32, val) }
}

pub fn cap_length(base: usize) -> usize {
    (read_cap(base, CAPLENGTH) & 0xFF) as usize
}

pub fn hci_version(base: usize) -> u16 {
    (read_cap(base, HCIVERSION) >> 16) as u16
}

pub fn max_ports(base: usize) -> u8 {
    let params = read_cap(base, HCSPARAMS1);
    ((params >> 24) & 0xFF) as u8
}

pub fn max_device_slots(base: usize) -> u8 {
    let params = read_cap(base, HCSPARAMS1);
    (params & 0xFF) as u8
}

pub fn max_interrupters(base: usize) -> u16 {
    let params = read_cap(base, HCSPARAMS1);
    ((params >> 8) & 0x7FF) as u16
}

pub fn reset_controller(base: usize) {
    let cl = cap_length(base);
    write_op(base, cl, USBCMD, USBCMD_HCRST);
    loop {
        let cmd = read_op(base, cl, USBCMD);
        if cmd & USBCMD_HCRST == 0 {
            break;
        }
    }
    loop {
        let sts = read_op(base, cl, USBSTS);
        if sts & USBSTS_CNR == 0 {
            break;
        }
    }
}

pub fn start(base: usize) {
    let cl = cap_length(base);
    let cmd = read_op(base, cl, USBCMD);
    write_op(base, cl, USBCMD, cmd | USBCMD_RUN | USBCMD_INTE);
}

pub fn stop(base: usize) {
    let cl = cap_length(base);
    let cmd = read_op(base, cl, USBCMD);
    write_op(base, cl, USBCMD, cmd & !USBCMD_RUN);
    loop {
        let sts = read_op(base, cl, USBSTS);
        if sts & USBSTS_HCH != 0 {
            break;
        }
    }
}

pub fn port_status(base: usize, cap_length: usize, port: u8) -> u32 {
    let offset = PORTSC_BASE + (port as usize) * PORTSC_STRIDE;
    read_op(base, cap_length, offset)
}

pub fn port_connected(base: usize, cap_length: usize, port: u8) -> bool {
    port_status(base, cap_length, port) & PORTSC_CCS != 0
}

pub fn port_speed(base: usize, cap_length: usize, port: u8) -> u8 {
    let sc = port_status(base, cap_length, port);
    ((sc & PORTSC_SPEED_MASK) >> PORTSC_SPEED_SHIFT) as u8
}

pub fn port_reset(base: usize, cap_length: usize, port: u8) {
    let offset = PORTSC_BASE + (port as usize) * PORTSC_STRIDE;
    let sc = read_op(base, cap_length, offset);
    write_op(base, cap_length, offset, sc | PORTSC_PR);
}