[Spice-commits] Branch 'usb-ccid' - hw/usb-ccid.c

Alon Levy alon at kemper.freedesktop.org
Thu Jul 8 02:42:16 PDT 2010


 hw/usb-ccid.c |  145 ++++++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 112 insertions(+), 33 deletions(-)

New commits:
commit 33095d8bc57ceb0ab44896515f0e32970fd045c8
Author: Alon Levy <alevy at redhat.com>
Date:   Thu Jul 8 12:05:15 2010 +0300

    usb-ccid: report error, handle removal/insertion

diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index acf5901..6d7c830 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -97,6 +97,38 @@ do { printf("usb-ccid: " fmt , ## __VA_ARGS__); } while (0)
 #define SLOT_0_STATE_MASK    1
 #define SLOT_0_CHANGED_MASK  2
 
+// Status codes that go in bStatus (see 6.2.6)
+enum {
+    ICC_STATUS_PRESENT_ACTIVE = 0,
+    ICC_STATUS_PRESENT_INACTIVE,
+    ICC_STATUS_NOT_PRESENT
+};
+
+enum {
+    COMMAND_STATUS_NO_ERROR = 0,
+    COMMAND_STATUS_FAILED,
+    COMMAND_STATUS_TIME_EXTENSION_REQUIRED
+};
+
+// Error codes that go in bError (see 6.2.6)
+// There is no such thing as ERROR_NONE, just easy to
+// spot places where the author thought there is no error code
+// to transmit.
+#define ERROR_NONE 0
+enum {
+    ERROR_CMD_ABORTED = -1,
+    ERROR_ICC_MUTE = -2,
+    ERROR_XFR_PARITY_ERROR = -3,
+    ERROR_XFR_OVERRUN = -4,
+    ERROR_HW_ERROR = -5,
+};
+
+// 6.2.6 RDR_to_PC_SlotStatus definitions
+enum {
+    CLOCK_STATUS_RUNNING=0,
+ // 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, 3 - unkonwn state. rest are RFU
+};
+
 typedef struct __attribute__ ((__packed__)) {
     uint8_t     bMessageType;
     uint32_t    dwLength;
@@ -140,16 +172,23 @@ typedef struct {
     uint8_t     bmSlotICCState;
 } CCID_Notify_Slot_Change;
 
+#define MAX_ATR_SIZE 40
 typedef struct {
     USBDevice dev;
-    uint8_t recv_buf[RECV_BUF];
+    CharDriverState *cs;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    uint8_t  recv_buf[RECV_BUF];
     uint16_t recv_ptr;
     uint16_t recv_used;
-    CharDriverState *cs;
-    uint8_t bmSlotICCState;
-    bool notify_slot_change;
-    uint8_t answer_slot;    // used for DataBlock response to XferBlock
-    uint8_t answer_seq;     // ditto
+    uint8_t  bmSlotICCState;
+    bool     notify_slot_change;
+    bool     waiting_for_answer;
+    uint64_t last_answer_error;
+    uint8_t  answer_slot;    // used for DataBlock response to XferBlock
+    uint8_t  answer_seq;     // ditto
+    uint8_t  bError;
+    uint8_t  bmCommandStatus;
 } USBCCIDState;
 
 /* Slot specific variables. We emulate a single slot card reader.
@@ -164,9 +203,6 @@ uint8_t DEFAULT_ATR[] = {
 
 }; // maximum size of ATR - from 7816-3
 
-#define MAX_ATR_SIZE 40
-uint8_t atr[MAX_ATR_SIZE];
-uint8_t atr_length;
 
 /* CCID Spec chapter 4: CCID uses a standard device descriptor per Chapter 9,
  * "USB Device Framework", section 9.6.1, in the Universal Serial Bus Specification.
@@ -481,12 +517,17 @@ static uint8_t* ccid_reserve_recv_buf(USBCCIDState* s, uint16_t len)
     return s->recv_buf + s->recv_ptr;
 }
 
-/*
-static void ccid_fill_header(CCID_Header* h, CCID_Header* recv,
-    uint8_t type, uint32_t length, uint8_t status, uint8_t error)
+static bool usb_ccid_card_inserted(USBCCIDState *s)
 {
+    return s->bmSlotICCState & SLOT_0_STATE_MASK;
+}
+
+static uint8_t usb_ccid_calc_status(USBCCIDState *s)
+{
+    // page 55, 6.2.6, calculation of bStatus from bmICCStatus and bmCommandStatus
+    return (usb_ccid_card_inserted(s) ? ICC_STATUS_PRESENT_ACTIVE : ICC_STATUS_NOT_PRESENT)
+            | (s->bmCommandStatus << 6);
 }
-*/
 
 static void ccid_write_slot_status(USBCCIDState* s, CCID_Header* recv)
 {
@@ -495,12 +536,13 @@ static void ccid_write_slot_status(USBCCIDState* s, CCID_Header* recv)
     h->b.hdr.dwLength = 0;
     h->b.hdr.bSlot = recv->bSlot;
     h->b.hdr.bSeq = recv->bSeq;
-    h->b.bStatus = 0;
-    h->b.bError = 0;
-    h->bClockStatus = 0; // 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, 3 - unkonwn state. rest are RFU
+    h->b.bStatus = usb_ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bClockStatus = CLOCK_STATUS_RUNNING;
 }
 
-static void ccid_write_data_block(USBCCIDState* s, uint8_t slot, uint8_t seq,
+static void ccid_write_data_block(
+    USBCCIDState* s, uint8_t slot, uint8_t seq,
     const uint8_t* data, uint32_t len)
 {
     CCID_DataBlock *p;
@@ -513,8 +555,8 @@ static void ccid_write_data_block(USBCCIDState* s, uint8_t slot, uint8_t seq,
     p->b.hdr.dwLength = len;
     p->b.hdr.bSlot = slot;
     p->b.hdr.bSeq = seq;
-    p->b.bStatus = 0;
-    p->b.bError = 0;
+    p->b.bStatus = usb_ccid_calc_status(s);
+    p->b.bError = s->bError;
     memcpy(p->abData, data, len);
 }
 
@@ -525,7 +567,7 @@ static void ccid_write_data_block_answer(USBCCIDState* s, const uint8_t* data, u
 
 static void ccid_write_data_block_atr(USBCCIDState* s, CCID_Header* recv)
 {
-    ccid_write_data_block(s, recv->bSlot, recv->bSeq, atr, atr_length);
+    ccid_write_data_block(s, recv->bSlot, recv->bSeq, s->atr, s->atr_length);
 }
 
 static void usb_ccid_on_slot_change(USBCCIDState* s, bool full)
@@ -543,6 +585,11 @@ static void usb_ccid_on_slot_change(USBCCIDState* s, bool full)
     s->notify_slot_change = true;
 }
 
+static void ccid_write_data_block_error(USBCCIDState *s, uint8_t slot, uint8_t seq)
+{
+    ccid_write_data_block(s, slot, seq, NULL, 0);
+}
+
 static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock* recv)
 {
     static SCRMsgHeader scr_msg_header;
@@ -551,13 +598,20 @@ static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock* recv)
         return;
         // XXX - dummy mode for testing, or just have the whole virtual card here.
     }
+    if (!usb_ccid_card_inserted(s)) {
+        printf("usb-ccid: not sending apdu to client, no card connected\n");
+        ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
+        return;
+    }
     s->answer_slot = recv->hdr.bSlot;
     s->answer_seq = recv->hdr.bSeq;
     printf("usb-ccid: apdu_from_guest %d: len %d\n", recv->hdr.bSeq, recv->hdr.dwLength);
     scr_msg_header.type = SCard_APDU;
     scr_msg_header.nLength = recv->hdr.dwLength;
+    // send request to remote card
     qemu_chr_write(s->cs, (uint8_t*)&scr_msg_header, sizeof(SCRMsgHeader));
     qemu_chr_write(s->cs, recv->abData, recv->hdr.dwLength);
+    s->waiting_for_answer = true;
 }
 
 static void usb_ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
@@ -682,13 +736,13 @@ static void usb_ccid_handle_destroy(USBDevice *dev)
 
 /* APDU chardev */
 
-static int usb_ccid_can_read(void *opaque)
+static int usb_ccid_scard_can_read(void *opaque)
 {
     //USBCCIDState *s = opaque;
     return 70000; // XXX number has meaning, or just positive for yes?
 }
 
-static void usb_ccid_read(void *opaque, const uint8_t *buf, int size)
+static void usb_ccid_scard_read(void *opaque, const uint8_t *buf, int size)
 {
     static uint8_t readbuf[10000];
     static uint32_t readpos = 0;
@@ -711,25 +765,46 @@ static void usb_ccid_read(void *opaque, const uint8_t *buf, int size)
         case SCard_ATR:
             DPRINTF("SCard_ATR %d\n", scr_msg_header->nLength);
             assert(scr_msg_header->nLength <= MAX_ATR_SIZE);
-            memcpy(atr, data, scr_msg_header->nLength);
-            atr_length = scr_msg_header->nLength;
+            memcpy(s->atr, data, scr_msg_header->nLength);
+            s->atr_length = scr_msg_header->nLength;
+            s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
             usb_ccid_on_slot_change(s, true);
             break;
         case SCard_APDU:
-            DPRINTF("SCard_APDU %d (answer seq %d, slot %d)\n", scr_msg_header->nLength,
+            if (!s->waiting_for_answer) {
+                DPRINTF("SCard_APDU: ERROR: got an APDU while not waiting_for_answer\n");
+                assert(s->waiting_for_answer);
+            }
+            s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+            DPRINTF("SCard_APDU %d (answer seq %d, slot %d)\n",
+                scr_msg_header->nLength,
                 s->answer_seq, s->answer_slot);
             ccid_write_data_block_answer(s, data, scr_msg_header->nLength);
             break;
         case SCard_Remove:
             DPRINTF("SCard_Remove\n");
             usb_ccid_on_slot_change(s, false);
+            if (s->waiting_for_answer) {
+                ccid_write_data_block_answer(s, NULL, 0);
+            }
+            break;
+        case SCard_Error:
+            // TODO - actually look at the error
+            s->bmCommandStatus = COMMAND_STATUS_FAILED;
+            s->last_answer_error = *(uint64_t*)data;
+            DPRINTF("SCard_Error: %lX\n", s->last_answer_error);
+            ccid_write_data_block_answer(s, NULL, 0);
             break;
         default:
-            printf("usb-ccid: chardev: unexpected message of type %X\n", scr_msg_header->type);
+            printf("usb-ccid: chardev: unexpected message of type %X\n",
+                   scr_msg_header->type);
     };
+    if (s->waiting_for_answer) {
+        s->waiting_for_answer = false;
+    }
 }
 
-static void usb_ccid_event(void *opaque, int event)
+static void usb_ccid_scard_event(void *opaque, int event)
 {
     //USBCCIDState *s = opaque;
 }
@@ -741,15 +816,19 @@ static int usb_ccid_initfn(USBDevice *dev)
     USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
     s->dev.speed = USB_SPEED_FULL;
     s->notify_slot_change = false;
-    s->answer_slot = -1; // should be an invalid number
-    s->answer_seq = -1;  // should be an invalid number
+    s->answer_slot = -1;
+    s->answer_seq = -1;
+    s->last_answer_error = 0;
+    s->waiting_for_answer = false;
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+    s->bError = ERROR_NONE;
     assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
-    memcpy(atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
-    atr_length = sizeof(DEFAULT_ATR);
+    memcpy(s->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
+    s->atr_length = sizeof(DEFAULT_ATR);
     if (s->cs) {
         DPRINTF("initing chardev\n");
-        qemu_chr_add_handlers(s->cs, usb_ccid_can_read, usb_ccid_read,
-                              usb_ccid_event, s);
+        qemu_chr_add_handlers(s->cs, usb_ccid_scard_can_read, usb_ccid_scard_read,
+                              usb_ccid_scard_event, s);
     }
     usb_ccid_handle_reset(dev);
     return 0;


More information about the Spice-commits mailing list