mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
convert HAL to be driver-friendly
rewrite so the driver obeys the type bounds necessary for USB stack integration, in particular, Sync requires all raw pointers to be AtomicPtr
This commit is contained in:
parent
8ef46fd081
commit
e9217b7eb6
4 changed files with 388 additions and 127 deletions
|
|
@ -18,11 +18,12 @@ rkyv = { version = "0.4.3", default-features = false, features = [
|
|||
num-derive = { version = "0.3.3", default-features = false }
|
||||
num-traits = { version = "0.2.14", default-features = false }
|
||||
riscv = { version = "0.5.6", package = "xous-riscv" }
|
||||
usb-device = { version = "0.2.8", features = ["log"], optional = true }
|
||||
|
||||
# [target.'cfg(target_os = "xous")'.dependencies]
|
||||
xous = { version = "0.9.61", features = ["v2p"] }
|
||||
|
||||
[features]
|
||||
std = ["log", "xous-api-names"]
|
||||
std = ["log", "xous-api-names", "usb-device"]
|
||||
derive-rkyv = ["rkyv"]
|
||||
default = []
|
||||
|
|
|
|||
84
libs/cramium-hal/src/usb/compat.rs
Normal file
84
libs/cramium-hal/src/usb/compat.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
use utralib::{Field, Register};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct AtomicCsr<T> {
|
||||
base: *mut T,
|
||||
}
|
||||
impl<T> AtomicCsr<T>
|
||||
where
|
||||
T: core::convert::TryFrom<usize> + core::convert::TryInto<usize> + core::default::Default,
|
||||
{
|
||||
pub fn new(base: *mut T) -> Self { AtomicCsr { base } }
|
||||
|
||||
/// Retrieve the raw pointer used as the base of the CSR. This is unsafe because the copied
|
||||
/// value can be used to do all kinds of awful shared mutable operations (like creating
|
||||
/// another CSR accessor owned by another thread). However, sometimes this is unavoidable
|
||||
/// because hardware is in fact shared mutable state.
|
||||
pub unsafe fn base(&self) -> *mut T { self.base }
|
||||
|
||||
/// Read the contents of this register
|
||||
pub fn r(&self, reg: Register) -> T {
|
||||
// prevent re-ordering
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
unsafe { usize_base.add(reg.offset()).read_volatile() }.try_into().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Read a field from this CSR
|
||||
pub fn rf(&self, field: Field) -> T {
|
||||
// prevent re-ordering
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
((unsafe { usize_base.add(field.register().offset()).read_volatile() } >> field.offset())
|
||||
& field.mask())
|
||||
.try_into()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Read-modify-write a given field in this CSR
|
||||
pub fn rmwf(&self, field: Field, value: T) {
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default() << field.offset();
|
||||
let previous = unsafe { usize_base.add(field.register().offset()).read_volatile() }
|
||||
& !(field.mask() << field.offset());
|
||||
unsafe { usize_base.add(field.register().offset()).write_volatile(previous | value_as_usize) };
|
||||
// prevent re-ordering
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Write a given field without reading it first
|
||||
pub fn wfo(&self, field: Field, value: T) {
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
let value_as_usize: usize = (value.try_into().unwrap_or_default() & field.mask()) << field.offset();
|
||||
unsafe { usize_base.add(field.register().offset()).write_volatile(value_as_usize) };
|
||||
// Ensure the compiler doesn't re-order the write.
|
||||
// We use `SeqCst`, because `Acquire` only prevents later accesses from being reordered before
|
||||
// *reads*, but this method only *writes* to the locations.
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Write the entire contents of a register without reading it first
|
||||
pub fn wo(&self, reg: Register, value: T) {
|
||||
let usize_base: *mut usize = unsafe { core::mem::transmute(self.base) };
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
||||
unsafe { usize_base.add(reg.offset()).write_volatile(value_as_usize) };
|
||||
// Ensure the compiler doesn't re-order the write.
|
||||
// We use `SeqCst`, because `Acquire` only prevents later accesses from being reordered before
|
||||
// *reads*, but this method only *writes* to the locations.
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
|
||||
/// Zero a field from a provided value
|
||||
pub fn zf(&self, field: Field, value: T) -> T {
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
||||
(value_as_usize & !(field.mask() << field.offset())).try_into().unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Shift & mask a value to its final field position
|
||||
pub fn ms(&self, field: Field, value: T) -> T {
|
||||
let value_as_usize: usize = value.try_into().unwrap_or_default();
|
||||
((value_as_usize & field.mask()) << field.offset()).try_into().unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,16 @@
|
|||
use core::convert::TryFrom;
|
||||
use core::mem::size_of;
|
||||
use core::sync::atomic::{compiler_fence, Ordering};
|
||||
use core::sync::atomic::{compiler_fence, AtomicPtr, Ordering};
|
||||
|
||||
use utralib::*;
|
||||
#[cfg(feature = "std")]
|
||||
use usb_device::bus::PollResult;
|
||||
#[cfg(feature = "std")]
|
||||
use usb_device::{class_prelude::*, Result, UsbDirection};
|
||||
#[cfg(feature = "std")]
|
||||
use utralib::generated::*;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
use super::compat::AtomicCsr;
|
||||
use crate::usb::utra::*;
|
||||
use crate::{print, println};
|
||||
|
||||
|
|
@ -29,8 +36,9 @@ const CRG_UDC_EP_TRSIZE: usize = CRG_TD_RING_SIZE * CRG_EP_NUM * 16;
|
|||
/// allocate 0x400 bytes for EP0 Buffer, Normally EP0 TRB transfer length will not greater than 1K
|
||||
const CRG_UDC_EP0_REQBUFSIZE: usize = 0x400;
|
||||
|
||||
const CRG_IFRAM_PAGES: usize = 16;
|
||||
const CRG_UDC_MEMBASE: usize = utralib::HW_IFRAM1_MEM + utralib::HW_IFRAM1_MEM_LEN - CRG_IFRAM_PAGES * 0x1000;
|
||||
pub const CRG_IFRAM_PAGES: usize = 16;
|
||||
pub const CRG_UDC_MEMBASE: usize =
|
||||
utralib::HW_IFRAM1_MEM + utralib::HW_IFRAM1_MEM_LEN - CRG_IFRAM_PAGES * 0x1000;
|
||||
|
||||
const CRG_UDC_ERST_OFFSET: usize = 0; // use relative offsets
|
||||
const CRG_UDC_EVENTRING_OFFSET: usize = CRG_UDC_ERST_OFFSET + CRG_UDC_ERSTSIZE;
|
||||
|
|
@ -186,7 +194,7 @@ pub enum TrbType {
|
|||
impl TryFrom<u32> for TrbType {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u32) -> Result<Self, Error> {
|
||||
fn try_from(value: u32) -> core::result::Result<Self, Error> {
|
||||
match value {
|
||||
1 => Ok(TrbType::XferNormal),
|
||||
2 => Ok(TrbType::Rsvd2),
|
||||
|
|
@ -226,7 +234,7 @@ impl TryFrom<u32> for CompletionCode {
|
|||
type Error = Error;
|
||||
|
||||
/// Note that this expects a raw dw2 value - the bit shift is built into this.
|
||||
fn try_from(dw2: u32) -> Result<Self, Error> {
|
||||
fn try_from(dw2: u32) -> core::result::Result<Self, Error> {
|
||||
match (dw2 >> 24) & 0xFF {
|
||||
1 => Ok(CompletionCode::Success),
|
||||
4 => Ok(CompletionCode::UsbTransactionError),
|
||||
|
|
@ -369,12 +377,12 @@ pub struct ErstS {
|
|||
|
||||
/// buffer info data structure
|
||||
pub struct BufferInfo {
|
||||
vaddr: *mut u8,
|
||||
vaddr: AtomicPtr<u8>,
|
||||
dma: u64,
|
||||
len: u32,
|
||||
}
|
||||
impl Default for BufferInfo {
|
||||
fn default() -> Self { Self { vaddr: core::ptr::null_mut(), dma: 0, len: 0 } }
|
||||
fn default() -> Self { Self { vaddr: AtomicPtr::new(core::ptr::null_mut()), dma: 0, len: 0 } }
|
||||
}
|
||||
|
||||
pub struct UdcEp {
|
||||
|
|
@ -385,10 +393,10 @@ pub struct UdcEp {
|
|||
ep_type: u8,
|
||||
max_packet_size: u16,
|
||||
tran_ring_info: BufferInfo,
|
||||
first_trb: *mut TransferTrbS,
|
||||
last_trb: *mut TransferTrbS,
|
||||
enq_pt: *mut TransferTrbS,
|
||||
deq_pt: *mut TransferTrbS,
|
||||
first_trb: AtomicPtr<TransferTrbS>,
|
||||
last_trb: AtomicPtr<TransferTrbS>,
|
||||
enq_pt: AtomicPtr<TransferTrbS>,
|
||||
deq_pt: AtomicPtr<TransferTrbS>,
|
||||
pcs: u8,
|
||||
tran_ring_full: bool,
|
||||
ep_state: EpState,
|
||||
|
|
@ -402,10 +410,10 @@ impl Default for UdcEp {
|
|||
ep_type: 0,
|
||||
max_packet_size: 0,
|
||||
tran_ring_info: BufferInfo::default(),
|
||||
first_trb: core::ptr::null_mut(),
|
||||
last_trb: core::ptr::null_mut(),
|
||||
enq_pt: core::ptr::null_mut(),
|
||||
deq_pt: core::ptr::null_mut(),
|
||||
first_trb: AtomicPtr::new(core::ptr::null_mut()),
|
||||
last_trb: AtomicPtr::new(core::ptr::null_mut()),
|
||||
enq_pt: AtomicPtr::new(core::ptr::null_mut()),
|
||||
deq_pt: AtomicPtr::new(core::ptr::null_mut()),
|
||||
pcs: 0,
|
||||
tran_ring_full: false,
|
||||
ep_state: EpState::Disabled,
|
||||
|
|
@ -417,21 +425,21 @@ impl Default for UdcEp {
|
|||
// Corigine USB device controller event data structure
|
||||
pub struct UdcEvent {
|
||||
erst: BufferInfo,
|
||||
p_erst: *mut ErstS,
|
||||
p_erst: AtomicPtr<ErstS>,
|
||||
event_ring: BufferInfo,
|
||||
evt_dq_pt: *mut EventTrbS,
|
||||
evt_dq_pt: AtomicPtr<EventTrbS>,
|
||||
ccs: u8,
|
||||
evt_seg0_last_trb: *mut EventTrbS,
|
||||
evt_seg0_last_trb: AtomicPtr<EventTrbS>,
|
||||
}
|
||||
impl Default for UdcEvent {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
erst: BufferInfo::default(),
|
||||
p_erst: core::ptr::null_mut(),
|
||||
p_erst: AtomicPtr::new(core::ptr::null_mut()),
|
||||
event_ring: BufferInfo::default(),
|
||||
evt_dq_pt: core::ptr::null_mut(),
|
||||
evt_dq_pt: AtomicPtr::new(core::ptr::null_mut()),
|
||||
ccs: 0,
|
||||
evt_seg0_last_trb: core::ptr::null_mut(),
|
||||
evt_seg0_last_trb: AtomicPtr::new(core::ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -479,20 +487,16 @@ enum UsbDeviceState {
|
|||
}
|
||||
|
||||
pub struct CorigineUsb {
|
||||
#[cfg(feature = "std")]
|
||||
range: xous::MemoryRange,
|
||||
#[cfg(feature = "std")]
|
||||
ifram_range: xous::MemoryRange,
|
||||
ifram_base_ptr: usize,
|
||||
pub csr: CSR<u32>,
|
||||
pub csr: AtomicCsr<u32>,
|
||||
// Because the init routine requires magic pokes
|
||||
magic_page: &'static mut [u32],
|
||||
// Seems necessary for some debug tricks
|
||||
dev_slice: &'static mut [u32],
|
||||
#[cfg(feature = "std")]
|
||||
conn: xous::CID,
|
||||
|
||||
udc_ep: [UdcEp; CRG_EP_NUM + 2],
|
||||
ep_cx: BufferInfo,
|
||||
p_epcx: *mut EpCxS,
|
||||
p_epcx: AtomicPtr<EpCxS>,
|
||||
|
||||
udc_event: [UdcEvent; CRG_EVENT_RING_NUM],
|
||||
|
||||
|
|
@ -513,7 +517,7 @@ pub struct CorigineUsb {
|
|||
connected: u32,
|
||||
|
||||
// actual hardware pointer value to pass to UDC; not directly accessed by Rust
|
||||
ep0_buf: *mut u8,
|
||||
ep0_buf: AtomicPtr<u8>,
|
||||
|
||||
u2_rwe: u32,
|
||||
feature_u1_enabled: u32,
|
||||
|
|
@ -524,57 +528,25 @@ pub struct CorigineUsb {
|
|||
max_speed: u32,
|
||||
}
|
||||
impl CorigineUsb {
|
||||
pub fn new() -> Self {
|
||||
#[cfg(feature = "std")]
|
||||
let mut usb_mapping = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(CORIGINE_USB_BASE),
|
||||
None,
|
||||
CORIGINE_USB_LEN,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("couldn't reserve register pages");
|
||||
#[cfg(feature = "std")]
|
||||
let ifram_range = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(CRG_UDC_MEMBASE),
|
||||
xous::MemoryAddress::new(CRG_UDC_MEMBASE), // make P & V addresses line up
|
||||
CRG_IFRAM_PAGES * 0x1000,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("couldn't allocate IFRAM pages");
|
||||
#[cfg(feature = "std")]
|
||||
let magic_page = unsafe { core::slice::from_raw_parts_mut(usb_mapping.as_ptr() as *mut u32, 1024) };
|
||||
#[cfg(not(feature = "std"))]
|
||||
let magic_page = unsafe { core::slice::from_raw_parts_mut(CORIGINE_USB_BASE as *mut u32, 1024) };
|
||||
|
||||
// note that the extent of this slice goes beyond the strict end of the register set because
|
||||
// I think there are extra hidden registers we may need to access later on.
|
||||
#[cfg(feature = "std")]
|
||||
let dev_slice: &mut [u32] = unsafe {
|
||||
core::slice::from_raw_parts_mut(usb_mapping.as_ptr() as *mut u32, 0x200 / size_of::<u32>())
|
||||
};
|
||||
#[cfg(not(feature = "std"))]
|
||||
let dev_slice = unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
(CORIGINE_USB_BASE + CORIGINE_DEV_OFFSET) as *mut u32,
|
||||
0x200 / size_of::<u32>(),
|
||||
)
|
||||
};
|
||||
/// Safety: this function is generally pretty unsafe because the underlying hardware needs raw pointers,
|
||||
/// and will mutate values underneath the OS with no regard for safety.
|
||||
///
|
||||
/// However, from the standpoint of Rust, the particular guarantee we need is that the `ifram_base_ptr`
|
||||
/// maps to the exact same value in virtual memory as in physical memory. This allows us to operate on
|
||||
/// the pointer directly in virtual memory, and pass it into the hardware (which thinks in physical
|
||||
/// memory), without having to do tedious translations back and forth. On a platform where these two
|
||||
/// could not be guaranteed to the same range, the driver would need to be written with a
|
||||
/// de-virtualization/re-virtualization layer every time we extract data records from the device's mapped
|
||||
/// RAM.
|
||||
pub unsafe fn new(_conn: u32, ifram_base_ptr: usize, csr: AtomicCsr<u32>) -> Self {
|
||||
let magic_page = unsafe { core::slice::from_raw_parts_mut(csr.base() as *mut u32, 1024) };
|
||||
|
||||
Self {
|
||||
ifram_base_ptr,
|
||||
csr,
|
||||
#[cfg(feature = "std")]
|
||||
range: usb_mapping,
|
||||
#[cfg(feature = "std")]
|
||||
ifram_range,
|
||||
#[cfg(feature = "std")]
|
||||
ifram_base_ptr: ifram_range.as_ptr() as usize,
|
||||
#[cfg(not(feature = "std"))]
|
||||
ifram_base_ptr: CRG_UDC_MEMBASE,
|
||||
#[cfg(feature = "std")]
|
||||
csr: CSR::new(usb_mapping.as_mut_ptr() as *mut u32),
|
||||
#[cfg(not(feature = "std"))]
|
||||
csr: CSR::new(CORIGINE_USB_BASE as *mut u32),
|
||||
conn: _conn,
|
||||
magic_page,
|
||||
dev_slice,
|
||||
// is there a way to make this less shitty?
|
||||
udc_ep: [
|
||||
UdcEp::default(),
|
||||
|
|
@ -585,7 +557,7 @@ impl CorigineUsb {
|
|||
UdcEp::default(),
|
||||
],
|
||||
ep_cx: BufferInfo::default(),
|
||||
p_epcx: core::ptr::null_mut(),
|
||||
p_epcx: AtomicPtr::new(core::ptr::null_mut()),
|
||||
udc_event: [UdcEvent::default(); CRG_EVENT_RING_NUM],
|
||||
sel_value: SelValue::default(),
|
||||
setup_status: 0,
|
||||
|
|
@ -598,7 +570,7 @@ impl CorigineUsb {
|
|||
set_tm: 0,
|
||||
cur_interface_num: 0,
|
||||
connected: 0,
|
||||
ep0_buf: core::ptr::null_mut(),
|
||||
ep0_buf: AtomicPtr::new(core::ptr::null_mut()),
|
||||
u2_rwe: 0,
|
||||
feature_u1_enabled: 0,
|
||||
feature_u2_enabled: 0,
|
||||
|
|
@ -654,21 +626,37 @@ impl CorigineUsb {
|
|||
while self.csr.rf(USBCMD_SOFT_RESET) != 0 {
|
||||
// wait for reset to finish
|
||||
}
|
||||
// dummy readback, from the sample code. not sure if important
|
||||
println!("Dummy");
|
||||
for i in 0..72 {
|
||||
if i % 16 == 0 {
|
||||
print!("\r\n {}: ", i);
|
||||
}
|
||||
print!("{:x} ", self.dev_slice[i]);
|
||||
}
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
println!("\rUSB reset done");
|
||||
}
|
||||
|
||||
pub fn init(&mut self) {
|
||||
let uicr = self.uicr();
|
||||
|
||||
// NOTE: the indices are byte-addressed, and so need to be divided by size_of::<u32>()
|
||||
const MAGIC_TABLE: [(usize, u32); 17] = [
|
||||
(0x084, 0x01401388),
|
||||
(0x0f4, 0x0000f023),
|
||||
(0x088, 0x3b066409),
|
||||
(0x08c, 0x0d020407),
|
||||
(0x090, 0x04055050),
|
||||
(0x094, 0x03030a07),
|
||||
(0x098, 0x05131304),
|
||||
(0x09c, 0x3b4b0d15),
|
||||
(0x0a0, 0x14168c6e),
|
||||
(0x0a4, 0x18060408),
|
||||
(0x0a8, 0x4b120c0f),
|
||||
(0x0ac, 0x03190d05),
|
||||
(0x0b0, 0x08080d09),
|
||||
(0x0b4, 0x20060b03),
|
||||
(0x0b8, 0x040a8c0e),
|
||||
(0x0bc, 0x44087d5a),
|
||||
(0x110, 0x00000000),
|
||||
];
|
||||
|
||||
for (offset, magic) in MAGIC_TABLE {
|
||||
self.magic_page[offset / size_of::<u32>()] = magic;
|
||||
}
|
||||
|
||||
// stop controller and disable interrupt
|
||||
self.csr.rmwf(USBCMD_INT_ENABLE, 0);
|
||||
self.csr.rmwf(USBCMD_RUN_STOP, 0);
|
||||
|
|
@ -713,8 +701,8 @@ impl CorigineUsb {
|
|||
*e = ErstS::default();
|
||||
}
|
||||
udc_event.erst.len = (erst.len() * size_of::<ErstS>()) as u32;
|
||||
udc_event.erst.vaddr = erst.as_mut_ptr() as *mut u8; // ErstS ??
|
||||
udc_event.p_erst = udc_event.erst.vaddr as *mut ErstS;
|
||||
udc_event.erst.vaddr = AtomicPtr::new(erst.as_mut_ptr() as *mut u8); // ErstS ??
|
||||
udc_event.p_erst = AtomicPtr::new(udc_event.erst.vaddr.load(Ordering::SeqCst) as *mut ErstS);
|
||||
|
||||
// allocate event ring
|
||||
let event_ring = unsafe {
|
||||
|
|
@ -730,24 +718,29 @@ impl CorigineUsb {
|
|||
|
||||
udc_event.event_ring.len = event_ring.len() as u32;
|
||||
// TODO: V->P
|
||||
udc_event.event_ring.vaddr = event_ring.as_mut_ptr(); // EventTrbS ??
|
||||
udc_event.evt_dq_pt = udc_event.event_ring.vaddr as *mut EventTrbS;
|
||||
udc_event.evt_seg0_last_trb =
|
||||
unsafe { (udc_event.event_ring.vaddr as *mut EventTrbS).add(CRG_EVENT_RING_SIZE - 1) };
|
||||
udc_event.event_ring.vaddr = AtomicPtr::new(event_ring.as_mut_ptr()); // EventTrbS ??
|
||||
udc_event.evt_dq_pt =
|
||||
AtomicPtr::new(udc_event.event_ring.vaddr.load(Ordering::SeqCst) as *mut EventTrbS);
|
||||
udc_event.evt_seg0_last_trb = AtomicPtr::new(unsafe {
|
||||
(udc_event.event_ring.vaddr.load(Ordering::SeqCst) as *mut EventTrbS)
|
||||
.add(CRG_EVENT_RING_SIZE - 1)
|
||||
});
|
||||
|
||||
udc_event.ccs = 1;
|
||||
|
||||
// copy control structure pointers to hardware-managed memory
|
||||
let p_erst = unsafe { udc_event.p_erst.as_mut() }.expect("invalid pointer");
|
||||
p_erst.seg_addr_lo = udc_event.event_ring.vaddr as u32;
|
||||
let p_erst =
|
||||
unsafe { udc_event.p_erst.load(Ordering::SeqCst).as_mut().expect("invalid pointer") };
|
||||
p_erst.seg_addr_lo = udc_event.event_ring.vaddr.load(Ordering::SeqCst) as u32;
|
||||
p_erst.seg_addr_hi = 0;
|
||||
p_erst.seg_size = CRG_EVENT_RING_SIZE as u32;
|
||||
p_erst.rsvd = 0;
|
||||
|
||||
uicr[index].erstsz = CRG_ERST_SIZE as u32;
|
||||
uicr[index].erstbalo = udc_event.erst.vaddr as u32;
|
||||
uicr[index].erstbalo = udc_event.erst.vaddr.load(Ordering::SeqCst) as u32;
|
||||
uicr[index].erstbahi = 0;
|
||||
uicr[index].erdplo = udc_event.event_ring.vaddr as u32 | self.csr.ms(ERDPLO_EHB, 1);
|
||||
uicr[index].erdplo =
|
||||
udc_event.event_ring.vaddr.load(Ordering::SeqCst) as u32 | self.csr.ms(ERDPLO_EHB, 1);
|
||||
uicr[index].erdphi = 0;
|
||||
|
||||
uicr[index].iman = self.csr.ms(IMAN_IE, 1) | self.csr.ms(IMAN_IP, 1);
|
||||
|
|
@ -759,10 +752,10 @@ impl CorigineUsb {
|
|||
println!("Begin init_device_context");
|
||||
// init device context and ep context, refer to 7.6.2
|
||||
self.ep_cx.len = (CRG_EP_NUM * size_of::<EpCxS>()) as u32;
|
||||
self.ep_cx.vaddr = (self.ifram_base_ptr + CRG_UDC_EPCX_OFFSET) as *mut u8; // EpCxS ??
|
||||
self.p_epcx = self.ep_cx.vaddr as *mut EpCxS;
|
||||
self.ep_cx.vaddr = AtomicPtr::new((self.ifram_base_ptr + CRG_UDC_EPCX_OFFSET) as *mut u8); // EpCxS ??
|
||||
self.p_epcx = AtomicPtr::new(self.ep_cx.vaddr.load(Ordering::SeqCst) as *mut EpCxS);
|
||||
|
||||
self.csr.wo(DCBAPLO, self.ep_cx.vaddr as u32);
|
||||
self.csr.wo(DCBAPLO, self.ep_cx.vaddr.load(Ordering::SeqCst) as u32);
|
||||
self.csr.wo(DCBAPHI, 0);
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
println!(" dcbaplo: {:x}", self.csr.r(DCBAPLO));
|
||||
|
|
@ -799,34 +792,37 @@ impl CorigineUsb {
|
|||
e.zeroize();
|
||||
}
|
||||
// TODO: V->P
|
||||
udc_ep.tran_ring_info.vaddr = ep0_tr_ring.as_mut_ptr() as *mut u8; // &[TransferTrbS] ??
|
||||
udc_ep.tran_ring_info.vaddr = AtomicPtr::new(ep0_tr_ring.as_mut_ptr() as *mut u8); // &[TransferTrbS] ??
|
||||
udc_ep.tran_ring_info.len = (ep0_tr_ring.len() * size_of::<TransferTrbS>()) as u32;
|
||||
udc_ep.first_trb = (&mut ep0_tr_ring[0]) as *mut TransferTrbS;
|
||||
udc_ep.first_trb = AtomicPtr::new((&mut ep0_tr_ring[0]) as *mut TransferTrbS);
|
||||
// TODO: P->V
|
||||
udc_ep.last_trb = (&ep0_tr_ring[ep0_tr_ring.len() - 1]) as *const TransferTrbS as *mut TransferTrbS;
|
||||
udc_ep.last_trb =
|
||||
AtomicPtr::new((&ep0_tr_ring[ep0_tr_ring.len() - 1]) as *const TransferTrbS as *mut TransferTrbS);
|
||||
|
||||
udc_ep.enq_pt = udc_ep.first_trb;
|
||||
udc_ep.deq_pt = udc_ep.first_trb;
|
||||
udc_ep.enq_pt = AtomicPtr::new(udc_ep.first_trb.load(Ordering::SeqCst));
|
||||
udc_ep.deq_pt = AtomicPtr::new(udc_ep.first_trb.load(Ordering::SeqCst));
|
||||
udc_ep.pcs = 1;
|
||||
udc_ep.tran_ring_full = false;
|
||||
|
||||
unsafe { udc_ep.last_trb.as_mut().expect("couldn't deref pointer") }
|
||||
.set_trb_link(true, udc_ep.tran_ring_info.vaddr as *mut TransferTrbS);
|
||||
unsafe { udc_ep.last_trb.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") }
|
||||
.set_trb_link(true, udc_ep.tran_ring_info.vaddr.load(Ordering::SeqCst) as *mut TransferTrbS);
|
||||
|
||||
let cmd_param0: u32 = self.csr.ms(CMDPARA0_CMD0_INIT_EP0_DQPTRLO, udc_ep.tran_ring_info.vaddr as u32)
|
||||
let cmd_param0: u32 = self
|
||||
.csr
|
||||
.ms(CMDPARA0_CMD0_INIT_EP0_DQPTRLO, udc_ep.tran_ring_info.vaddr.load(Ordering::SeqCst) as u32)
|
||||
| self.csr.ms(CMDPARA0_CMD0_INIT_EP0_DCS, udc_ep.pcs as u32);
|
||||
let cmd_param1: u32 = 0;
|
||||
println!("ep0 ring dma addr = {:x}", udc_ep.tran_ring_info.vaddr as usize);
|
||||
println!("ep0 ring dma addr = {:x}", udc_ep.tran_ring_info.vaddr.load(Ordering::SeqCst) as usize);
|
||||
println!("INIT EP0 CMD par0 = {:x} par1 = {:x}", cmd_param0, cmd_param1);
|
||||
|
||||
self.issue_command(CmdType::InitEp0, cmd_param0, cmd_param1)
|
||||
.expect("couldn't issue ep0 init command");
|
||||
|
||||
self.ep0_buf = (self.ifram_base_ptr + CRG_UDC_EP0_BUF_OFFSET) as *mut u8;
|
||||
self.ep0_buf = AtomicPtr::new((self.ifram_base_ptr + CRG_UDC_EP0_BUF_OFFSET) as *mut u8);
|
||||
println!("End init_ep0");
|
||||
}
|
||||
|
||||
pub fn issue_command(&mut self, cmd: CmdType, p0: u32, p1: u32) -> Result<(), Error> {
|
||||
pub fn issue_command(&mut self, cmd: CmdType, p0: u32, p1: u32) -> core::result::Result<(), Error> {
|
||||
let check_complete = self.csr.r(USBCMD) & self.csr.ms(USBCMD_RUN_STOP, 1) != 0;
|
||||
if check_complete {
|
||||
if self.csr.r(CMDCTRL) & self.csr.ms(CMDCTRL_ACTIVE, 1) != 0 {
|
||||
|
|
@ -890,10 +886,10 @@ impl CorigineUsb {
|
|||
loop {
|
||||
let event = {
|
||||
let udc_event = &mut self.udc_event[index];
|
||||
if udc_event.evt_dq_pt.is_null() {
|
||||
if udc_event.evt_dq_pt.load(Ordering::SeqCst).is_null() {
|
||||
break;
|
||||
}
|
||||
let event_ptr = udc_event.evt_dq_pt as usize;
|
||||
let event_ptr = udc_event.evt_dq_pt.load(Ordering::SeqCst) as usize;
|
||||
unsafe { (event_ptr as *mut EventTrbS).as_mut().expect("couldn't deref pointer") }
|
||||
};
|
||||
|
||||
|
|
@ -904,19 +900,23 @@ impl CorigineUsb {
|
|||
self.handle_event(event);
|
||||
|
||||
let udc_event = &mut self.udc_event[index];
|
||||
if udc_event.evt_dq_pt == udc_event.evt_seg0_last_trb {
|
||||
println!(" evt_last_trb {:x}", udc_event.evt_seg0_last_trb as usize);
|
||||
if udc_event.evt_dq_pt.load(Ordering::SeqCst)
|
||||
== udc_event.evt_seg0_last_trb.load(Ordering::SeqCst)
|
||||
{
|
||||
println!(" evt_last_trb {:x}", udc_event.evt_seg0_last_trb.load(Ordering::SeqCst) as usize);
|
||||
udc_event.ccs = if udc_event.ccs != 0 { 0 } else { 1 };
|
||||
// does this...go to null to end the transfer??
|
||||
udc_event.evt_dq_pt = udc_event.event_ring.vaddr as *mut EventTrbS;
|
||||
udc_event.evt_dq_pt =
|
||||
AtomicPtr::new(udc_event.event_ring.vaddr.load(Ordering::SeqCst) as *mut EventTrbS);
|
||||
} else {
|
||||
udc_event.evt_dq_pt = unsafe { udc_event.evt_dq_pt.add(1) };
|
||||
udc_event.evt_dq_pt =
|
||||
AtomicPtr::new(unsafe { udc_event.evt_dq_pt.load(Ordering::SeqCst).add(1) });
|
||||
}
|
||||
}
|
||||
let udc_event = &mut self.udc_event[index];
|
||||
// update dequeue pointer
|
||||
uicr[index].erdphi = 0;
|
||||
uicr[index].erdplo = udc_event.evt_dq_pt as u32 | CRG_UDC_ERDPLO_EHB;
|
||||
uicr[index].erdplo = udc_event.evt_dq_pt.load(Ordering::SeqCst) as u32 | CRG_UDC_ERDPLO_EHB;
|
||||
compiler_fence(Ordering::SeqCst);
|
||||
}
|
||||
|
||||
|
|
@ -987,9 +987,9 @@ impl CorigineUsb {
|
|||
(event_trb.dw0 as *mut TransferTrbS).add(1).as_mut().expect("Couldn't deref ptr")
|
||||
};
|
||||
if deq_pt.get_trb_type() == TrbType::Link {
|
||||
udc_ep.deq_pt = udc_ep.first_trb;
|
||||
udc_ep.deq_pt = AtomicPtr::new(udc_ep.first_trb.load(Ordering::SeqCst));
|
||||
} else {
|
||||
udc_ep.deq_pt = deq_pt as *mut TransferTrbS;
|
||||
udc_ep.deq_pt = AtomicPtr::new(deq_pt as *mut TransferTrbS);
|
||||
}
|
||||
println!("EventTransfer: comp_code {:?}, PEI {}", comp_code, pei);
|
||||
|
||||
|
|
@ -1072,16 +1072,18 @@ impl CorigineUsb {
|
|||
|
||||
let udc_ep = &mut self.udc_ep[0];
|
||||
// TODO: P->A
|
||||
let enq_pt = unsafe { udc_ep.enq_pt.as_mut().expect("couldn't deref pointer") };
|
||||
let enq_pt =
|
||||
unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") };
|
||||
enq_pt.set_trb_status(udc_ep.pcs, true, false, self.setup_tag, target, false);
|
||||
|
||||
// TODO: fix raw pointer manips with something more sane?
|
||||
udc_ep.enq_pt = unsafe { udc_ep.enq_pt.add(1) };
|
||||
udc_ep.enq_pt = AtomicPtr::new(unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).add(1) });
|
||||
// TODO: P->A
|
||||
let enq_pt = unsafe { udc_ep.enq_pt.as_mut().expect("couldn't deref pointer") };
|
||||
let enq_pt =
|
||||
unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") };
|
||||
if enq_pt.get_trb_type() == TrbType::Link {
|
||||
enq_pt.set_cycle_bit(udc_ep.pcs);
|
||||
udc_ep.enq_pt = udc_ep.first_trb;
|
||||
udc_ep.enq_pt = AtomicPtr::new(udc_ep.first_trb.load(Ordering::SeqCst));
|
||||
udc_ep.pcs ^= 1;
|
||||
}
|
||||
self.knock_doorbell(0);
|
||||
|
|
@ -1166,4 +1168,176 @@ impl CorigineUsb {
|
|||
|
||||
self.set_addr(0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
Functions below implement the "device management" APIs.
|
||||
|
||||
These are mostly dummy thunks on the Cramium target because only a single core is possible
|
||||
on this SoC.
|
||||
|
||||
This would be cleaner if we implemented it as a trait, I think - but we've got a lot to do
|
||||
in term sof getting the cores up and running, so we'll leave this as technical debt for when
|
||||
we uh...decide to implement a third target, or something like that.
|
||||
*/
|
||||
/// Force and hold the reset pin according to the state selected
|
||||
pub fn ll_reset(&mut self, state: bool) {
|
||||
#[cfg(feature = "std")]
|
||||
log::warn!("ll_reset is UNSURE");
|
||||
// There is a PHY control, it looks like 0x1C bit 1 set to 1 will cause the device to hi-Z
|
||||
|
||||
// But we might not even have to do that, I think it could be the case that we may just
|
||||
// need to do a soft-disconnect routine per 8.3.4 in manual.
|
||||
if state {
|
||||
self.reset();
|
||||
} else {
|
||||
self.init();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ll_connect_device_core(&self, _state: bool) {}
|
||||
|
||||
pub fn connect_device_core(&self, _state: bool) {}
|
||||
|
||||
pub fn is_device_connected(&self) -> bool { true }
|
||||
|
||||
pub fn disable_debug(&mut self, _disable: bool) {}
|
||||
|
||||
pub fn get_disable_debug(&self) -> bool { true }
|
||||
|
||||
pub fn xous_suspend(&self) {}
|
||||
|
||||
pub fn xous_resume1(&self) {}
|
||||
|
||||
pub fn xous_resume2(&self) {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl UsbBus for CorigineUsb {
|
||||
/// Indicates that `set_device_address` must be called before accepting the corresponding
|
||||
/// control transfer, not after.
|
||||
///
|
||||
/// The default value for this constant is `false`, which corresponds to the USB 2.0 spec, 9.4.6
|
||||
const QUIRK_SET_ADDRESS_BEFORE_STATUS: bool = false;
|
||||
|
||||
/// Allocates an endpoint and specified endpoint parameters. This method is called by the device
|
||||
/// and class implementations to allocate endpoints, and can only be called before
|
||||
/// [`enable`](UsbBus::enable) is called.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `ep_dir` - The endpoint direction.
|
||||
/// * `ep_addr` - A static endpoint address to allocate. If Some, the implementation should attempt to
|
||||
/// return an endpoint with the specified address. If None, the implementation should return the next
|
||||
/// available one.
|
||||
/// * `max_packet_size` - Maximum packet size in bytes.
|
||||
/// * `interval` - Polling interval parameter for interrupt endpoints.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`EndpointOverflow`](crate::UsbError::EndpointOverflow) - Available total number of endpoints,
|
||||
/// endpoints of the specified type, or endpoind packet memory has been exhausted. This is generally
|
||||
/// caused when a user tries to add too many classes to a composite device.
|
||||
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - A specific `ep_addr` was specified but the
|
||||
/// endpoint in question has already been allocated.
|
||||
fn alloc_ep(
|
||||
&mut self,
|
||||
ep_dir: UsbDirection,
|
||||
ep_addr: Option<EndpointAddress>,
|
||||
ep_type: EndpointType,
|
||||
max_packet_size: u16,
|
||||
interval: u8,
|
||||
) -> Result<EndpointAddress> {
|
||||
Ok(EndpointAddress::from_parts(1, UsbDirection::Out))
|
||||
}
|
||||
|
||||
/// Enables and initializes the USB peripheral. Soon after enabling the device will be reset, so
|
||||
/// there is no need to perform a USB reset in this method.
|
||||
fn enable(&mut self) {}
|
||||
|
||||
/// Called when the host resets the device. This will be soon called after
|
||||
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Reset`]. This method should
|
||||
/// reset the state of all endpoints and peripheral flags back to a state suitable for
|
||||
/// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are
|
||||
/// initialized as specified.
|
||||
fn reset(&self) {}
|
||||
|
||||
/// Sets the device USB address to `addr`.
|
||||
fn set_device_address(&self, addr: u8) {}
|
||||
|
||||
/// Writes a single packet of data to the specified endpoint and returns number of bytes
|
||||
/// actually written.
|
||||
///
|
||||
/// The only reason for a short write is if the caller passes a slice larger than the amount of
|
||||
/// memory allocated earlier, and this is generally an error in the class implementation.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - The `ep_addr` does not point to a valid
|
||||
/// endpoint that was previously allocated with [`UsbBus::alloc_ep`].
|
||||
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - A previously written packet is still pending to be
|
||||
/// sent.
|
||||
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The packet is too long to fit in the
|
||||
/// transmission buffer. This is generally an error in the class implementation, because the class
|
||||
/// shouldn't provide more data than the `max_packet_size` it specified when allocating the endpoint.
|
||||
///
|
||||
/// Implementations may also return other errors if applicable.
|
||||
fn write(&self, ep_addr: EndpointAddress, buf: &[u8]) -> Result<usize> { Ok(0) }
|
||||
|
||||
/// Reads a single packet of data from the specified endpoint and returns the actual length of
|
||||
/// the packet.
|
||||
///
|
||||
/// This should also clear any NAK flags and prepare the endpoint to receive the next packet.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`InvalidEndpoint`](crate::UsbError::InvalidEndpoint) - The `ep_addr` does not point to a valid
|
||||
/// endpoint that was previously allocated with [`UsbBus::alloc_ep`].
|
||||
/// * [`WouldBlock`](crate::UsbError::WouldBlock) - There is no packet to be read. Note that this is
|
||||
/// different from a received zero-length packet, which is valid in USB. A zero-length packet will
|
||||
/// return `Ok(0)`.
|
||||
/// * [`BufferOverflow`](crate::UsbError::BufferOverflow) - The received packet is too long to fit in
|
||||
/// `buf`. This is generally an error in the class implementation, because the class should use a buffer
|
||||
/// that is large enough for the `max_packet_size` it specified when allocating the endpoint.
|
||||
///
|
||||
/// Implementations may also return other errors if applicable.
|
||||
fn read(&self, ep_addr: EndpointAddress, buf: &mut [u8]) -> Result<usize> { Ok(0) }
|
||||
|
||||
/// Sets or clears the STALL condition for an endpoint. If the endpoint is an OUT endpoint, it
|
||||
/// should be prepared to receive data again.
|
||||
fn set_stalled(&self, ep_addr: EndpointAddress, stalled: bool) {}
|
||||
|
||||
/// Gets whether the STALL condition is set for an endpoint.
|
||||
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool { false }
|
||||
|
||||
/// Instruct EP0 to configure itself with an OUT descriptor, so that it may receive a STATUS
|
||||
/// update during configuration. This is for devices that support an EP0 which can only either
|
||||
/// be IN or OUT, but not both at the same time. Devices with both IN/OUT may leave this as
|
||||
/// an empty stub.
|
||||
fn set_ep0_out(&self) {}
|
||||
|
||||
/// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and
|
||||
/// preparing to detect a USB wakeup event. This will be called after
|
||||
/// [`poll`](crate::device::UsbDevice::poll) returns [`PollResult::Suspend`]. The device will
|
||||
/// continue be polled, and it shall return a value other than `Suspend` from `poll` when it no
|
||||
/// longer detects the suspend condition.
|
||||
fn suspend(&self) {}
|
||||
|
||||
/// Resumes from suspend mode. This may only be called after the peripheral has been previously
|
||||
/// suspended.
|
||||
fn resume(&self) {}
|
||||
|
||||
/// Gets information about events and incoming data. Usually called in a loop or from an
|
||||
/// interrupt handler. See the [`PollResult`] struct for more information.
|
||||
fn poll(&self) -> PollResult { PollResult::None }
|
||||
|
||||
/// Simulates a disconnect from the USB bus, causing the host to reset and re-enumerate the
|
||||
/// device.
|
||||
///
|
||||
/// The default implementation just returns `Unsupported`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * [`Unsupported`](crate::UsbError::Unsupported) - This UsbBus implementation doesn't support
|
||||
/// simulating a disconnect or it has not been enabled at creation time.
|
||||
fn force_reset(&self) -> Result<()> { Err(UsbError::Unsupported) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,2 +1,4 @@
|
|||
#[cfg(not(feature = "std"))]
|
||||
pub mod compat;
|
||||
pub mod driver;
|
||||
pub mod utra;
|
||||
|
|
|
|||
Loading…
Reference in a new issue