[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 05/10] ccid: add passthru card device
From: |
Alon Levy |
Subject: |
[Qemu-devel] [PATCH 05/10] ccid: add passthru card device |
Date: |
Mon, 28 Mar 2011 18:11:09 +0200 |
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 <address@hidden>
---
Changes from v23->v24:
* fixed double license line in header.
Changes from v20->v21: (Jes Sorensen review)
* add reference to COPYING in header
* long comment reformatting
Changes from v19->v20:
* checkpatch.pl
Changes from v18->v19:
* add qdev.desc
* remove .qdev.unplug (no hot unplug support for ccid bus)
Changes from v16->v17:
* fix wrong cast when receiving VSC_Error
* ccid-card-passthru: force chardev user wakeup by sending Init
see lengthy comment below.
Changes from v15->v16:
Behavioral changes:
* return correct size
* return error instead of assert if client sent too large ATR
* don't assert if client sent too large a size, but add asserts for indices to
buffer
* reset vscard_in indices on chardev disconnect
* handle init from client
* error if no chardev supplied
* use ntoh, hton
* eradicate reader_id_t
* remove Reconnect usage (removed from VSCARD protocol)
* send VSC_SUCCESS on card insert/remove and reader add/remove
Style fixes:
* width of line fix
* update copyright
* remove old TODO's
* update file header comment
* use macros for debug levels
* c++ style comment replacement
* update copyright license
* fix ATR size comment
* fix whitespace in struct def
* fix DPRINTF prefix
* line width fix
ccid-card-passthru: force chardev user wakeup by sending Init
The problem: how to wakeup the user of the smartcard when the smartcard
device is initialized?
Long term solution: have a callback interface. This was done via
the deprecated so called chardev ioctl interface.
Short term solution: do a write. Specifically we write an Init message.
And we change the client to send it's own Init message regardless of
receiving this one. Additional Init messages will be regarded as
acceptable, the first one received after connection establishment is
the determining one wrt capabilities.
---
Makefile.objs | 2 +-
hw/ccid-card-passthru.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 341 insertions(+), 1 deletions(-)
create mode 100644 hw/ccid-card-passthru.c
diff --git a/Makefile.objs b/Makefile.objs
index 489a46b..744e1d3 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -200,7 +200,7 @@ hw-obj-$(CONFIG_APM) += pm_smbus.o apm.o
hw-obj-$(CONFIG_DMA) += dma.o
hw-obj-$(CONFIG_HPET) += hpet.o
hw-obj-$(CONFIG_APPLESMC) += applesmc.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..8506fed
--- /dev/null
+++ b/hw/ccid-card-passthru.c
@@ -0,0 +1,340 @@
+/*
+ * CCID Passthru Card Device emulation
+ *
+ * Copyright (c) 2011 Red Hat.
+ * Written by Alon Levy.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.1 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <arpa/inet.h>
+
+#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-passthru: " fmt , ## __VA_ARGS__); \
+ } \
+} while (0)
+
+#define D_WARN 1
+#define D_INFO 2
+#define D_MORE_INFO 3
+#define D_VERBOSE 4
+
+/* 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
+};
+
+
+#define PASSTHRU_DEV_NAME "ccid-card-passthru"
+#define VSCARD_IN_SIZE 65536
+
+/* maximum size of ATR - from 7816-3 */
+#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, uint32_t reader_id,
+ const uint8_t *payload, uint32_t length)
+{
+ VSCMsgHeader scr_msg_header;
+
+ scr_msg_header.type = htonl(type);
+ scr_msg_header.reader_id = htonl(reader_id);
+ scr_msg_header.length = htonl(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,
+ uint32_t reader_id, VSCErrorCode code)
+{
+ VSCMsgError msg = {.code = htonl(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 = htonl(VSCARD_VERSION),
+ .magic = VSCARD_MAGIC,
+ .capabilities = {0}
+ };
+
+ 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)
+{
+ PassthruState *card = opaque;
+
+ return VSCARD_IN_SIZE >= card->vscard_in_pos ?
+ VSCARD_IN_SIZE - card->vscard_in_pos : 0;
+}
+
+static void ccid_card_vscard_handle_init(
+ PassthruState *card, VSCMsgHeader *hdr, VSCMsgInit *init)
+{
+ uint32_t *capabilities;
+ int num_capabilities;
+ int i;
+
+ capabilities = init->capabilities;
+ num_capabilities =
+ 1 + ((hdr->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
+ init->version = ntohl(init->version);
+ for (i = 0 ; i < num_capabilities; ++i) {
+ capabilities[i] = ntohl(capabilities[i]);
+ }
+ if (init->magic != VSCARD_MAGIC) {
+ error_report("wrong magic");
+ /* we can't disconnect the chardev */
+ }
+ if (init->version != VSCARD_VERSION) {
+ DPRINTF(card, D_WARN,
+ "got version %d, have %d", init->version, VSCARD_VERSION);
+ }
+ /* future handling of capabilities, none exist atm */
+ ccid_card_vscard_send_init(card);
+}
+
+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, D_INFO, "VSC_ATR %d\n", scr_msg_header->length);
+ if (scr_msg_header->length > MAX_ATR_SIZE) {
+ error_report("ATR size exceeds spec, ignoring");
+ ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+ VSC_GENERAL_ERROR);
+ }
+ memcpy(card->atr, data, scr_msg_header->length);
+ card->atr_length = scr_msg_header->length;
+ ccid_card_card_inserted(&card->base);
+ ccid_card_vscard_send_error(card, scr_msg_header->reader_id,
+ VSC_SUCCESS);
+ break;
+ case VSC_APDU:
+ ccid_card_send_apdu_to_guest(
+ &card->base, data, scr_msg_header->length);
+ break;
+ case VSC_CardRemove:
+ DPRINTF(card, D_INFO, "VSC_CardRemove\n");
+ ccid_card_card_removed(&card->base);
+ ccid_card_vscard_send_error(card,
+ scr_msg_header->reader_id, VSC_SUCCESS);
+ break;
+ case VSC_Init:
+ ccid_card_vscard_handle_init(
+ card, scr_msg_header, (VSCMsgInit *)data);
+ break;
+ case VSC_Error:
+ ccid_card_card_error(&card->base, *(uint32_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_error(card, VSCARD_MINIMAL_READER_ID,
+ VSC_SUCCESS);
+ }
+ break;
+ case VSC_ReaderRemove:
+ ccid_card_ccid_detach(&card->base);
+ ccid_card_vscard_send_error(card,
+ scr_msg_header->reader_id, VSC_SUCCESS);
+ 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_drop_connection(PassthruState *card)
+{
+ qemu_chr_close(card->cs);
+ card->vscard_in_pos = card->vscard_in_hdr = 0;
+}
+
+static void ccid_card_vscard_read(void *opaque, const uint8_t *buf, int size)
+{
+ PassthruState *card = opaque;
+ VSCMsgHeader *hdr;
+
+ if (card->vscard_in_pos + size > VSCARD_IN_SIZE) {
+ error_report(
+ "no room for data: pos %d + size %d > %d. dropping connection.",
+ card->vscard_in_pos, size, VSCARD_IN_SIZE);
+ ccid_card_vscard_drop_connection(card);
+ return;
+ }
+ assert(card->vscard_in_pos < VSCARD_IN_SIZE);
+ assert(card->vscard_in_hdr < 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) + ntohl(hdr->length))) {
+ hdr->reader_id = ntohl(hdr->reader_id);
+ hdr->length = ntohl(hdr->length);
+ hdr->type = ntohl(hdr->type);
+ 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:
+ card->vscard_in_pos = card->vscard_in_hdr = 0;
+ break;
+ case CHR_EVENT_FOCUS:
+ break;
+ case CHR_EVENT_OPENED:
+ DPRINTF(card, D_INFO, "%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, D_INFO, "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);
+ } else {
+ error_report("missing chardev");
+ return -1;
+ }
+ 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 VMStateDescription passthru_vmstate = {
+ .name = PASSTHRU_DEV_NAME,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .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.desc = "passthrough smartcard",
+ .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.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);
+}
+
+device_init(ccid_card_passthru_register_devices)
--
1.7.4.1
- [Qemu-devel] [PATCH v24 00/10] usb-ccid, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 01/10] trace: move trace objects from Makefile to Makefile.objs, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 04/10] introduce libcacard/vscard_common.h, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 02/10] qemu-thread.h: include inttypes.h, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 10/10] ccid: add docs, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH v24 00/10] usb-ccid, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 05/10] ccid: add passthru card device,
Alon Levy <=
- [Qemu-devel] [PATCH 07/10] libcacard: add vscclient, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 09/10] ccid: add ccid-card-emulated device, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 08/10] libcacard: add docs, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 06/10] libcacard: initial commit, Alon Levy, 2011/03/28
- [Qemu-devel] [PATCH 03/10] usb-ccid: add CCID bus, Alon Levy, 2011/03/28
- Re: [Qemu-devel] [PATCH v24 00/10] usb-ccid, Jes Sorensen, 2011/03/29