#![no_std] #![allow(dead_code)] mod device; mod pipe; mod usbproto; use device::DeviceTable; use pipe::{DataBuf, PipeErr, PipeTable, USBPipeType, USBToken}; 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, WaitResetComplete, WaitSOF, } #[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, delay: usize, // 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), delay: 0, 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 { self.delay = (self.millis)() + SETTLE_DELAY; TaskState::Attached(AttachedState::WaitForSettle) } 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.poll_devices(); 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 => { if (self.millis)() >= self.delay { 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.delay = (self.millis)() + 20; self.task_state = TaskState::Attached(AttachedState::WaitSOF); } } AttachedState::WaitSOF => { if self.usb.host().intflag.read().hsof().bit_is_set() { self.usb.host().intflag.write(|w| w.hsof().set_bit()); if (self.millis)() >= self.delay { self.task_state = TaskState::Steady(SteadyState::Configuring); } } } } } fn steady_fsm(&mut self, s: SteadyState) { match s { SteadyState::Configuring => { let low_speed = 0; self.task_state = match self.configure_dev(0, 0, low_speed) { Ok(_) => TaskState::Steady(SteadyState::Running), Err(e) => { warn!("Enumeration error: {:?}", e); TaskState::Steady(SteadyState::Error) } } } SteadyState::Running => {} SteadyState::Error => {} } } fn configure_dev(&mut self, _parent: u32, _port: u32, _low_speed: u32) -> Result<(), PipeErr> { let mut vol_descr = ::vcell::VolatileCell::::new(Default::default()); self.control_req( 0, 0, BMRequestType::get_descr(), USBRequest::GetDescriptor, WValue::from((0, USBDescriptor::Device as u8)), 0, Some(DataBuf::from(&mut vol_descr)), )?; let desc = vol_descr.get(); trace!(" -- devDesc: {:?}", desc); // Assign address to this device and: // - Stash bMaxPacketSize // Then SET_ADDRESS(newAddr) let new_address: u8 = 5; debug!("Setting address to {}.", new_address); self.control_req( 0, 0, BMRequestType::set(), USBRequest::SetAddress, WValue::from((new_address, 0)), 0, None, )?; // Delay according to §9.2.6.3 of USB 2.0 let until = (self.millis)() + 300; while (self.millis)() < until {} // Everything after this point is what linux does. So do that? // Dunno why we get the device descriptor a second time. trace!( " -- getting devDesc2 from {}:{}: {:?}", new_address, 0, desc ); let tmp: USBDeviceDescriptor = Default::default(); let mut vol_descr = ::vcell::VolatileCell::new(tmp); self.control_req( new_address, 0, BMRequestType::get_descr(), USBRequest::GetDescriptor, WValue::from((0, USBDescriptor::Device as u8)), 0, Some(DataBuf::from(&mut vol_descr)), )?; let desc = vol_descr.get(); trace!(" -- devDesc2: {:?}", desc); // Get config descriptor with minimal data, to see how much we need to allocate for the full descriptor. let mut vol_descr = ::vcell::VolatileCell::::new(Default::default()); self.control_req( new_address, 0, BMRequestType::get_descr(), USBRequest::GetDescriptor, WValue::from((0, USBDescriptor::Configuration as u8)), 0, Some(DataBuf::from(&mut vol_descr)), )?; let desc = vol_descr.get(); debug!("config: {:?}", desc); // TODO: do real allocation later. assert!(desc.w_total_length < 64); let buf: [u8; 64] = [0; 64]; let mut tmp = &buf[..desc.w_total_length as usize]; self.control_req( new_address, 0, BMRequestType::get_descr(), USBRequest::GetDescriptor, WValue::from((0, USBDescriptor::Configuration as u8)), 0, Some(DataBuf::from(&mut tmp)), )?; // Once addressed, SET_CONFIGURATION(0) debug!("+++ setting configuration"); let conf: u8 = 1; self.control_req( new_address, 0, BMRequestType::set(), USBRequest::SetConfiguration, WValue::from((conf, 0)), 0, None, )?; debug!(" -- configuration set"); // Now we should be able to access it normally. Ok(()) } fn control_req( &mut self, addr: u8, ep: u8, bm_request_type: BMRequestType, b_request: USBRequest, w_value: WValue, w_index: u16, buf: Option, ) -> Result<(), PipeErr> { if let Some(ref b) = buf { assert!(b.ptr as usize & 0x3 == 0); assert!(b.len <= 65_535); } // Pipe data toggles for control pipes defined in SAMD21 data // sheet §32.6.3.9 /* * Setup stage. */ let mut setup_packet = USBSetupPacket { bm_request_type: bm_request_type, b_request: b_request, w_value: w_value, w_index: w_index, w_length: match buf { None => 0, Some(ref b) => b.len as u16, }, }; let mut pipe = self.pipe_table.pipe_for(self.usb.host_mut(), addr, ep); pipe.dtgl_clear(); pipe.send( USBToken::Setup, &DataBuf::from(&mut setup_packet), NAK_LIMIT, self.millis, )?; /* * Data stage. */ pipe.dtgl_set(); if let Some(b) = buf { match bm_request_type.direction() { USBSetupDirection::DeviceToHost => { trace!("buf0: {:?}", &b); pipe.in_transfer(&b, NAK_LIMIT, self.millis)?; trace!("buf1: {:?}", &b); } USBSetupDirection::HostToDevice => { debug!("Should OUT for {}b", b.len); } } } /* * Status stage. */ pipe.dtgl_set(); pipe.desc.bank0.pcksize.write(|w| { unsafe { w.byte_count().bits(0) }; unsafe { w.multi_packet_size().bits(0) } }); // PSTATUSSET.DTGL set -- TODO: figure out if this is // necessary. pipe.regs.statusset.write(|w| w.dtgl().set_bit()); let token = match bm_request_type.direction() { USBSetupDirection::DeviceToHost => USBToken::Out, USBSetupDirection::HostToDevice => USBToken::In, }; // TODO: should probably make `pipe.send` have optional // `DataBuf`, rather than exposing `dispatch_retries`. trace!("dispatching status stage"); pipe.dispatch_retries(token, NAK_LIMIT, self.millis)?; 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); } }