[PATCH 25/27] hw/usb/dev-smartcard-reader: advertise T=1 protocol support

Marc-AndrĂ© Lureau <[email protected]> Wed, 05 Aug 2026 15:51:15 +0400
Newsgroups gmane.comp.emulators.qemu
Message-ID <[email protected]>
The CCID descriptor only advertised T=0, but it actually handles t=1
since it shuttles framed blocks to the backend. libcacard advertized T=0
in the ATR, so it works fine. This allows passthrough cards to work
at t=1 if they need to.

Add a configurable property to also advertise T=1 in the dwProtocols
field, enabled by default.

Use the correct error code (7, ICC protocol not supported) instead
of CMD_NOT_SUPPORTED when SetParameters receives an invalid protocol.

Note: I only tested with libcacard emulation.

Signed-off-by: Marc-AndrĂ© Lureau <[email protected]>
---
 hw/core/machine.c             |  1 +
 hw/usb/dev-smartcard-reader.c | 10 +++++++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/hw/core/machine.c b/hw/core/machine.c
index 790d947e28d1..e9fecdec18df 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -53,6 +53,7 @@ GlobalProperty hw_compat_11_0[] = {
     { "migration", "switchover-ack-legacy", "on" },
     { "usb-ccid", "x-accurate-message-length", "off" },
     { "usb-ccid", "x-pin-support", "on" },
+    { "usb-ccid", "x-t1-support", "off" },
     { "usb-ccid", "x-migrate-pending-answers", "off" },
 };
 const size_t hw_compat_11_0_len = G_N_ELEMENTS(hw_compat_11_0);
diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c
index 8520a2dab216..99086befe0b8 100644
--- a/hw/usb/dev-smartcard-reader.c
+++ b/hw/usb/dev-smartcard-reader.c
@@ -169,6 +169,7 @@ enum {
     ERROR_CMD_SLOT_BUSY     = -32,
     ERROR_BAD_DWLENGTH      = 1,
     ERROR_SLOT_NOT_EXIST    = 5,
+    ERROR_PROTOCOL_INVALID  = 7,
 };
 
 /* 6.2.6 RDR_to_PC_SlotStatus definitions */
@@ -330,6 +331,7 @@ struct USBCCIDState {
     bool migrate_pending_answers;
     bool pending_answers_loaded;
     bool pin_support;
+    bool t1_support;
 };
 
 static uint32_t ccid_bulk_in_pending_num(USBCCIDState *s)
@@ -368,6 +370,7 @@ static uint8_t qemu_ccid_descriptor[] = {
                      */
         0x07,       /* u8  bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
 
+#define CCID_DESC_OFFSET_DW_PROTOCOLS 0x06
         0x01, 0x00, /* u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
         0x00, 0x00, /* PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
                     /* u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
@@ -968,7 +971,7 @@ static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
     uint32_t protocol_num = ph->bProtocolNum & 3;
 
     if (protocol_num != 0 && protocol_num != 1) {
-        ccid_report_error_failed(s, ERROR_CMD_NOT_SUPPORTED);
+        ccid_report_error_failed(s, ERROR_PROTOCOL_INVALID);
         return;
     }
     s->bProtocolNum = protocol_num;
@@ -1546,6 +1549,9 @@ static void ccid_realize(USBDevice *dev, Error **errp)
     if (!s->pin_support) {
         qemu_ccid_descriptor[CCID_DESC_OFFSET_B_PIN_SUPPORT] = 0;
     }
+    if (s->t1_support) {
+        qemu_ccid_descriptor[CCID_DESC_OFFSET_DW_PROTOCOLS] |= 0x2;
+    }
     usb_desc_create_serial(dev);
     usb_desc_init(dev);
     qbus_init(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), NULL);
@@ -1742,6 +1748,8 @@ static const Property ccid_properties[] = {
                      accurate_message_length, true),
     DEFINE_PROP_BOOL("x-pin-support", USBCCIDState,
                      pin_support, false),
+    DEFINE_PROP_BOOL("x-t1-support", USBCCIDState,
+                     t1_support, true),
     DEFINE_PROP_BOOL("x-migrate-pending-answers", USBCCIDState,
                      migrate_pending_answers, true),
 };

-- 
2.55.0