diff options
Diffstat (limited to 'app/src/usb.rs')
-rw-r--r-- | app/src/usb.rs | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/app/src/usb.rs b/app/src/usb.rs new file mode 100644 index 0000000..c15e1b5 --- /dev/null +++ b/app/src/usb.rs @@ -0,0 +1,612 @@ +mod pipe; +mod usbproto; + +use crate::{log, logln, logln_now, rtc}; +use pipe::{DataBuf, PipeErr, PipeTable, USBPipeType, USBToken}; +use rb::{Reader, RingBuffer, Writer}; +use usbproto::*; + +use embedded_hal::digital::v2::OutputPin; +use trinket_m0::{ + calibration::{usb_transn_cal, usb_transp_cal, usb_trim_cal}, + clock::{ClockGenId, ClockSource, GenericClockController}, + gpio::{self, Floating, Input, OpenDrain, Output}, + PM, USB, +}; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Event { + Error, + Detached, + Attached, +} +type Events = RingBuffer<Event>; +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 { + 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<gpio::PfG>, + _dm_pad: gpio::Pa24<gpio::PfG>, + _dp_pad: gpio::Pa25<gpio::PfG>, + host_enable_pin: Option<gpio::Pa28<Output<OpenDrain>>>, +} + +impl USBHost { + pub fn new( + usb: USB, + sof_pin: gpio::Pa23<Input<Floating>>, + dm_pin: gpio::Pa24<Input<Floating>>, + dp_pin: gpio::Pa25<Input<Floating>>, + host_enable_pin: Option<gpio::Pa28<Input<Floating>>>, + port: &mut gpio::Port, + clocks: &mut GenericClockController, + pm: &mut PM, + ) -> (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, + }; + + if let Some(he_pin) = host_enable_pin { + rc.host_enable_pin = Some(he_pin.into_open_drain_output(port)); + } + + logln_now!("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) { + log!("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()); + logln!("...done"); + } + + pub fn task(&mut self) { + static mut LAST_EVENT: Event = Event::Error; + unsafe { + if LAST_EVENT != LATEST_EVENT { + logln!("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 = rtc::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 { + logln!( + "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() { + // logln!("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 = rtc::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 rtc::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() { + logln!("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 = rtc::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 rtc::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) => { + logln!("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(); + logln_now!( + " -- len: {}, ver: {:04x}, bMaxPacketSize: {}, bNumConfigurations: {}", + desc.b_length, + desc.bcd_usb, + desc.b_max_packet_size, + desc.b_num_configurations + ); + logln_now!(" -- 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, + )?; + logln_now!(" -- address set"); + // Delay according to §9.2.6.3 of USB 2.0 + let until = rtc::millis() + 300; + while rtc::millis() < until {} + + // Once addressed, SET_CONFIGURATION(0) + logln_now!("+++ setting configuration"); + let conf: u8 = 0; + self.control_req( + new_address, + 0, + BMRequestType::set(), + USBRequest::SetConfiguration, + WValue::from((conf, 0)), + 0, + None, + )?; + logln_now!(" -- 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<DataBuf>, + ) -> 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)?; + + /* + * Data stage. + */ + if let Some(b) = buf { + match bm_request_type.direction() { + USBSetupDirection::DeviceToHost => { + logln_now!("buf0: {:?}", &b); + pipe.in_transfer(&b, NAK_LIMIT)?; + logln_now!("buf1: {:?}", &b); + } + + USBSetupDirection::HostToDevice => { + logln_now!("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`. + logln_now!("dispatching status stage"); + pipe.dispatch_retries(token, NAK_LIMIT)?; + 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(); + + logln!("USB - {:x}", flags.bits()); + + let mut unshift_event = |e: Event| { + unsafe { LATEST_EVENT = e }; + if let Err(_) = events.unshift(e) { + logln!("Couldn't write USB event to queue."); + } + }; + + if flags.hsof().bit_is_set() { + logln!(" +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. + logln!(" +rst"); + usb.host().intflag.write(|w| w.rst().set_bit()); + unshift_event(Event::Detached); + } + + if flags.uprsm().bit_is_set() { + logln!(" +uprsm"); + usb.host().intflag.write(|w| w.uprsm().set_bit()); + unshift_event(Event::Detached); + } + + if flags.dnrsm().bit_is_set() { + logln!(" +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. + logln!(" +wakeup"); + usb.host().intflag.write(|w| w.wakeup().set_bit()); + unshift_event(Event::Attached); + } + + if flags.ramacer().bit_is_set() { + logln!(" +ramacer"); + usb.host().intflag.write(|w| w.ramacer().set_bit()); + unshift_event(Event::Detached); + } + + if flags.dconn().bit_is_set() { + logln!(" +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() { + logln!(" +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); + } +} |