diff --git a/services/usb-test/src/api.rs b/services/usb-test/src/api.rs index aa52304cc..de64eec59 100644 --- a/services/usb-test/src/api.rs +++ b/services/usb-test/src/api.rs @@ -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 diff --git a/services/usb-test/src/hw.rs b/services/usb-test/src/hw.rs index 6e96aa59d..bed59c291 100644 --- a/services/usb-test/src/hw.rs +++ b/services/usb-test/src/hw.rs @@ -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, usb: xous::MemoryRange, - csr: AtomicCsr, + csr: AtomicCsr, // consider using VolatileCell and/or refactory AtomicCsr so it is non-mutable srmem: ManagedMem<{ utralib::generated::HW_USBDEV_MEM_LEN / core::mem::size_of::() }>, 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::(); + 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::()) 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::()) 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(()) } } diff --git a/services/usb-test/src/main.rs b/services/usb-test/src/main.rs index 56a169a1f..357264cc1 100644 --- a/services/usb-test/src/main.rs +++ b/services/usb-test/src/main.rs @@ -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) } diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 970af9df4..ff89c52d8 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -312,7 +312,7 @@ fn main() -> Result<(), Box> { 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") => {