Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework USB OUT endpoint interrupts #459

Merged
merged 2 commits into from
Mar 28, 2022

Conversation

ianrrees
Copy link
Contributor

@ianrrees ianrrees commented Jun 24, 2021

(this PR has been heavily edited from the original - sorry)

Previously, when the USB interrupt was unmasked and a host issued an OUT transfer to an endpoint, the USB interrupt would continually fire until read() was called on that endpoint. This could lead to a hang if the USB class controlling the endpoint was dependent on some lower-priority task before it could successfully read. For example: if the class received data over USB to feed out to an interrupt-driven serial port, the class might have a full buffer so not call the endpoint read(), but the buffer would not drain because the serial port interrupt was lower priority than the USB one. This behaviour came about because the USB poll() method didn't clear the endpoint interrupts for OUT transfers, but relied on the read() methods on endpoints to clear them.

The endpoints have a non-interrupting flag that indicates when the endpoint is ready to be read, and this change switches to using that to indicate data is available.

One subtle consequence of this change is that if a USB class doesn't read() a successful OUT transfer, the OUT data will sit idle indefinitely, until either it or the host transfers data. A simple workaround is to enable the SOF interrupts using #456 so that poll() gets called within the next millisecond.

ianrrees added a commit to ianrrees/atsamd-usbd-uart that referenced this pull request Jun 24, 2021
@ianrrees ianrrees changed the title Use the USB OUT endpoint transfer failed interrupt Draft: Use the USB OUT endpoint transfer failed interrupt Jul 2, 2021
@sajattack sajattack marked this pull request as draft July 2, 2021 18:09
@ianrrees ianrrees force-pushed the usb-transfer-fail branch from 166798f to f242e68 Compare July 8, 2021 03:04
@@ -995,6 +997,7 @@ impl Inner {
}
Err(err) => {
dbgprint!("UsbBus::read from ep {:?} -> {:?}\n", ep, err);
drop(bank); // bank holds a reference to self.desc, which print_epstatus() needs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed this might've broken isochronous transfers; I've got some other local changes that might also be where the problem comes from, but need to do some more digging.

This was the problem - previously this PR just deleted the drop(bank); above.

@ianrrees ianrrees force-pushed the usb-transfer-fail branch from f242e68 to 53ef89f Compare July 8, 2021 11:09
@ianrrees
Copy link
Contributor Author

ianrrees commented Jul 8, 2021

I've made a few changes from the original version of this PR, the main one is that the transfer complete and transfer failed flags are cleared in poll(). usb-device says that the "[OUT] event should continue to be reported until the packet is read" - I think we're still doing that because poll() will continue to report the event, and will be called regularly because endpoint's transfer failed interrupt will now fire when the host retries.

@ianrrees ianrrees force-pushed the usb-transfer-fail branch from 53ef89f to 4a0fff5 Compare July 8, 2021 11:30
@ianrrees ianrrees changed the title Draft: Use the USB OUT endpoint transfer failed interrupt Rework USB OUT endpoint interrupts Jul 8, 2021
@ianrrees ianrrees marked this pull request as ready for review July 8, 2021 11:31
@ianrrees ianrrees force-pushed the usb-transfer-fail branch 3 times, most recently from 799dc17 to ca977fd Compare July 16, 2021 21:53
ianrrees added a commit to ianrrees/usb-spi that referenced this pull request Jul 22, 2021
Has a little debug code left from
atsamd-rs/atsamd#459
@ianrrees ianrrees force-pushed the usb-transfer-fail branch from 49ba6e7 to 8227495 Compare March 26, 2022 01:23
@ianrrees ianrrees force-pushed the usb-transfer-fail branch from 8227495 to 0eb198a Compare March 28, 2022 21:54
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.
@ianrrees ianrrees force-pushed the usb-transfer-fail branch from 0eb198a to bbd02ec Compare March 28, 2022 22:33
@TDHolmes TDHolmes merged commit 2081375 into atsamd-rs:master Mar 28, 2022
rtsuk pushed a commit to rtsuk/atsamd that referenced this pull request Mar 29, 2022
* 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.

* Optimisation in USB poll()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants