[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