#![no_std] #![allow(dead_code)] mod device; mod pipe; mod usbproto; use device::DeviceTable; use pipe::{DataBuf, PipeErr, PipeTable, USBPipeType}; use rb::{Reader, RingBuffer, Writer}; use usbproto::*; use atsamd_hal::{ calibration::{usb_transn_cal, usb_transp_cal, usb_trim_cal}, clock::{ClockGenId, ClockSource, GenericClockController}, gpio::{self, Floating, Input, OpenDrain, Output}, target_device::{PM, USB}, }; use embedded_hal::digital::v2::OutputPin; use log::{debug, error, trace, warn}; #[derive(Clone, Copy, Debug, PartialEq)] pub enum Event { Error, Detached, Attached, } type Events = RingBuffer; type EventReader = Reader<'static, Event>; type EventWriter = Writer<'static, Event>; #[derive(Clone, Copy, Debug, PartialEq)] enum DetachedState { Initialize, WaitForDevice, Illegal, } #[derive(Clone, Copy, Debug, PartialEq)] enum AttachedState { WaitForSettle(usize), WaitResetComplete, WaitSOF(usize), } #[derive(Clone, Copy, Debug, PartialEq)] enum SteadyState { Configuring, Running, Error, } #[derive(Clone, Copy, Debug, PartialEq)] enum TaskState { Detached(DetachedState), Attached(AttachedState), Steady(SteadyState), } const SETTLE_DELAY: usize = 205; // Delay in sec/1024 const NAK_LIMIT: usize = 15; static mut EVENTS: Events = Events::new(Event::Error); // FIXME: this is just for testing. The enum needs to be // thread-safe if this is the way we're going. static mut LATEST_EVENT: Event = Event::Detached; pub struct USBHost where F: Fn() -> usize + 'static, { usb: USB, events: EventReader, task_state: TaskState, // Need chunk of RAM for USB pipes, which gets used with DESCADD // register. pipe_table: PipeTable, devices: DeviceTable, // need sof 1kHz pad? _sof_pad: gpio::Pa23, _dm_pad: gpio::Pa24, _dp_pad: gpio::Pa25, host_enable_pin: Option>>, // To get current milliseconds. millis: &'static F, } impl USBHost where F: Fn() -> usize + 'static, { pub fn new( usb: USB, sof_pin: gpio::Pa23>, dm_pin: gpio::Pa24>, dp_pin: gpio::Pa25>, host_enable_pin: Option>>, port: &mut gpio::Port, clocks: &mut GenericClockController, pm: &mut PM, millis: &'static F, ) -> (Self, impl FnMut()) { let (eventr, mut eventw) = unsafe { EVENTS.split() }; let mut rc = Self { usb: usb, events: eventr, task_state: TaskState::Detached(DetachedState::Initialize), pipe_table: PipeTable::new(), devices: DeviceTable::new(), _sof_pad: sof_pin.into_function_g(port), _dm_pad: dm_pin.into_function_g(port), _dp_pad: dp_pin.into_function_g(port), host_enable_pin: None, millis: millis, }; if let Some(he_pin) = host_enable_pin { rc.host_enable_pin = Some(he_pin.into_open_drain_output(port)); } pm.apbbmask.modify(|_, w| w.usb_().set_bit()); // Set up USB clock from 48MHz source on generic clock 6. clocks.configure_gclk_divider_and_source(ClockGenId::GCLK6, 1, ClockSource::DFLL48M, false); let gclk6 = clocks .get_gclk(ClockGenId::GCLK6) .expect("Could not get clock 6"); clocks.usb(&gclk6); let usbp = &rc.usb as *const _ as usize; (rc, move || handler(usbp, &mut eventw)) } pub fn reset_periph(&mut self) { debug!("resetting usb"); // Reset the USB peripheral and wait for sync. self.usb.host().ctrla.write(|w| w.swrst().set_bit()); while self.usb.host().syncbusy.read().swrst().bit_is_set() {} // Specify host mode. self.usb.host().ctrla.modify(|_, w| w.mode().host()); // Unsafe due to use of raw bits method. unsafe { self.usb.host().padcal.write(|w| { w.transn().bits(usb_transn_cal()); w.transp().bits(usb_transp_cal()); w.trim().bits(usb_trim_cal()) }); } // Use normal, which is 0 and apparently means low-and-full capable self.usb.host().ctrlb.modify(|_, w| w.spdconf().normal()); // According to docs, 1,2,3 are reserved, but .fs returns 3 //self.usb.host().ctrlb.modify(|_, w| w.spdconf().fs()); self.usb.host().ctrla.modify(|_, w| w.runstdby().set_bit()); // keep usb clock running in standby. // Set address of USB SRAM. // Unsafe due to use of raw bits method. unsafe { self.usb .host() .descadd .write(|w| w.bits(&self.pipe_table as *const _ as u32)); } if let Some(he_pin) = &mut self.host_enable_pin { he_pin.set_high().expect("turning on usb host enable pin"); } self.usb.host().intenset.write(|w| { w.wakeup().set_bit(); w.dconn().set_bit(); w.ddisc().set_bit() }); self.usb.host().ctrla.modify(|_, w| w.enable().set_bit()); while self.usb.host().syncbusy.read().enable().bit_is_set() {} // Set VBUS OK to allow host operation. self.usb.host().ctrlb.modify(|_, w| w.vbusok().set_bit()); debug!("...done"); } pub fn task(&mut self) { static mut LAST_EVENT: Event = Event::Error; unsafe { if LAST_EVENT != LATEST_EVENT { trace!("new event: {:?}", LATEST_EVENT); } } static mut LAST_TASK_STATE: TaskState = TaskState::Detached(DetachedState::Illegal); self.task_state = match unsafe { LATEST_EVENT } { Event::Error => TaskState::Detached(DetachedState::Illegal), Event::Detached => { if let TaskState::Detached(_) = self.task_state { self.task_state } else { TaskState::Detached(DetachedState::Initialize) } } Event::Attached => { if let TaskState::Detached(_) = self.task_state { TaskState::Attached(AttachedState::WaitForSettle( (self.millis)() + SETTLE_DELAY, )) } else { self.task_state } } }; static mut LAST_CBITS: u16 = 0; static mut LAST_FLAGS: u16 = 0; let cbits = self.usb.host().ctrlb.read().bits(); let bits = self.usb.host().intflag.read().bits(); unsafe { if LAST_CBITS != cbits || LAST_FLAGS != bits || LAST_TASK_STATE != self.task_state { trace!( "cb: {:x}, f: {:x} changing state {:?} -> {:?}", cbits, bits, LAST_TASK_STATE, self.task_state, ); } LAST_CBITS = cbits; LAST_FLAGS = bits; LAST_TASK_STATE = self.task_state }; if let Some(_event) = self.events.shift() { // trace!("Found event: {:?}", event); // self.task_state = match event { // Event::None => TaskState::Detached(DetachedState::Illegal), // Event::Detached => { // if let TaskState::Detached(_) = self.task_state { // self.task_state // } else { // TaskState::Detached(DetachedState::Initialize) // } // } // Event::Attached => { // if let TaskState::Detached(_) = self.task_state { // self.delay = self.millis() + SETTLE_DELAY; // TaskState::Attached(AttachedState::WaitForSettle) // } else { // self.task_state // } // } // }; } self.fsm(); unsafe { LAST_EVENT = LATEST_EVENT; } } fn poll_devices(&mut self) {} fn fsm(&mut self) { // respond to events from interrupt. match self.task_state { TaskState::Detached(s) => self.detached_fsm(s), TaskState::Attached(s) => self.attached_fsm(s), TaskState::Steady(s) => self.steady_fsm(s), }; } fn detached_fsm(&mut self, s: DetachedState) { match s { DetachedState::Initialize => { self.reset_periph(); // TODO: Free resources. self.task_state = TaskState::Detached(DetachedState::WaitForDevice); } // Do nothing state. Just wait for an interrupt to come in // saying we have a device attached. DetachedState::WaitForDevice => {} // TODO: should probably reset everything if we end up here somehow. DetachedState::Illegal => {} } } fn attached_fsm(&mut self, s: AttachedState) { match s { AttachedState::WaitForSettle(until) => { if (self.millis)() >= until { self.usb.host().ctrlb.modify(|_, w| w.busreset().set_bit()); self.task_state = TaskState::Attached(AttachedState::WaitResetComplete); } } AttachedState::WaitResetComplete => { if self.usb.host().intflag.read().rst().bit_is_set() { trace!("reset was sent"); self.usb.host().intflag.write(|w| w.rst().set_bit()); // Make sure we always have a control pipe set up. self.init_pipe0(); // Seems unneccesary, since SOFE will be set // immediately after reset according to §32.6.3.3. self.usb.host().ctrlb.modify(|_, w| w.sofe().set_bit()); // USB spec requires 20ms of SOF after bus reset. self.task_state = TaskState::Attached(AttachedState::WaitSOF((self.millis)() + 20)); } } AttachedState::WaitSOF(until) => { if self.usb.host().intflag.read().hsof().bit_is_set() { self.usb.host().intflag.write(|w| w.hsof().set_bit()); if (self.millis)() >= until { self.task_state = TaskState::Steady(SteadyState::Configuring); } } } } } fn steady_fsm(&mut self, s: SteadyState) { match s { SteadyState::Configuring => { self.task_state = match self.configure_dev() { Ok(_) => TaskState::Steady(SteadyState::Running), Err(e) => { warn!("Enumeration error: {:?}", e); TaskState::Steady(SteadyState::Error) } } } SteadyState::Running => { self.devices.run(&mut self.pipe_table, self.usb.host_mut()); } SteadyState::Error => {} } } fn configure_dev(&mut self) -> Result<(), PipeErr> { let mut pipe = self.pipe_table.pipe_for(self.usb.host_mut(), 0, 0); let mut vol_descr = ::vcell::VolatileCell::::new(Default::default()); pipe.control_req( BMRequestType::get_descr(), USBRequest::GetDescriptor, WValue::from((0, USBDescriptor::Device as u8)), 0, Some(DataBuf::from(&mut vol_descr)), self.millis, )?; let desc = vol_descr.get(); trace!(" -- devDesc: {:?}", desc); match self.devices.next(self.millis) { // TODO: new error for being out of devices. None => Err(PipeErr::Other), Some(device) => { device.max_packet_size = desc.b_max_packet_size; debug!("Setting address to {}.", device.addr); pipe.control_req( BMRequestType::set(), USBRequest::SetAddress, WValue::from((device.addr, 0)), 0, None, self.millis, )?; // Now that the device is addressed, `Device` can handle the // rest of the setup in its FSM. Ok(()) } } } // Set up a default pipe for the control endpoint 0 on pipe 0. fn init_pipe0(&mut self) { let speed = self.usb.host().status.read().speed().bits(); let pipe = self.pipe_table.pipe_for(self.usb.host_mut(), 0, 0); pipe.regs.cfg.write(|w| { unsafe { w.ptype().bits(USBPipeType::Control as u8) }; w.bk().clear_bit() }); pipe.desc.bank0.pcksize.write(|w| match speed { 0 => w.size().bytes64(), _ => w.size().bytes8(), }); } } pub fn handler(usbp: usize, events: &mut EventWriter) { let usb: &mut USB = unsafe { core::mem::transmute(usbp) }; let flags = usb.host().intflag.read(); trace!("USB - {:x}", flags.bits()); let mut unshift_event = |e: Event| { unsafe { LATEST_EVENT = e }; if let Err(_) = events.unshift(e) { error!("Couldn't write USB event to queue."); } }; if flags.hsof().bit_is_set() { trace!(" +hsof"); usb.host().intflag.write(|w| w.hsof().set_bit()); unshift_event(Event::Attached); } if flags.rst().bit_is_set() { // We seem to get this whenever a device attaches/detaches. trace!(" +rst"); usb.host().intflag.write(|w| w.rst().set_bit()); unshift_event(Event::Detached); } if flags.uprsm().bit_is_set() { trace!(" +uprsm"); usb.host().intflag.write(|w| w.uprsm().set_bit()); unshift_event(Event::Detached); } if flags.dnrsm().bit_is_set() { trace!(" +dnrsm"); usb.host().intflag.write(|w| w.dnrsm().set_bit()); unshift_event(Event::Detached); } if flags.wakeup().bit_is_set() { // §32.8.5.8 - since VBUSOK is set, then this happens when a // device is connected. trace!(" +wakeup"); usb.host().intflag.write(|w| w.wakeup().set_bit()); unshift_event(Event::Attached); } if flags.ramacer().bit_is_set() { trace!(" +ramacer"); usb.host().intflag.write(|w| w.ramacer().set_bit()); unshift_event(Event::Detached); } if flags.dconn().bit_is_set() { trace!(" +dconn"); usb.host().intflag.write(|w| w.dconn().set_bit()); usb.host().intenclr.write(|w| w.dconn().set_bit()); usb.host().intflag.write(|w| w.ddisc().set_bit()); usb.host().intenset.write(|w| w.ddisc().set_bit()); usb.host().intflag.write(|w| w.dconn().set_bit()); unshift_event(Event::Attached); } if flags.ddisc().bit_is_set() { trace!(" +ddisc"); usb.host().intflag.write(|w| w.ddisc().set_bit()); usb.host().intenclr.write(|w| w.ddisc().set_bit()); // // Stop reset signal, in case of disconnection during reset // uhd_stop_reset(); // nothing on samd21 usb.host().intflag.write(|w| w.dconn().set_bit()); usb.host().intenset.write(|w| w.dconn().set_bit()); usb.host().intflag.write(|w| w.ddisc().set_bit()); unshift_event(Event::Detached); } }