[PATCH 14/27] hw/usb/dev-smartcard-reader: release short bulk-in responses with xHCI

Marc-AndrĂ© Lureau <[email protected]> Wed, 05 Aug 2026 15:51:04 +0400
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
USBPacket describes the host controller's complete transfer request, not
necessarily one packet on the USB wire. xHCI maps the buffers from all
TRBs in a transfer descriptor into a single USBPacket, so p->iov.size
can be much larger than the CCID bulk endpoint's 64-byte wMaxPacketSize.

The smartcard reader kept a completed bulk-in response queued whenever
the last copied chunk was exactly 64 bytes. This is necessary when the
guest requested exactly 64 bytes: the next IN request must receive a
zero-length packet to mark the end of the response. It is wrong when
xHCI supplied a larger buffer, however. In that case the 64-byte result
is already short relative to the USBPacket and xHCI completes the TD.
Keeping the response current leaves it waiting for a ZLP request which
never arrives and prevents later smartcard responses from being
delivered. Release an exact-64-byte response when it is shorter than the
host controller request, while retaining it for a request of exactly one
maximum-sized packet.

This fixes smartcard reader with xHCI.

Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
---
 hw/usb/dev-smartcard-reader.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index d91376826c5c..699e236f624e 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -1132,8 +1132,7 @@ err:
     s->bulk_out_pos = 0;
 }
 
-static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p,
-    unsigned int max_packet_size)
+static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p)
 {
     int len = 0;
 
@@ -1148,7 +1147,7 @@ static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p,
         }
         s->current_bulk_in->pos += len;
         if (s->current_bulk_in->pos == s->current_bulk_in->len
-            && len != max_packet_size) {
+            && (len != CCID_MAX_PACKET_SIZE || len < p->iov.size)) {
             ccid_bulk_in_release(s);
         }
     } else {
@@ -1180,7 +1179,7 @@ static void ccid_handle_data(USBDevice *dev, USBPacket *p)
     case USB_TOKEN_IN:
         switch (p->ep->nr) {
         case CCID_BULK_IN_EP:
-            ccid_bulk_in_copy_to_guest(s, p, dev->ep_ctl.max_packet_size);
+            ccid_bulk_in_copy_to_guest(s, p);
             break;
         case CCID_INT_IN_EP:
             if (s->notify_slot_change) {

-- 
2.55.0