Patchwork [09/20] ccid: add passthru card device

login
register
mail settings
Submitter Alon Levy
Date Feb. 2, 2011, 8:28 p.m.
Message ID <1296678500-19497-10-git-send-email-alevy@redhat.com>
Download mbox | patch
Permalink /patch/81532/
State New
Headers show

Comments

Alon Levy - Feb. 2, 2011, 8:28 p.m.
The passthru ccid card is a device sitting on the usb-ccid bus and
using a chardevice to communicate with a remote device using the
VSCard protocol defined in libcacard/vscard_common.h

Usage docs available in following patch in docs/ccid.txt

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile.objs           |    2 +-
 hw/ccid-card-passthru.c |  273 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 274 insertions(+), 1 deletions(-)
 create mode 100644 hw/ccid-card-passthru.c

Patch

diff --git a/Makefile.objs b/Makefile.objs
index a1f3853..94c36a2 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -195,7 +195,7 @@  hw-obj-$(CONFIG_FDC) += fdc.o
 hw-obj-$(CONFIG_ACPI) += acpi.o acpi_piix4.o
 hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
 hw-obj-$(CONFIG_DMA) += dma.o
-hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o
+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
 
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
new file mode 100644
index 0000000..6ec4f21
--- /dev/null
+++ b/hw/ccid-card-passthru.c
@@ -0,0 +1,273 @@ 
+/*
+ * CCID Card Device emulation
+ *
+ * Copyright (c) 2010 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This code is licenced under the LGPL.
+ */
+
+#include "qemu-char.h"
+#include "monitor.h"
+#include "hw/ccid.h"
+#include "libcacard/vscard_common.h"
+
+#define DPRINTF(card, lvl, fmt, ...) \
+do { if (lvl <= card->debug) { printf("ccid-card: " fmt , ## __VA_ARGS__); } } while (0)
+
+/* Passthru card */
+
+
+// TODO: do we still need this?
+uint8_t DEFAULT_ATR[] = {
+/* From some example somewhere
+ 0x3B, 0xB0, 0x18, 0x00, 0xD1, 0x81, 0x05, 0xB1, 0x40, 0x38, 0x1F, 0x03, 0x28
+ */
+
+/* From an Athena smart card */
+ 0x3B, 0xD5, 0x18, 0xFF, 0x80, 0x91, 0xFE, 0x1F, 0xC3, 0x80, 0x73, 0xC8, 0x21, 0x13, 0x08
+
+}; /* maximum size of ATR - from 7816-3 */
+
+
+#define PASSTHRU_DEV_NAME "ccid-card-passthru"
+#define VSCARD_IN_SIZE 65536
+#define MAX_ATR_SIZE        40
+
+typedef struct PassthruState PassthruState;
+
+struct PassthruState {
+    CCIDCardState base;
+    CharDriverState *cs;
+    uint8_t  vscard_in_data[VSCARD_IN_SIZE];
+    uint32_t vscard_in_pos;
+    uint32_t vscard_in_hdr;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    uint8_t debug;
+};
+
+/* VSCard protocol over chardev
+ * This code should not depend on the card type.
+ * */
+
+static void ccid_card_vscard_send_msg(
+    PassthruState *s, VSCMsgType type, reader_id_t reader_id,
+        const uint8_t* payload, uint32_t length)
+{
+    VSCMsgHeader scr_msg_header;
+
+    scr_msg_header.type = type;
+    scr_msg_header.reader_id = reader_id;
+    scr_msg_header.length = length;
+    qemu_chr_write(s->cs, (uint8_t*)&scr_msg_header, sizeof(VSCMsgHeader));
+    qemu_chr_write(s->cs, payload, length);
+}
+
+static void ccid_card_vscard_send_apdu(
+    PassthruState *s, const uint8_t* apdu, uint32_t length)
+{
+    ccid_card_vscard_send_msg(s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
+}
+
+static void ccid_card_vscard_send_error(
+    PassthruState *s, reader_id_t reader_id, VSCErrorCode code)
+{
+    VSCMsgError msg = {.code=code};
+
+    ccid_card_vscard_send_msg(s, VSC_Error, reader_id, (uint8_t*)&msg, sizeof(msg));
+}
+
+static void ccid_card_vscard_send_init(PassthruState *s)
+{
+    VSCMsgInit msg = {.version=VSCARD_VERSION};
+
+    ccid_card_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
+                         (uint8_t*)&msg, sizeof(msg));
+}
+
+static int ccid_card_vscard_can_read(void *opaque)
+{
+    return 65535;
+}
+
+static void ccid_card_vscard_handle_message(PassthruState *card,
+    VSCMsgHeader* scr_msg_header)
+{
+    uint8_t *data = (uint8_t*)&scr_msg_header[1];
+
+    switch (scr_msg_header->type) {
+        case VSC_ATR:
+            DPRINTF(card, 1, "VSC_ATR %d\n", scr_msg_header->length);
+            assert(scr_msg_header->length <= MAX_ATR_SIZE);
+            memcpy(card->atr, data, scr_msg_header->length);
+            card->atr_length = scr_msg_header->length;
+            ccid_card_card_inserted(&card->base);
+            break;
+        case VSC_APDU:
+            ccid_card_send_apdu_to_guest(&card->base, data, scr_msg_header->length);
+            break;
+        case VSC_CardRemove:
+            DPRINTF(card, 1, "VSC_CardRemove\n");
+            ccid_card_card_removed(&card->base);
+            break;
+        case VSC_Init:
+            break;
+        case VSC_Error:
+            ccid_card_card_error(&card->base, *(uint64_t*)data);
+            break;
+        case VSC_ReaderAdd:
+            if (ccid_card_ccid_attach(&card->base) < 0) {
+                ccid_card_vscard_send_error(card, VSCARD_UNDEFINED_READER_ID,
+                                          VSC_CANNOT_ADD_MORE_READERS);
+            } else {
+                ccid_card_vscard_send_msg(card, VSC_ReaderAddResponse,
+                                             VSCARD_MINIMAL_READER_ID, NULL, 0);
+            }
+            break;
+        case VSC_ReaderRemove:
+            ccid_card_ccid_detach(&card->base);
+            break;
+        default:
+            printf("usb-ccid: chardev: unexpected message of type %X\n",
+                   scr_msg_header->type);
+            ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+                VSC_GENERAL_ERROR);
+    }
+}
+
+static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
+{
+    PassthruState *card = opaque;
+    VSCMsgHeader *hdr;
+
+    assert(card->vscard_in_pos + size <= VSCARD_IN_SIZE);
+    memcpy(card->vscard_in_data + card->vscard_in_pos, buf, size);
+    card->vscard_in_pos += size;
+    hdr = (VSCMsgHeader*)(card->vscard_in_data + card->vscard_in_hdr);
+
+    while ((card->vscard_in_pos - card->vscard_in_hdr >= sizeof(VSCMsgHeader)) &&
+           (card->vscard_in_pos - card->vscard_in_hdr - sizeof(VSCMsgHeader) >=
+           hdr->length)) {
+        ccid_card_vscard_handle_message(card, hdr);
+        card->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
+        hdr = (VSCMsgHeader*)(card->vscard_in_data + card->vscard_in_hdr);
+    }
+    if (card->vscard_in_hdr == card->vscard_in_pos) {
+        card->vscard_in_pos = card->vscard_in_hdr = 0;
+    }
+}
+
+static void ccid_card_vscard_event(void *opaque, int event)
+{
+    PassthruState *card = opaque;
+
+    switch (event) {
+        case CHR_EVENT_BREAK:
+            break;
+        case CHR_EVENT_FOCUS:
+            break;
+        case CHR_EVENT_OPENED:
+            DPRINTF(card, 1, "%s: CHR_EVENT_OPENED\n", __func__);
+            break;
+    }
+}
+
+/* End VSCard handling */
+
+static void passthru_apdu_from_guest(CCIDCardState *base, const uint8_t *apdu, uint32_t len)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    if (!card->cs) {
+        printf("ccid-passthru: no chardev, discarding apdu length %d\n", len);
+        return;
+    }
+    ccid_card_vscard_send_apdu(card, apdu, len);
+}
+
+static const uint8_t* passthru_get_atr(CCIDCardState *base, uint32_t *len)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    *len = card->atr_length;
+    return card->atr;
+}
+
+static int passthru_initfn(CCIDCardState *base)
+{
+    PassthruState *card = DO_UPCAST(PassthruState, base, base);
+
+    card->vscard_in_pos = 0;
+    card->vscard_in_hdr = 0;
+    if (card->cs) {
+        DPRINTF(card, 1, "initing chardev\n");
+        qemu_chr_add_handlers(card->cs,
+            ccid_card_vscard_can_read,
+            ccid_card_vscard_read,
+            ccid_card_vscard_event, card);
+        ccid_card_vscard_send_init(card);
+    }
+    assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
+    memcpy(card->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
+    card->atr_length = sizeof(DEFAULT_ATR);
+    return 0;
+}
+
+static int passthru_exitfn(CCIDCardState *base)
+{
+    return 0;
+}
+
+static void passthru_pre_save(void *opaque)
+{
+    PassthruState *card = opaque;
+    VSCMsgReconnect reconnect;
+
+    reconnect.ip = 0; // TODO - does the bus keep the target ip? s->migration_target_ip;
+    reconnect.port = 0; // TODO - does the bus keep the target ip? s->migration_target_port;
+    ccid_card_vscard_send_msg(card, VSC_Reconnect, VSCARD_UNDEFINED_READER_ID,
+                         (uint8_t*)&reconnect, sizeof(reconnect));
+}
+
+static VMStateDescription passthru_vmstate = {
+    .name = PASSTHRU_DEV_NAME,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .pre_save = passthru_pre_save,
+    .fields = (VMStateField []) {
+        VMSTATE_BUFFER(vscard_in_data, PassthruState),
+        VMSTATE_UINT32(vscard_in_pos, PassthruState),
+        VMSTATE_UINT32(vscard_in_hdr, PassthruState),
+        VMSTATE_BUFFER(atr, PassthruState),
+        VMSTATE_UINT8(atr_length, PassthruState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static CCIDCardInfo passthru_card_info = {
+    .qdev.name = PASSTHRU_DEV_NAME,
+    .qdev.size = sizeof(PassthruState),
+    .qdev.vmsd = &passthru_vmstate,
+    .initfn = passthru_initfn,
+    .exitfn = passthru_exitfn,
+    .get_atr = passthru_get_atr,
+    .apdu_from_guest = passthru_apdu_from_guest,
+    .qdev.unplug    = qdev_simple_unplug_cb,
+    .qdev.props     = (Property[]) {
+        DEFINE_PROP_CHR("chardev", PassthruState, cs),
+        DEFINE_PROP_UINT8("debug", PassthruState, debug, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void ccid_card_passthru_register_devices(void)
+{
+    ccid_card_qdev_register(&passthru_card_info);
+    // TODO: passthru local card (or: just a case of passthru with no chardev
+    //  given and instead some other arguments that would be required for local
+    //  card anyway and can be shared with the emulated local card)
+    // TODO: emulated local card
+}
+
+device_init(ccid_card_passthru_register_devices)