Skip to content

Commit 03d3d3c

Browse files
committed
Rework USB OUT endpoint interrupts
Previously, when the USB interrupt was unmasked and a host issued an OUT transfer to an endpoint that was not `read()` before the next OUT transfer attempt to that endpoint, the USB ISR would continually fire until `read()` was called on that endpoint. If a higher priority ISR didn't somehow clear the condition that prevented the `read()`, the CPU would lock up. This change moves the clearing of the endpoint's transfer complete and transfer failed interrupt flags in to `poll(), so that a USB Class can safely skip a `read()` without having to chose between discarding data, or locking up in the USB ISR.
1 parent 8c40356 commit 03d3d3c

File tree

2 files changed

+84
-83
lines changed

2 files changed

+84
-83
lines changed

hal/src/thumbv6m/usb/bus.rs

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -872,57 +872,59 @@ impl Inner {
872872
// unconnected & unsuspended, we do not handle them to avoid spurious
873873
// transitions.
874874

875-
let intbits = self.usb().epintsmry.read().bits();
876-
if intbits == 0 {
877-
return PollResult::None;
878-
}
879-
880875
let mut ep_out = 0;
881876
let mut ep_in_complete = 0;
882877
let mut ep_setup = 0;
883878

884879
for ep in 0..8u16 {
885880
let mask = 1 << ep;
886-
if (intbits & mask) == 0 {
887-
continue;
888-
}
889881

890882
let idx = ep as usize;
891883

892-
let bank1 = self
893-
.bank1(EndpointAddress::from_parts(idx, UsbDirection::In))
894-
.unwrap();
895-
if bank1.is_transfer_complete() {
896-
bank1.clear_transfer_complete();
897-
dbgprint!("ep {} WRITE DONE\n", ep);
898-
ep_in_complete |= mask;
899-
// Continuing (and hence not setting masks to indicate complete
900-
// OUT transfers) is necessary for operation to proceed beyond
901-
// the device-address + descriptor stage. The authors suspect a
902-
// deadlock caused by waiting on a write when handling a read
903-
// somewhere in an underlying class or control crate, but we
904-
// can't be sure. Either way, if a write has finished, we only
905-
// set the flag for a completed write on that endpoint index.
906-
// Future polls will handle the reads.
907-
continue;
908-
}
909-
drop(bank1);
910-
911-
let bank0 = self
912-
.bank0(EndpointAddress::from_parts(idx, UsbDirection::Out))
913-
.unwrap();
914-
if bank0.received_setup_interrupt() {
915-
dbgprint!("ep {} GOT SETUP\n", ep);
916-
ep_setup |= mask;
917-
// usb-device crate:
918-
// "This event should continue to be reported until the packet
919-
// is read." So we don't clear the flag here,
920-
// instead it is cleared in the read handler.
884+
if let Ok(bank1) = self.bank1(EndpointAddress::from_parts(idx, UsbDirection::In)) {
885+
if bank1.is_transfer_complete() {
886+
bank1.clear_transfer_complete();
887+
dbgprint!("ep {} WRITE DONE\n", ep);
888+
ep_in_complete |= mask;
889+
// Continuing (and hence not setting masks to indicate complete
890+
// OUT transfers) is necessary for operation to proceed beyond
891+
// the device-address + descriptor stage. The authors suspect a
892+
// deadlock caused by waiting on a write when handling a read
893+
// somewhere in an underlying class or control crate, but we
894+
// can't be sure. Either way, if a write has finished, we only
895+
// set the flag for a completed write on that endpoint index.
896+
// Future polls will handle the reads.
897+
continue;
898+
}
921899
}
922900

923-
if bank0.is_transfer_complete() {
924-
dbgprint!("ep {} READABLE\n", ep);
925-
ep_out |= mask;
901+
if let Ok(bank0) = self.bank0(EndpointAddress::from_parts(idx, UsbDirection::Out)) {
902+
if bank0.received_setup_interrupt() {
903+
dbgprint!("ep {} GOT SETUP\n", ep);
904+
ep_setup |= mask;
905+
// usb-device crate:
906+
// "This event should continue to be reported until the packet
907+
// is read." So we don't clear the flag here,
908+
// instead it is cleared in the read handler.
909+
}
910+
911+
// Clear the transfer complete and transfer failed interrupt flags
912+
// so that execution leaves the USB interrupt until the host makes
913+
// another transaction. The transfer failed flag may have been set
914+
// if an OUT transaction wasn't read() from the endpoint by the
915+
// Class; the hardware will have NAKed (unless the endpoint is
916+
// isochronous) and the host may retry.
917+
bank0.clear_transfer_complete();
918+
919+
// Use the bk0rdy flag via is_ready() to indicate that data has been
920+
// received successfully, rather than the interrupting trcpt0 via
921+
// is_transfer_ready(), because data may have been received on an
922+
// earlier poll() which cleared trcpt0. bk0rdy is cleared in the
923+
// endpoint read().
924+
if bank0.is_ready() {
925+
dbgprint!("ep {} READABLE\n", ep);
926+
ep_out |= mask;
927+
}
926928
}
927929
}
928930

@@ -976,8 +978,6 @@ impl Inner {
976978
}
977979

978980
// self.print_epstatus(idx, "read");
979-
980-
bank.clear_transfer_complete();
981981
bank.set_ready(false);
982982

983983
drop(bank);

hal/src/thumbv7em/usb/bus.rs

Lines changed: 42 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -818,57 +818,59 @@ impl Inner {
818818
// unconnected & unsuspended, we do not handle them to avoid spurious
819819
// transitions.
820820

821-
let intbits = self.usb().epintsmry.read().bits();
822-
if intbits == 0 {
823-
return PollResult::None;
824-
}
825-
826821
let mut ep_out = 0;
827822
let mut ep_in_complete = 0;
828823
let mut ep_setup = 0;
829824

830825
for ep in 0..8u16 {
831826
let mask = 1 << ep;
832-
if (intbits & mask) == 0 {
833-
continue;
834-
}
835827

836828
let idx = ep as usize;
837829

838-
let bank1 = self
839-
.bank1(EndpointAddress::from_parts(idx, UsbDirection::In))
840-
.unwrap();
841-
if bank1.is_transfer_complete() {
842-
bank1.clear_transfer_complete();
843-
dbgprint!("ep {} WRITE DONE\n", ep);
844-
ep_in_complete |= mask;
845-
// Continuing (and hence not setting masks to indicate complete
846-
// OUT transfers) is necessary for operation to proceed beyond
847-
// the device-address + descriptor stage. The authors suspect a
848-
// deadlock caused by waiting on a write when handling a read
849-
// somewhere in an underlying class or control crate, but we
850-
// can't be sure. Either way, if a write has finished, we only
851-
// set the flag for a completed write on that endpoint index.
852-
// Future polls will handle the reads.
853-
continue;
854-
}
855-
drop(bank1);
856-
857-
let bank0 = self
858-
.bank0(EndpointAddress::from_parts(idx, UsbDirection::Out))
859-
.unwrap();
860-
if bank0.received_setup_interrupt() {
861-
dbgprint!("ep {} GOT SETUP\n", ep);
862-
ep_setup |= mask;
863-
// usb-device crate:
864-
// "This event should continue to be reported until the packet
865-
// is read." So we don't clear the flag here,
866-
// instead it is cleared in the read handler.
830+
if let Ok(bank1) = self.bank1(EndpointAddress::from_parts(idx, UsbDirection::In)) {
831+
if bank1.is_transfer_complete() {
832+
bank1.clear_transfer_complete();
833+
dbgprint!("ep {} WRITE DONE\n", ep);
834+
ep_in_complete |= mask;
835+
// Continuing (and hence not setting masks to indicate complete
836+
// OUT transfers) is necessary for operation to proceed beyond
837+
// the device-address + descriptor stage. The authors suspect a
838+
// deadlock caused by waiting on a write when handling a read
839+
// somewhere in an underlying class or control crate, but we
840+
// can't be sure. Either way, if a write has finished, we only
841+
// set the flag for a completed write on that endpoint index.
842+
// Future polls will handle the reads.
843+
continue;
844+
}
867845
}
868846

869-
if bank0.is_transfer_complete() {
870-
dbgprint!("ep {} READABLE\n", ep);
871-
ep_out |= mask;
847+
if let Ok(bank0) = self.bank0(EndpointAddress::from_parts(idx, UsbDirection::Out)) {
848+
if bank0.received_setup_interrupt() {
849+
dbgprint!("ep {} GOT SETUP\n", ep);
850+
ep_setup |= mask;
851+
// usb-device crate:
852+
// "This event should continue to be reported until the packet
853+
// is read." So we don't clear the flag here,
854+
// instead it is cleared in the read handler.
855+
}
856+
857+
// Clear the transfer complete and transfer failed interrupt flags
858+
// so that execution leaves the USB interrupt until the host makes
859+
// another transaction. The transfer failed flag may have been set
860+
// if an OUT transaction wasn't read() from the endpoint by the
861+
// Class; the hardware will have NAKed (unless the endpoint is
862+
// isochronous) and the host may retry.
863+
bank0.clear_transfer_complete();
864+
865+
// Use the bk0rdy flag via is_ready() to indicate that data has been
866+
// received successfully, rather than the interrupting trcpt0 via
867+
// is_transfer_ready(), because data may have been received on an
868+
// earlier poll() which cleared trcpt0. bk0rdy is cleared in the
869+
// endpoint read().
870+
if bank0.is_ready() {
871+
dbgprint!("ep {} READABLE\n", ep);
872+
ep_out |= mask;
873+
}
872874
}
873875
}
874876

@@ -923,7 +925,6 @@ impl Inner {
923925

924926
// self.print_epstatus(idx, "read");
925927

926-
bank.clear_transfer_complete();
927928
bank.set_ready(false);
928929

929930
drop(bank);

0 commit comments

Comments
 (0)