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

rp/usb: return error on disconnect #3629

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

birktj
Copy link

@birktj birktj commented Dec 9, 2024

This PR causes USB to return EndpointError::Disabled when trying to read or write to a disconnected usb port.

It seems to work as expected, but I haven't tested it very heavily and I am not super familiar with USB internals so there might be some bugs.

This fixes #3426

@@ -567,14 +580,17 @@ impl<'d, T: Instance> driver::EndpointOut for Endpoint<'d, T, Out> {
let index = self.info.addr.index();
let val = poll_fn(|cx| {
EP_OUT_WAKERS[index].register(cx.waker());
let ctrl_val = T::dpram().ep_out_control(index - 1).read();
Copy link
Author

Choose a reason for hiding this comment

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

I could not find any documentation that says why this has to be - 1, but it is what the wait_enabled functions do so I figure it must be correct.

@Dirbaio
Copy link
Member

Dirbaio commented Dec 10, 2024

in USB, "suspend" and "disconnect" are different things. It's only on actual disconnection that we should disable all endpoints and reset the USB device state. On suspend, the host expects everything to stay as-is when it unsuspends.

Here's how you tell which state you're in:

VBUS present, activity on the bus -> USB is active
VBUS present, no activity on the bus -> USB is suspended
VBUS no present -> USB is disconnected

the USB peripheral only looks at the DP/DM lines so it can only tell whether there's activity on the bus or not. To distinguish "suspended" and "disconnected" states you need to use GPIO. On the Pico VBUS is wired to GPIO24, but it varies across boards so it has to be configurable. Some boards don't wire it, in which case it's simply impossible to support the "disconnected" state.

Also, embassy-usb has builtin support for handling the "disconnected" state. The driver should return PowerDetected, PowerRemoved events from poll() when VBUS comes/goes.

@birktj
Copy link
Author

birktj commented Dec 10, 2024

Thanks for the explanation.

So if I understand correctly the correct approach here would be to remove the code that disables the endpoints on suspend and instead add some code for vbus detection?

But should there also be a way to check for the suspended case? It is not very practical with read and writes that can potentially block forever. Would a EndpointError::Suspended be an option in this case?

In my case my hardware does not support VBUS detection, but I need to be able to react to a disconnect so this code works well for me. But if this is a unstandard usecase it probably doesn't need to be upstreamed.

BTW, do you have any good resources on USB internals? I have been looking a bit around for some time, but never found a nice overview over all the terminology.

@Dirbaio
Copy link
Member

Dirbaio commented Dec 10, 2024

So if I understand correctly the correct approach here would be to remove the code that disables the endpoints on suspend and instead add some code for vbus detection?

yes

But should there also be a way to check for the suspended case? It is not very practical with read and writes that can potentially block forever. Would a EndpointError::Suspended be an option in this case?

I think blocking forever is the intended behavior. consider the case where you're copying a file from a USB drive and you suspend your laptop. You don't want the transfer to fail, it'll just hang and then resume when you unsuspend whic hcan be days, weeks later.

@birktj
Copy link
Author

birktj commented Dec 10, 2024

Okay great, then I will try to go for a model similar to embasy-nrf with a VbusDetect trait. Possibly moving it into emabssy-usb?

I think there also are cases where you would not want to block on suspend, e.g. a keyboard where you don't want to automatically send the first keystroke after suspend when the suspend ends. But it is maybe a bit of-topic here, I will open an issue where it can be discussed a bit.

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.

RP2: EndpointIn::write doesn't check enabled status
2 participants