The endpoint type in the xHCI endpoint context comes from the guest and
is not checked against the device. A guest can configure the interrupt
IN endpoint of usb-kbd as Isoch IN. The idle HID endpoint NAKs, and as
soon as the transfer goes through the retry path in xhci_kick_epctx()
it hits
assert(xfer->packet.status != USB_RET_NAK);
No device model NAKs on an isoch endpoint, so this only triggers with a
mismatched endpoint type.
The two retry branches differ only in what they do on NAK: the isoch one
asserts, the other keeps the transfer pending. Merge them.
Fixes: 3d1396842d ("xhci: iso xfer support")
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3886
Reported-by: Feifan Qian <[email protected]>
Cc: [email protected]
Signed-off-by: Junjie Cao <[email protected]>
---
hw/usb/hcd-xhci.c | 27 ++++++++-------------------
1 file changed, 8 insertions(+), 19 deletions(-)
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index ce042e74bd..6cd5ac87b8 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -1912,26 +1912,15 @@ static void xhci_kick_epctx(XHCIEPContext *epctx,
unsigned int streamid)
xfer->timed_xfer = 0;
xfer->running_retry = 1;
}
- if (xfer->iso_xfer) {
- /* retry iso transfer */
- if (xhci_setup_packet(xfer) < 0) {
- return;
- }
- usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
- assert(xfer->packet.status != USB_RET_NAK);
- xhci_try_complete_packet(xfer);
- } else {
- /* retry nak'ed transfer */
- if (xhci_setup_packet(xfer) < 0) {
- return;
- }
- usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
- if (xfer->packet.status == USB_RET_NAK) {
- xhci_xfer_unmap(xfer);
- return;
- }
- xhci_try_complete_packet(xfer);
+ if (xhci_setup_packet(xfer) < 0) {
+ return;
+ }
+ usb_handle_packet(xfer->packet.ep->dev, &xfer->packet);
+ if (xfer->packet.status == USB_RET_NAK) {
+ xhci_xfer_unmap(xfer);
+ return;
}
+ xhci_try_complete_packet(xfer);
assert(!xfer->running_retry);
if (xfer->complete) {
/* update ring dequeue ptr */
--
2.43.0