[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2)
From: |
Alon Levy |
Subject: |
Re: [Qemu-devel] [PATCH 4/7] ccid: add ccid-card-emulated device (v2) |
Date: |
Mon, 31 Jan 2011 21:28:03 +0200 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
On Tue, Jan 25, 2011 at 08:21:13AM -0600, Anthony Liguori wrote:
> On 01/11/2011 02:42 AM, Alon Levy wrote:
> >This devices uses libcacard (internal) to emulate a smartcard conforming
> >to the CAC standard. It attaches to the usb-ccid bus. Usage instructions
> >(example command lines) are in the following patch in docs/ccid.txt. It
> >uses libcacard which uses nss, so it can work with both hw cards and
> >certificates (files).
> >
> >changes from v1:
> > remove stale comments, use only c-style comments
> > bugfix, forgot to set recv_len
> > change reader name to 'Virtual Reader'
> >
> >Signed-off-by: Alon Levy<address@hidden>
> >---
> > Makefile.objs | 2 +-
> > hw/ccid-card-emulated.c | 534
> > +++++++++++++++++++++++++++++++++++++++++++++++
> > hw/ccid-card-passthru.c | 1 -
> > 3 files changed, 535 insertions(+), 2 deletions(-)
> > create mode 100644 hw/ccid-card-emulated.c
> >
> >diff --git a/Makefile.objs b/Makefile.objs
> >index 6a0030b..303b642 100644
> >--- a/Makefile.objs
> >+++ b/Makefile.objs
> >@@ -197,7 +197,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 ccid-card-passthru.o
> >+hw-obj-$(CONFIG_SMARTCARD) += usb-ccid.o ccid-card-passthru.o
> >ccid-card-emulated.o
> >
> > # PPC devices
> > hw-obj-$(CONFIG_OPENPIC) += openpic.o
> >diff --git a/hw/ccid-card-emulated.c b/hw/ccid-card-emulated.c
> >new file mode 100644
> >index 0000000..5531ce1
> >--- /dev/null
> >+++ b/hw/ccid-card-emulated.c
> >@@ -0,0 +1,534 @@
> >+/*
> >+ * CCID Card Device. Emulated card.
> >+ *
> >+ * It can be used to provide access to the local hardware in a non exclusive
> >+ * way, or it can use certificates. It requires the usb-ccid bus.
> >+ *
> >+ * Usage 1: standard, mirror hardware reader+card:
> >+ * qemu .. -usb -device usb-ccid -device ccid-card-emulated
> >+ *
> >+ * Usage 2: use certificates, no hardware required
> >+ * one time: create the certificates:
> >+ * for i in 1 2 3; do certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s
> >"CN=user$i" -n user$i; done
> >+ * qemu .. -usb -device usb-ccid -device
> >ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
> >+ *
> >+ * If you use a non default db for the certificates you can specify it
> >using the db parameter.
> >+ *
> >+ *
> >+ * Copyright (c) 2010 Red Hat.
> >+ * Written by Alon Levy.
> >+ *
> >+ * This code is licenced under the LGPL.
> >+ */
> >+
> >+#include<pthread.h>
> >+#include<eventt.h>
> >+#include<vevent.h>
> >+#include<vreader.h>
> >+#include<vcard_emul.h>
> >+#include "qemu-char.h"
> >+#include "monitor.h"
> >+#include "hw/ccid.h"
> >+
> >+#define DPRINTF(card, lvl, fmt, ...) \
> >+do { if (lvl<= card->debug) { printf("ccid-card-emul: %s: " fmt , __func__,
> >## __VA_ARGS__); } } while (0)
> >+
> >+#define EMULATED_DEV_NAME "ccid-card-emulated"
> >+
> >+#define BACKEND_NSS_EMULATED "nss-emulated" /* the default */
> >+#define BACKEND_CERTIFICATES "certificates"
> >+
> >+typedef struct EmulatedState EmulatedState;
> >+
> >+enum {
> >+ EMUL_READER_INSERT = 0,
> >+ EMUL_READER_REMOVE,
> >+ EMUL_CARD_INSERT,
> >+ EMUL_CARD_REMOVE,
> >+ EMUL_GUEST_APDU,
> >+ EMUL_RESPONSE_APDU,
> >+ EMUL_ERROR,
> >+};
> >+
> >+static const char* emul_event_to_string(uint32_t emul_event)
> >+{
> >+ switch (emul_event) {
> >+ case EMUL_READER_INSERT: return "EMUL_READER_INSERT";
> >+ case EMUL_READER_REMOVE: return "EMUL_READER_REMOVE";
> >+ case EMUL_CARD_INSERT: return "EMUL_CARD_INSERT";
> >+ case EMUL_CARD_REMOVE: return "EMUL_CARD_REMOVE";
> >+ case EMUL_GUEST_APDU: return "EMUL_GUEST_APDU";
> >+ case EMUL_RESPONSE_APDU: return "EMUL_RESPONSE_APDU";
> >+ case EMUL_ERROR: return "EMUL_ERROR";
> >+ default:
> >+ break;
> >+ }
> >+ return "UNKNOWN";
> >+}
> >+
> >+typedef struct EmulEvent {
> >+ QSIMPLEQ_ENTRY(EmulEvent) entry;
> >+ union {
> >+ struct {
> >+ uint32_t type;
> >+ } gen;
> >+ struct {
> >+ uint32_t type;
> >+ uint64_t code;
> >+ } error;
> >+ struct {
> >+ uint32_t type;
> >+ uint32_t len;
> >+ uint8_t data[];
> >+ } data;
> >+ } p;
> >+} EmulEvent;
> >+
> >+#define MAX_ATR_SIZE 40
> >+struct EmulatedState {
> >+ CCIDCardState base;
> >+ uint8_t debug;
> >+ char* backend;
> >+ char* cert1;
> >+ char* cert2;
> >+ char* cert3;
> >+ char* db;
> >+ uint8_t atr[MAX_ATR_SIZE];
> >+ uint8_t atr_length;
> >+ QSIMPLEQ_HEAD(event_list, EmulEvent) event_list;
> >+ pthread_mutex_t event_list_mutex;
> >+ VReader *reader;
> >+ QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list;
> >+ pthread_mutex_t vreader_mutex; /* and guest_apdu_list mutex */
> >+ pthread_mutex_t handle_apdu_mutex;
> >+ pthread_cond_t handle_apdu_cond;
> >+ int pipe[2];
> >+ int quit_apdu_thread;
> >+ pthread_mutex_t apdu_thread_quit_mutex;
> >+ pthread_cond_t apdu_thread_quit_cond;
> >+};
> >+
> >+static void emulated_apdu_from_guest(CCIDCardState *base, const uint8_t
> >*apdu, uint32_t len)
> >+{
> >+ EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+ EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> >+
> >+ assert(event);
> >+ event->p.data.type = EMUL_GUEST_APDU;
> >+ event->p.data.len = len;
> >+ memcpy(event->p.data.data, apdu, len);
> >+ pthread_mutex_lock(&card->vreader_mutex);
> >+ QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
> >+ pthread_mutex_unlock(&card->vreader_mutex);
> >+ pthread_mutex_lock(&card->handle_apdu_mutex);
> >+ pthread_cond_signal(&card->handle_apdu_cond);
> >+ pthread_mutex_unlock(&card->handle_apdu_mutex);
> >+}
> >+
> >+static const uint8_t* emulated_get_atr(CCIDCardState *base, uint32_t *len)
> >+{
> >+ EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+
> >+ *len = card->atr_length;
> >+ return card->atr;
> >+}
> >+
> >+static void emulated_push_event(EmulatedState *card, EmulEvent *event)
> >+{
> >+ pthread_mutex_lock(&card->event_list_mutex);
> >+ QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
> >+ pthread_mutex_unlock(&card->event_list_mutex);
> >+ if (write(card->pipe[1], card, 1) != 1) {
> >+ DPRINTF(card, 1, "write to pipe failed\n");
> >+ }
> >+}
> >+
> >+static void emulated_push_type(EmulatedState *card, uint32_t type)
> >+{
> >+ EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> >+
> >+ assert(event);
> >+ event->p.gen.type = type;
> >+ emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_error(EmulatedState *card, uint64_t code)
> >+{
> >+ EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent));
> >+
> >+ assert(event);
> >+ event->p.error.type = EMUL_ERROR;
> >+ event->p.error.code = code;
> >+ emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_data_type(EmulatedState *card, uint32_t type,
> >+ const uint8_t *data, uint32_t len)
> >+{
> >+ EmulEvent *event = (EmulEvent*)malloc(sizeof(EmulEvent) + len);
> >+
> >+ assert(event);
> >+ event->p.data.type = type;
> >+ event->p.data.len = len;
> >+ memcpy(event->p.data.data, data, len);
> >+ emulated_push_event(card, event);
> >+}
> >+
> >+static void emulated_push_reader_insert(EmulatedState *card)
> >+{
> >+ emulated_push_type(card, EMUL_READER_INSERT);
> >+}
> >+
> >+static void emulated_push_reader_remove(EmulatedState *card)
> >+{
> >+ emulated_push_type(card, EMUL_READER_REMOVE);
> >+}
> >+
> >+static void emulated_push_card_insert(EmulatedState *card,
> >+ const uint8_t *atr, uint32_t len)
> >+{
> >+ emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
> >+}
> >+
> >+static void emulated_push_card_remove(EmulatedState *card)
> >+{
> >+ emulated_push_type(card, EMUL_CARD_REMOVE);
> >+}
> >+
> >+static void emulated_push_response_apdu(EmulatedState *card,
> >+ const uint8_t* apdu, uint32_t len)
> >+{
> >+ emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
> >+}
> >+
> >+#define APDU_BUF_SIZE 270
> >+static void *handle_apdu_thread(void* arg)
> >+{
> >+ EmulatedState *card = arg;
> >+ uint8_t recv_data[APDU_BUF_SIZE];
> >+ int recv_len;
> >+ VReaderStatus reader_status;
> >+ EmulEvent *event;
> >+
> >+ while (1) {
> >+ pthread_mutex_lock(&card->handle_apdu_mutex);
> >+ pthread_cond_wait(&card->handle_apdu_cond,&card->handle_apdu_mutex);
> >+ pthread_mutex_unlock(&card->handle_apdu_mutex);
> >+ if (card->quit_apdu_thread) {
> >+ card->quit_apdu_thread = 0; // debugging
> >+ break;
> >+ }
> >+ pthread_mutex_lock(&card->vreader_mutex);
> >+ while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
> >+ event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
> >+ assert((unsigned long)event> 1000);
> >+ QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
> >+ if (event->p.data.type != EMUL_GUEST_APDU) {
> >+ DPRINTF(card, 1, "unexpected message in
> >handle_apdu_thread\n");
> >+ free(event);
> >+ continue;
> >+ }
> >+ if (card->reader == NULL) {
> >+ DPRINTF(card, 1, "reader is NULL\n");
> >+ free(event);
> >+ continue;
> >+ }
> >+ recv_len = sizeof(recv_data);
> >+ reader_status = vreader_xfr_bytes(card->reader,
> >+ event->p.data.data, event->p.data.len,
> >+ recv_data,&recv_len);
> >+ DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
> >+ if (reader_status == VREADER_OK) {
> >+ emulated_push_response_apdu(card, recv_data, recv_len);
> >+ } else {
> >+ emulated_push_error(card, reader_status);
> >+ }
> >+ free(event);
> >+ }
> >+ pthread_mutex_unlock(&card->vreader_mutex);
> >+ }
> >+ pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> >+ pthread_cond_signal(&card->apdu_thread_quit_cond);
> >+ pthread_mutex_unlock(&card->apdu_thread_quit_mutex);
> >+ return NULL;
> >+}
> >+
> >+static void *event_thread(void *arg)
> >+{
> >+ int atr_len = MAX_ATR_SIZE;
> >+ uint8_t atr[MAX_ATR_SIZE];
> >+ VEvent *event = NULL;
> >+ EmulatedState *card = arg;
> >+
> >+ while (1) {
> >+ const char *reader_name;
> >+
> >+ event = vevent_wait_next_vevent();
> >+ if (event == NULL || event->type == VEVENT_LAST) {
> >+ break;
> >+ }
> >+ if (event->type != VEVENT_READER_INSERT) {
> >+ if (card->reader == NULL&& event->reader != NULL) {
> >+ // Happens after device_add followed by card remove or
> >insert.
> >+ // XXX: create synthetic add_reader events if
> >vcard_emul_init
> >+ // already called, which happens if device_del and
> >device_add are
> >+ // called
> >+ card->reader = vreader_reference(event->reader);
> >+ } else {
> >+ if (event->reader != card->reader) {
> >+ fprintf(stderr, "ERROR: wrong reader: quiting
> >event_thread\n");
> >+ break;
> >+ }
> >+ }
> >+ }
> >+ switch (event->type) {
> >+ case VEVENT_READER_INSERT:
> >+ /* TODO: take a specific reader. i.e. track which reader
> >+ * we are seeing here, check it is the one we want (the first,
> >+ * or by a particular name), and ignore if we don't want it.
> >+ */
> >+ reader_name = vreader_get_name(event->reader);
> >+ if (card->reader != NULL) {
> >+ DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n",
> >+ vreader_get_name(card->reader), reader_name);
> >+ pthread_mutex_lock(&card->vreader_mutex);
> >+ vreader_free(card->reader);
> >+ pthread_mutex_unlock(&card->vreader_mutex);
> >+ emulated_push_reader_remove(card);
> >+ }
> >+ pthread_mutex_lock(&card->vreader_mutex);
> >+ DPRINTF(card, 2, "READER INSERT %s\n", reader_name);
> >+ card->reader = vreader_reference(event->reader);
> >+ pthread_mutex_unlock(&card->vreader_mutex);
> >+ emulated_push_reader_insert(card);
> >+ break;
> >+ case VEVENT_READER_REMOVE:
> >+ DPRINTF(card, 2, " READER REMOVE: %s \n",
> >+ vreader_get_name(event->reader));
> >+ pthread_mutex_lock(&card->vreader_mutex);
> >+ vreader_free(card->reader);
> >+ card->reader = NULL;
> >+ pthread_mutex_unlock(&card->vreader_mutex);
> >+ emulated_push_reader_remove(card);
> >+ break;
> >+ case VEVENT_CARD_INSERT:
> >+ /* get the ATR (intended as a response to a power on from the
> >+ * reader */
> >+ atr_len = MAX_ATR_SIZE;
> >+ vreader_power_on(event->reader, atr,&atr_len);
> >+ card->atr_length = (uint8_t)atr_len;
> >+ DPRINTF(card, 2, " CARD INSERT\n");
> >+ emulated_push_card_insert(card, atr, atr_len);
> >+ break;
> >+ case VEVENT_CARD_REMOVE:
> >+ DPRINTF(card, 2, " CARD REMOVE\n");
> >+ emulated_push_card_remove(card);
> >+ break;
> >+ case VEVENT_LAST: /* quit */
> >+ vevent_delete(event);
> >+ return NULL;
> >+ break;
> >+ default:
> >+ break;
> >+ }
> >+ vevent_delete(event);
> >+ }
> >+ return NULL;
> >+}
> >+
> >+static void pipe_read(void *opaque)
> >+{
> >+ EmulatedState *card = opaque;
> >+ EmulEvent *event, *next;
> >+ char dummy;
> >+ int len;
> >+
> >+ do {
> >+ len = read(card->pipe[0],&dummy, sizeof(dummy));
> >+ } while (len == sizeof(dummy));
> >+ pthread_mutex_lock(&card->event_list_mutex);
> >+ QSIMPLEQ_FOREACH_SAFE(event,&card->event_list, entry, next) {
> >+ DPRINTF(card, 2, "event %s\n",
> >emul_event_to_string(event->p.gen.type));
> >+ switch (event->p.gen.type) {
> >+ case EMUL_RESPONSE_APDU:
> >+ ccid_card_send_apdu_to_guest(&card->base,
> >event->p.data.data,
> >+ event->p.data.len);
> >+ break;
> >+ case EMUL_READER_INSERT:
> >+ ccid_card_ccid_attach(&card->base);
> >+ break;
> >+ case EMUL_READER_REMOVE:
> >+ ccid_card_ccid_detach(&card->base);
> >+ break;
> >+ case EMUL_CARD_INSERT:
> >+ assert(event->p.data.len<= MAX_ATR_SIZE);
> >+ card->atr_length = event->p.data.len;
> >+ memcpy(card->atr, event->p.data.data, card->atr_length);
> >+ ccid_card_card_inserted(&card->base);
> >+ break;
> >+ case EMUL_CARD_REMOVE:
> >+ ccid_card_card_removed(&card->base);
> >+ break;
> >+ case EMUL_ERROR:
> >+ ccid_card_card_error(&card->base, event->p.error.code);
> >+ break;
> >+ default:
> >+ DPRINTF(card, 2, "unexpected event\n");
> >+ break;
> >+ }
> >+ free(event);
> >+ }
> >+ QSIMPLEQ_INIT(&card->event_list);
> >+ pthread_mutex_unlock(&card->event_list_mutex);
> >+}
> >+
> >+static int init_pipe_signaling(EmulatedState *card)
> >+{
> >+ if (pipe(card->pipe)< 0) {
> >+ DPRINTF(card, 2, "pipe creation failed\n");
> >+ return -1;
> >+ }
> >+ fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
> >+ fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
> >+ fcntl(card->pipe[0], F_SETOWN, getpid());
> >+ qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card);
> >+ return 0;
> >+}
> >+
> >+#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb"
> >+#define CERTIFICATES_ARGS_TEMPLATE "db=\"%s\" use_hw=no soft=(,Virtual
> >Reader,CAC,,%s,%s,%s)"
> >+
> >+static int wrap_vcard_emul_init(VCardEmulOptions *options)
> >+{
> >+ static int called = 0;
> >+ static int options_was_null = 0;
> >+
> >+ if (called) {
> >+ if ((options == NULL) != options_was_null) {
> >+ printf("%s: warning: running emulated with certificates and
> >emulated side by side is not supported\n", __FUNCTION__);
> >+ return VCARD_EMUL_FAIL;
> >+ }
> >+ vcard_emul_replay_insertion_events();
> >+ return VCARD_EMUL_OK;
> >+ }
> >+ options_was_null = (options == NULL);
> >+ called = 1;
> >+ return vcard_emul_init(options);
> >+}
> >+
> >+static int emulated_initialize_vcard_from_certificates(EmulatedState *card)
> >+{
> >+ char emul_args[200];
> >+ VCardEmulOptions *options = NULL;
> >+
> >+ snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE,
> >+ card->db ? card->db : CERTIFICATES_DEFAULT_DB,
> >+ card->cert1, card->cert2, card->cert3);
> >+ options = vcard_emul_options(emul_args);
> >+ if (options == NULL) {
> >+ printf("%s: warning: not using certificates due to initialization
> >error\n", __func__);
> >+ }
> >+ return wrap_vcard_emul_init(options);
> >+}
> >+
> >+static int emulated_initfn(CCIDCardState *base)
> >+{
> >+ EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+ int rv;
> >+ pthread_t thread_id;
> >+ VCardEmulError ret;
> >+
> >+ QSIMPLEQ_INIT(&card->event_list);
> >+ QSIMPLEQ_INIT(&card->guest_apdu_list);
> >+ pthread_mutex_init(&card->event_list_mutex, NULL);
> >+ pthread_mutex_init(&card->vreader_mutex, NULL);
> >+ pthread_mutex_init(&card->handle_apdu_mutex, NULL);
> >+ pthread_cond_init(&card->handle_apdu_cond, NULL);
> >+ card->reader = NULL;
> >+ card->quit_apdu_thread = 0;
> >+ if (init_pipe_signaling(card)< 0) {
> >+ return -1;
> >+ }
> >+ if (!card->backend) {
> >+ card->backend = strdup((char*)BACKEND_NSS_EMULATED);
> >+ }
> >+ /* TODO: a passthru backened that works on local machine. third card
> >type? */
> >+ if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0
> >+&& card->cert1 != NULL&& card->cert2 != NULL&& card->cert3 != NULL) {
> >+ ret = emulated_initialize_vcard_from_certificates(card);
> >+ } else {
> >+ if (strcmp(BACKEND_CERTIFICATES, card->backend) == 0) {
> >+ printf("%s: you must provide all three certs for certificates
> >backend\n",
> >+ EMULATED_DEV_NAME);
> >+ return -1;
> >+ }
> >+ if (card->backend&& strcmp(BACKEND_NSS_EMULATED, card->backend) !=
> >0) {
> >+ printf("%s: bad backend specified. The options are:\n%s
> >(default), %s.\n",
> >+ EMULATED_DEV_NAME, BACKEND_NSS_EMULATED,
> >BACKEND_CERTIFICATES);
> >+ return -1;
> >+ }
> >+ /* default to mirroring the local hardware readers */
> >+ ret = wrap_vcard_emul_init(NULL);
> >+ }
> >+ if (ret != VCARD_EMUL_OK) {
> >+ printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
> >+ return -1;
> >+ }
> >+ rv = pthread_create(&thread_id, NULL, event_thread, card);
> >+ if (rv< 0) {
> >+ printf("%s: error creating event thread\n", EMULATED_DEV_NAME);
> >+ return -1;
> >+ }
>
> Why can't this be implemented with qemu_set_fd_handler? The event
> thread si just spinning on read.
>
I've looked a little more closely at why I did it exactly this way.
The short answer is that both threads are not spinning, they are waiting
on conditions, the event_thread on the libcacard vevent_queue condition, and
the apdu_thread on the ccid-card-emulated internal guest_apdu queue condition,
triggered by the main thread.
This is the ccid-card-emulated threads and flow (hope it's understandable):
libcacard details:
vcard_emul_event_thread for each reader
blocking on SECMOD_WaitForAnyTokenEvent
pushes events to vevent queue
ccid-card-emulated details:
event_thread
read vevent queue -> {reader,card}x{insert,remove} -> write to apdu_pipe
(writes to apdu_pipe are mutex protected)
handle_apdu_thread
pop guest_apdu_list -> vreader_xfr_bytes -> write result to apdu_pipe
main_thread
usb event
apdu from guest -> push to guest_apdu_list
apdu_pipe read
apdu to guest
ccid attach
ccid detach
ccid card removed
ccid card inserted
If I had a mechanism to let the qemu main loop wait on a mutex or condition
variable I could remove both event_thread and handle_apdu_thread. (reading
a little more about this let me to believe the solution I came up with, i.e.
a queue and a condition variable, is commonly used for this in POSIX).
But right now:
I need a separate thread to block on the vevent queue condition
(pthread_cond_t/CONDITION_VARIABLE in linux/windows).
I need a separate thread to call vreader_xfr_bytes (it may block on the NSS
thread).
Alternatives:
launch a thread for each apdu from guest:
since the apdu's are linear any way (i.e. each one would wait for the next)
this seems wasteful.
Since we need to wait on a condition signaled by NSS, there is no alternative
then having a separate thread for that.
Alon
> Regards,
>
> Anthony Liguori
>
> >+ rv = pthread_create(&thread_id, NULL, handle_apdu_thread, card);
> >+ if (rv< 0) {
> >+ printf("%s: error creating handle_apdu thread\n",
> >EMULATED_DEV_NAME);
> >+ return -1;
> >+ }
> >+ return 0;
> >+}
> >+
> >+static int emulated_exitfn(CCIDCardState *base)
> >+{
> >+ EmulatedState *card = DO_UPCAST(EmulatedState, base, base);
> >+ VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
> >+
> >+ vevent_queue_vevent(vevent); /* stop vevent thread */
> >+ pthread_mutex_lock(&card->apdu_thread_quit_mutex);
> >+ card->quit_apdu_thread = 1; /* stop handle_apdu thread */
> >+ pthread_cond_signal(&card->handle_apdu_cond);
> >+
> >pthread_cond_wait(&card->apdu_thread_quit_cond,&card->apdu_thread_quit_mutex);
> >+ /* handle_apdu thread stopped, can destroy all of it's mutexes */
> >+ pthread_cond_destroy(&card->handle_apdu_cond);
> >+ pthread_cond_destroy(&card->apdu_thread_quit_cond);
> >+ pthread_mutex_destroy(&card->apdu_thread_quit_mutex);
> >+ pthread_mutex_destroy(&card->handle_apdu_mutex);
> >+ pthread_mutex_destroy(&card->vreader_mutex);
> >+ pthread_mutex_destroy(&card->event_list_mutex);
> >+ return 0;
> >+}
> >+
> >+static CCIDCardInfo emulated_card_info = {
> >+ .qdev.name = EMULATED_DEV_NAME,
> >+ .qdev.size = sizeof(EmulatedState),
> >+ .initfn = emulated_initfn,
> >+ .exitfn = emulated_exitfn,
> >+ .get_atr = emulated_get_atr,
> >+ .apdu_from_guest = emulated_apdu_from_guest,
> >+ .qdev.unplug = qdev_simple_unplug_cb,
> >+ .qdev.props = (Property[]) {
> >+ DEFINE_PROP_STRING("backend", EmulatedState, backend),
> >+ DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
> >+ DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
> >+ DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
> >+ DEFINE_PROP_STRING("db", EmulatedState, db),
> >+ DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
> >+ DEFINE_PROP_END_OF_LIST(),
> >+ },
> >+};
> >+
> >+static void ccid_card_emulated_register_devices(void)
> >+{
> >+ ccid_card_qdev_register(&emulated_card_info);
> >+}
> >+
> >+device_init(ccid_card_emulated_register_devices)
> >diff --git a/hw/ccid-card-passthru.c b/hw/ccid-card-passthru.c
> >index 6ec4f21..f9fb82c 100644
> >--- a/hw/ccid-card-passthru.c
> >+++ b/hw/ccid-card-passthru.c
> >@@ -253,7 +253,6 @@ static CCIDCardInfo passthru_card_info = {
> > .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),
>
- [Qemu-devel] [PATCH 0/7] usb-ccid (v15), Alon Levy, 2011/01/11
- [Qemu-devel] [PATCH 5/7] ccid: add docs, Alon Levy, 2011/01/11
- [Qemu-devel] [PATCH 6/6] ccid: configure: add --enable-smartcard and --disable-smartcard, Alon Levy, 2011/01/11
- [Qemu-devel] [PATCH 2/7] ccid: add passthru card device, Alon Levy, 2011/01/11
- Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device, Alon Levy, 2011/01/28
- Re: [Qemu-devel] [PATCH 2/7] ccid: add passthru card device, Anthony Liguori, 2011/01/28