#![no_std] mod pipe; mod usbproto; 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::{info, 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 MAX_DEVICES: usize = 16; 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; #[repr(C)] #[derive(Debug)] struct EPInfo { ep_addr: u32, mak_pkt_size: u32, ep_attribs: u8, } impl EPInfo { fn bm_snd_toggle(&self) -> bool { const POS: u8 = 0; const MASK: u8 = 0x1; ((self.ep_attribs >> POS) & MASK) == 1 } fn bm_rcv_toggle(&self) -> bool { const POS: u8 = 1; const MASK: u8 = 0x1; ((self.ep_attribs >> POS) & MASK) == 1 } fn bm_nak_power(&self) -> u8 { const POS: u8 = 2; const MASK: u8 = 0x3f; (self.ep_attribs >> POS) & MASK } } #[derive(Debug)] struct USBDeviceAddress(u32); impl USBDeviceAddress { fn bm_address(&self) -> u8 { const POS: u8 = 0; const MASK: u32 = 0x7; ((self.0 >> POS) & MASK) as u8 } fn bm_parent(&self) -> u8 { const POS: u8 = 3; const MASK: u32 = 0x7; ((self.0 >> POS) & MASK) as u8 } fn bm_hub(&self) -> bool { const POS: u8 = 6; const MASK: u32 = 0x1; ((self.0 >> POS) & MASK) == 1 } } 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, // 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(), _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)); } info!("setting up usb clock"); 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); rc.reset_periph(); let usbp = &rc.usb as *const _ as usize; (rc, move || handler(usbp, &mut eventw)) } pub fn reset_periph(&mut self) { info!("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()); info!("...done"); } pub fn task(&mut self) { static mut LAST_EVENT: Event = Event::Error; unsafe { if LAST_EVENT != LATEST_EVENT { info!("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 { info!( "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() { // info!("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) { for _ in 0..MAX_DEVICES {} } 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() { info!("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> { // addr: 0x20007774 let tmp: USBDeviceDescriptor = Default::default(); // addr: 0x20007788 let vol_descr = ::vcell::VolatileCell::new(tmp); self.control_req( 0, 0, BMRequestType::get_descr(), USBRequest::GetDescriptor, WValue::from((0, USBDescriptor::Device as u8)), 0, Some(DataBuf::from(&vol_descr)), )?; let desc = vol_descr.get(); info!( " -- len: {}, ver: {:04x}, bMaxPacketSize: {}, bNumConfigurations: {}", desc.b_length, desc.bcd_usb, desc.b_max_packet_size, desc.b_num_configurations ); info!(" -- vid: {:x}, pid: {:x}", desc.id_vendor, desc.id_product); // Assign address to this device and: // - Stash bMaxPacketSize // Then SET_ADDRESS(newAddr) let new_address: u8 = 1; self.control_req( 0, 0, BMRequestType::set(), USBRequest::SetAddress, WValue::from((new_address, 0)), 0, None, )?; info!(" -- address set"); // Delay according to §9.2.6.3 of USB 2.0 let until = (self.millis)() + 300; while (self.millis)() < until {} info!("getting config"); let tmp: USBConfigurationDescriptor = Default::default(); //let vol_descr = ::vcell::VolatileCell::new(tmp); self.control_req( new_address, 0, BMRequestType::get_descr(), USBRequest::GetConfiguration, WValue::from((0, 0)), 0, Some(DataBuf::from(&tmp)), )?; //let desc = vol_descr.get(); info!("cdesc.len: {}", tmp.b_length); // Once addressed, SET_CONFIGURATION(0) info!("+++ setting configuration"); let conf: u8 = 0; self.control_req( new_address, 0, BMRequestType::set(), USBRequest::SetConfiguration, WValue::from((conf, 0)), 0, None, )?; info!(" -- 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); } /* * Setup stage. */ let 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.send( USBToken::Setup, &DataBuf::from(&setup_packet), NAK_LIMIT, self.millis, )?; /* * Data stage. */ if let Some(b) = buf { match bm_request_type.direction() { USBSetupDirection::DeviceToHost => { info!("buf0: {:?}", &b); pipe.in_transfer(&b, NAK_LIMIT, self.millis)?; info!("buf1: {:?}", &b); } USBSetupDirection::HostToDevice => { info!("Should OUT for {}b", b.len); } } } /* * Status stage. */ pipe.desc.bank0.pcksize.write(|w| { // FIXME: see note in `Pipe.send`. unsafe { w.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`. info!("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(); info!("USB - {:x}", flags.bits()); let mut unshift_event = |e: Event| { unsafe { LATEST_EVENT = e }; if let Err(_) = events.unshift(e) { info!("Couldn't write USB event to queue."); } }; if flags.hsof().bit_is_set() { info!(" +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. info!(" +rst"); usb.host().intflag.write(|w| w.rst().set_bit()); unshift_event(Event::Detached); } if flags.uprsm().bit_is_set() { info!(" +uprsm"); usb.host().intflag.write(|w| w.uprsm().set_bit()); unshift_event(Event::Detached); } if flags.dnrsm().bit_is_set() { info!(" +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. info!(" +wakeup"); usb.host().intflag.write(|w| w.wakeup().set_bit()); unshift_event(Event::Attached); } if flags.ramacer().bit_is_set() { info!(" +ramacer"); usb.host().intflag.write(|w| w.ramacer().set_bit()); unshift_event(Event::Detached); } if flags.dconn().bit_is_set() { info!(" +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() { info!(" +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); } }