aboutsummaryrefslogtreecommitdiffstats
path: root/usbh/src/pipe.rs
diff options
context:
space:
mode:
Diffstat (limited to 'usbh/src/pipe.rs')
-rw-r--r--usbh/src/pipe.rs91
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,