mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-04 04:27:34 +00:00
still struggling a bit to get basic USB connectivity test to pass
This commit is contained in:
parent
1c21482796
commit
fd2aa438c0
4 changed files with 122 additions and 33 deletions
|
|
@ -4,6 +4,8 @@ pub(crate) const SERVER_NAME_USBTEST: &'static str = "_USB test and development
|
|||
pub(crate) enum Opcode {
|
||||
/// Handle the USB interrupt
|
||||
UsbIrqHandler,
|
||||
/// Simulate a disconnect/reconnect event
|
||||
ForceReset,
|
||||
/// Parse a commandlne
|
||||
DoCmd,
|
||||
/// Keyboard input
|
||||
|
|
|
|||
|
|
@ -8,6 +8,22 @@ use core::mem::size_of;
|
|||
use usb_device::{class_prelude::*, Result, UsbDirection};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub fn log_init() -> *mut u32 {
|
||||
let gpio_base = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(utra::gpio::HW_GPIO_BASE),
|
||||
None,
|
||||
4096,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("couldn't map GPIO CSR range");
|
||||
let mut gpio_csr = CSR::new(gpio_base.as_mut_ptr() as *mut u32);
|
||||
// setup the initial logging output
|
||||
// 0 = kernel, 1 = log, 2 = app, 3 = invalid
|
||||
gpio_csr.wfo(utra::gpio::UARTSEL_UARTSEL, 1);
|
||||
|
||||
gpio_base.as_mut_ptr() as *mut u32
|
||||
}
|
||||
|
||||
const NUM_ENDPOINTS: usize = 16;
|
||||
|
||||
bitfield! {
|
||||
|
|
@ -25,7 +41,7 @@ bitfield! {
|
|||
impl Debug;
|
||||
pub endpointid, set_endpointid: 3, 0;
|
||||
pub enable_req, set_enable_req: 4;
|
||||
pub enable_ack, _: 5; // question: can we make this ... read-only?
|
||||
pub enable_ack, _: 5;
|
||||
}
|
||||
bitfield! {
|
||||
pub struct UdcConfig(u32);
|
||||
|
|
@ -92,6 +108,8 @@ bitfield! {
|
|||
pub max_packet_size, set_max_packet_size: 31, 22;
|
||||
}
|
||||
/// This is located at 0x0000-0x0047 inside the UDC region
|
||||
/// This is mostly documentation, it's not actually instantiated
|
||||
#[allow(dead_code)]
|
||||
#[repr(C)]
|
||||
pub struct SpinalUdcMem {
|
||||
endpoints: [UdcEpStatus; 16],
|
||||
|
|
@ -162,10 +180,8 @@ fn handle_usb(_irq_no: usize, arg: *mut usize) {
|
|||
}
|
||||
pub struct SpinalUsbDevice {
|
||||
pub(crate) conn: CID,
|
||||
/// gpio_csr is just for testing, normally this would be a call to `llio` to enable the block
|
||||
gpio_csr: AtomicCsr<u32>,
|
||||
usb: xous::MemoryRange,
|
||||
csr: AtomicCsr<u32>,
|
||||
csr: AtomicCsr<u32>, // consider using VolatileCell and/or refactory AtomicCsr so it is non-mutable
|
||||
srmem: ManagedMem<{ utralib::generated::HW_USBDEV_MEM_LEN / core::mem::size_of::<u32>() }>,
|
||||
regs: &'static mut SpinalUdcRegs,
|
||||
// 1:1 mapping of endpoint structures to offsets in the memory space for the actual ep storage
|
||||
|
|
@ -175,13 +191,6 @@ pub struct SpinalUsbDevice {
|
|||
}
|
||||
impl SpinalUsbDevice {
|
||||
pub fn new(sid: xous::SID) -> SpinalUsbDevice {
|
||||
let gpio_base = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(utra::gpio::HW_GPIO_BASE),
|
||||
None,
|
||||
4096,
|
||||
xous::MemoryFlags::R | xous::MemoryFlags::W,
|
||||
)
|
||||
.expect("couldn't map GPIO CSR range");
|
||||
// this particular core does not use CSRs for control - it uses directly memory mapped registers
|
||||
let usb = xous::syscall::map_memory(
|
||||
xous::MemoryAddress::new(utralib::HW_USBDEV_MEM),
|
||||
|
|
@ -199,7 +208,6 @@ impl SpinalUsbDevice {
|
|||
.expect("couldn't map USB CSR range");
|
||||
|
||||
let mut usbdev = SpinalUsbDevice {
|
||||
gpio_csr: AtomicCsr::new(gpio_base.as_mut_ptr() as *mut u32),
|
||||
conn: xous::connect(sid).unwrap(),
|
||||
csr: AtomicCsr::new(csr.as_mut_ptr() as *mut u32),
|
||||
usb,
|
||||
|
|
@ -221,6 +229,7 @@ impl SpinalUsbDevice {
|
|||
],
|
||||
allocs: BTreeMap::new(),
|
||||
};
|
||||
|
||||
xous::claim_interrupt(
|
||||
utra::usbdev::USBDEV_IRQ,
|
||||
handle_usb,
|
||||
|
|
@ -231,6 +240,9 @@ impl SpinalUsbDevice {
|
|||
usbdev.csr.wo(utra::usbdev::EV_PENDING, p); // clear in case it's pending for some reason
|
||||
usbdev.csr.wfo(utra::usbdev::EV_ENABLE_USB, 1);
|
||||
|
||||
// also have to enable ints at the SpinalHDL layer
|
||||
usbdev.regs.config.set_enable_ints(true);
|
||||
|
||||
usbdev
|
||||
}
|
||||
pub fn print_regs(&self) {
|
||||
|
|
@ -250,12 +262,13 @@ impl SpinalUsbDevice {
|
|||
}
|
||||
|
||||
pub fn connect_device_core(&mut self, state: bool) {
|
||||
log::info!("previous state: {}", self.csr.rf(utra::usbdev::USBSELECT_USBSELECT));
|
||||
if state {
|
||||
log::info!("connecting USB device core");
|
||||
self.gpio_csr.wfo(utra::gpio::USBSELECT_USBSELECT, 1);
|
||||
self.csr.wfo(utra::usbdev::USBSELECT_USBSELECT, 1);
|
||||
} else {
|
||||
log::info!("connecting USB debug core");
|
||||
self.gpio_csr.wfo(utra::gpio::USBSELECT_USBSELECT, 0);
|
||||
self.csr.wfo(utra::usbdev::USBSELECT_USBSELECT, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -343,6 +356,35 @@ impl UsbBus for SpinalUsbDevice {
|
|||
/// 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) {
|
||||
self.regs.config.set_disable_ints(true);
|
||||
// clear the endpoint RAM
|
||||
self.eps = [
|
||||
None, None, None, None,
|
||||
None, None, None, None,
|
||||
None, None, None, None,
|
||||
None, None, None, None,
|
||||
];
|
||||
self.allocs.clear();
|
||||
// set the RAM from 0x0-0xFF00 to all 0's
|
||||
let usbmem = self.usb.as_slice_mut::<u32>();
|
||||
for m in usbmem.iter_mut() {
|
||||
*m = 0;
|
||||
}
|
||||
|
||||
// clear the interrupts
|
||||
self.regs.interrupts.clear_endpoint(0xFFFF); // clear all the endpoints
|
||||
self.regs.interrupts.clear_reset(true);
|
||||
self.regs.interrupts.clear_ep0_setup(true);
|
||||
self.regs.interrupts.clear_suspend(true);
|
||||
self.regs.interrupts.clear_resume(true);
|
||||
self.regs.interrupts.clear_disconnect(true);
|
||||
|
||||
// clear other registers
|
||||
self.regs.address = 0;
|
||||
|
||||
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
|
||||
// re-enable the interrupt
|
||||
self.regs.config.set_enable_ints(true);
|
||||
}
|
||||
|
||||
/// Called when the host resets the device. This will be soon called after
|
||||
|
|
@ -351,10 +393,14 @@ impl UsbBus for SpinalUsbDevice {
|
|||
/// enumeration, as well as ensure that all endpoints previously allocated with alloc_ep are
|
||||
/// initialized as specified.
|
||||
fn reset(&self) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
/// Sets the device USB address to `addr`.
|
||||
fn set_device_address(&self, addr: u8) {
|
||||
// apparently we need to implement interior mutablity for all the things to be compatible
|
||||
// with this API...
|
||||
// self.regs.address = addr as u32;
|
||||
}
|
||||
|
||||
/// Writes a single packet of data to the specified endpoint and returns number of bytes
|
||||
|
|
@ -404,12 +450,32 @@ impl UsbBus for SpinalUsbDevice {
|
|||
/// 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) {
|
||||
// it looks like a STALL condition could be forced even on unallocated endpoints, so
|
||||
// we alias into the register block and force it to happen.
|
||||
let ep_status = unsafe {
|
||||
(self.usb.as_mut_ptr().add(ep_addr.index() * size_of::<UdcEpStatus>()) as *mut UdcEpStatus).as_mut().unwrap()
|
||||
};
|
||||
|
||||
match (stalled, ep_addr.direction()) {
|
||||
(true, UsbDirection::In) => {
|
||||
ep_status.set_force_nack(false);
|
||||
ep_status.set_force_stall(true);
|
||||
},
|
||||
(true, UsbDirection::Out) => ep_status.set_force_stall(true),
|
||||
(false, UsbDirection::In) => {
|
||||
ep_status.set_force_nack(true); // not sure if this is correct -- STM32 reference sets state to "nack" but the meaning might be different for this core
|
||||
ep_status.set_force_stall(false);
|
||||
},
|
||||
(false, UsbDirection::Out) => ep_status.set_force_stall(false),
|
||||
};
|
||||
}
|
||||
|
||||
/// Gets whether the STALL condition is set for an endpoint.
|
||||
fn is_stalled(&self, ep_addr: EndpointAddress) -> bool {
|
||||
false
|
||||
let ep_status = unsafe {
|
||||
(self.usb.as_mut_ptr().add(ep_addr.index() * size_of::<UdcEpStatus>()) as *mut UdcEpStatus).as_mut().unwrap()
|
||||
};
|
||||
ep_status.force_stall()
|
||||
}
|
||||
|
||||
/// Causes the USB peripheral to enter USB suspend mode, lowering power consumption and
|
||||
|
|
@ -441,7 +507,12 @@ impl UsbBus for SpinalUsbDevice {
|
|||
/// * [`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)
|
||||
xous::send_message(self.conn,
|
||||
Message::new_blocking_scalar(Opcode::ForceReset.to_usize().unwrap(),
|
||||
0, 0, 0, 0
|
||||
)
|
||||
).expect("couldn't send message");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,16 +25,18 @@ use std::collections::BTreeMap;
|
|||
fn xmain() -> ! {
|
||||
use crate::SpinalUsbDevice;
|
||||
|
||||
let _gpio_base = crate::log_init();
|
||||
log_server::init_wait().unwrap();
|
||||
log::set_max_level(log::LevelFilter::Info);
|
||||
log::info!("my PID is {}", xous::process::id());
|
||||
|
||||
let xns = xous_names::XousNames::new().unwrap();
|
||||
let usbtest_sid = xns.register_name(api::SERVER_NAME_USBTEST, None).expect("can't register server");
|
||||
log::trace!("registered with NS -- {:?}", usbtest_sid);
|
||||
let usbdev_sid = xns.register_name(api::SERVER_NAME_USBTEST, None).expect("can't register server");
|
||||
log::trace!("registered with NS -- {:?}", usbdev_sid);
|
||||
|
||||
let mut usbtest = SpinalUsbDevice::new(usbtest_sid);
|
||||
let mut kbd = Keyboard::new(usbtest_sid);
|
||||
let mut usbdev = SpinalUsbDevice::new(usbdev_sid);
|
||||
let mut kbd = Keyboard::new(usbdev_sid);
|
||||
let tt = ticktimer_server::Ticktimer::new().unwrap();
|
||||
|
||||
log::trace!("ready to accept requests");
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ fn xmain() -> ! {
|
|||
});
|
||||
|
||||
// register a suspend/resume listener
|
||||
let cid = xous::connect(usbtest_sid).expect("couldn't create suspend callback connection");
|
||||
let cid = xous::connect(usbdev_sid).expect("couldn't create suspend callback connection");
|
||||
let mut susres = susres::Susres::new(
|
||||
None,
|
||||
&xns,
|
||||
|
|
@ -61,17 +63,24 @@ fn xmain() -> ! {
|
|||
|
||||
let mut cmdline = String::new();
|
||||
loop {
|
||||
let msg = xous::receive_message(usbtest_sid).unwrap();
|
||||
let msg = xous::receive_message(usbdev_sid).unwrap();
|
||||
match FromPrimitive::from_usize(msg.body.id()) {
|
||||
Some(Opcode::SuspendResume) => xous::msg_scalar_unpack!(msg, token, _, _, _, {
|
||||
kbd.suspend();
|
||||
usbtest.xous_suspend();
|
||||
usbdev.xous_suspend();
|
||||
susres.suspend_until_resume(token).expect("couldn't execute suspend/resume");
|
||||
kbd.resume();
|
||||
usbtest.xous_resume();
|
||||
usbdev.xous_resume();
|
||||
}),
|
||||
Some(Opcode::UsbIrqHandler) => {
|
||||
log::info!("got USB interrupt");
|
||||
usbdev.print_regs();
|
||||
}
|
||||
Some(Opcode::ForceReset) => {
|
||||
usbdev.connect_device_core(false);
|
||||
tt.sleep_ms(5).unwrap(); // have to wait long enough for the auto-reset mechanism to kick in
|
||||
usbdev.connect_device_core(true);
|
||||
xous::return_scalar(msg.sender, 0).unwrap();
|
||||
}
|
||||
Some(Opcode::DoCmd) => {
|
||||
log::info!("got command line: {}", cmdline);
|
||||
|
|
@ -83,13 +92,20 @@ fn xmain() -> ! {
|
|||
}
|
||||
"conn" => {
|
||||
match args {
|
||||
"1" => usbtest.connect_device_core(true),
|
||||
"0" => usbtest.connect_device_core(false),
|
||||
"1" => {
|
||||
usbdev.connect_device_core(true);
|
||||
log::info!("device core connected");
|
||||
},
|
||||
"0" => {
|
||||
usbdev.connect_device_core(false);
|
||||
log::info!("debug core connected");
|
||||
},
|
||||
_ => log::info!("usage: conn [1,0]; got: 'conn {}'", args),
|
||||
}
|
||||
usbdev.print_regs();
|
||||
}
|
||||
_ => {
|
||||
log::info!("unrecognied command {}", cmd);
|
||||
log::info!("unrecognized command {}", cmd);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -99,12 +115,12 @@ fn xmain() -> ! {
|
|||
log::info!("wouldn't that be nice...");
|
||||
}
|
||||
"conn" => {
|
||||
usbtest.connect_device_core(true);
|
||||
usbdev.connect_device_core(true);
|
||||
log::info!("device core connected");
|
||||
usbtest.print_regs();
|
||||
usbdev.print_regs();
|
||||
}
|
||||
"regs" => {
|
||||
usbtest.print_regs();
|
||||
usbdev.print_regs();
|
||||
}
|
||||
_ => {
|
||||
log::info!("unrecognized command");
|
||||
|
|
@ -160,8 +176,8 @@ fn xmain() -> ! {
|
|||
}
|
||||
// clean up our program
|
||||
log::trace!("main loop exit, destroying servers");
|
||||
xns.unregister_server(usbtest_sid).unwrap();
|
||||
xous::destroy_server(usbtest_sid).unwrap();
|
||||
xns.unregister_server(usbdev_sid).unwrap();
|
||||
xous::destroy_server(usbdev_sid).unwrap();
|
||||
log::trace!("quitting");
|
||||
xous::terminate_process(0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
None,
|
||||
None,
|
||||
None,
|
||||
extra_packages.as_slice(), Some(&["--no-default-features", "--features", "renode-bypass", "--features", "debug-print"]))?;
|
||||
extra_packages.as_slice(), None)?;
|
||||
}
|
||||
}
|
||||
Some("libstd-test") => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue