endpoint enumeration now working

turns out that you *have* to enable the endpoints after your
address has been assigned. You can't do it before.

Very subtle one-liner in the docs that I missed until now. :-/
This commit is contained in:
bunnie 2024-06-11 22:30:55 +08:00
parent f004c45f2a
commit 7b2804eec5

View file

@ -17,8 +17,21 @@ use utralib::generated::*;
#[cfg(not(feature = "std"))]
use super::compat::AtomicCsr;
#[cfg(not(feature = "std"))]
use crate::print;
use crate::println;
use crate::usb::utra::*;
use crate::{print, println};
// Directional nomenclature.
//
// Manual says "outbound" means outbound packets from device going to host. This is an IN packet in USB.
// "inbound" means inbound packets to device, coming from host. This is the OUT packet in USB.
pub const USB_SEND: bool = false; // USB IN -> corigine USB_SEND (outbound) -> 0/even on PEI
pub const USB_RECV: bool = true; // USB OUT -> corigine USB_RECV (inbound) -> 1/odd on PEI
// these names are picked so that the boolean value maps to the same thing in the reference code
// this effectively does the "reversal of direction" from USB spec to corigine speak
pub const CRG_IN: bool = false;
pub const CRG_OUT: bool = true;
const CRG_EVENT_RING_NUM: usize = 1;
const CRG_ERST_SIZE: usize = 1;
@ -63,9 +76,6 @@ pub const CRG_UDC_TOTAL_MEM_LEN: usize = CRG_UDC_APP_BUFOFFSET + CRG_UDC_APP_BUF
const MAX_TRB_XFER_LEN: usize = 64 * 1024;
pub const HOST_SEND: bool = false; // OUT; USB_SEND -> 0/even on PEI
pub const HOST_RECV: bool = true; // IN; USB_RECV -> 1/odd on PEI
#[cfg(feature = "std")]
static INTERRUPT_INIT_DONE: AtomicBool = AtomicBool::new(false);
@ -579,7 +589,7 @@ pub struct EpCxS {
impl EpCxS {
pub fn epcx_setup(&mut self, udc_ep: &UdcEp) {
// corigine gadget dir should be opposite to host dir
let ep_type = if udc_ep.direction == HOST_SEND {
let ep_type = if udc_ep.direction == USB_RECV {
// transforms the base type into INBOUND
EpType::try_from(udc_ep.ep_type as u8 + EpType::Invalid2 as u8).unwrap()
} else {
@ -705,7 +715,7 @@ impl Default for UdcEp {
fn default() -> Self {
Self {
ep_num: 0,
direction: HOST_RECV,
direction: USB_RECV,
ep_type: EpType::ControlOrInvalid,
max_packet_size: 0,
tran_ring_info: BufferInfo::default(),
@ -1108,7 +1118,7 @@ impl CorigineUsb {
let udc_ep = &mut self.udc_ep[0];
udc_ep.ep_num = 0;
udc_ep.direction = HOST_RECV;
udc_ep.direction = USB_SEND;
udc_ep.ep_type = EpType::ControlOrInvalid;
udc_ep.max_packet_size = 64;
@ -1477,7 +1487,7 @@ impl CorigineUsb {
if pei == 0 {
if comp_code == CompletionCode::Success {
// ep0_xfer_complete
if dir == HOST_SEND {
if dir == USB_SEND {
ret = CrgEvent::Data(0, 1, 0); // FIXME: this ordering contradicts the `dir` bit, but seems necessary to trigger the next packet send
} else {
ret = CrgEvent::Data(1, 0, 0); // FIXME: this ordering contradicts the `dir` bit, but seems necessary to trigger the next packet send
@ -1490,10 +1500,10 @@ impl CorigineUsb {
} else if pei >= 2 {
if comp_code == CompletionCode::Success || comp_code == CompletionCode::ShortPacket {
#[cfg(feature = "std")]
log::info!("EP{} xfer event, dir {}", ep_num, if dir { "IN" } else { "OUT" });
log::info!("EP{} xfer event, dir {}", ep_num, if dir { "OUT" } else { "IN" });
// xfer_complete
if dir == HOST_SEND {
let addr = self.get_app_buf_ptr(ep_num, HOST_SEND);
if dir == CRG_OUT {
let addr = self.get_app_buf_ptr(ep_num, dir);
let hw_buf = unsafe {
core::slice::from_raw_parts_mut(addr as *mut u8, CRG_UDC_APP_BUF_LEN)
};
@ -1590,7 +1600,7 @@ impl CorigineUsb {
compiler_fence(Ordering::SeqCst);
self.csr.wfo(DOORBELL_TARGET, 0);
}
enq_pt.control_status_trb(pcs, false, false, tag, CRG_INT_TARGET, HOST_RECV);
enq_pt.control_status_trb(pcs, false, false, tag, CRG_INT_TARGET, USB_RECV);
let (_enq_pt, _pcs) = self.udc_ep[0].increment_enq_pt();
self.knock_doorbell(0);
println!("ep0 sent");
@ -1657,7 +1667,7 @@ impl CorigineUsb {
let udc_ep = &mut self.udc_ep[0];
let enq_pt =
unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") };
enq_pt.control_status_trb(udc_ep.pcs, true, false, self.setup_tag, target, HOST_SEND);
enq_pt.control_status_trb(udc_ep.pcs, true, false, self.setup_tag, target, USB_SEND);
// TODO: fix raw pointer manips with something more sane?
let (_enq_pt, _pcs) = udc_ep.increment_enq_pt();
@ -1766,7 +1776,7 @@ impl CorigineUsb {
(enq_pt, pcs) = udc_ep.increment_enq_pt();
}
enq_pt.control_status_trb(pcs, false, false, tag, intr_target, HOST_RECV);
enq_pt.control_status_trb(pcs, false, false, tag, intr_target, USB_RECV);
let (_enq_pt, _pcs) = udc_ep.increment_enq_pt();
compiler_fence(Ordering::SeqCst);
@ -1796,7 +1806,7 @@ impl CorigineUsb {
let udc_ep = &mut self.udc_ep[0];
let enq_pt =
unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") };
enq_pt.control_status_trb(udc_ep.pcs, false, stall, self.setup_tag, intr_target, HOST_RECV);
enq_pt.control_status_trb(udc_ep.pcs, false, stall, self.setup_tag, intr_target, USB_RECV);
let (_, _) = udc_ep.increment_enq_pt();
self.knock_doorbell(0);
}
@ -1805,7 +1815,7 @@ impl CorigineUsb {
let udc_ep = &mut self.udc_ep[0];
let enq_pt =
unsafe { udc_ep.enq_pt.load(Ordering::SeqCst).as_mut().expect("couldn't deref pointer") };
enq_pt.control_status_trb(udc_ep.pcs, false, stall, self.setup_tag, intr_target, HOST_SEND);
enq_pt.control_status_trb(udc_ep.pcs, false, stall, self.setup_tag, intr_target, USB_SEND);
let (_, _) = udc_ep.increment_enq_pt();
self.knock_doorbell(0);
}
@ -1940,7 +1950,7 @@ impl CorigineUsb {
"udc_ep->PEI = {}, xfer ring addr {:x}, dir {}, mps: {}",
pei,
vaddr,
if dir { "IN" } else { "OUT" },
if dir { "OUT" } else { "IN" },
max_packet_size,
);
assert!(
@ -1980,7 +1990,7 @@ impl CorigineUsb {
epcx.epcx_setup(&udc_ep);
#[cfg(feature = "std")]
log::info!(
"dcbap {:x}/{:x}; ecpx * {:x}; epcx: {:x?}",
"dcbap {:x}/{:x}; ecpx *{:x}; epcx: {:x?}",
self.csr.r(DCBAPHI),
self.csr.r(DCBAPLO),
epcx as *const EpCxS as usize,
@ -1988,8 +1998,7 @@ impl CorigineUsb {
);
self.issue_command(CmdType::ConfigEp, 1 << pei as u32, 0).expect("couldn't issue command");
self.udc_ep[pei].ep_state = EpState::Running;
#[cfg(feature = "std")]
log::info!("config ep and start, PEI = {}", pei);
#[cfg(feature = "std")]
log::info!("waiting for EP to go to enabled, baseline: {:x}, epcx: {:x?}", baseline_enable, epcx);
loop {
@ -2009,7 +2018,7 @@ impl CorigineUsb {
pub fn ep_disable(&mut self, ep_num: u8, dir: bool) {
#[cfg(feature = "std")]
log::info!("Disable ep {}, dir {}", ep_num, if dir { "IN " } else { "OUT" });
log::info!("Disable ep {}, dir {}", ep_num, if dir { "OUT" } else { "IN" });
let pei = 2 * ep_num as usize + if dir { 1 } else { 0 };
let param0 = 1 << pei as u32;
if param0 & self.csr.r(EPRUNNING) != 0 {
@ -2139,7 +2148,7 @@ impl CorigineWrapper {
txn_offset: Arc::new(Mutex::new([0; CRG_EP_NUM + 1])),
};
// ep0 is allocated by default
c.free_ep[0] = Some((HOST_SEND, 8, EpType::ControlOrInvalid));
c.free_ep[0] = Some((USB_SEND, 8, EpType::ControlOrInvalid));
c
}
@ -2150,7 +2159,7 @@ impl CorigineWrapper {
txn_offset: Arc::new(Mutex::new([0; CRG_EP_NUM + 1])),
};
// ep0 is allocated by default
c.free_ep[0] = Some((HOST_SEND, 8, EpType::ControlOrInvalid));
c.free_ep[0] = Some((USB_SEND, 8, EpType::ControlOrInvalid));
c
}
@ -2195,8 +2204,8 @@ impl UsbBus for CorigineWrapper {
) -> Result<EndpointAddress> {
log::info!("alloc_ep {:?} size: {} dir: {:?}", ep_addr, max_packet_size, ep_dir);
let dir = match ep_dir {
UsbDirection::Out => HOST_SEND,
UsbDirection::In => HOST_RECV,
UsbDirection::Out => CRG_OUT,
UsbDirection::In => CRG_IN,
};
let hw_ep_type = match ep_type {
EndpointType::Control => EpType::ControlOrInvalid,
@ -2255,6 +2264,35 @@ impl UsbBus for CorigineWrapper {
fn set_device_address(&self, addr: u8) {
log::debug!(" ******** set address");
self.hw.lock().unwrap().set_addr(addr, CRG_INT_TARGET);
// this core has a quirk that you can't actually enable the endpoints until *after* the address
// has been set. :-/
for (index, &maybe_ep) in self.free_ep.iter().enumerate() {
if index == 0 {
continue;
}
if let Some((dir, max_packet_size, hw_ep_type)) = maybe_ep {
self.core().ep_enable(index as u8, dir, max_packet_size, hw_ep_type);
if dir == CRG_OUT {
let addr = self.core().get_app_buf_ptr(index as u8, dir);
// TODO: how do we deal with bulk endpoints??
self.core().intr_xfer(
index as u8,
dir,
addr,
CRG_UDC_APP_BUF_LEN,
CRG_INT_TARGET,
false,
false,
);
}
} else {
self.core().ep_disable(index as u8, true);
self.core().ep_disable(index as u8, false);
}
}
log::info!("enabled EPs: {:x}", self.core().csr.r(EPENABLE));
log::info!("running EPs: {:x}", self.core().csr.r(EPRUNNING));
}
/// Writes a single packet of data to the specified endpoint and returns number of bytes
@ -2306,13 +2344,13 @@ impl UsbBus for CorigineWrapper {
buf.len(),
&buf[..8.min(buf.len())]
);
let addr = self.core().get_app_buf_ptr(ep_addr.index() as u8, HOST_RECV);
let addr = self.core().get_app_buf_ptr(ep_addr.index() as u8, USB_RECV);
let hw_buf = unsafe { core::slice::from_raw_parts_mut(addr as *mut u8, CRG_UDC_APP_BUF_LEN) };
assert!(buf.len() < CRG_UDC_APP_BUF_LEN, "write buffer size exceeded");
hw_buf[..buf.len()].copy_from_slice(&buf);
self.core().intr_xfer(
ep_addr.index() as u8,
HOST_RECV,
USB_RECV,
addr,
buf.len(),
CRG_INT_TARGET,
@ -2438,41 +2476,10 @@ impl UsbBus for CorigineWrapper {
/// simulating a disconnect or it has not been enabled at creation time.
fn force_reset(&self) -> Result<()> {
log::info!(" ******* force_reset");
/*
self.core().reset();
self.core().init();
self.core().start();
// This is the minimum we need to do to restart EP0, but, I think we also need to reset
// TRB pointers etc. See page 67 of the manual.
self.core().update_current_speed();
*/
for (index, &maybe_ep) in self.free_ep.iter().enumerate() {
log::info!("Resetting EP{}", index);
if index == 0 {
// EP0 -> reset & start the machine. Happens before the code below.
self.core().update_current_speed();
} else {
if let Some((dir, max_packet_size, hw_ep_type)) = maybe_ep {
self.core().ep_enable(index as u8, dir, max_packet_size, hw_ep_type);
if dir == HOST_SEND {
let addr = self.core().get_app_buf_ptr(index as u8, dir);
// TODO: how do we deal with bulk endpoints??
self.core().intr_xfer(
index as u8,
dir,
addr,
CRG_UDC_APP_BUF_LEN,
CRG_INT_TARGET,
false,
false,
);
}
} else {
self.core().ep_disable(index as u8, true);
self.core().ep_disable(index as u8, false);
}
}
}
log::info!("enabled EPs: {:x}", self.core().csr.r(EPENABLE));
log::info!("running EPs: {:x}", self.core().csr.r(EPRUNNING));
Ok(())
}