diff options
Diffstat (limited to 'usbh/src/pipe.rs')
-rw-r--r-- | usbh/src/pipe.rs | 91 |
1 files changed, 67 insertions, 24 deletions
diff --git a/usbh/src/pipe.rs b/usbh/src/pipe.rs index 833ddea..af2bc33 100644 --- a/usbh/src/pipe.rs +++ b/usbh/src/pipe.rs @@ -28,6 +28,7 @@ const MAX_PIPES: usize = 8; #[derive(Copy, Clone, Debug, PartialEq)] pub(crate) enum PipeErr { + ShortPacket, InvalidPipe, InvalidToken, Stall, @@ -123,18 +124,21 @@ impl Pipe<'_, '_> { // byte_count section of register is 14 bits. assert!(buf.len < 16_384); + // TODO: pull this from pipe descriptor for this addr/ep. + let packet_size = 8; + trace!("p{}: Should IN for {}b.", self.num, buf.len); self.desc.bank0.pcksize.write(|w| { unsafe { w.byte_count().bits(buf.len as u16) }; unsafe { w.multi_packet_size().bits(0) } }); + // Read until we get a short packet (indicating that there's + // nothing left for us in this transaction) or the buffer is + // full. let mut bytes_received = 0; while bytes_received < buf.len { - // Update buffer pointer. - // - // FIXME: This only works when the packet size is a word - // multiple, since `addr` requires word-aligned pointers. + // Move the buffer pointer forward as we get data. self.desc .bank0 .addr @@ -142,21 +146,70 @@ impl Pipe<'_, '_> { self.regs.statusclr.write(|w| w.bk0rdy().set_bit()); self.dispatch_retries(USBToken::In, nak_limit, millis)?; - bytes_received += self.desc.bank0.pcksize.read().byte_count().bits() as usize; - assert!(bytes_received <= buf.len); + let recvd = self.desc.bank0.pcksize.read().byte_count().bits() as usize; + bytes_received += recvd; trace!("!! read {} of {}", bytes_received, buf.len); + if recvd < packet_size { + // If we receive a short packet, we should be done + // here. It /may/ be possible to get a short packet + // and continue, but only if recvd is a word-sized + // multiple. I'm going to assume, for simplicity's + // sake, that that's not possible. + break; + } + + // Don't allow writing past the buffer. + assert!(bytes_received <= buf.len); + } + //self.dtgl(); + + if bytes_received < buf.len { + self.log_regs(); + // TODO: honestly, this is probably a panic condition, + // since whatever's in DataBuf.ptr is totally + // invalid. Alternately, this function should be declared + // `unsafe`. + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Err(PipeErr::ShortPacket) + } else { + self.regs.statusset.write(|w| w.pfreeze().set_bit()); + Ok(()) } - self.regs.statusset.write(|w| w.pfreeze().set_bit()); + } + // TODO: these two functions shouldn't be exposed, since they're + // pretty hardware-dependent. Instead, move USBHost.control_req() + // into this module (where it will sit with its peers + // `in_transfer` and `out_transfer`) and combine it with `send` + // (which is its only use). + pub(crate) fn dtgl_set(&mut self) { + self.regs.statusset.write(|w| w.dtgl().set_bit()); + } + + pub(crate) fn dtgl_clear(&mut self) { + self.regs.statusclr.write(|w| unsafe { + // No function for this. FIXME: need to patch the SVD for + // PSTATUSCLR.DTGL at bit0. No? This is in the SVD, but + // not the rust output. + w.bits(1) + }); + } + + fn dtgl(&mut self) { + // TODO: this makes no sense to me, and docs are unclear. If + // the status bit is set, set it again? if it's clear then + // clear it? This is what I get for having to work from + // Arduino sources. + trace!( + "~~~ curbk: {}, dtgl: {}", + self.regs.status.read().curbk().bit(), + self.regs.status.read().dtgl().bit() + ); if self.regs.status.read().dtgl().bit_is_set() { - self.regs.statusset.write(|w| w.dtgl().set_bit()); + self.dtgl_set(); } else { - self.regs.statusclr.write(|w| unsafe { - // No function for this. FIXME: need to patch the SVD for PSTATUSCLR.DTGL at bit0 - w.bits(1) - }); + self.dtgl_clear(); } - Ok(()) } pub(crate) fn dispatch_retries( @@ -180,17 +233,7 @@ impl Pipe<'_, '_> { last_result = self.dispatch_result(token, until, millis); match last_result { Ok(_) => return Ok(()), - // FIXME: handle datatoggle - Err(PipeErr::DataToggle) => { - if self.regs.status.read().dtgl().bit_is_set() { - self.regs.statusset.write(|w| w.dtgl().set_bit()); - } else { - self.regs.statusclr.write(|w| unsafe { - // No function for this. FIXME: need to patch the SVD for PSTATUSCLR.DTGL at bit0 - w.bits(1) - }); - } - } + Err(PipeErr::DataToggle) => self.dtgl(), Err(PipeErr::SWTimeout) => break, Err(PipeErr::Stall) => break, Err(_) => naks += 1, |