dwc3: unusual handling of setup requests with wLength == 0

From: Andrey Konovalov
Date: Thu Aug 17 2023 - 20:16:43 EST


Hi Alan and Thinh,

I have been testing Raw Gadget with the dwc3 UDC driver and stumbled
upon an issue related to how dwc3 handles setup requests with wLength
== 0.

When running a simple Raw Gadget-based keyboard emulator [1],
everything works as expected until the point when the host sends a
SET_CONFIGURATION request, which has wLength == 0.

For setup requests with wLength != 0, just like the other UDC drivers
I tested, dwc3 calls the gadget driver's ->setup() callback and then
waits until the gadget driver queues an URB to EP0 as a response.

However, for a setup request with wLength == 0, dwc3 does not wait
until the gadget driver queues an URB to ack the transfer. It appears
that dwc3 just acks the request internally and then proceeds with
calling the ->setup() callback for the next request received from the
host. This confuses Raw Gadget, as it does not expect to get a new
->setup() call before it explicitly acks the previous one by queuing
an URB. As a result, the emulation fails.

I suspect this issue has not been observed with other gadget drivers,
as they queue an URB immediately after receiving a ->setup() call:
dwc3 appears to somehow correctly handle this internally even though
it acks the transfer by itself. But the timings with Raw Gadget are
different, as it requires userspace to ack the transfer. Sometimes
though, the Raw Gadget-based emulator also manages to queue an URB
before the next request is received from the host and the enumeration
continues properly (until the next request with wLength == 0).

What do you think would be the best approach to deal with this?

Can this be considered a bug in dwc3 that should be fixed? There's a
seemingly related comment in dwc3 code [2], but I'm not familiar
enough with its internals to understand whether this is what leads to
the issue I'm seeing.

Or should I adapt Raw Gadget to handle this unusual dwc3 behavior?
This might be tricky to do, as I cannot change the existing userspace
API.

On a side note, as an experiment, I tried returning
USB_GADGET_DELAYED_STATUS from the Raw Gadget's ->setup() callback if
the UDC driver calls it too early: some UDC drivers, including dwc3,
appear to contain a special handling for this return value. However,
that didn't work out. Perhaps, I misunderstand the meaning of this
value.

Thank you!

[1] https://github.com/xairy/raw-gadget/blob/master/examples/keyboard.c
[2] https://elixir.bootlin.com/linux/v6.5-rc6/source/drivers/usb/dwc3/ep0.c#L145