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 <marcandre.lureau@redhat.com>
---
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