diff --git a/Cargo.lock b/Cargo.lock index 11b9282..2e100fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,17 +33,7 @@ dependencies = [ "qemu-exit", "r0", "shared", -] - -[[package]] -name = "aos-program" -version = "0.1.0" -dependencies = [ - "cortex-a", - "mmio", - "qemu-exit", - "r0", - "shared", + "usb", ] [[package]] @@ -246,3 +236,18 @@ name = "unicode-xid" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "usb" +version = "0.1.0" +dependencies = [ + "mmio", + "register 0.5.1", + "usb-device", +] + +[[package]] +name = "usb-device" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "849eed9b4dc61a1f17ba1d7a5078ceb095b9410caa38a506eb281ed5eff12fbd" diff --git a/Cargo.toml b/Cargo.toml index c5e8b5f..ea0ed26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] members = [ + "usb", "mmio", "shared", "init", - "program", "kernel", "boot", ] diff --git a/Makefile b/Makefile index 8b333ce..546a594 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ TOPTARGETS := all clean -SUBDIRS := mmio shared program init kernel boot +SUBDIRS := mmio usb shared init kernel boot $(TOPTARGETS): $(SUBDIRS) $(SUBDIRS): diff --git a/README.MD b/README.MD index d75b178..c107115 100644 --- a/README.MD +++ b/README.MD @@ -72,7 +72,7 @@ EC : Exception Class - Disassemble stripped version -```aarch64-none-elf-objdump -b binary -maarch64 -D program.img``` +```aarch64-none-elf-objdump -b binary -maarch64 -D init.img``` - Disassemble version with symbol diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..88ee906 --- /dev/null +++ b/TODO.md @@ -0,0 +1,8 @@ +# TODO + +- Improve Framebuffer - dual or triple buffering +- Move Framebuffer memory and DMA into kernel memory +- Refactorize Process and scheduler +- Remove tmp programs +- Implements fork/exec syscalls +- Improve mbox interface ('DMA' only) \ No newline at end of file diff --git a/init.img b/init.img index 1203e22..e93be31 100755 Binary files a/init.img and b/init.img differ diff --git a/init/.cargo/config b/init/.cargo/config index 61dd30d..d2d53ca 100644 --- a/init/.cargo/config +++ b/init/.cargo/config @@ -1,6 +1,6 @@ [target.aarch64-unknown-none] rustflags = [ - "-C", "link-arg=-Tprogram/link.ld", + "-C", "link-arg=-Tinit/link.ld", "-C", "target-feature=-fp-armv8", "-C", "target-cpu=cortex-a53", ] \ No newline at end of file diff --git a/init/src/main.rs b/init/src/main.rs index 7890176..72fe233 100644 --- a/init/src/main.rs +++ b/init/src/main.rs @@ -38,7 +38,7 @@ pub unsafe extern "C" fn _main() -> () { let mut count:u128 = 0; loop { - if count % 10000 == 0 { + if count % 1000000 == 0 { println!("init program run at level {}", count); } count = count + 1; diff --git a/kernel-high.img b/kernel-high.img index 80610dd..d91d4b5 100755 Binary files a/kernel-high.img and b/kernel-high.img differ diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 8ef3496..b7928e3 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" [dependencies] mmio = { path = "../mmio" } +usb = { path = "../usb" } shared = { path = "../shared" } qemu-exit = "1.0.0" cortex-a = { git = "https://github.com/AlbanSeurat/cortex-a", branch = "master" } diff --git a/kernel/src/exceptions/interruptions.rs b/kernel/src/exceptions/interruptions.rs index a0fe586..fbc0283 100644 --- a/kernel/src/exceptions/interruptions.rs +++ b/kernel/src/exceptions/interruptions.rs @@ -1,7 +1,6 @@ use crate::global::{BCMDEVICES, SCHEDULER}; use crate::exceptions::{syscalls, debug_halt}; use shared::exceptions::handlers::ExceptionContext; -use mmio::PhysicalTimer; use core::time::Duration; pub unsafe fn irq_handler(e: &ExceptionContext) { diff --git a/kernel/src/global.rs b/kernel/src/global.rs index 0319637..1a5898c 100644 --- a/kernel/src/global.rs +++ b/kernel/src/global.rs @@ -1,14 +1,19 @@ -use mmio::{BCMDeviceMemory, Uart, PhysicalTimer, USB}; +use mmio::{BCMDeviceMemory, Uart}; use crate::memory; use qemu_exit::QEMUExit; use crate::scheduler::Scheduler; use core::time::Duration; +use mmio::timer::{PhysicalTimer, SystemTimer}; +use usb::UsbBus; +use core::fmt::{Debug, Formatter}; pub const BCMDEVICES: BCMDeviceMemory = BCMDeviceMemory::new(memory::map::virt::peripheral::START); -pub const USB: USB = mmio::USB::new(memory::map::virt::USB_BASE); pub const IRQ: mmio::IRQ = mmio::IRQ::new(memory::map::virt::IRQ_BASE); pub const UART: Uart = mmio::Uart::new(memory::map::virt::UART_BASE); -pub const TIMER: PhysicalTimer = PhysicalTimer::new(Duration::from_millis(100)); +pub const PTIMER: PhysicalTimer = PhysicalTimer::new(Duration::from_millis(100)); +pub const STIMER: SystemTimer = SystemTimer::new(memory::map::virt::SYS_TIMER_BASE); +//pub const USB: USB = mmio::USB::new(memory::map::virt::USB_BASE, &STIMER); +pub const USB: UsbBus = UsbBus::new(memory::map::virt::USB_BASE, &STIMER); pub static mut SCHEDULER: Scheduler = Scheduler::new(); #[panic_handler] @@ -21,4 +26,4 @@ fn my_panic(info: &core::panic::PanicInfo) -> ! { #[alloc_error_handler] fn foo(layout: core::alloc::Layout) -> ! { panic!("Can not allocate {:?}", layout); -} \ No newline at end of file +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index da7cf6a..62651b0 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -6,29 +6,31 @@ #![feature(duration_constants)] #![feature(alloc_error_handler)] #![feature(llvm_asm)] +#![feature(allocator_api)] #[macro_use] extern crate alloc; #[macro_use] extern crate mmio; -use alloc::vec::Vec; - use cortex_a::asm; -use cortex_a::regs::{ELR_EL1, RegisterReadWrite, SP_EL0, SPSR_EL1}; +use cortex_a::regs::{ELR_EL1, RegisterReadWrite, SP_EL0, SPSR_EL1, CNTP_TVAL_EL0}; use memory::descriptors::{KERNEL_VIRTUAL_LAYOUT, PROGRAM_VIRTUAL_LAYOUT}; -use mmio::{BCMDeviceMemory, DMA, HEAP, Uart, PhysicalTimer, IRQ}; +use mmio::{BCMDeviceMemory, DMA, HEAP, Uart, IRQ}; use shared::memory::mmu::{VIRTUAL_ADDR_START, switch_user_tables}; -use crate::global::{BCMDEVICES, UART, TIMER}; -use crate::scheduler::process::{create_init_program, create_tmp_init_program}; +use crate::global::{BCMDEVICES, UART, PTIMER, STIMER, USB}; +use crate::scheduler::process::{create_init_program}; use core::str::from_utf8_unchecked; use core::time::Duration; use core::slice; +use alloc::boxed::Box; +use crate::usb::setup_usb; mod memory; mod exceptions; mod global; mod scheduler; +mod usb; extern "C" { // Boundaries of the .bss section, provided by the linker script @@ -51,10 +53,8 @@ pub unsafe extern "C" fn _upper_kernel() -> ! { DMA.lock().init(memory::map::physical::MMA_MEMORY_START, memory::map::physical::MMA_MEMORY_END - memory::map::physical::MMA_MEMORY_START); } - let v_mbox = mmio::Mbox::new_with_dma(memory::map::virt::MBOX_BASE); + let mut v_mbox = mmio::Mbox::new_with_dma(memory::map::virt::MBOX_BASE); mmio::LOGGER.appender(UART.into()); - let mut console = mmio::FrameBufferConsole::new(v_mbox, VIRTUAL_ADDR_START); - mmio::SCREEN.appender( console.into()); unsafe { print!("MMU Kernel mapping : \n{}", shared::memory::mmu::kernel_tables()); } unsafe { print!("MMU Program mapping : \n{}", shared::memory::mmu::user_tables()); } @@ -63,19 +63,18 @@ pub unsafe extern "C" fn _upper_kernel() -> ! { HEAP.lock().init(memory::map::virt::KERNEL_HEAP_START, memory::map::virt::KERNEL_HEAP_END - memory::map::virt::KERNEL_HEAP_START); } + setup_usb(&mut v_mbox); - // setup IRQs - //UART.enable_rx_irq(&irq, &bcm); + let mut console = mmio::FrameBufferConsole::new(v_mbox, VIRTUAL_ADDR_START); + mmio::SCREEN.appender( console.into()); - create_tmp_init_program(); - create_tmp_init_program(); - create_tmp_init_program(); - create_tmp_init_program(); - create_tmp_init_program(); + // TODO : remove tmp program and have init with fork syscall + create_init_program(); create_init_program(); - TIMER.setup(&BCMDEVICES); + PTIMER.setup(&BCMDEVICES); + unsafe { IRQ::enable(); } loop { diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index 990b1ab..b42dd2f 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -6,8 +6,7 @@ use shared::memory::mapping::{Descriptor, Mapping, Translation, AttributeFields, use core::ops::RangeInclusive; use shared::exceptions::handlers::ExceptionContext; use crate::scheduler::process::ProcessState::Running; -use mmio::PhysicalTimer; -use crate::global::TIMER; +use crate::global::PTIMER; use cortex_a::regs::{CNTV_TVAL_EL0, RegisterReadWrite}; use shared::memory::mmu::VIRTUAL_ADDR_START; use core::str::from_utf8_unchecked; @@ -53,8 +52,7 @@ impl Scheduler { } pub unsafe fn schedule(&mut self, e: &ExceptionContext) { - - TIMER.reset_counter(); + PTIMER.reset_counter(); self.processes.iter_mut() .find( | p | p.is_running()) diff --git a/kernel/src/scheduler/process.rs b/kernel/src/scheduler/process.rs index 591ee0a..6bb442b 100644 --- a/kernel/src/scheduler/process.rs +++ b/kernel/src/scheduler/process.rs @@ -15,7 +15,7 @@ use crate::scheduler::PROG_START; use crate::scheduler::process::ProcessState::{Sleep, Running}; use core::cell::Cell; use cortex_a::asm::eret; -use crate::global::{SCHEDULER, BCMDEVICES, TIMER}; +use crate::global::{SCHEDULER, BCMDEVICES, PTIMER}; use core::fmt::{Debug, Formatter}; use core::{fmt, slice}; use alloc::fmt::format; @@ -97,16 +97,6 @@ impl Process { unsafe { print!("MMU Program mapping : \n{}", self.tlb); } } - pub fn run(&mut self) { - self.state = Running; - println!("START PROCESS PID {} at {:x}", self.pid, PROG_START as u64); - SP_EL0.set(0x0040_0000); - // Indicate that we "return" to EL0 - SPSR_EL1.write(SPSR_EL1::M::EL0t); - ELR_EL1.set(PROG_START as u64); - asm::eret(); - } - pub fn is_running(&self) -> bool { self.state == Running } @@ -130,17 +120,10 @@ impl Process { } } -pub(crate) fn create_tmp_init_program() -> &'static mut Process { - let process = unsafe { SCHEDULER.create_process() }; - let bytes = include_bytes!("../../../program.img"); - unsafe { core::ptr::copy(bytes as *const u8, PROG_START as *mut u8, bytes.len()) }; - process -} pub(crate) fn create_init_program() -> &'static mut Process { let process = unsafe { SCHEDULER.create_process() }; let bytes = include_bytes!("../../../init.img"); unsafe { core::ptr::copy(bytes as *const u8, PROG_START as *mut u8, bytes.len()) }; - //process.run(); process } \ No newline at end of file diff --git a/kernel/src/usb.rs b/kernel/src/usb.rs new file mode 100644 index 0000000..d5df0a2 --- /dev/null +++ b/kernel/src/usb.rs @@ -0,0 +1,32 @@ +use alloc::boxed::Box; +use crate::global::USB; +use core::ops::Deref; +use mmio::Mbox; + +pub fn setup_usb(v_mbox: &mut Mbox) { + + /*let mut device = Box::new_in(UsbDevice::new(), DmaAllocator); + + USB.init(v_mbox).expect("Issue with USB.init"); + USB.start().expect("ISSUE with USB.start"); + + let request = Box::new_in(UsbDeviceRequest { + requestType: 0x80, + requestName: 6, + value: 1 << 8, + index: 0, + length: 8 + }, DmaAllocator); + + USB.send_request(device.as_mut(), request.as_ref()).expect("ISSUE with USB.attachRoot"); + + */ + + + USB.init().expect("USB.init failed"); + + debugln!("USB starting..."); +} + + + diff --git a/kernel8.img b/kernel8.img index cc48bb5..c838a24 100755 Binary files a/kernel8.img and b/kernel8.img differ diff --git a/mmio/Makefile b/mmio/Makefile index a271d51..19584df 100644 --- a/mmio/Makefile +++ b/mmio/Makefile @@ -1,5 +1,5 @@ -all: lib -lib: +all: mmio +mmio: cargo xrustc --target aarch64-unknown-none --release clean: cargo clean \ No newline at end of file diff --git a/mmio/src/dma.rs b/mmio/src/dma.rs index d5d9d05..fcebe7e 100644 --- a/mmio/src/dma.rs +++ b/mmio/src/dma.rs @@ -3,6 +3,7 @@ use core::ptr::{NonNull}; use core::cell::RefCell; use core::{mem, slice}; use linked_list_allocator::{Heap, LockedHeap}; +use crate::DMA; pub trait SliceAllocator { fn alloc_slice_zeroed<'a, T>( @@ -26,3 +27,16 @@ impl SliceAllocator for LockedHeap { Ok(unsafe { slice::from_raw_parts_mut(self.alloc_zeroed(l) as *mut T, count_of_items) }) } } + +pub struct DmaAllocator; + +unsafe impl Allocator for DmaAllocator { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + let allocated = unsafe { core::slice::from_raw_parts_mut(DMA.alloc(layout), layout.size()) }; + Ok(NonNull::new(allocated).expect("Null Allocation")) + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + DMA.dealloc(ptr.as_ptr(), layout) + } +} \ No newline at end of file diff --git a/mmio/src/lib.rs b/mmio/src/lib.rs index 80524f4..db85fc1 100644 --- a/mmio/src/lib.rs +++ b/mmio/src/lib.rs @@ -1,7 +1,7 @@ #![no_std] #![feature(llvm_asm)] -#![feature(allocator_api)] #![feature(nonnull_slice_from_raw_parts)] +#![feature(allocator_api)] #[macro_use] extern crate num_derive; @@ -12,7 +12,7 @@ pub mod io; mod delays; mod gpio; -mod mbox; +pub mod mbox; mod uart; mod irq; pub mod timer; @@ -28,12 +28,11 @@ pub use gpio::GPIO; pub use mbox::Mbox; pub use uart::Uart; pub use syscall::SysCall; -pub use usb::USB; -pub use timer::PhysicalTimer; pub use irq::IRQ; pub use bcm::BCMDeviceMemory; pub use console::FrameBufferConsole; use linked_list_allocator::LockedHeap; +pub use dma::DmaAllocator; pub static mut LOGGER: Logger = Logger::new(); pub static mut SCREEN: Logger = Logger::new(); diff --git a/mmio/src/macros.rs b/mmio/src/macros.rs index 915ea1f..d02233f 100644 --- a/mmio/src/macros.rs +++ b/mmio/src/macros.rs @@ -1,5 +1,8 @@ use core::fmt::{Write, Arguments}; use crate::{LOGGER, SCREEN}; +use crate::timer::SystemTimer; +use register::{Field, IntLike, RegisterLongName, FieldValue}; +use register::mmio::ReadWrite; #[macro_export] macro_rules! debug { @@ -38,4 +41,8 @@ pub fn _print(args: Arguments) { unsafe { SCREEN.write_fmt(args).unwrap(); } -} \ No newline at end of file +} + +pub unsafe fn any_as_u8_slice(p: &T) -> &[u8] { + core::slice::from_raw_parts((p as *const T) as *const u8,core::mem::size_of::()) +} diff --git a/mmio/src/mbox.rs b/mmio/src/mbox.rs index 9b97d25..2fbafe0 100644 --- a/mmio/src/mbox.rs +++ b/mmio/src/mbox.rs @@ -30,6 +30,7 @@ use register::{ use crate::{debugln, debug, DMA, mbox}; use crate::dma::SliceAllocator; use core::ptr::null; +use core::sync::atomic::{compiler_fence, Ordering}; register_bitfields! { u32, @@ -69,8 +70,11 @@ pub mod channel { // Tags #[allow(dead_code)] pub mod tag { - pub const GETSERIAL: u32 = 0x10004; - pub const SETCLKRATE: u32 = 0x38002; + pub const GETSERIAL: u32 = 0x10004; + pub const GET_POWER_STATE: u32 = 0x20001; + pub const SET_POWER_STATE: u32 = 0x28001; + + pub const SETCLKRATE: u32 = 0x38002; pub const GET_SCREEN_FRAME_BUFFER: u32 = 0x40001; pub const GET_PITCH: u32 = 0x40008; @@ -157,6 +161,7 @@ impl<'a> Mbox<'a> { self.set_and_inc(mbox::tag::LAST); self.set_at_pos((self.pos * 4) as u32, 0); self.set_at_pos(mbox::REQUEST, 1); + compiler_fence(Ordering::Release); self.call(if self.is_dma { self.dma } else { &self.stack }, channel) } diff --git a/mmio/src/timer.rs b/mmio/src/timer.rs index 1db8563..14cba4f 100644 --- a/mmio/src/timer.rs +++ b/mmio/src/timer.rs @@ -1,4 +1,5 @@ mod physical; +mod system; pub use physical::PhysicalTimer; - +pub use system::SystemTimer; diff --git a/mmio/src/timer/system.rs b/mmio/src/timer/system.rs new file mode 100644 index 0000000..ea4c180 --- /dev/null +++ b/mmio/src/timer/system.rs @@ -0,0 +1,54 @@ +use register::mmio::{ReadWrite, ReadOnly}; +use core::ops; +use core::time::Duration; + +#[allow(non_snake_case)] +#[repr(C)] +pub struct SystemTimerMemoryBlock { + + CONTROL_STATUS : ReadWrite, // 0x00 + TIMER_LOW : ReadOnly, // 0x04 + TIMER_HIGH : ReadOnly, // 0x08 + COMPARE_0 : ReadWrite, // 0x0C + COMPARE_1 : ReadWrite, // 0x10 + COMPARE_2 : ReadWrite, // 0x14 + COMPARE_3 : ReadWrite, // 0x18 +} + + +pub struct SystemTimer { + base_addr: usize, +} + + +impl ops::Deref for SystemTimer { + type Target = SystemTimerMemoryBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + + +impl SystemTimer { + pub const fn new(base_addr: usize) -> Self { + SystemTimer { + base_addr + } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const SystemTimerMemoryBlock { + self.base_addr as *const _ + } + + + pub fn tick_count(&self) -> Duration { + return Duration::from_micros((self.TIMER_HIGH.get() as u64) << 32u64 | (self.TIMER_LOW.get() as u64)); + } + + pub fn wait(&self, time: Duration) { + let tick = self.tick_count(); + while self.tick_count() - tick < time {} + } +} diff --git a/mmio/src/usb.rs b/mmio/src/usb.rs index 95b3dec..047a8dc 100644 --- a/mmio/src/usb.rs +++ b/mmio/src/usb.rs @@ -1,24 +1,184 @@ -mod hcd; - +/*use cortex_a::asm; use register::{mmio::ReadWrite}; -use core::ops; use register::mmio::ReadOnly; -pub enum USBError { +use usb::core::core_ahb::CORE_AHB_CONFIG; +use usb::core::core_hard_config::HCD_HARDWARE_CONFIG_2; +use usb::core::core_hard_config::HCD_HARDWARE_CONFIG_2::FULL_SPEED_PHYSICAL::Value::Dedicated; +use usb::core::core_hard_config::HCD_HARDWARE_CONFIG_2::HIGH_SPEED_PHYSICAL::Value::Ulpi; +use usb::core::core_hard_config::HCD_HARDWARE_CONFIG_2::OPERATING_MODE::Value::{HNP_SRP_CAPABLE, + NO_HNP_SRP_CAPABLE, + NO_SRP_CAPABLE_DEVICE, + NO_SRP_CAPABLE_HOST, + SRP_CAPABLE_DEVICE, + SRP_CAPABLE_HOST, + SRP_ONLY_CAPABLE}; +use usb::core::core_otg_ctrl::CORE_OTG_CONFIG; +use usb::core::core_reset::CORE_RESET; +use usb::core::core_reset::CoreFifoFlush::FlushAll; +use usb::core::core_usb_cfg::USB_CONTROL; +use usb::core::core_usb_cfg::USB_CONTROL::MODE_SELECT::ULPI; +use usb::core::UsbCoreController; +use usb::host::{HostDeviceController, UsbHostRegisterBlock}; +use usb::host::config::HOST_CONFIG; +use usb::power::UsbPower; -} +use crate::{debug, debugln, mbox}; +use crate::timer::SystemTimer; +pub use crate::usb::device::{UsbDevice, UsbDeviceRequest}; + +mod device; +mod pipe; + + +// TODO : use DMA ? +const RECEIVE_FIFO_SIZE: u32 = 20480; /* 16 to 32768 */ +const NON_PERIODIC_FIFO_SIZE: u32 = 20480; /* 16 to 32768 */ +const PERIODIC_FIFO_SIZE: u32 = 20480; /* 16 to 32768 */ pub struct USB { - base_addr: usize, + timer: &'static SystemTimer, + core: UsbCoreController, + host: HostDeviceController, + power: UsbPower, + started: bool, } impl USB { - pub const fn new(base_addr: usize) -> USB { - USB { base_addr } + pub const fn new(base_addr: usize, timer: &'static SystemTimer) -> USB { + USB { + timer, + core: UsbCoreController::new(base_addr, timer), + host: HostDeviceController::new(base_addr + 0x400, timer), + power: UsbPower::new(base_addr + 0xE00), + started: false, + } } - pub fn init(&self) -> Result<(), USBError> { + pub fn init(&self, v_mbox: &mut mbox::Mbox) -> Result<(), &'static str> { + debugln!("USB vendor : {:x} ", self.core.VENDOR_ID.get()); + + if self.core.VENDOR_ID.get() & 0xfffff000 != 0x4f542000 { + return Err("HCD: Hardware: Driver incompatible. Expected OT2.xxx (BCM2708x)."); + } + + if !self.core.HARDWARE_CFG1.matches_all(HCD_HARDWARE_CONFIG_2::ARCHITECTURE::InternalDma) { + return Err("HCD: Host architecture does not support Internal DMA"); + } + + if self.core.HARDWARE_CFG1.matches_all(HCD_HARDWARE_CONFIG_2::HIGH_SPEED_PHYSICAL::NotSupported) { + return Err("HCD: High speed physical unsupported"); + } + self.core.AHB_CFG.write(CORE_AHB_CONFIG::INTERRUPT_ENABLED::CLEAR); + // clear all interrupts mask + self.core.INT_MASK.set(0); + + self.core.power(v_mbox).expect("USB Power failed"); Ok(()) } -} \ No newline at end of file + + pub fn start(&mut self) -> Result<(), &'static str> { + self.core.USB_CFG.write(USB_CONTROL::ULPI_DRIVE_EXTERNAL_VUS::CLEAR + + USB_CONTROL::TS_DLINE_PULSE_ENABLE::CLEAR); + self.core.reset()?; + + if !self.started { + // If physical interface hasn't been initialized + debugln!("HCD: One time phy initialisation."); + self.started = true; + self.core.USB_CFG.write(USB_CONTROL::MODE_SELECT::UTMI + USB_CONTROL::PHYSICAL_INTERFACE::CLEAR); + self.core.reset()?; + } + + if self.core.HARDWARE_CFG1.matches_any(HCD_HARDWARE_CONFIG_2::HIGH_SPEED_PHYSICAL::Ulpi) { + debugln!("HCD: ULPI FSLS configuration: enabled."); + self.core.USB_CFG.write(USB_CONTROL::ULPI_FSLS::SET + USB_CONTROL::ULPI_CLK_SUM_M::SET); + } else { + debugln!("HCD: ULPI FSLS configuration: disabled."); + self.core.USB_CFG.write(USB_CONTROL::ULPI_FSLS::CLEAR + USB_CONTROL::ULPI_CLK_SUM_M::CLEAR); + } + self.core.AHB_CFG.write(CORE_AHB_CONFIG::DMA_ENABLED::SET + + CORE_AHB_CONFIG::DMA_REMAINDER_MODE::Incremental); + + match self.core.HARDWARE_CFG1.read_as_enum(HCD_HARDWARE_CONFIG_2::OPERATING_MODE) { + Some(HNP_SRP_CAPABLE) => self.core.USB_CFG + .write(USB_CONTROL::HNP_CAPABLE::SET + USB_CONTROL::SRP_CAPABLE::SET), + Some(SRP_ONLY_CAPABLE) | Some(SRP_CAPABLE_DEVICE) | Some(SRP_CAPABLE_HOST) => self.core.USB_CFG + .write(USB_CONTROL::HNP_CAPABLE::CLEAR + USB_CONTROL::SRP_CAPABLE::SET), + Some(NO_HNP_SRP_CAPABLE) | Some(NO_SRP_CAPABLE_DEVICE) | Some(NO_SRP_CAPABLE_HOST) | None => self.core.USB_CFG + .write(USB_CONTROL::HNP_CAPABLE::CLEAR + USB_CONTROL::SRP_CAPABLE::CLEAR) + } + debugln!("HCD: Core started."); + debugln!("HCD: Starting host."); + + self.power.pw.set(0); + + if self.core.HARDWARE_CFG1.matches_any(HCD_HARDWARE_CONFIG_2::HIGH_SPEED_PHYSICAL::Ulpi) + && self.core.HARDWARE_CFG1.matches_any(HCD_HARDWARE_CONFIG_2::FULL_SPEED_PHYSICAL::Dedicated) { + debugln!("HCD: Host clock: 48Mhz."); + self.host.CONFIG.write(HOST_CONFIG::CLOCK_RATE::Clock48MHz); + } else { + debugln!("HCD: Host clock: 30-60Mhz."); + self.host.CONFIG.write(HOST_CONFIG::CLOCK_RATE::Clock30_60MHz); + } + + self.host.CONFIG.write(HOST_CONFIG::FSLS_ONLY::SET); + + self.core.RX_FIFO_SIZ.set(RECEIVE_FIFO_SIZE); + + // TODO : START_ADDR SHOULD MAYBE NEED TO BE CHANGED ? + self.core.NPER_TX_FIFO_SIZ.SIZE.START_ADDR.set(NON_PERIODIC_FIFO_SIZE as u16); + self.core.NPER_TX_FIFO_SIZ.SIZE.DEPTH.set(RECEIVE_FIFO_SIZE as u16); + + // TODO : START_ADDR SHOULD MAYBE NEED TO BE CHANGED ? + self.core.PER_TX_FIFO_SIZ.HOST_SIZE.START_ADDR.set((RECEIVE_FIFO_SIZE + NON_PERIODIC_FIFO_SIZE) as u16); + self.core.PER_TX_FIFO_SIZ.HOST_SIZE.DEPTH.set(PERIODIC_FIFO_SIZE as u16); + + debugln!("HCD: Set HNP: enabled."); + + self.core.OTG_CTRL.write(CORE_OTG_CONFIG::HOST_SET_HNP_ENABLE::SET); + self.core.flush_tx(FlushAll)?; + self.core.flush_rx()?; + + if self.host.CONFIG.matches_all(HOST_CONFIG::ENABLE_DMA_DESCRIPTOR::CLEAR) { + self.host.reset_channels(self.core.HARDWARE_CFG1.read(HCD_HARDWARE_CONFIG_2::HOST_CHANNEL_COUNT) as usize)?; + } + + self.host.power()?; + + Ok(()) + } + + pub fn send_request(&self, root : &mut UsbDevice, request: &UsbDeviceRequest) -> Result<(), &'static str> { + + root.send_control_message(self.timer, request, &self.host.CHANNELS[0]) + + /* + +/*-INTERNAL: EnumerateDevice ------------------------------------------------ + This is called from USBInitialize and will allocate our fake rootHub device + and then begin enumeration of the whole USB bus. + 11Feb17 LdB + --------------------------------------------------------------------------*/ +RESULT UsbAttachRootHub(void) { + RESULT result; + struct UsbDevice *rootHub = NULL; + LOG_DEBUG("Allocating RootHub\n"); + if (DeviceTable[0].PayLoadId != 0) // If RootHub is already in use + UsbDeallocateDevice(&DeviceTable[0]); // We will need to deallocate it and every child + result = UsbAllocateDevice(&rootHub); // Try allocating the root hub now + if (rootHub != &DeviceTable[0]) result = ErrorCompiler; // Somethign really wrong .. 1st allocation should always be DeviceList[0] + if (result != OK) return result; // Return error result somethging fatal happened + DeviceTable[0].Pipe0.Speed = USB_SPEED_FULL; // Set our fake hub to full speed .. as it's fake we cant really ask it speed can we :-) + DeviceTable[0].Pipe0.MaxSize = Bits64; // Set our fake hub to 64 byte packets .. as it's fake we need to do it manually + DeviceTable[0].Config.Status = USB_STATUS_POWERED; // Set our fake hub status to configured .. as it's fake we need to do manually + RootHubDeviceNumber = 0; // Roothub number is zero + return EnumerateDevice(&DeviceTable[0], NULL, 0); // Ok start enumerating the USB bus as roothub port 1 is the physical bus +} + + */ + } + +} +*/ diff --git a/mmio/src/usb/device.rs b/mmio/src/usb/device.rs new file mode 100644 index 0000000..0d3720e --- /dev/null +++ b/mmio/src/usb/device.rs @@ -0,0 +1,143 @@ + +/* + +/*--------------------------------------------------------------------------} +{ USB parent used mainly by internal routines (details of parent hub) } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__)) UsbParent { + unsigned Number : 8; // @0 Unique device number of our parent sometimes called address or id + unsigned PortNumber : 8; // @8 This is the port we are connected to on our parent hub + unsigned reserved : 16; // @16 Reserved 16 bits +}; + + +/*--------------------------------------------------------------------------} +{ Our structure that hold details about any USB device we have detected } +{--------------------------------------------------------------------------*/ +struct UsbDevice { + struct UsbParent ParentHub; // Details of our parent hub + struct UsbPipe Pipe0; // Usb device pipe AKA pipe0 + struct UsbPipeControl PipeCtrl0; // Usb device pipe control AKA pipectrl0 + struct UsbConfigControl Config; // Usb config control + uint8_t MaxInterface ALIGN4; // Maxiumum interface in array (varies with config and usually a lot less than the max array size) + struct UsbInterfaceDescriptor Interfaces[MaxInterfacesPerDevice] ALIGN4; // These are available interfaces on this device + struct UsbEndpointDescriptor Endpoints[MaxInterfacesPerDevice][MaxEndpointsPerDevice] ALIGN4; // These are available endpoints on this device + struct usb_device_descriptor Descriptor ALIGN4; // Device descriptor it's accessed a bit so we have a copy to save USB bus ... align it for ARM7/8 + + enum PayLoadType PayLoadId; // Payload type being carried + union { // It can only be any of the different payloads + struct HubDevice* HubPayload; // If this is a USB gateway node of a hub this pointer will be set to the hub data which is about the ports + struct HidDevice* HidPayload; // If this node has a HID function this pointer will be to the HID payload + struct MassStorageDevice* MassPayload; // If this node has a MASS STORAGE function this pointer will be to the Mass Storage payload + }; +}; + + */ +use crate::usb::pipe::{Pipe, PIPE_CONTROL, PIPE}; +use register::InMemoryRegister; +use crate::timer::SystemTimer; +use usb::host::channel::HostChannel; +use usb::host::channel::transfer_size::CHANNEL_TRANSFER_SIZE::PACKET_ID::Usb_Pid_Setup; +use crate::macros::any_as_u8_slice; + +#[allow(non_snake_case)] +#[repr(C)] +#[derive(Debug)] +pub struct DeviceParent { + number: u8, + portNumber: u8, + __reserved: u16 +} + +#[allow(non_snake_case)] +#[repr(C)] +#[derive(Debug)] +pub struct UsbDevice { + parentHub : DeviceParent, + pipe0 : Pipe, +} + +/* + +/*--------------------------------------------------------------------------} +{ Device Request structure (8 bytes) as per the USB 2.0 standard } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__)) UsbDeviceRequest { + uint8_t Type; // +0x0 + enum UsbDeviceRequestRequest { + // USB requests + GetStatus = 0, + ClearFeature = 1, + SetFeature = 3, + SetAddress = 5, + GetDescriptor = 6, + SetDescriptor = 7, + GetConfiguration = 8, + SetConfiguration = 9, + GetInterface = 10, + SetInterface = 11, + SynchFrame = 12, + // HID requests + GetReport = 1, + GetIdle = 2, + GetProtocol = 3, + SetReport = 9, + SetIdle = 10, + SetProtocol = 11, + } Request : 8; // +0x1 + uint16_t Value; // +0x2 + uint16_t Index; // +0x4 + uint16_t Length; // +0x6 +}; + */ + +#[allow(non_snake_case)] +#[repr(C)] +pub struct UsbDeviceRequest { + pub requestType: u8, + pub requestName: u8, + pub value: u16, + pub index: u16, + pub length: u16 +} + +impl UsbDevice { + + pub fn new() -> Self { + UsbDevice { + parentHub: DeviceParent { + number: 0xFF, + portNumber: 0, + __reserved: 0 + }, + pipe0: Pipe { + o: InMemoryRegister::new(0), + c: InMemoryRegister::new(0), + }, + } + } + + pub fn send_control_message(&self, timer: &'static SystemTimer, request: &UsbDeviceRequest, channel : &HostChannel) -> Result<(), &'static str> { + + self.pipe0.o.write(PIPE::NUMBER.val(0) + PIPE::PACKET_SIZE::Bits8 + PIPE::SPEED::Full); + self.pipe0.c.write( PIPE_CONTROL::TRANSFER_TYPE::Control + PIPE_CONTROL::DIRECTION::OUT + PIPE_CONTROL::CHANNEL.val(0)); + + channel.send_receive_buffer(timer, &self.pipe0, unsafe { any_as_u8_slice(request) }, Usb_Pid_Setup.value) + /* + uint32_t lastTransfer = 0; + + // LOG("Setup phase "); + // Setup phase + struct UsbPipeControl intPipeCtrl = pipectrl; // Copy the pipe control (We want channel really) + intPipeCtrl.Type = USB_TRANSFER_TYPE_CONTROL; // Set pipe to control + intPipeCtrl.Direction = USB_DIRECTION_OUT; // Set pipe to out + if ((result = HCDChannelTransfer(pipe, intPipeCtrl, + (uint8_t*)request, 8, USB_PID_SETUP)) != OK) { // Send the 8 byte setup request packet + LOG("HCD: SETUP packet to device: %#x req: %#x req Type: %#x Speed: %i PacketSize: %i LowNode: %i LowPort: %i Error: %i\n", + pipe.Number, request->Request, request->Type, pipe.Speed, pipe.MaxSize, pipe.lowSpeedNodePoint, pipe.lowSpeedNodePort, result);// Some parameter issue + return OK; + } + */ + + } +} \ No newline at end of file diff --git a/mmio/src/usb/hcd.rs b/mmio/src/usb/hcd.rs deleted file mode 100644 index d2c07ff..0000000 --- a/mmio/src/usb/hcd.rs +++ /dev/null @@ -1,70 +0,0 @@ -use register::mmio::{ReadWrite, ReadOnly}; - -/* -#define DWC_CORE_OTGCONTROL ((volatile __attribute__((aligned(4))) struct CoreOtgControl*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x00)) -#define DWC_CORE_OTGINTERRUPT ((volatile __attribute__((aligned(4))) struct CoreOtgInterrupt*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x04)) -#define DWC_CORE_AHB ((volatile __attribute__((aligned(4))) struct CoreAhb*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x08)) -#define DWC_CORE_CONTROL ((volatile __attribute__((aligned(4))) struct UsbControl*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x0C)) -#define DWC_CORE_RESET ((volatile __attribute__((aligned(4))) struct CoreReset*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x10)) -#define DWC_CORE_INTERRUPT ((volatile __attribute__((aligned(4))) struct CoreInterrupts*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x14)) -#define DWC_CORE_INTERRUPTMASK ((volatile __attribute__((aligned(4))) struct CoreInterrupts*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x18)) -#define DWC_CORE_RECEIVESIZE ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x24)) -#define DWC_CORE_NONPERIODICFIFO ((volatile __attribute__((aligned(4))) struct CoreNonPeriodicInfo*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x28)) -#define DWC_CORE_USERID ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x3C)) -#define DWC_CORE_VENDORID ((volatile __attribute__((aligned(4))) const uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x40)) -#define DWC_CORE_HARDWARE ((volatile __attribute__((aligned(4))) const struct CoreHardware*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x44)) -#define DWC_CORE_PERIODICINFO ((volatile __attribute__((aligned(4))) struct CorePeriodicInfo*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x100)) - - */ - -#[allow(non_snake_case)] -#[repr(C)] -pub struct RegisterBlock { - pub OTG_CTRL: ReadWrite, // 0x00 - pub OTG_INT: ReadWrite, // 0x04 - pub AHB_CFG: ReadWrite, // 0x08 - pub USB_CFG: ReadWrite, // 0x0C - pub RESET: ReadWrite, // 0x10 - pub INT_STAT: ReadWrite, // 0x14 - pub INT_MASK: ReadWrite, // 0x18 - pub RX_STAT_RD: ReadOnly, // 0x1C - pub RX_STAT_POP: ReadOnly,// 0x20 - pub RX_FIFO_SIZ: ReadWrite,// 0x24 - pub NPER_TX_FIFO_SIZ: ReadWrite,// 0x28 - pub NPER_TX_STAT: ReadWrite,// 0x2C - pub I2C_CTRL: ReadWrite, // 0x30 - pub PHY_VENDOR_CTRL: ReadWrite, // 0x34 - pub GPIO: ReadWrite, // 0x38 - pub USER_ID: ReadWrite, // 0x3C - pub VENDOR_ID: ReadWrite, // 0x40 - pub HW_CFG1: ReadOnly, - pub HW_CFG2: ReadOnly, - pub HW_CFG3: ReadOnly, - pub HW_CFG4: ReadOnly, -} - - -pub struct HostDeviceController { - base_addr: usize -} - -impl HostDeviceController { - - pub const fn new(base_addr: usize) -> Self { - HostDeviceController { - base_addr - } - } - - /// Returns a pointer to the register block - fn ptr(&self) -> *const RegisterBlock { - self.base_addr as *const _ - } - - pub fn init(&self) -> Result<(), &'static str>{ - /*if self.ptr().VENDOR_ID & 0xfffff000 != 0x4f542000 { - Err("HCD: Hardware: Driver incompatible. Expected OT2.xxx (BCM2708x).") - }*/ - Ok(()) - } -} \ No newline at end of file diff --git a/mmio/src/usb/pipe.rs b/mmio/src/usb/pipe.rs new file mode 100644 index 0000000..4a6a1f0 --- /dev/null +++ b/mmio/src/usb/pipe.rs @@ -0,0 +1,108 @@ +use register::{register_bitfields, FieldValue, InMemoryRegister, TryFromValue}; +use register::mmio::ReadWrite; +use crate::timer::SystemTimer; +use crate::usb::pipe::PIPE::PACKET_SIZE::Value::{Bits8, Bits16, Bits32, Bits64}; +use usb::host::{HostDeviceController, channel::HostChannel}; +use usb::host::channel::transfer_size::CHANNEL_TRANSFER_SIZE::PACKET_ID::Usb_Pid_Setup; +use core::fmt::{Debug, Formatter}; +use core::fmt; + +/* + + +/*--------------------------------------------------------------------------} +{ USB pipe our own special structure encompassing a pipe in the USB spec } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__)) UsbPipe { + UsbPacketSize MaxSize : 2; // @0 Maximum packet size + UsbSpeed Speed : 2; // @2 Speed of device + unsigned EndPoint : 4; // @4 Endpoint address + unsigned Number : 8; // @8 Unique device number sometimes called address or id + unsigned _reserved : 2; // @16-17 + unsigned lowSpeedNodePort : 7; // @18-24 In low speed transfers it is port device is on closest parent high speed hub + unsigned lowSpeedNodePoint : 7; // @25-31 In low speed transfers it is closest parent high speed hub +}; + +/*--------------------------------------------------------------------------} +{ USB pipe control used mainly by internal routines } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__)) UsbPipeControl { + unsigned _reserved : 14; // @0-13 + enum usb_transfer_type Type : 2; // @14-15 Packet type + unsigned Channel : 8; // @16-23 Channel to use + unsigned Direction : 1; // @24 Direction 1=IN, 0=OUT + unsigned _reserved1 : 7; // @25-31 +}; + + */ + + +register_bitfields! { + u32, + + pub PIPE [ + LOW_SPEED_NODE_PORT OFFSET(25) NUMBITS(7) [], + LOW_SPEED_NODE_POINT OFFSET(18) NUMBITS(7) [], + NUMBER OFFSET(8) NUMBITS(1) [], + ENDPOINT OFFSET(4) NUMBITS(4) [], + SPEED OFFSET(2) NUMBITS(2) [ + High = 0b00, + Full = 0b01, + Low = 0b10 + ], + PACKET_SIZE OFFSET(0) NUMBITS(2) [ + Bits8 = 0b0, + Bits16 = 0b01, + Bits32 = 0b10, + Bits64 = 0b11 + ] + ] +} + +register_bitfields! { + u32, + + pub PIPE_CONTROL [ + + DIRECTION OFFSET(24) NUMBITS(1) [ + OUT = 0b0, + IN = 0b1 + ], + + CHANNEL OFFSET(16) NUMBITS(8) [], + + TRANSFER_TYPE OFFSET(14) NUMBITS(2) [ + Control = 0b00, + Isochronous = 0b01, + Bulk = 0b10, + Interrupt = 0b11 + ] + + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct Pipe { + pub o: InMemoryRegister, + pub c: InMemoryRegister, +} + +impl Debug for Pipe { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("CONTROL: {:b}, PIPE: {:b}", self.o.get(), self.c.get())) + } +} + +impl Pipe { + + pub fn size(&self) -> u32 { + match self.o.read_as_enum(PIPE::PACKET_SIZE) { + Some(Bits8) => 8, + Some(Bits16) => 16, + Some(Bits32) => 32, + Some(Bits64) => 63, + _ => 8 + } + } +} \ No newline at end of file diff --git a/program.img b/program.img deleted file mode 100755 index 14bb473..0000000 Binary files a/program.img and /dev/null differ diff --git a/program/.cargo/config b/program/.cargo/config deleted file mode 100644 index 61dd30d..0000000 --- a/program/.cargo/config +++ /dev/null @@ -1,6 +0,0 @@ -[target.aarch64-unknown-none] -rustflags = [ - "-C", "link-arg=-Tprogram/link.ld", - "-C", "target-feature=-fp-armv8", - "-C", "target-cpu=cortex-a53", -] \ No newline at end of file diff --git a/program/Makefile b/program/Makefile deleted file mode 100644 index 70cf413..0000000 --- a/program/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -all: kernel -kernel: elf - #cargo objcopy -- --strip-all -O binary ../target/aarch64-unknown-none/release/aos-program ../program.img - rust-objcopy --strip-all -O binary ../target/aarch64-unknown-none/release/aos-program ../program.img -elf: - cargo xrustc --target aarch64-unknown-none --release -clean: - cargo clean \ No newline at end of file diff --git a/program/link.ld b/program/link.ld deleted file mode 100644 index 0b93fea..0000000 --- a/program/link.ld +++ /dev/null @@ -1,29 +0,0 @@ -ENTRY(_boot_cores); - -SECTIONS -{ - . = 0x200000; - __ro_start = .; - .text : - { - KEEP(*(.text.start)) *(.text .text.*) - } - .rodata : - { - *(.rodata .rodata.*) - } - __ro_end = .; - . = ALIGN(4096); /* Fill up to 4KiB */ - .data : - { - *(.data .data.*) - } - .bss ALIGN(8): - { - __bss_start = .; - *(.bss .bss.*) - *(COMMON) - __bss_end = .; - } - /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } -} \ No newline at end of file diff --git a/program/src/main.rs b/program/src/main.rs deleted file mode 100644 index 8fc4e56..0000000 --- a/program/src/main.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![no_std] -#![no_main] -#![feature(asm)] -#![feature(core_intrinsics)] -#![feature(global_asm)] -#![feature(duration_constants)] -#![feature(llvm_asm)] - -#[macro_use] extern crate mmio; -use cortex_a::asm; -use mmio::syscall::SysCall; -use qemu_exit::QEMUExit; - -use cortex_a::regs::{SP, RegisterReadWrite}; - -extern "C" { - // Boundaries of the .bss section, provided by the linker script - static mut __bss_start: u64; - static mut __bss_end: u64; -} - -#[panic_handler] -fn my_panic(info: &core::panic::PanicInfo) -> ! { - println!("{:?}", info); - const QEMU_EXIT_HANDLE: qemu_exit::AArch64 = qemu_exit::AArch64::new(); - QEMU_EXIT_HANDLE.exit_failure() -} - -/// Entrypoint of the program -#[link_section = ".text.start"] -#[no_mangle] -pub unsafe extern "C" fn _main() -> () { - - r0::zero_bss(&mut __bss_start, &mut __bss_end); - mmio::SCREEN.appender(SysCall { }.into()); - - println!("show a message using SVC call"); - - let mut count:u128 = 0; - loop { - if count % 10000 == 0 { - println!("current stack pointer {:x}", SP.get()); - println!("show string from time to time {}", count); - } - count = count + 1; - } - -} diff --git a/program/Cargo.toml b/usb/Cargo.toml similarity index 51% rename from program/Cargo.toml rename to usb/Cargo.toml index d8091d2..63fb28b 100644 --- a/program/Cargo.toml +++ b/usb/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "aos-program" +name = "usb" version = "0.1.0" authors = ["Alban Seurat "] edition = "2018" @@ -7,8 +7,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mmio = { path = "../mmio" } -shared = { path = "../shared" } -qemu-exit = "1.0.0" -cortex-a = { git = "https://github.com/AlbanSeurat/cortex-a", branch = "master" } -r0 = "1.0.0" +usb-device = "0.2.7" +register = "0.5.1" +# MUST BE REMOVED AT SOME POINT +mmio = { path = "../mmio" } \ No newline at end of file diff --git a/usb/Makefile b/usb/Makefile new file mode 100644 index 0000000..471726d --- /dev/null +++ b/usb/Makefile @@ -0,0 +1,5 @@ +all: usb +usb: + cargo xrustc --target aarch64-unknown-none --release +clean: + cargo clean \ No newline at end of file diff --git a/usb/src/bus.rs b/usb/src/bus.rs new file mode 100644 index 0000000..47da5ca --- /dev/null +++ b/usb/src/bus.rs @@ -0,0 +1,58 @@ +use usb_device::class_prelude::{EndpointType, EndpointAddress}; +use usb_device::{UsbDirection, UsbError}; +use usb_device::bus::PollResult; +use crate::core::UsbCoreController; +use crate::host::HostDeviceController; +use crate::power::UsbPower; +use mmio::timer::SystemTimer; +use crate::core::core_hard_config::HCD_HARDWARE_CONFIG_2; +use crate::core::core_ahb::CORE_AHB_CONFIG; +use crate::core::core_reset::CORE_RESET; +use register::mmio::ReadWrite; +use crate::wait_for; +use core::time::Duration; + +pub struct UsbBus { + core: UsbCoreController, + host: HostDeviceController, + power: UsbPower, + timer: &'static SystemTimer, +} + +/* + + p->host_rx_fifo_size = 774; + p->max_transfer_size = 65535; + p->max_packet_count = 511; + p->ahbcfg = 0x10; + + */ + +impl UsbBus { + pub const fn new(base_addr: usize, timer: &'static SystemTimer) -> Self { + UsbBus { + core: UsbCoreController::new(base_addr, timer), + host: HostDeviceController::new(base_addr + 0x400, timer), + power: UsbPower::new(base_addr + 0xE00), + timer + } + } + + pub fn init(&self) -> Result<(), &'static str> { + debugln!("USB vendor : {:x} ", self.core.VENDOR_ID.get()); + + if self.core.VENDOR_ID.get() & 0xfffff000 != 0x4f542000 { + return Err("HCD: Hardware: Driver incompatible. Expected OT2.xxx (BCM2708x)."); + } + self.core.reset(); + //wait for being in host mode... + self.timer.wait(Duration::from_millis(100)); + debugln!("core is host mode now : {}", self.core.is_host_mode()); // works! + debugln!("IRQ global status : {}", self.core.AHB_CFG.read(CORE_AHB_CONFIG::DMA_ENABLED)); + + self.core.init(); + self.host.init() + } + + +} diff --git a/usb/src/core.rs b/usb/src/core.rs new file mode 100644 index 0000000..bcf2c2e --- /dev/null +++ b/usb/src/core.rs @@ -0,0 +1,376 @@ +use register::mmio::{ReadOnly, ReadWrite}; + +use core_ahb::CORE_AHB_CONFIG; +use core_reset::CORE_RESET; +use core_usb_cfg::USB_CONFIG; +use core_irq::CORE_INTERRUPT; +use core::ops; +use mmio::mbox; +use register::FieldValue; +use crate::core::core_reset::CoreFifoFlush; +use crate::core::core_periodic_info::{PeriodicFifoSize, NonPeriodicFifoSize}; +use crate::core::core_otg_ctrl::CORE_OTG_CONFIG; +use mmio::timer::SystemTimer; +use crate::wait_for; +use core::time::Duration; +use crate::core::core_hard_config::{HCD_HARDWARE_CONFIG_4, HCD_HARDWARE_CONFIG_2}; +use crate::core::core_hard_config::HCD_HARDWARE_CONFIG_2::HIGH_SPEED_PHYSICAL::Value::{NotSupported, Utmi, Ulpi, UtmiUlpi}; +use crate::core::core_usb_cfg::USB_CONFIG::PHYSICAL_INTERFACE::Value::Width16bit; +use crate::core::core_hard_config::HCD_HARDWARE_CONFIG_2::ARCHITECTURE::Value::{ExternalDma, InternalDma, SlaveOnly}; +use crate::core::core_hard_config::HCD_HARDWARE_CONFIG_2::OPERATING_MODE::Value::{HNP_SRP_CAPABLE, + NO_HNP_SRP_CAPABLE, + NO_SRP_CAPABLE_DEVICE, + NO_SRP_CAPABLE_HOST, + SRP_CAPABLE_DEVICE, + SRP_CAPABLE_HOST, + SRP_ONLY_CAPABLE}; +use mmio::macros::_debug; + +pub mod core_ahb; +pub mod core_hard_config; +pub mod core_reset; +pub mod core_usb_cfg; +pub mod core_irq; +mod core_periodic_info; +pub mod core_otg_ctrl; + +/* + +/*--------------------------------------------------------------------------} +{ DWC USB CORE REGISTER POINTERS } +{--------------------------------------------------------------------------*/ +#define DWC_CORE_OTGCONTROL ((volatile __attribute__((aligned(4))) struct CoreOtgControl*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x00)) +#define DWC_CORE_OTGINTERRUPT ((volatile __attribute__((aligned(4))) struct CoreOtgInterrupt*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x04)) +#define DWC_CORE_AHB ((volatile __attribute__((aligned(4))) struct CoreAhb*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x08)) +#define DWC_CORE_CONTROL ((volatile __attribute__((aligned(4))) struct UsbControl*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x0C)) +#define DWC_CORE_RESET ((volatile __attribute__((aligned(4))) struct CoreReset*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x10)) +#define DWC_CORE_INTERRUPT ((volatile __attribute__((aligned(4))) struct CoreInterrupts*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x14)) +#define DWC_CORE_INTERRUPTMASK ((volatile __attribute__((aligned(4))) struct CoreInterrupts*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x18)) +#define DWC_CORE_RECEIVESIZE ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x24)) +#define DWC_CORE_NONPERIODICFIFO ((volatile __attribute__((aligned(4))) struct CoreNonPeriodicInfo*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x28)) +#define DWC_CORE_USERID ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x3C)) +#define DWC_CORE_VENDORID ((volatile __attribute__((aligned(4))) const uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x40)) +#define DWC_CORE_HARDWARE ((volatile __attribute__((aligned(4))) const struct CoreHardware*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x44)) +#define DWC_CORE_PERIODICINFO ((volatile __attribute__((aligned(4))) struct CorePeriodicInfo*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x100)) + +*/ + +#[allow(non_snake_case)] +#[repr(C)] +pub struct UsbCoreRegisterBlock { + pub OTG_CTRL: ReadWrite, + // 0x00 + pub OTG_INT: ReadWrite, + // 0x04 + pub AHB_CFG: ReadWrite, + // 0x08 + pub USB_CFG: ReadWrite, + // 0x0C + pub RESET: ReadWrite, + // 0x10 + pub INT_STAT: ReadWrite, + // 0x14 + pub INT_MASK: ReadWrite, + // 0x18 + pub RX_STAT_RD: ReadOnly, + // 0x1C + pub RX_STAT_POP: ReadOnly, + // 0x20 + pub RX_FIFO_SIZ: ReadWrite, + // 0x24 + pub NPER_TX_FIFO_SIZ: NonPeriodicFifoSize, + // 0x2C + pub I2C_CTRL: ReadWrite, + // 0x30 + pub PHY_VENDOR_CTRL: ReadWrite, + // 0x34 + pub GPIO: ReadWrite, + // 0x38 + pub USER_ID: ReadWrite, + // 0x3C + pub VENDOR_ID: ReadWrite, + // 0x40 + pub HARDWARE_CFG1: ReadOnly, + // 0x44 + pub HARDWARE_CFG2: ReadOnly, + // 0x4C + pub HARDWARE_CFG3: ReadOnly, + // 0x48 + pub HARDWARE_CFG4: ReadOnly, + // 0x4C + + pub __reserved: [u64; 21], + // 0x54 + + pub PER_TX_FIFO_SIZ: PeriodicFifoSize, + // 0x100 +} + +/* + + static const char dwc2_driver_name[] = "dwc2"; + ++static const struct dwc2_core_params params_bcm2835 = { ++ .otg_cap = 0, /* HNP/SRP capable */ ++ .otg_ver = 0, /* 1.3 */ ++ .dma_enable = 1, ++ .dma_desc_enable = 0, ++ .speed = 0, /* High Speed */ ++ .enable_dynamic_fifo = 1, ++ .en_multiple_tx_fifo = 1, ++ .host_rx_fifo_size = 774, /* 774 DWORDs */ ++ .host_nperio_tx_fifo_size = 256, /* 256 DWORDs */ ++ .host_perio_tx_fifo_size = 512, /* 512 DWORDs */ ++ .max_transfer_size = 65535, ++ .max_packet_count = 511, ++ .host_channels = 8, ++ .phy_type = 1, /* UTMI */ ++ .phy_utmi_width = 8, /* 8 bits */ ++ .phy_ulpi_ddr = 0, /* Single */ ++ .phy_ulpi_ext_vbus = 0, ++ .i2c_enable = 0, ++ .ulpi_fs_ls = 0, ++ .host_support_fs_ls_low_power = 0, ++ .host_ls_low_power_phy_clk = 0, /* 48 MHz */ ++ .ts_dline = 0, ++ .reload_ctl = 0, ++ .ahbcfg = 0x10, ++}; ++ + */ + + +pub struct UsbCoreController { + base_addr: usize, + timer: &'static SystemTimer, +} + +impl ops::Deref for UsbCoreController { + type Target = UsbCoreRegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl UsbCoreController { + pub const fn new(base_addr: usize, timer: &'static SystemTimer) -> Self { + UsbCoreController { + base_addr, + timer, + } + } + /// Returns a pointer to the register block + fn ptr(&self) -> *const UsbCoreRegisterBlock { + self.base_addr as *const _ + } + + + pub fn is_otg(&self) -> bool { + match self.HARDWARE_CFG2.read_as_enum(HCD_HARDWARE_CONFIG_2::OPERATING_MODE) { + Some(HNP_SRP_CAPABLE) | Some(SRP_ONLY_CAPABLE) | Some(NO_HNP_SRP_CAPABLE) => true, + _ => false + } + } + + pub fn is_device(&self) -> bool { + match self.HARDWARE_CFG2.read_as_enum( HCD_HARDWARE_CONFIG_2::OPERATING_MODE) { + Some(SRP_CAPABLE_DEVICE) | Some(NO_SRP_CAPABLE_DEVICE) => true, + _ => false + } + } + + pub fn is_host(&self) -> bool { + match self.HARDWARE_CFG2.read_as_enum(HCD_HARDWARE_CONFIG_2::OPERATING_MODE) { + Some(SRP_CAPABLE_HOST) | Some(NO_SRP_CAPABLE_HOST) => true, + _ => false + } + } + + pub fn is_host_mode(&self) -> bool { + return self.INT_STAT.is_set(CORE_INTERRUPT::CURRENT_MODE); + } + + + pub fn power(&self, v_mbox: &mut mbox::Mbox) -> Result<(), &'static str> { + v_mbox.clear(); + v_mbox.prepare(mbox::tag::SET_POWER_STATE, 8, 8, &[3, 0b11 /* on and wait */]); + v_mbox.request(mbox::channel::PROP).expect("Can not start usb device"); + Ok(()) + } + + pub fn reset(&self) -> Result<(), &'static str> { + self.RESET.write(CORE_RESET::CORE_SOFT::SET); + + wait_for(self.timer, || { self.RESET.matches_all(CORE_RESET::CORE_SOFT::CLEAR) }, Duration::from_millis(10000)) + .map_err(|_| { "reset failed" })?; + + wait_for(self.timer, || { self.RESET.matches_all(CORE_RESET::AHB_MASTER_IDLE::SET) }, Duration::from_millis(10000)) + .map_err(|_| { "AHN master idle state" })?; + + Ok(()) + } + + pub fn force_mode(&self) -> Result<(), &'static str> { + + + Ok(()) + + /* + void dwc2_force_mode(struct dwc2_hsotg *hsotg, bool host) +{ + u32 gusbcfg; + u32 set; + u32 clear; + + dev_dbg(hsotg->dev, "Forcing mode to %s\n", host ? "host" : "device"); + + /* + * Force mode has no effect if the hardware is not OTG. + */ + if (!dwc2_hw_is_otg(hsotg)) + return; + + /* + * If dr_mode is either peripheral or host only, there is no + * need to ever force the mode to the opposite mode. + */ + if (WARN_ON(host && hsotg->dr_mode == USB_DR_MODE_PERIPHERAL)) + return; + + if (WARN_ON(!host && hsotg->dr_mode == USB_DR_MODE_HOST)) + return; + + gusbcfg = dwc2_readl(hsotg, GUSBCFG); + + set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE; + clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE; + + gusbcfg &= ~clear; + gusbcfg |= set; + dwc2_writel(hsotg, gusbcfg, GUSBCFG); + + dwc2_wait_for_mode(hsotg, host); + return; +} + */ + } + + pub(crate) fn init(&self) -> Result<(), &'static str> { + self.AHB_CFG.write(CORE_AHB_CONFIG::DMA_ENABLED::CLEAR); + self.USB_CFG.write(USB_CONFIG::ULPI_DRIVE_EXTERNAL_VUS::CLEAR + + USB_CONFIG::TS_DLINE_PULSE_ENABLE::CLEAR); + + self.init_phy()?; + self.init_abh_config()?; + self.init_usb_config()?; + + self.OTG_CTRL.write(CORE_OTG_CONFIG::OTG_VERSION::CLEAR); + + //self.enable_common_interrupts()?; + + if self.is_host_mode() { + debugln!("Host Mode"); + } else { + debugln!("device mode"); + } + Ok(()) + } + + + pub(crate) fn init_phy(&self) -> Result<(), &'static str> { + + // Assume we are using UTMI + let size = self.HARDWARE_CFG4.read(HCD_HARDWARE_CONFIG_4::UTMI_PHYSICAL_DATA_WIDTH); + debugln!("PHY TYPE DATA WIDTH : {:b}", size); + + let mut field_value: FieldValue = match self.HARDWARE_CFG2.read_as_enum(HCD_HARDWARE_CONFIG_2::HIGH_SPEED_PHYSICAL) { + Some(Ulpi) => Err("Ulpi mode not supported"), + Some(Utmi) | Some(UtmiUlpi) => Ok(USB_CONFIG::MODE_SELECT::UTMI + USB_CONFIG::PHYSICAL_INTERFACE::Width8bit), + Some(NotSupported) | None => Err("FS PHY selected at HS!") + }?; + + if self.HARDWARE_CFG4.matches_all(HCD_HARDWARE_CONFIG_4::UTMI_PHYSICAL_DATA_WIDTH::Width16bit) { + field_value = field_value + USB_CONFIG::PHYSICAL_INTERFACE::Width16bit; + } + + if !self.USB_CFG.matches_all(field_value) { + debugln!("device change"); + self.USB_CFG.write(field_value); + self.reset()?; + } + + // no ulpi_fs_ls for BCM2835 + self.USB_CFG.write(USB_CONFIG::ULPI_FSLS::CLEAR + USB_CONFIG::ULPI_CLK_SUSP_M::CLEAR); + + Ok(()) + } + + pub(crate) fn init_abh_config(&self) -> Result<(), &'static str> { + let field_value = match self.HARDWARE_CFG2.read_as_enum(HCD_HARDWARE_CONFIG_2::ARCHITECTURE) { + Some(ExternalDma) => Err("External DMA unsupported"), + Some(SlaveOnly) => Err("Slave Only mode Unsupported"), + Some(InternalDma) => Ok(CORE_AHB_CONFIG::DMA_ENABLED::SET + + CORE_AHB_CONFIG::INTERRUPT_ENABLED::SET + + CORE_AHB_CONFIG::EMPTY_LEVEL::SET + + CORE_AHB_CONFIG::PERIODIC_EMPTY_LEVEL::SET), + None => Err("Unsupported Mode") + }?; + + /* p->ahbcfg = 0x10; + ahbcfg &= GAHBCFG_CTRL_MASK; + ahbcfg |= hsotg->params.ahbcfg & + ~GAHBCFG_CTRL_MASK; + */ + let mut ahbcfg = self.AHB_CFG.extract().bitand(field_value.value).get() | (0x10 & !field_value.value); + if !self.HARDWARE_CFG2.matches_all(HCD_HARDWARE_CONFIG_2::ARCHITECTURE::SlaveOnly) { + ahbcfg = ahbcfg | CORE_AHB_CONFIG::DMA_ENABLED::SET.value + } + + self.AHB_CFG.set(ahbcfg); + debugln!("setup AHB_CFG to {:x}, results {:x}", ahbcfg, self.AHB_CFG.get()); + Ok(()) + } + + pub(crate) fn init_usb_config(&self) -> Result<(), &'static str> { + match self.HARDWARE_CFG2.read_as_enum(HCD_HARDWARE_CONFIG_2::OPERATING_MODE) { + Some(HNP_SRP_CAPABLE) => self.USB_CFG + .write(USB_CONFIG::HNP_CAPABLE::SET + USB_CONFIG::SRP_CAPABLE::SET), + Some(SRP_ONLY_CAPABLE) | Some(SRP_CAPABLE_DEVICE) | Some(SRP_CAPABLE_HOST) => self.USB_CFG + .write(USB_CONFIG::HNP_CAPABLE::CLEAR + USB_CONFIG::SRP_CAPABLE::SET), + Some(NO_HNP_SRP_CAPABLE) | Some(NO_SRP_CAPABLE_DEVICE) | Some(NO_SRP_CAPABLE_HOST) | None => self.USB_CFG + .write(USB_CONFIG::HNP_CAPABLE::CLEAR + USB_CONFIG::SRP_CAPABLE::CLEAR) + } + Ok(()) + } + + fn enable_common_interrupts(&self) -> Result<(), &'static str> { + /* + u32 intmsk; + + /* Clear any pending OTG Interrupts */ + dwc2_writel(hsotg, 0xffffffff, GOTGINT); + + /* Clear any pending interrupts */ + dwc2_writel(hsotg, 0xffffffff, GINTSTS); + + /* Enable the interrupts in the GINTMSK */ + intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT; + + if (!hsotg->params.host_dma) + intmsk |= GINTSTS_RXFLVL; + if (!hsotg->params.external_id_pin_ctl) + intmsk |= GINTSTS_CONIDSTSCHNG; + + intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | + GINTSTS_SESSREQINT; + + if (dwc2_is_device_mode(hsotg) && hsotg->params.lpm) + intmsk |= GINTSTS_LPMTRANRCVD; + + dwc2_writel(hsotg, intmsk, GINTMSK); + */ + + Err("Not Implemented") + } +} diff --git a/usb/src/core/core_ahb.rs b/usb/src/core/core_ahb.rs new file mode 100644 index 0000000..11a2d7d --- /dev/null +++ b/usb/src/core/core_ahb.rs @@ -0,0 +1,56 @@ +use register::{register_bitfields, FieldValue}; +/* + +/*--------------------------------------------------------------------------} +{ USB CORE AHB STRUCTURE ... CARE WRITE WHOLE REGISTER .. NO BIT OPS } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) CoreAhb { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool InterruptEnable : 1; // @0 + volatile enum { + Length4 = 0, + Length3 = 1, + Length2 = 2, + Length1 = 3, + } AxiBurstLength : 2; // @1 + volatile unsigned _reserved3 : 1; // @3 + volatile bool WaitForAxiWrites : 1; // @4 + volatile bool DmaEnable : 1; // @5 + volatile unsigned _reserved6 : 1; // @6 + volatile enum EmptyLevel { + Empty = 1, + Half = 0, + } TransferEmptyLevel : 1; // @7 + volatile enum EmptyLevel PeriodicTransferEmptyLevel : 1;// @8 + volatile unsigned _reserved9_20 : 12; // @9 + volatile bool remmemsupp : 1; // @21 + volatile bool notialldmawrit : 1; // @22 + volatile enum { + Incremental = 0, + Single = 1, // (default) + } DmaRemainderMode : 1; // @23 + volatile unsigned _reserved24_31 : 8; // @24-31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + */ + +// The AHB is a single-channel, shared bus to communicate between device +register_bitfields! { + u32, + + pub CORE_AHB_CONFIG [ + + DMA_REMAINDER_MODE OFFSET(23) NUMBITS(1) [ + Incremental = 0b0, + Single = 0b1 + ], + PERIODIC_EMPTY_LEVEL OFFSET(8) NUMBITS(1) [], + EMPTY_LEVEL OFFSET(7) NUMBITS(1) [], + DMA_ENABLED OFFSET(5) NUMBITS(1) [], + INTERRUPT_ENABLED OFFSET(0) NUMBITS(1) [] + + ] +} diff --git a/usb/src/core/core_hard_config.rs b/usb/src/core/core_hard_config.rs new file mode 100644 index 0000000..362fcc0 --- /dev/null +++ b/usb/src/core/core_hard_config.rs @@ -0,0 +1,154 @@ +use register::register_bitfields; +/* + +struct __attribute__((__packed__, aligned(4))) CoreHardware { + struct __attribute__((__packed__, aligned(1))) { + volatile const unsigned Direction0 : 2; // @0 + volatile const unsigned Direction1 : 2; // @2 + volatile const unsigned Direction2 : 2; // @4 + volatile const unsigned Direction3 : 2; // @6 + volatile const unsigned Direction4 : 2; // @8 + volatile const unsigned Direction5 : 2; // @10 + volatile const unsigned Direction6 : 2; // @12 + volatile const unsigned Direction7 : 2; // @14 + volatile const unsigned Direction8 : 2; // @16 + volatile const unsigned Direction9 : 2; // @18 + volatile const unsigned Direction10 : 2; // @20 + volatile const unsigned Direction11 : 2; // @22 + volatile const unsigned Direction12 : 2; // @24 + volatile const unsigned Direction13 : 2; // @26 + volatile const unsigned Direction14 : 2; // @28 + volatile const unsigned Direction15 : 2; // @30 + volatile const enum { + HNP_SRP_CAPABLE, + SRP_ONLY_CAPABLE, + NO_HNP_SRP_CAPABLE, + SRP_CAPABLE_DEVICE, + NO_SRP_CAPABLE_DEVICE, + SRP_CAPABLE_HOST, + NO_SRP_CAPABLE_HOST, + } OperatingMode : 3; // @32-34 + volatile const enum { + SlaveOnly, + ExternalDma, + InternalDma, + } Architecture : 2; // @35 + volatile bool PointToPoint : 1; // @37 + volatile const enum { + NotSupported, + Utmi, + Ulpi, + UtmiUlpi, + } HighSpeedPhysical : 2; // @38-39 + volatile const enum { + Physical0, + Dedicated, + Physical2, + Physcial3, + } FullSpeedPhysical : 2; // @40-41 + volatile const unsigned DeviceEndPointCount : 4; // @42 + volatile const unsigned HostChannelCount : 4; // @46 + volatile const bool SupportsPeriodicEndpoints : 1; // @50 + volatile const bool DynamicFifo : 1; // @51 + volatile const bool multi_proc_int : 1; // @52 + volatile const unsigned _reserver21 : 1; // @53 + volatile const unsigned NonPeriodicQueueDepth : 2; // @54 + volatile const unsigned HostPeriodicQueueDepth : 2; // @56 + volatile const unsigned DeviceTokenQueueDepth : 5; // @58 + volatile const bool EnableIcUsb : 1; // @63 + volatile const unsigned TransferSizeControlWidth : 4; // @64 + volatile const unsigned PacketSizeControlWidth : 3; // @68 + volatile const bool otg_func : 1; // @71 + volatile const bool I2c : 1; // @72 + volatile const bool VendorControlInterface : 1; // @73 + volatile const bool OptionalFeatures : 1; // @74 + volatile const bool SynchronousResetType : 1; // @75 + volatile const bool AdpSupport : 1; // @76 + volatile const bool otg_enable_hsic : 1; // @77 + volatile const bool bc_support : 1; // @78 + volatile const bool LowPowerModeEnabled : 1; // @79 + volatile const unsigned FifoDepth : 16; // @80 + volatile const unsigned PeriodicInEndpointCount : 4; // @96 + volatile const bool PowerOptimisation : 1; // @100 + volatile const bool MinimumAhbFrequency : 1; // @101 + volatile const bool PartialPowerOff : 1; // @102 + volatile const unsigned _reserved103_109 : 7; // @103 + volatile const enum { + Width8bit, + Width16bit, + Width8or16bit, + } UtmiPhysicalDataWidth : 2; // @110 + volatile const unsigned ModeControlEndpointCount : 4; // @112 + volatile const bool ValidFilterIddigEnabled : 1; // @116 + volatile const bool VbusValidFilterEnabled : 1; // @117 + volatile const bool ValidFilterAEnabled : 1; // @118 + volatile const bool ValidFilterBEnabled : 1; // @119 + volatile const bool SessionEndFilterEnabled : 1; // @120 + volatile const bool ded_fifo_en : 1; // @121 + volatile const unsigned InEndpointCount : 4; // @122 + volatile const bool DmaDescription : 1; // @126 + volatile const bool DmaDynamicDescription : 1; // @127 + }; +}; + + */ + +register_bitfields! { + u32, + + pub HCD_HARDWARE_CONFIG_2 [ + + HOST_CHANNEL_COUNT OFFSET(14) NUMBITS(4) [], + + FULL_SPEED_PHYSICAL OFFSET(8) NUMBITS(2) [ + Physical0 = 0b00, + Dedicated = 0b01, + Physical2 = 0b10, + Physical3 = 0b11 + ], + + HIGH_SPEED_PHYSICAL OFFSET(6) NUMBITS(2) [ + NotSupported = 0b00, + Utmi = 0b01, + Ulpi = 0b10, + UtmiUlpi = 0b11 + ], + + POINT_TO_POINT OFFSET(5) NUMBITS(1) [], + + ARCHITECTURE OFFSET(3) NUMBITS(2) [ + SlaveOnly = 0x00, + ExternalDma = 0b01, + InternalDma = 0b10 + ], + + OPERATING_MODE OFFSET(0) NUMBITS(3) [ + HNP_SRP_CAPABLE = 0b000, + SRP_ONLY_CAPABLE = 0b001, + NO_HNP_SRP_CAPABLE = 0b010, + SRP_CAPABLE_DEVICE = 0b011, + NO_SRP_CAPABLE_DEVICE = 0b100, + SRP_CAPABLE_HOST = 0b101, + NO_SRP_CAPABLE_HOST = 0b111 + ] + + ] +} + +register_bitfields! { + u32, + + + pub HCD_HARDWARE_CONFIG_4 [ + + + DED_FIFO_EN OFFSET(25) NUMBITS(1) [], + + UTMI_PHYSICAL_DATA_WIDTH OFFSET(14) NUMBITS(2) [ + Width8bit = 0b00, + Width16bit = 0b01, + Width8or16bit = 0b10 + ] + + ] +} \ No newline at end of file diff --git a/usb/src/core/core_irq.rs b/usb/src/core/core_irq.rs new file mode 100644 index 0000000..bb8e599 --- /dev/null +++ b/usb/src/core/core_irq.rs @@ -0,0 +1,63 @@ +use register::register_bitfields; +/* + +/*--------------------------------------------------------------------------} +{ INTERRUPT BITS ON THE USB CORE OF THE DESIGNWARE 2.0 } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) CoreInterrupts { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool CurrentMode : 1; // @0 + volatile bool ModeMismatch : 1; // @1 + volatile bool Otg : 1; // @2 + volatile bool DmaStartOfFrame : 1; // @3 + volatile bool ReceiveStatusLevel : 1; // @4 + volatile bool NpTransmitFifoEmpty : 1; // @5 + volatile bool ginnakeff : 1; // @6 + volatile bool goutnakeff : 1; // @7 + volatile bool ulpick : 1; // @8 + volatile bool I2c : 1; // @9 + volatile bool EarlySuspend : 1; // @10 + volatile bool UsbSuspend : 1; // @11 + volatile bool UsbReset : 1; // @12 + volatile bool EnumerationDone : 1; // @13 + volatile bool IsochronousOutDrop : 1; // @14 + volatile bool eopframe : 1; // @15 + volatile bool RestoreDone : 1; // @16 + volatile bool EndPointMismatch : 1; // @17 + volatile bool InEndPoint : 1; // @18 + volatile bool OutEndPoint : 1; // @19 + volatile bool IncompleteIsochronousIn : 1; // @20 + volatile bool IncompleteIsochronousOut : 1; // @21 + volatile bool fetsetup : 1; // @22 + volatile bool ResetDetect : 1; // @23 + volatile bool Port : 1; // @24 + volatile bool HostChannel : 1; // @25 + volatile bool HpTransmitFifoEmpty : 1; // @26 + volatile bool LowPowerModeTransmitReceived : 1; // @27 + volatile bool ConnectionIdStatusChange : 1; // @28 + volatile bool Disconnect : 1; // @29 + volatile bool SessionRequest : 1; // @30 + volatile bool Wakeup : 1; // @31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + */ + + +register_bitfields! { + u32, + + pub CORE_INTERRUPT [ + + DISCONNECT OFFSET(3) NUMBITS(1) [], + SESSION_REQUEST OFFSET(2) NUMBITS(1) [], + WAKE_UP OFFSET(1) NUMBITS(1) [], + CURRENT_MODE OFFSET(0) NUMBITS(1) [ + Device = 0b0, + Host = 0b1 + ] + ] + +} \ No newline at end of file diff --git a/usb/src/core/core_otg_ctrl.rs b/usb/src/core/core_otg_ctrl.rs new file mode 100644 index 0000000..5a306e0 --- /dev/null +++ b/usb/src/core/core_otg_ctrl.rs @@ -0,0 +1,52 @@ +use register::register_bitfields; +/* + +/*--------------------------------------------------------------------------} +{ USB CORE OTG CONTROL STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) CoreOtgControl { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool sesreqscs : 1; // @0 + volatile bool sesreq : 1; // @1 + volatile bool vbvalidoven : 1; // @2 + volatile bool vbvalidovval : 1; // @3 + volatile bool avalidoven : 1; // @4 + volatile bool avalidovval : 1; // @5 + volatile bool bvalidoven : 1; // @6 + volatile bool bvalidovval : 1; // @7 + volatile bool hstnegscs : 1; // @8 + volatile bool hnpreq : 1; // @9 + volatile bool HostSetHnpEnable : 1; // @10 + volatile bool devhnpen : 1; // @11 + volatile unsigned _reserved12_15 : 4; // @12-15 + volatile bool conidsts : 1; // @16 + volatile unsigned dbnctime : 1; // @17 + volatile bool ASessionValid : 1; // @18 + volatile bool BSessionValid : 1; // @19 + volatile unsigned OtgVersion : 1; // @20 + volatile unsigned _reserved21 : 1; // @21 + volatile unsigned multvalidbc : 5; // @22-26 + volatile bool chirpen : 1; // @27 + volatile unsigned _reserved28_31 : 4; // @28-31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + + */ + + +register_bitfields! { + u32, + + pub CORE_OTG_CONFIG [ + + OTG_VERSION OFFSET(20) NUMBITS(1) [], + + CONID_STATUS OFFSET(16) NUMBITS(1) [], + + HOST_SET_HNP_ENABLE OFFSET(10) NUMBITS(1) [] + ] + +} \ No newline at end of file diff --git a/usb/src/core/core_periodic_info.rs b/usb/src/core/core_periodic_info.rs new file mode 100644 index 0000000..7acd4a1 --- /dev/null +++ b/usb/src/core/core_periodic_info.rs @@ -0,0 +1,78 @@ +/* + +/*--------------------------------------------------------------------------} +{ USB CORE NON PERIODIC INFO STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) CoreNonPeriodicInfo { + volatile __attribute__((aligned(4))) struct FifoSize Size; // +0x28 + volatile __attribute__((aligned(4))) const struct NonPeriodicFifoStatus Status; // Read Only +0x2c +}; + +/*--------------------------------------------------------------------------} +{ USB CORE PERIODIC INFO STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) CorePeriodicInfo { + volatile __attribute__((aligned(4))) struct FifoSize HostSize; // +0x100 + volatile __attribute__((aligned(4))) struct FifoSize DataSize[15];// +0x104 +}; + +/*--------------------------------------------------------------------------} +{ USB CORE NON PERIODIC FIFO STATUS STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) NonPeriodicFifoStatus { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile unsigned SpaceAvailable : 16; // @0 + volatile unsigned QueueSpaceAvailable : 8; // @16 + volatile unsigned Terminate : 1; // @24 + volatile enum { + InOut = 0, + ZeroLengthOut = 1, + PingCompleteSplit = 2, + ChannelHalt = 3, + } TokenType : 2; // @25 + volatile unsigned Channel : 4; // @27 + volatile unsigned Odd : 1; // @31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + +/*--------------------------------------------------------------------------} +{ FIFOSIZE STRUCTURE .. THERE ARE A FEW OF THESE ON DESIGNWARE 2.0 } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) FifoSize { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile unsigned StartAddress : 16; // @0 + volatile unsigned Depth : 16; // @16 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + + */ + +use register::mmio::{ReadWrite, ReadOnly}; + +#[allow(non_snake_case)] +#[repr(C)] +pub struct FifoSize { + pub START_ADDR: ReadWrite, + pub DEPTH: ReadWrite, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct NonPeriodicFifoSize { + pub SIZE: FifoSize, + pub STATUS: ReadOnly, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct PeriodicFifoSize { + pub HOST_SIZE: FifoSize, + pub DATA_SIZE: [FifoSize; 15], +} + diff --git a/usb/src/core/core_reset.rs b/usb/src/core/core_reset.rs new file mode 100644 index 0000000..189aa27 --- /dev/null +++ b/usb/src/core/core_reset.rs @@ -0,0 +1,65 @@ +use register::register_bitfields; + +pub enum CoreFifoFlush { + FlushNonPeriodic = 0, + FlushPeriodic1 = 1, + FlushPeriodic2 = 2, + FlushPeriodic3 = 3, + FlushPeriodic4 = 4, + FlushPeriodic5 = 5, + FlushPeriodic6 = 6, + FlushPeriodic7 = 7, + FlushPeriodic8 = 8, + FlushPeriodic9 = 9, + FlushPeriodic10 = 10, + FlushPeriodic11 = 11, + FlushPeriodic12 = 12, + FlushPeriodic13 = 13, + FlushPeriodic14 = 14, + FlushPeriodic15 = 15, + FlushAll = 16, +} + + +/* + +/*--------------------------------------------------------------------------} +{ USB CORE RESET STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) CoreReset { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool CoreSoft : 1; // @0 + volatile bool HclkSoft : 1; // @1 + volatile bool HostFrameCounter : 1; // @2 + volatile bool InTokenQueueFlush : 1; // @3 + volatile bool ReceiveFifoFlush : 1; // @4 + volatile bool TransmitFifoFlush : 1; // @5 + volatile unsigned TransmitFifoFlushNumber : 5; // @6 + volatile unsigned _reserved11_29 : 19; // @11 + volatile bool DmaRequestSignal : 1; // @30 + volatile bool AhbMasterIdle : 1; // @31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + */ + + +register_bitfields! { + u32, + + pub CORE_RESET [ + + AHB_MASTER_IDLE OFFSET(31) NUMBITS(1) [], + + TRANSMIT_FIFO_FLUSH_NUMBER OFFSET(6) NUMBITS(5) [], + + TRANSMIT_FIFO_FLUSH OFFSET(5) NUMBITS(1) [], + RECEIVE_FIFO_FLUSH OFFSET(4) NUMBITS(1) [], + + HARDWARE_CLOCK_SOFT OFFSET(1) NUMBITS(1) [], + CORE_SOFT OFFSET(0) NUMBITS(1) [] + ] + +} \ No newline at end of file diff --git a/usb/src/core/core_usb_cfg.rs b/usb/src/core/core_usb_cfg.rs new file mode 100644 index 0000000..a1238b4 --- /dev/null +++ b/usb/src/core/core_usb_cfg.rs @@ -0,0 +1,75 @@ +use register::register_bitfields; + +/* + +/*--------------------------------------------------------------------------} +{ USB CORE CONTROL STRUCTURE .. CARE WRITE WHOLE REGISTER .. NO BIT OPS } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) UsbControl { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile unsigned toutcal : 3; // @0 + volatile bool PhyInterface : 1; // @3 + volatile enum UMode { + ULPI, + UTMI, + } ModeSelect : 1; // @4 + volatile bool fsintf : 1; // @5 + volatile bool physel : 1; // @6 + volatile bool ddrsel : 1; // @7 + volatile bool SrpCapable : 1; // @8 + volatile bool HnpCapable : 1; // @9 + volatile unsigned usbtrdtim : 4; // @10 + volatile unsigned reserved1 : 1; // @14 + volatile bool phy_lpm_clk_sel : 1; // @15 + volatile bool otgutmifssel : 1; // @16 + volatile bool UlpiFsls : 1; // @17 + volatile bool ulpi_auto_res : 1; // @18 + volatile bool ulpi_clk_sus_m : 1; // @19 + volatile bool UlpiDriveExternalVbus : 1; // @20 + volatile bool ulpi_int_vbus_indicator : 1; // @21 + volatile bool TsDlinePulseEnable : 1; // @22 + volatile bool indicator_complement : 1; // @23 + volatile bool indicator_pass_through : 1; // @24 + volatile bool ulpi_int_prot_dis : 1; // @25 + volatile bool ic_usb_capable : 1; // @26 + volatile bool ic_traffic_pull_remove : 1; // @27 + volatile bool tx_end_delay : 1; // @28 + volatile bool force_host_mode : 1; // @29 + volatile bool force_dev_mode : 1; // @30 + volatile unsigned _reserved31 : 1; // @31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + + */ + + + +register_bitfields! { + u32, + + pub USB_CONFIG [ + + TS_DLINE_PULSE_ENABLE OFFSET(22) NUMBITS(1) [], + ULPI_DRIVE_EXTERNAL_VUS OFFSET(20) NUMBITS(1) [], + + ULPI_CLK_SUSP_M OFFSET(19) NUMBITS(1) [], + ULPI_FSLS OFFSET(17) NUMBITS(1) [], + + HNP_CAPABLE OFFSET(9) NUMBITS(1) [], + SRP_CAPABLE OFFSET(8) NUMBITS(1) [], + + MODE_SELECT OFFSET(4) NUMBITS(1) [ + ULPI = 0b1, + UTMI = 0b0 + ], + + PHYSICAL_INTERFACE OFFSET(3) NUMBITS(1) [ + Width8bit = 0b0, + Width16bit = 0b1 + ] + ] + +} diff --git a/usb/src/host.rs b/usb/src/host.rs new file mode 100644 index 0000000..bcba041 --- /dev/null +++ b/usb/src/host.rs @@ -0,0 +1,78 @@ +pub mod config; +pub mod channel; +mod port; + +use core::ops; + +use mmio::mbox; +use register::mmio::{ReadWrite, ReadOnly}; +use register::FieldValue; +use crate::host::channel::HostChannel; +use crate::host::port::{HOST_PORT_MASK, HOST_PORT}; +use crate::host::config::HOST_CONFIG; +use mmio::timer::SystemTimer; +use mmio::{debug, debugln}; + + +/* + +/*--------------------------------------------------------------------------} +{ DWC USB HOST REGISTER POINTERS } +{--------------------------------------------------------------------------*/ +#define DWC_HOST_CONFIG ((volatile __attribute__((aligned(4))) struct HostConfig*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x400)) +#define DWC_HOST_FRAMEINTERVAL ((volatile __attribute__((aligned(4))) struct HostFrameInterval*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x404)) +#define DWC_HOST_FRAMECONTROL ((volatile __attribute__((aligned(4))) struct HostFrameControl*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x408)) +#define DWC_HOST_FIFOSTATUS ((volatile __attribute__((aligned(4))) struct HostFifoStatus*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x410)) +#define DWC_HOST_INTERRUPT ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x414)) +#define DWC_HOST_INTERRUPTMASK ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x418)) +#define DWC_HOST_FRAMELIST ((volatile __attribute__((aligned(4))) uint32_t*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x41C)) +#define DWC_HOST_PORT ((volatile __attribute__((aligned(4))) struct HostPort*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x440)) +#define DWC_HOST_CHANNEL ((volatile __attribute__((aligned(4))) struct HostChannel*)(uintptr_t)(RPi_IO_Base_Addr + USB_CORE_OFFSET + 0x500)) + + */ + + +#[allow(non_snake_case)] +#[repr(C)] +pub struct UsbHostRegisterBlock { + pub CONFIG: ReadWrite, + // 0x400 + pub __reserved1: [u64; 7], + // 0x404 + pub PORT: ReadWrite, + // 0x440 + pub __reserved2: [u64; 23], + pub CHANNELS: [HostChannel; 8], // 0x500 // TODO : replace hardcoded value (found in my own raspi ...) +} + +pub struct HostDeviceController { + base_addr: usize, + timer: &'static SystemTimer, +} + + +impl ops::Deref for HostDeviceController { + type Target = UsbHostRegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl HostDeviceController { + pub const fn new(base_addr: usize, timer: &'static SystemTimer) -> Self { + HostDeviceController { + base_addr, + timer, + } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const UsbHostRegisterBlock { + self.base_addr as *const _ + } + + pub(crate) fn init(&self) -> Result<(), &'static str> { + Ok(()) + } +} \ No newline at end of file diff --git a/usb/src/host/channel.rs b/usb/src/host/channel.rs new file mode 100644 index 0000000..f24c6c0 --- /dev/null +++ b/usb/src/host/channel.rs @@ -0,0 +1,344 @@ +mod interrupts; +mod characteristic; +pub mod transfer_size; +mod split; + +use register::{register_bitfields, LocalRegisterCopy}; +use register::mmio::ReadWrite; +use core::convert::TryInto; +use mmio::macros::_debug; +use crate::host::channel::HostChannelError::{RecoverableError, Timeout, FatalError}; +use crate::host::channel::HostChannelAction::{Success, ResendSplit}; +use crate::host::channel::characteristic::HOST_CHANNEL_CHARACTERISTIC; +use crate::host::channel::split::HOST_SPLIT_CONTROL; +use crate::host::channel::interrupts::CHANNEL_INTERRUPTS; +use crate::host::channel::transfer_size::CHANNEL_TRANSFER_SIZE; +use mmio::timer::SystemTimer; +use core::time::Duration; + +/* +/*--------------------------------------------------------------------------} +{ USB HOST CHANNEL STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) HostChannel { + volatile __attribute__((aligned(4))) struct HostChannelCharacteristic Characteristic; // +0x0 + volatile __attribute__((aligned(4))) struct HostChannelSplitControl SplitCtrl; // +0x4 + volatile __attribute__((aligned(4))) struct ChannelInterrupts Interrupt; // +0x8 + volatile __attribute__((aligned(4))) struct ChannelInterrupts InterruptMask; // +0xc + volatile __attribute__((aligned(4))) struct HostTransferSize TransferSize; // +0x10 + volatile __attribute__((aligned(4))) uint32_t DmaAddr; // +0x14 + volatile __attribute__((aligned(4))) uint32_t _reserved18; // +0x18 + volatile __attribute__((aligned(4))) uint32_t _reserved1c; // +0x1c +}; + + */ +#[derive(Debug)] +pub enum HostChannelError { + FatalError, + RecoverableError, + Timeout +} +#[derive(Debug)] +pub enum HostChannelAction { + Success, + ResendSplit, +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct HostChannel { + pub CHARACTERISTIC: ReadWrite, + pub SPLIT_CONTROL: ReadWrite, + pub INTERRUPT: ReadWrite, + pub INTERRUPT_MASK: ReadWrite, + pub TRANSFER_SIZE: ReadWrite, + pub DMA_ADDR: ReadWrite, +} + +impl HostChannel { + pub fn reset(&self, timer: &'static SystemTimer) -> Result<(), &'static str> { + self.CHARACTERISTIC + .write(HOST_CHANNEL_CHARACTERISTIC::CHANNEL_ENABLED::CLEAR + + HOST_CHANNEL_CHARACTERISTIC::CHANNEL_DISABLED::SET + + HOST_CHANNEL_CHARACTERISTIC::ENDPOINT_DIRECTION::IN + ); + self.CHARACTERISTIC + .write(HOST_CHANNEL_CHARACTERISTIC::CHANNEL_ENABLED::SET + + HOST_CHANNEL_CHARACTERISTIC::CHANNEL_DISABLED::SET + + HOST_CHANNEL_CHARACTERISTIC::ENDPOINT_DIRECTION::IN + ); + + let mut tick = timer.tick_count(); + while self.CHARACTERISTIC + .matches_all(HOST_CHANNEL_CHARACTERISTIC::CHANNEL_ENABLED::SET) { + if timer.tick_count() - tick > Duration::from_micros(100000) { + return Err("HCD: Unable to clear halt on channel"); + } + } + Ok(()) + } + + fn clear_irq(&self) { + self.INTERRUPT.set(0xFFFFFFFF); + self.INTERRUPT_MASK.set(0); + } + + fn wait_transmission(&self, timeout: Duration, timer: &'static SystemTimer) -> Result, &'static str> { + timer.wait(Duration::from_millis(100)); + + let tick = timer.tick_count(); + while self.INTERRUPT.matches_all(CHANNEL_INTERRUPTS::HALT::CLEAR) { + if timer.tick_count() - tick > timeout { + return Err("USB Channel Transmission Failed"); + } + } + Ok(self.INTERRUPT.extract()) + } + + pub fn handle_error(&self, irqs: &LocalRegisterCopy) -> Result { + if irqs.matches_any(CHANNEL_INTERRUPTS::AHB_ERROR::SET + + CHANNEL_INTERRUPTS::DATA_TOGGLE_ERROR::SET) { + return Err(FatalError); + } + + if irqs.matches_all(CHANNEL_INTERRUPTS::ACKNOWLEDGEMENT::SET) { + return Ok(if irqs.matches_all(CHANNEL_INTERRUPTS::TRANSFERT_COMPLETE::SET) { Success } else { ResendSplit }); + } + /* Check no transmission errors and if so deal with minor cases */ + if irqs.matches_all( CHANNEL_INTERRUPTS::STALL::CLEAR + CHANNEL_INTERRUPTS::BABBLE_ERROR::CLEAR + CHANNEL_INTERRUPTS::FRAME_OVERRUN::CLEAR ) { + if irqs.matches_any( CHANNEL_INTERRUPTS::NEGATIVE_ACK::SET + CHANNEL_INTERRUPTS::NOT_YET_READY::SET) { + return Err(RecoverableError) + } else { + return Err(Timeout) + } + } + + Err(FatalError) + + /* + +/*==========================================================================} +{ INTERNAL HOST TRANSMISSION ROUTINES } +{==========================================================================*/ + +/*-INTERNAL: HCDCheckErrorAndAction ----------------------------------------- + Given a channel interrupt flags and whether packet was complete (not split) + it will set sendControl structure with what to do next. + 24Feb17 LdB + --------------------------------------------------------------------------*/ +RESULT HCDCheckErrorAndAction(struct ChannelInterrupts interrupts, bool packetSplit, struct UsbSendControl* sendCtrl) { + sendCtrl->ActionResendSplit = false; // Make sure resend split flag is cleared + sendCtrl->ActionRetry = false; // Make sure retry flag is cleared + /* First deal with all the fatal errors .. no use dealing with trivial errors if these are set */ + if (interrupts.AhbError) { // Ahb error signalled .. which means packet size too large + sendCtrl->ActionFatalError = true; // This is a fatal error the packet size is all wrong + return ErrorDevice; // Return error device + } + if (interrupts.DataToggleError) { // In bulk tranmission endpoint is supposed to toggle between data0/data1 + sendCtrl->ActionFatalError = true; // Pretty sure this is a fatal error you can't fix it by resending + return ErrorTransmission; // Transmission error + } + /* Next deal with the fully successful case ... we can return OK */ + if (interrupts.Acknowledgement) { // Endpoint device acknowledge + if (interrupts.TransferComplete) sendCtrl->Success = true; // You can set the success flag + else sendCtrl->ActionResendSplit = true; // Action is to try sending split again + sendCtrl->GlobalTries = 0; + return OK; // Return OK result + } + /* Everything else is minor error invoking a retry .. so first update counts */ + if (packetSplit) { + sendCtrl->SplitTries++; // Increment split tries as we have a split packet + if (sendCtrl->SplitTries == 5) { // Ridiculous number of split resends reached .. fatal error + sendCtrl->ActionFatalError = true; // This is a fatal error something is very wrong + return ErrorTransmission; // Transmission error + } + sendCtrl->ActionResendSplit = true; // Action is to try sending split again + } else { + sendCtrl->PacketTries++; // Increment packet tries as packet was not split + if (sendCtrl->PacketTries == 3) { // Ridiculous number of packet resends reached .. fatal error + sendCtrl->ActionFatalError = true; // This is a fatal error something is very wrong + return ErrorTransmission; // Transmission error + } + sendCtrl->ActionRetry = true; // Action is to try sending the packet again + } + /* Check no transmission errors and if so deal with minor cases */ + if (!interrupts.Stall && !interrupts.BabbleError && + !interrupts.FrameOverrun) { // No transmission error + /* If endpoint NAK nothing wrong just demanding a retry */ + if (interrupts.NegativeAcknowledgement) // Endpoint device NAK ..nothing wrong + return ErrorTransmission; // Simple tranmission error .. resend + /* Next deal with device not ready case */ + if (interrupts.NotYet) + return ErrorTransmission; // Endpoint device not yet ... resend + return ErrorTimeout; // Let guess program just timed out + } + /* Everything else updates global count as it is serious */ + sendCtrl->GlobalTries++; // Increment global tries + /* If global tries reaches 3 .. its a fatal error */ + if (sendCtrl->GlobalTries == 3) { // Global tries has reached 3 + sendCtrl->ActionRetry = false; // Clear retry action flag .. it's fatal + sendCtrl->ActionResendSplit = false; // Clear retyr sending split again .. it's fatal + sendCtrl->ActionFatalError = true; // This is a fatal error to many global errors + return ErrorTransmission; // Transmission error + } + /* Deal with stall */ + if (interrupts.Stall) { // Stall signalled .. device endpoint problem + return ErrorStall; // Return the stall error + } + /* Deal with true transmission errors */ + if ((interrupts.BabbleError) || // Bable error is a packet transmission problem + (interrupts.FrameOverrun) || // Frame overrun error means stop bit failed at packet end + (interrupts.TransactionError)) + { + return ErrorTransmission; // Transmission error + } + return ErrorGeneral; // If we get here then no idea why error occured (probably program error) +} + */ + } +/* + pub fn send_receive_buffer(&self, timer: &'static SystemTimer, pipe: &Pipe, buffer: &[u8], packet_id: u32) -> Result<(), &'static str> { + + debugln!("INTERRUPT register {:b}", self.INTERRUPT.get()); + /* Clear all existing interrupts. */ + self.clear_irq(); + + debugln!("INTERRUPT register {:b}", self.INTERRUPT.get()); + + let max_packet_size = pipe.size(); + let low_speed = pipe.o.matches_all(PIPE::SPEED::Low); + + /* Program the channel. */ + self.CHARACTERISTIC.write(HOST_CHANNEL_CHARACTERISTIC::DEVICE_ADDRESS.val(pipe.o.read(PIPE::NUMBER)) + + HOST_CHANNEL_CHARACTERISTIC::ENDPOINT_NUMBER.val(pipe.o.read(PIPE::ENDPOINT)) + + HOST_CHANNEL_CHARACTERISTIC::ENDPOINT_DIRECTION.val(pipe.c.read((PIPE_CONTROL::DIRECTION))) + + if low_speed { HOST_CHANNEL_CHARACTERISTIC::LOW_SPEED::SET } else { HOST_CHANNEL_CHARACTERISTIC::LOW_SPEED::CLEAR } + + HOST_CHANNEL_CHARACTERISTIC::ENDPOINT_TYPE.val(pipe.c.read(PIPE_CONTROL::TRANSFER_TYPE)) + + HOST_CHANNEL_CHARACTERISTIC::MAX_PACKET_SIZE.val(max_packet_size) + + HOST_CHANNEL_CHARACTERISTIC::CHANNEL_ENABLED::CLEAR + + HOST_CHANNEL_CHARACTERISTIC::CHANNEL_DISABLED::CLEAR); + + /* Clear and setup split control to low speed devices */ + if !pipe.o.matches_all(PIPE::SPEED::High) { + let low_speed_point = pipe.o.read(PIPE::LOW_SPEED_NODE_POINT); + let low_speed_port = pipe.o.read(PIPE::LOW_SPEED_NODE_PORT); + debugln!("Setting split control, addr: {} port: {}, packetSize: PacketSize: {}", + low_speed_point, low_speed_port, max_packet_size); + self.SPLIT_CONTROL.write(HOST_SPLIT_CONTROL::SPLIT_ENABLE::SET + + HOST_SPLIT_CONTROL::HUB_ADDRESS.val(low_speed_point) + + HOST_SPLIT_CONTROL::PORT_ADDRESS.val(low_speed_port)); + } + + /* Set transfer size. */ + let buffer_length = buffer.len() as u32; + let packet_count = if pipe.o.matches_all(PIPE::SPEED::Low) + { (buffer_length + 7) / 8 } else { (buffer_length + max_packet_size - 1) / max_packet_size }; + + self.TRANSFER_SIZE.write(CHANNEL_TRANSFER_SIZE::SIZE.val(buffer_length) + + CHANNEL_TRANSFER_SIZE::PACKET_COUNT.val(if packet_count > 0 { packet_count } else { 1 }) + + CHANNEL_TRANSFER_SIZE::PACKET_ID.val(packet_id)); + + loop { + // Clear any left over channel interrupts + self.clear_irq(); + // Clear any left over split + self.SPLIT_CONTROL.write(HOST_SPLIT_CONTROL::COMPLETE_SPLIT::CLEAR); + + // set buffer addr (4 byte aligned and with physical addr rather than ARM addr) + // TODO : create macro rather than oring the buffer + self.DMA_ADDR.set(buffer.as_ptr() as u32 | 0xC0000000); + + debugln!("INTERRUPT register {:b}", self.INTERRUPT.get()); + + self.CHARACTERISTIC.write(HOST_CHANNEL_CHARACTERISTIC::PACKETS_PER_FRAME.val(1) + + HOST_CHANNEL_CHARACTERISTIC::CHANNEL_ENABLED::SET + + HOST_CHANNEL_CHARACTERISTIC::CHANNEL_DISABLED::CLEAR); + + let irq_register = self.wait_transmission(5000, timer)?; + + debugln!(" IRQ register after sending a message {:b} {:x} = {:?}", irq_register.get(), irq_register.get(), self.handle_error(&irq_register)); + debugln!("Transfert Size after write {}", self.TRANSFER_SIZE.read(CHANNEL_TRANSFER_SIZE::SIZE)); + debugln!("characteristic : {:x}", self.CHARACTERISTIC.get()); + + if self.TRANSFER_SIZE.read(CHANNEL_TRANSFER_SIZE::PACKET_COUNT) == 0 { + break; + } + break; + } + /* + sendCtrl.PacketTries = 0; // Zero packet tries + do { + + + + // Polling wait on transmission only option right now .. other options soon :-) + if (HCDWaitOnTransmissionResult(5000, pipectrl.Channel, &tempInt) != OK) { + LOG("HCD: Request on channel %i has timed out.\n", pipectrl.Channel);// Log the error + return ErrorTimeout; // Return timeout error + } + + tempSplit = DWC_HOST_CHANNEL[pipectrl.Channel].SplitCtrl; // Fetch the split details + result = HCDCheckErrorAndAction(tempInt, + tempSplit.split_enable, &sendCtrl); // Check transmisson RESULT and set action flags + if (result) LOG("Result: %i Action: 0x%08x tempInt: 0x%08x tempSplit: 0x%08x Bytes sent: %i\n", + result, (unsigned int)sendCtrl.Raw32, (unsigned int)tempInt.Raw32, + (unsigned int)tempSplit.Raw32, result ? 0 : DWC_HOST_CHANNEL[pipectrl.Channel].TransferSize.size); + if (sendCtrl.ActionFatalError) return result; // Fatal error occured we need to bail + + sendCtrl.SplitTries = 0; // Zero split tries count + while (sendCtrl.ActionResendSplit) { // Decision was made to resend split + /* Clear channel interrupts */ + DWC_HOST_CHANNEL[pipectrl.Channel].Interrupt.Raw32 = 0xFFFFFFFF; + DWC_HOST_CHANNEL[pipectrl.Channel].InterruptMask.Raw32 = 0x0; + + /* Set we are completing the split */ + tempSplit = DWC_HOST_CHANNEL[pipectrl.Channel].SplitCtrl; + tempSplit.complete_split = true; // Set complete split flag + DWC_HOST_CHANNEL[pipectrl.Channel].SplitCtrl = tempSplit; + + /* Launch transmission */ + tempChar = DWC_HOST_CHANNEL[pipectrl.Channel].Characteristic; + tempChar.channel_enable = true; + tempChar.channel_disable = false; + DWC_HOST_CHANNEL[pipectrl.Channel].Characteristic = tempChar; + + // Polling wait on transmission only option right now .. other options soon :-) + if (HCDWaitOnTransmissionResult(5000, pipectrl.Channel, &tempInt) != OK) { + LOG("HCD: Request split completion on channel:%i has timed out.\n", pipectrl.Channel);// Log error + return ErrorTimeout; // Return timeout error + } + + tempSplit = DWC_HOST_CHANNEL[pipectrl.Channel].SplitCtrl;// Fetch the split details again + result = HCDCheckErrorAndAction(tempInt, + tempSplit.split_enable, &sendCtrl); // Check RESULT of split resend and set action flags + //if (result) LOG("Result: %i Action: 0x%08lx tempInt: 0x%08lx tempSplit: 0x%08lx Bytes sent: %i\n", + // result, sendCtrl.RawUsbSendContol, tempInt.RawInterrupt, tempSplit.RawSplitControl, RESULT ? 0 : DWC_HOST_CHANNEL[pipectrl.Channel].TransferSize.TransferSize); + if (sendCtrl.ActionFatalError) return result; // Fatal error occured bail + if (sendCtrl.LongerDelay) timer_wait(10000); // Not yet response slower delay + else timer_wait(2500); // Small delay between split resends + } + + if (sendCtrl.Success) { // Send successful adjust buffer position + unsigned int this_transfer; + this_transfer = DWC_HOST_CHANNEL[pipectrl.Channel].TransferSize.size; + + if (((uint32_t)(intptr_t)&buffer[offset] & 3) != 0) { // Buffer address is unaligned + + // Since our buffer is unaligned for IN endpoints + // Copy the data from the the aligned buffer to the buffer + // We know the aligned buffer was used because it is unaligned + if (pipectrl.Direction == USB_DIRECTION_IN) + { + memcpy(&buffer[offset], aligned_bufs[pipectrl.Channel], this_transfer); + } + } + + offset = bufferLength - this_transfer; + } + + } while (DWC_HOST_CHANNEL[pipectrl.Channel].TransferSize.packet_count > 0);// Full data not sent + */ + + Ok(()) + }*/ +} \ No newline at end of file diff --git a/usb/src/host/channel/characteristic.rs b/usb/src/host/channel/characteristic.rs new file mode 100644 index 0000000..f367278 --- /dev/null +++ b/usb/src/host/channel/characteristic.rs @@ -0,0 +1,45 @@ +use register::register_bitfields; +/* + +/*--------------------------------------------------------------------------} +{ USB HOST CHANNEL CHARACTERISTIC STRUCTURE } +{--------------------------------------------------------------------------*/ +struct HostChannelCharacteristic { + union { + struct { + unsigned max_packet_size : 11; // @0-10 Maximum packet size the endpoint is capable of sending or receiving + unsigned endpoint_number : 4; // @11-14 Endpoint number (low 4 bits of bEndpointAddress) + unsigned endpoint_direction : 1; // @15 Endpoint direction 1=IN, 0=OUT + unsigned _reserved : 1; // @16 + unsigned low_speed : 1; // @17 1 when the device being communicated with is at low speed, 0 otherwise + unsigned endpoint_type : 2; // @18-19 Endpoint type (low 2 bits of bmAttributes) + unsigned packets_per_frame : 2; // @20-21 Maximum number of transactions that can be executed per microframe + unsigned device_address : 7; // @22-28 USB device address of the device on which the endpoint is located + unsigned odd_frame : 1; // @29 Before enabling channel must be set to opposite of low bit of host_frame_number + unsigned channel_disable : 1; // @30 Software can set this to 1 to halt the channel + unsigned channel_enable : 1; // @31 Software can set this to 1 to enable the channel + } __packed; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +} __packed; + */ + +register_bitfields! { + u32, + + pub HOST_CHANNEL_CHARACTERISTIC [ + + CHANNEL_ENABLED OFFSET(31) NUMBITS(1) [], + CHANNEL_DISABLED OFFSET(30) NUMBITS(1) [], + DEVICE_ADDRESS OFFSET(22) NUMBITS(7) [], + PACKETS_PER_FRAME OFFSET(20) NUMBITS(2) [], + ENDPOINT_TYPE OFFSET(18) NUMBITS(2) [], + LOW_SPEED OFFSET(17) NUMBITS(1) [], + ENDPOINT_DIRECTION OFFSET(15) NUMBITS(1) [ + OUT = 0b0, + IN = 0b1 + ], + ENDPOINT_NUMBER OFFSET(11) NUMBITS(4) [], + MAX_PACKET_SIZE OFFSET(0) NUMBITS(11) [] + ] +} diff --git a/usb/src/host/channel/interrupts.rs b/usb/src/host/channel/interrupts.rs new file mode 100644 index 0000000..b0ec3da --- /dev/null +++ b/usb/src/host/channel/interrupts.rs @@ -0,0 +1,49 @@ +use register::register_bitfields; +/* + +/*--------------------------------------------------------------------------} +{ INTERRUPT BITS ON THE USB CHANNELS ON THE DESIGNWARE 2.0 } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) ChannelInterrupts { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool TransferComplete : 1; // @0 + volatile bool Halt : 1; // @1 + volatile bool AhbError : 1; // @2 + volatile bool Stall : 1; // @3 + volatile bool NegativeAcknowledgement : 1; // @4 + volatile bool Acknowledgement : 1; // @5 + volatile bool NotYet : 1; // @6 + volatile bool TransactionError : 1; // @7 + volatile bool BabbleError : 1; // @8 + volatile bool FrameOverrun : 1; // @9 + volatile bool DataToggleError : 1; // @10 + volatile bool BufferNotAvailable : 1; // @11 + volatile bool ExcessiveTransmission : 1; // @12 + volatile bool FrameListRollover : 1; // @13 + unsigned _reserved14_31 : 18; // @14-31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; +*/ + +register_bitfields! { + u32, + + pub CHANNEL_INTERRUPTS [ + + DATA_TOGGLE_ERROR OFFSET(10) NUMBITS(1) [], + + FRAME_OVERRUN OFFSET(9) NUMBITS(1) [], + BABBLE_ERROR OFFSET(8) NUMBITS(1) [], + NOT_YET_READY OFFSET(6) NUMBITS(1) [], + ACKNOWLEDGEMENT OFFSET(5) NUMBITS(1) [], + NEGATIVE_ACK OFFSET(4) NUMBITS(1) [], + STALL OFFSET(3) NUMBITS(1) [], + AHB_ERROR OFFSET(2) NUMBITS(1) [], + HALT OFFSET(1) NUMBITS(1) [], + + TRANSFERT_COMPLETE OFFSET(0) NUMBITS(1) [] + ] +} diff --git a/usb/src/host/channel/split.rs b/usb/src/host/channel/split.rs new file mode 100644 index 0000000..af96895 --- /dev/null +++ b/usb/src/host/channel/split.rs @@ -0,0 +1,39 @@ +use register::register_bitfields; + +/* + +/*--------------------------------------------------------------------------} +{ USB HOST CHANNEL SPLIT CONTROL STRUCTURE } +{--------------------------------------------------------------------------*/ +struct HostChannelSplitControl { + union { + struct { + unsigned port_address : 7; // @0-6 0-based index of the port on the high-speed hub Transaction Translator occurs + unsigned hub_address : 7; // @7-13 USB device address of the high-speed hub that acts as Transaction Translator + unsigned transaction_position : 2; // @14-15 If we are processing split the transation position Begin=2,End=1,Middle=0,All=3 + unsigned complete_split : 1; // @16 1 to complete a Split transaction, 0 = normal transaction + unsigned _reserved : 14; // @17-30 + unsigned split_enable : 1; // @31 Set to 1 to enable Split Transactions + } __packed; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +} __packed; + + */ + + +register_bitfields! { + u32, + + pub HOST_SPLIT_CONTROL [ + + SPLIT_ENABLE OFFSET(31) NUMBITS(1) [], + + TRANSITION_COMPLETE OFFSET(14) NUMBITS(2) [], + + COMPLETE_SPLIT OFFSET(16) NUMBITS(1) [], + + HUB_ADDRESS OFFSET(7) NUMBITS(7) [], + PORT_ADDRESS OFFSET(0) NUMBITS(7) [] + ] +} diff --git a/usb/src/host/channel/transfer_size.rs b/usb/src/host/channel/transfer_size.rs new file mode 100644 index 0000000..16d638b --- /dev/null +++ b/usb/src/host/channel/transfer_size.rs @@ -0,0 +1,48 @@ +use register::register_bitfields; + +/* +/*--------------------------------------------------------------------------} +{ USB HOST CHANNEL TRANSFER SIZE STRUCTURE } +{--------------------------------------------------------------------------*/ +struct HostTransferSize { + union { + struct { + unsigned size : 19; // @0-18 Size of data to send or receive, in bytes and can be greater than maximum packet length + unsigned packet_count : 10; // @19-28 Number of packets left to transmit or maximum number of packets left to receive + enum PacketId { + USB_PID_DATA0 = 0, + USB_PID_DATA1 = 2, + USB_PID_DATA2 = 1, + USB_PID_SETUP = 3, + USB_MDATA = 3, + } packet_id : 2; // @29 Various packet phase ID + unsigned do_ping : 1; // @31 + } __packed; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t +}; +} __packed; + +*/ + + +register_bitfields! { + u32, + + pub CHANNEL_TRANSFER_SIZE [ + + SIZE OFFSET(0) NUMBITS(19) [], + + PACKET_COUNT OFFSET(19) NUMBITS(10) [], + + PACKET_ID OFFSET(29) NUMBITS(2) [ + Usb_Pid_Data0 = 0b00, + Usb_Pid_Data1 = 0b10, + Usb_Pid_Data2 = 0b01, + Usb_Pid_Setup = 0b11 + //Usb_Mdata = 0b11 + ], + + DO_PING OFFSET(31) NUMBITS(1) [] + + ] +} diff --git a/usb/src/host/config.rs b/usb/src/host/config.rs new file mode 100644 index 0000000..fd8fcbd --- /dev/null +++ b/usb/src/host/config.rs @@ -0,0 +1,45 @@ +use register::register_bitfields; + +/* + + +/*--------------------------------------------------------------------------} +{ USB HOST CONFIG STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) HostConfig { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile unsigned ClockRate : 2; // @0 + volatile bool FslsOnly : 1; // @2 + volatile unsigned _reserved3_6 : 4; // @3 + volatile unsigned en_32khz_susp : 1; // @7 + volatile unsigned res_val_period : 8; // @8 + volatile unsigned _reserved16_22 : 7; // @16 + volatile bool EnableDmaDescriptor : 1; // @23 + volatile unsigned FrameListEntries : 2; // @24 + volatile bool PeriodicScheduleEnable : 1; // @26 + volatile bool PeriodicScheduleStatus : 1; // @27 + volatile unsigned reserved28_30 : 3; // @28 + volatile bool mode_chg_time : 1; // @31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + */ + +register_bitfields! { + u32, + + pub HOST_CONFIG [ + + ENABLE_DMA_DESCRIPTOR OFFSET(23) NUMBITS(1) [], + + FSLS_ONLY OFFSET(2) NUMBITS(1) [], + + CLOCK_RATE OFFSET(0) NUMBITS(2) [ + Clock30_60MHz = 0b00, + Clock48MHz = 0b01, + Clock6MHz = 0b10 + ] + ] +} \ No newline at end of file diff --git a/usb/src/host/port.rs b/usb/src/host/port.rs new file mode 100644 index 0000000..cb54b80 --- /dev/null +++ b/usb/src/host/port.rs @@ -0,0 +1,67 @@ +use register::{register_bitfields, FieldValue}; +/* + +/*--------------------------------------------------------------------------} +{ USB HOST PORT STRUCTURE } +{--------------------------------------------------------------------------*/ +/* Due to the inconsistent design of the bits in this register, sometime it requires zeroing + bits in the register before the write, so you do not unintentionally write 1's to them. */ +#define HOSTPORTMASK ~0x2E // These are the funky bits on this register and we "NOT" them to make "AND" mask +struct __attribute__((__packed__, aligned(4))) HostPort { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool Connect : 1; // @0 + volatile bool ConnectChanged : 1; // @1 + volatile bool Enable : 1; // @2 + volatile bool EnableChanged : 1; // @3 + volatile bool OverCurrent : 1; // @4 + volatile bool OverCurrentChanged : 1; // @5 + volatile bool Resume : 1; // @6 + volatile bool Suspend : 1; // @7 + volatile bool Reset : 1; // @8 + volatile unsigned _reserved9 : 1; // @9 + volatile unsigned PortLineStatus : 2; // @10 + volatile bool Power : 1; // @12 + volatile unsigned TestControl : 4; // @13 + volatile UsbSpeed Speed : 2; // @17 + volatile unsigned _reserved19_31 : 13; // @19-31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + + /* #define DWHCI_HOST_PORT_DEFAULT_MASK ( DWHCI_HOST_PORT_CONNECT_CHANGED \ + | DWHCI_HOST_PORT_ENABLE \ + | DWHCI_HOST_PORT_ENABLE_CHANGED \ + | DWHCI_HOST_PORT_OVERCURRENT_CHANGED) */ + */ + +pub const HOST_PORT_MASK : u32 = !0x2E; + +register_bitfields! { + u32, + + pub HOST_PORT [ + + SPEED OFFSET(17) NUMBITS(2) [ + High = 0b00, + Full = 0b01, + Low = 0b10 + ], + + POWER OFFSET(12) NUMBITS(1) [], + + RESET OFFSET(8) NUMBITS(1) [], + + OVERCURRENT_CHANGED OFFSET(5) NUMBITS(1) [], + + ENABLED_CHANGED OFFSET(3) NUMBITS(1) [], + + ENABLED OFFSET(2) NUMBITS(1) [], + + CONNECT_CHANGED OFFSET(1) NUMBITS(1) [], + + CONNECT OFFSET(0) NUMBITS(1) [] + ] +} + diff --git a/usb/src/lib.rs b/usb/src/lib.rs new file mode 100644 index 0000000..230b8ed --- /dev/null +++ b/usb/src/lib.rs @@ -0,0 +1,23 @@ +#![no_std] + +#[macro_use] extern crate mmio; + +pub use bus::UsbBus; +use mmio::timer::SystemTimer; +use ::core::time::Duration; + +mod bus; +pub mod host; +pub mod core; +pub mod power; + +fn wait_for(timer: &'static SystemTimer, check: F, timeout : Duration) + -> Result<(), &'static str> where F: Fn() -> bool { + let mut tick = timer.tick_count(); + while !check() { + if timer.tick_count() - tick > timeout { + return Err("poll error"); + } + } + Ok(()) +} \ No newline at end of file diff --git a/usb/src/power.rs b/usb/src/power.rs new file mode 100644 index 0000000..a64a528 --- /dev/null +++ b/usb/src/power.rs @@ -0,0 +1,66 @@ +use register::{register_bitfields, InMemoryRegister}; +/* + +/*--------------------------------------------------------------------------} +{ DWC POWER AND CLOCK REGISTER STRUCTURE } +{--------------------------------------------------------------------------*/ +struct __attribute__((__packed__, aligned(4))) PowerReg { + union { + struct __attribute__((__packed__, aligned(1))) { + volatile bool StopPClock : 1; // @0 + volatile bool GateHClock : 1; // @1 + volatile bool PowerClamp : 1; // @2 + volatile bool PowerDownModules : 1; // @3 + volatile bool PhySuspended : 1; // @4 + volatile bool EnableSleepClockGating : 1; // @5 + volatile bool PhySleeping : 1; // @6 + volatile bool DeepSleep : 1; // @7 + volatile unsigned _reserved8_31 : 24; // @8-31 + }; + volatile uint32_t Raw32; // Union to access all 32 bits as a uint32_t + }; +}; + + */ + + +register_bitfields! { + u32, + pub POWER_REGISTER [ + STOP_PCLOCK OFFSET(0) NUMBITS(1) [] + ] +} + +#[allow(non_snake_case)] +#[repr(C)] +pub struct UsbPowerRegisterBlock { + pub pw: ReadWrite, +} + +use core::ops; +use register::mmio::ReadWrite; + +pub struct UsbPower { + base_addr: usize +} + +impl ops::Deref for UsbPower { + type Target = UsbPowerRegisterBlock; + + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr() } + } +} + +impl UsbPower { + pub const fn new(base_addr: usize) -> Self { + UsbPower { + base_addr + } + } + + /// Returns a pointer to the register block + fn ptr(&self) -> *const UsbPowerRegisterBlock { + self.base_addr as *const _ + } +} \ No newline at end of file