aboutsummaryrefslogtreecommitdiffstats
path: root/app/src/usb.rs
diff options
context:
space:
mode:
authorBrian Cully <bjc@kublai.com>2019-06-24 21:14:32 -0400
committerBrian Cully <bjc@kublai.com>2019-07-23 19:55:18 -0400
commit63d4ce8154eb8f6feb67986e98ea9b5007632460 (patch)
tree187e66104eb7c848dcf73a5ee067c5f27235360a /app/src/usb.rs
downloadsamd21-demo-63d4ce8154eb8f6feb67986e98ea9b5007632460.tar.gz
samd21-demo-63d4ce8154eb8f6feb67986e98ea9b5007632460.zip
Initial commit.
Diffstat (limited to 'app/src/usb.rs')
-rw-r--r--app/src/usb.rs612
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);
+ }
+}