qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH 0/2] USB CCID device
@ 2010-10-05 21:32 Alon Levy
  2010-10-05 21:32 ` [Qemu-devel] [PATCH 1/2] usb-ccid: add CCID device. add configure option Alon Levy
                   ` (2 more replies)
  0 siblings, 3 replies; 14+ messages in thread
From: Alon Levy @ 2010-10-05 21:32 UTC (permalink / raw)
  To: qemu-devel, alevy

This patch adds a new device, it is described in full in the second patch
intro and also in the documentation in docs. In brief it provides a standard
smart card reader device.

The first patch is the configure change and docs.
The second patch contains the actual device, I couldn't figure out a good
way to split it to ease review, so if the first reviewer can suggest
a good way to split it I would gladly do that.

Alon Levy (2):
  usb-ccid: add CCID device. add configure option.
  usb-ccid: add CCID device (device itself)

 Makefile.objs      |    1 +
 configure          |   12 +
 docs/usb-ccid.txt  |  115 +++++
 hw/usb-ccid.c      | 1254 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/vscard_common.h |  123 +++++
 5 files changed, 1505 insertions(+), 0 deletions(-)
 create mode 100644 docs/usb-ccid.txt
 create mode 100644 hw/usb-ccid.c
 create mode 100644 hw/vscard_common.h

-- 
1.7.3.1

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 1/2] usb-ccid: add CCID device. add configure option.
  2010-10-05 21:32 [Qemu-devel] [PATCH 0/2] USB CCID device Alon Levy
@ 2010-10-05 21:32 ` Alon Levy
  2010-10-05 21:33 ` [Qemu-devel] [PATCH 2/2] usb-ccid: add CCID device (device itself) Alon Levy
  2010-10-05 23:02 ` [Qemu-devel] [PATCH 0/2] USB CCID device Anthony Liguori
  2 siblings, 0 replies; 14+ messages in thread
From: Alon Levy @ 2010-10-05 21:32 UTC (permalink / raw)
  To: qemu-devel, alevy

This patch adds the configure option --enable-smartcard, adds hw/usb-ccid.c
to Makefile.objs, and adds the documentation. Only an empty hw/usb-ccid.c
is provided (couldn't add an empty file so added initial comment only).

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 Makefile.objs      |    1 +
 configure          |   12 +++++
 docs/usb-ccid.txt  |  115 ++++++++++++++++++++++++++++++++++++++++++++++++
 hw/usb-ccid.c      |   13 ++++++
 hw/vscard_common.h |  123 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 264 insertions(+), 0 deletions(-)
 create mode 100644 docs/usb-ccid.txt
 create mode 100644 hw/usb-ccid.c
 create mode 100644 hw/vscard_common.h

diff --git a/Makefile.objs b/Makefile.objs
index 9c13bb3..4b581e7 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -170,6 +170,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
 
 # PPC devices
 hw-obj-$(CONFIG_OPENPIC) += openpic.o
diff --git a/configure b/configure
index e0d34fd..fc59a40 100755
--- a/configure
+++ b/configure
@@ -327,6 +327,7 @@ user_pie="no"
 zero_malloc=""
 trace_backend="nop"
 trace_file="trace"
+smartcard="no"
 
 # OS specific
 if check_define __linux__ ; then
@@ -730,6 +731,10 @@ for opt do
   ;;
   --enable-vhost-net) vhost_net="yes"
   ;;
+  --disable-smartcard) smartcard="no"
+  ;;
+  --enable-smartcard) smartcard="yes"
+  ;;
   --*dir)
   ;;
   *) echo "ERROR: unknown option $opt"; show_help="yes"
@@ -921,6 +926,8 @@ echo "  --enable-vhost-net       enable vhost-net acceleration support"
 echo "  --trace-backend=B        Trace backend nop simple ust"
 echo "  --trace-file=NAME        Full PATH,NAME of file to store traces"
 echo "                           Default:trace-<pid>"
+echo "  --disable-smartcard      disable smartcard support"
+echo "  --enable-smartcard       enable smartcard support"
 echo ""
 echo "NOTE: The object files are built at the place where configure is launched"
 exit 1
@@ -2285,6 +2292,7 @@ echo "uuid support      $uuid"
 echo "vhost-net support $vhost_net"
 echo "Trace backend     $trace_backend"
 echo "Trace output file $trace_file-<pid>"
+echo "smartcard support $smartcard"
 
 if test $sdl_too_old = "yes"; then
 echo "-> Your SDL version is too old - please upgrade to have SDL support"
@@ -2540,6 +2548,10 @@ if test "$posix_madvise" = "yes" ; then
   echo "CONFIG_POSIX_MADVISE=y" >> $config_host_mak
 fi
 
+if test "$smartcard" = "yes" ; then
+  echo "CONFIG_SMARTCARD=y" >> $config_host_mak
+fi
+
 # XXX: suppress that
 if [ "$bsd" = "yes" ] ; then
   echo "CONFIG_BSD=y" >> $config_host_mak
diff --git a/docs/usb-ccid.txt b/docs/usb-ccid.txt
new file mode 100644
index 0000000..e418456
--- /dev/null
+++ b/docs/usb-ccid.txt
@@ -0,0 +1,115 @@
+Contents
+1. What is the USB CCID device?
+2. What is it the CCID used for?
+3. How can I remote my smart card reader to the guest?
+4. How do I emulate a smart card using certificates?
+5. Build instructions for qemu with usb-ccid and libcaccard
+6. How does it work?
+
+1. What is the USB CCID device?
+
+The USB CCID device is a USB device implementing the CCID specification, which lets one connect smart card readers that implement the same spec. For more information see the specification:
+
+ Universal Serial Bus
+ Device Class: Smart Card
+ CCID
+ Specification for
+ Integrated Circuit(s) Cards Interface Devices
+ Revision 1.1
+ April 22rd, 2005
+
+2. What is the CCID used for?
+
+Smartcard are used for authentication, single sign on, decryption in
+public/private schemes and digital signatures. A smartcard reader on the client
+cannot be used on a guest with simple usb passthrough since it will then not be
+available on the client, possibly locking the computer when it is "removed". On
+the other hand this device can let you use the smartcard on both the client and
+the guest machine. It is also possible to have a completely virtual smart card
+reader and smart card (i.e. not backed by a physical device) using this device.
+
+3. How do I remote a smartcard reader to the guest?
+
+After following the building instructions for qemu and vscclient run vscclient on the client machine (the one with the smart card reader) and qemu on another (possibly the same) machine like this:
+
+Assume we use port 2001 on the qemu machine, which we call qemuhost:
+
+on qemuhost: (run this first, qemu acts as the server)
+
+    qemu -chardev socket,server,host=0.0.0.0,port=2001,id=ccid,nowait -usb -device usb-ccid,chardev=ccid
+
+on client:
+
+    vscclient qemuhost 2001
+
+4. How do I emulate a smartcard using certificates?
+
+qmeu side doesn't change.
+
+on client side:
+create the certificates. vscclient uses libcac_card, which currently uses
+NSS as the backend. To create some self signed certificates using nss:
+
+    certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=cert1" -n cert1
+
+Note: three certificates are the maximum the emulated card will take.
+
+Then run vscclient with the local certificates:
+(Note: vscclient command line interface is in a state of change)
+
+    ./vscclient -e "db=\"/etc/pki/nssdb\" use_hw=no soft=(,Test,CAC,,cert1,cert2,cert3)" localhost 2001
+
+
+5. Build instructions for qemu with usb-ccid and libcaccard
+
+prerequisites:
+clone libcaccard (there are no binary or source releases at this time):
+git clone git://anongit.freedesktop.org/~alon/cac_card
+
+fedora:
+    yum install nss-devel
+ubuntu:
+    apt-get install libnss3-dev
+    (not tested on ubuntu)
+
+build cac_card:
+    cd cac_card
+    ./configure && make && sudo make install
+
+qemu:
+    ./configure --enable-smartcard && make
+
+Note that if you install to a non default prefix you need to set
+PKG_CONFIG_PATH to compile qemu successfully.
+
+6. How does it work?
+
+usb-ccid is a usb device. It defaults to an unattached usb device on startup.
+usb-ccid expects a chardev and expects the protocol defined in cac_card/vscard_common.h to be passed over that.
+A typical interchange is:
+
+client event           vscclient               usb-ccid         guest event
+
+                        VSC_Init
+                        VSC_ReaderAdd
+                                                                sees new usb device.
+card inserted
+                        VSC_ATR
+                                                                some guest operation on the card
+                                               VSC_APDU
+                        VSC_APDU
+                            [APDU<->APDU repeats several times]
+card removed
+                        VSC_CardRemove
+kill/quit vscclient
+                        VSC_ReaderRemove
+                                                                usb device removed.
+
+
+vscclient implements a completely virtual CAC (DoD standard for smart cards)
+compliant card and uses NSS to actually retrive certificates and do any
+encryption using the backend (real reader + card or file backed certificates
+and on pc encryption).
+
+For documentation of cac_card see README in libcac_card package.
+
diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
new file mode 100644
index 0000000..98805bb
--- /dev/null
+++ b/hw/usb-ccid.c
@@ -0,0 +1,13 @@
+/*
+ * CCID Device emulation
+ *
+ * Based on usb-serial.c:
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault,
+ * reused for CCID by Alon Levy.
+ * Copyright (c) 2010 Red Hat.
+ *
+ * This code is licenced under the LGPL.
+ */
+
diff --git a/hw/vscard_common.h b/hw/vscard_common.h
new file mode 100644
index 0000000..7966258
--- /dev/null
+++ b/hw/vscard_common.h
@@ -0,0 +1,123 @@
+/* Virtual Smart Card protocol definition
+ *
+ * This protocol is between a host implementing a group of virtual smart card
+ * reader, and a client implementing a virtual smart card, or passthrough to
+ * a real card.
+ *
+ * The current implementation passes the raw APDU's from 7816 and additionally
+ * contains messages to setup and teardown readers, handle insertion and
+ * removal of cards, negotiate the protocol and provide for error responses.
+ *
+ * Copyright (c) 2010 Red Hat.
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#ifndef _VSCARD_COMMON_H
+#define _VSCARD_COMMON_H
+
+#include <stdint.h>
+
+#define VERSION_MAJOR_BITS 11
+#define VERSION_MIDDLE_BITS 11
+#define VERSION_MINOR_BITS 10
+
+#define MAKE_VERSION(major, middle, minor) \
+     (  (major  << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
+      | (middle <<  VERSION_MINOR_BITS) \
+      | (minor)  )
+
+/** IMPORTANT NOTE on VERSION
+ *
+ * The version below MUST be changed whenever a change in this file is made.
+ *
+ * The last digit, the minor, is for bug fix changes only.
+ *
+ * The middle digit is for backward / forward compatible changes, updates
+ * to the existing messages, addition of fields.
+ *
+ * The major digit is for a breaking change of protocol, presumably
+ * something that cannot be accomodated with the existing protocol.
+ */
+
+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
+
+#define VSCARD_UNDEFINED_READER_ID -1
+#define VSCARD_MINIMAL_READER_ID    0
+
+typedef enum {
+    VSC_Init,
+    VSC_Error,
+    VSC_ReaderAdd,
+    VSC_ReaderAddResponse,
+    VSC_ReaderRemove,
+    VSC_ATR,
+    VSC_CardRemove,
+    VSC_APDU
+} VSCMsgType;
+
+typedef enum {
+    VSC_GENERAL_ERROR=1,
+    VSC_CANNOT_ADD_MORE_READERS,
+} VSCErrorCode;
+
+typedef uint32_t reader_id_t;
+
+typedef struct VSCMsgHeader {
+    VSCMsgType type;
+    reader_id_t   reader_id;
+    uint32_t   length;
+    uint8_t    data[0];
+} VSCMsgHeader;
+
+/* VSCMsgInit               Client <-> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * */
+typedef struct VSCMsgInit {
+    uint32_t   version;
+} VSCMsgInit;
+
+/* VSCMsgError              Client <-> Host
+ * */
+typedef struct VSCMsgError {
+    uint32_t   code;
+} VSCMsgError;
+
+/* VSCMsgReaderAdd          Client -> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * name - name of the reader on client side.
+ * */
+typedef struct VSCMsgReaderAdd {
+    uint8_t    name[0];
+} VSCMsgReaderAdd;
+
+/* VSCMsgReaderAddResponse  Host -> Client
+ * Reply to ReaderAdd
+ * */
+typedef struct VSCMsgReaderAddResponse {
+} VSCMsgReaderAddResponse;
+
+/* VSCMsgReaderRemove       Client -> Host
+ * */
+typedef struct VSCMsgReaderRemove {
+} VSCMsgReaderRemove;
+
+/* VSCMsgATR                Client -> Host
+ * Answer to reset. Sent for card insertion or card reset.
+ * */
+typedef struct VSCMsgATR {
+    uint8_t     atr[0];
+} VSCMsgATR;
+
+/* VSCMsgCardRemove         Client -> Host
+ * */
+typedef struct VSCMsgCardRemove {
+} VSCMsgCardRemove;
+
+/* VSCMsgAPDU               Client <-> Host
+ * */
+typedef struct VSCMsgAPDU {
+    uint8_t    data[0];
+} VSCMsgAPDU;
+
+#endif
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [Qemu-devel] [PATCH 2/2] usb-ccid: add CCID device (device itself)
  2010-10-05 21:32 [Qemu-devel] [PATCH 0/2] USB CCID device Alon Levy
  2010-10-05 21:32 ` [Qemu-devel] [PATCH 1/2] usb-ccid: add CCID device. add configure option Alon Levy
@ 2010-10-05 21:33 ` Alon Levy
  2010-10-05 23:02 ` [Qemu-devel] [PATCH 0/2] USB CCID device Anthony Liguori
  2 siblings, 0 replies; 14+ messages in thread
From: Alon Levy @ 2010-10-05 21:33 UTC (permalink / raw)
  To: qemu-devel, alevy

A CCID device is a smart card reader. It is a USB device, defined at [2].
The usb-ccid device expects a chardev option and uses the VSCARD protocol
defined in vscard_common.h. It starts as an unattached device
unless auto_attach=1.

The utility vscclient, bundled with libcac_card, provides the client side,
together they provide simultaneous access to the card reader an
the card attached to it, both in the guest and client machines, as opposed t
a simple usb host passthrough. You can also use it to provide a virtual card

 [1] libcac_card http://cgit.freedesktop.org/~alon/cac_card (temporary home)
     written by Robert Relyea <rrelyea@redhat.com>

 [2] http://www.usb.org/developers/devclass_docs/DWG_Smart-Card_CCID_Rev110.

Signed-off-by: Alon Levy <alevy@redhat.com>
---
 hw/usb-ccid.c | 1241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1241 insertions(+), 0 deletions(-)

diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c
index 98805bb..cc4d6d1 100644
--- a/hw/usb-ccid.c
+++ b/hw/usb-ccid.c
@@ -11,3 +11,1244 @@
  * This code is licenced under the LGPL.
  */
 
+/* References:
+ *
+ * CCID Specification Revision 1.1 April 22nd 2005
+ *  "Universal Serial Bus, Device Class: Smart Card"
+ *  Specification for Integrated Circuit(s) Cards Interface Devices
+ *
+ * KNOWN BUGS
+ * 1. remove/insert can sometimes result in removed state instead of inserted.
+ * 2. if the chardev connection drops we don't get any notification. this can leave us in a state where there is a reader attached. this will cause vscclient to fail when it starts again since it will get VSC_Error in response to VSC_ReaderAdd. The correct fix is flow control. There is a trivial fix using chrdev ioctls, but it has not been accepted upstream.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu-error.h"
+#include "usb.h"
+#include "qemu-char.h"
+
+#include "hw/vscard_common.h"
+
+static int debug = 0;
+
+#define DPRINTF(fmt, ...) \
+do { if (debug) { printf("usb-ccid: " fmt , ## __VA_ARGS__); } } while (0)
+
+#define RECV_BUF 384
+#define BULK_IN_FREE_START_NUM 5
+
+#define DeviceOutVendor    ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+#define DeviceInVendor     ((USB_DIR_IN |USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+#define CCID_CONTROL_ABORT                  0x1
+#define CCID_CONTROL_GET_CLOCK_FREQUENCIES  0x2
+#define CCID_CONTROL_GET_DATA_RATES         0x3
+
+#define CCID_PRODUCT_DESCRIPTION        "QEMU USB CCID"
+#define CCID_VENDOR_DESCRIPTION         "QEMU " QEMU_VERSION
+#define CCID_INTERFACE_NAME             "CCID Interface"
+#define CCID_SERIAL_NUMBER_STRING       "1"
+/* Using Gemplus Vendor and Product id
+  Effect on various drivers:
+  * usbccid.sys (winxp, others untested) is a class driver so it doesn't care.
+  * linux has a number of class drivers, but openct filters based on
+    vendor/product (/etc/openct.conf under fedora), hence Gemplus.
+ */
+#define CCID_VENDOR_ID                  0x08e6
+#define CCID_PRODUCT_ID                 0x4433
+#define CCID_DEVICE_VERSION             0x0000
+
+/* BULK_OUT messages from PC to Reader
+   Defined in CCID Rev 1.1 6.1 (page 26)
+ */
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn              0x62
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff             0x63
+#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus           0x65
+#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock                0x6f
+#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters           0x6c
+#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters         0x6d
+#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters           0x61
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape                  0x6b
+#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock                0x6e
+#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU                  0x6a
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure                  0x69
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical              0x71
+#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort                   0x72
+#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73
+
+/* BULK_IN messages from Reader to PC
+   Defined in CCID Rev 1.1 6.2 (page 48)
+ */
+#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock               0x80
+#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus              0x81
+#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters              0x82
+#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape                  0x83
+#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84
+
+/* INTERRUPT_IN messages from Reader to PC
+   Defined in CCID Rev 1.1 6.3 (page 56)
+ */
+#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange        0x50
+#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError           0x51
+
+/* Endpoints for CCID - addresses are up to us to decide.
+   To support slot insertion and removal we must have an interrupt in ep
+   in addition we need a bulk in and bulk out ep
+   5.2, page 20
+ */
+#define CCID_INT_IN_EP       1
+#define CCID_BULK_IN_EP      2
+#define CCID_BULK_OUT_EP     3
+
+/* bmSlotICCState masks */
+#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)
+ */
+enum {
+    ERROR_CMD_NOT_SUPPORTED = 0,
+    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;
+    uint8_t     bSlot;
+    uint8_t     bSeq;
+} CCID_Header;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_Header hdr;
+    uint8_t     bStatus;        /* Only used in BULK_IN */
+    uint8_t     bError;         /* Only used in BULK_IN */
+} CCID_BULK_IN;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_BULK_IN b;
+    uint8_t     bClockStatus;
+} CCID_SlotStatus;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_BULK_IN b;
+    uint8_t     bProtocolNum;
+    uint8_t     abProtocolDataStructure[0];
+} CCID_Parameter;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_BULK_IN b;
+    uint8_t      bChainParameter;
+    uint8_t      abData[0];
+} CCID_DataBlock;
+
+/* 6.1.4 PC_to_RDR_XfrBlock */
+typedef struct __attribute__ ((__packed__)) {
+    CCID_Header  hdr;
+    uint8_t      bBWI; /* Block Waiting Timeout */
+    uint16_t     wLevelParameter;
+    uint8_t      abData[0];
+} CCID_XferBlock;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_Header hdr;
+    uint8_t     bPowerSelect;
+    uint16_t    abRFU;
+} CCID_IccPowerOn;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_Header hdr;
+    uint16_t    abRFU;
+} CCID_IccPowerOff;
+
+typedef struct __attribute__ ((__packed__)) {
+    CCID_Header hdr;
+    uint8_t     bProtocolNum;
+    uint8_t    abProtocolDataStructure[0];
+} CCID_SetParameter;
+
+typedef struct {
+    uint8_t     bMessageType; /* CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange */
+    uint8_t     bmSlotICCState;
+} CCID_Notify_Slot_Change;
+
+#define MAX_ATR_SIZE        40
+#define MAX_PROTOCOL_SIZE   7
+
+/* used for DataBlock response to XferBlock */
+typedef struct answer_t {
+    uint8_t slot;
+    uint8_t seq;
+    QSIMPLEQ_ENTRY(answer_t) entry;
+} answer_t;
+
+/* pending bulk_in messages */
+typedef struct bulk_in_t {
+    uint8_t* data;
+    int      len;
+    int      pos;
+    QSIMPLEQ_ENTRY(bulk_in_t) entry;
+} bulk_in_t;
+
+/**
+ * powered - defaults to true, changed by PowerOn/PowerOff messages
+ */
+typedef struct {
+    USBDevice dev;
+    CharDriverState *cs;
+    uint8_t  debug;
+    uint8_t  auto_attach;
+    uint8_t  atr[MAX_ATR_SIZE];
+    uint8_t  atr_length;
+    QSIMPLEQ_HEAD(bulk_in, bulk_in_t) bulk_in_pending;
+    QSIMPLEQ_HEAD(bulk_in_free, bulk_in_t) bulk_in_free;
+    bulk_in_t   *current_bulk_in;
+    uint8_t* bulk_out_data;
+    uint32_t bulk_out_len;
+    uint32_t bulk_out_pos;
+    uint8_t* vscard_in_data;
+    uint32_t vscard_in_pos;
+    uint32_t vscard_in_hdr;
+    uint32_t vscard_in_len;
+    uint8_t  bmSlotICCState;
+    bool     powered;
+    bool     notify_slot_change;
+    uint64_t last_answer_error;
+    QSIMPLEQ_HEAD(pending_answers, answer_t) pending_answers;
+    uint8_t  bError;
+    uint8_t  bmCommandStatus;
+    uint8_t  bProtocolNum;
+    uint8_t  abProtocolDataStructure[MAX_PROTOCOL_SIZE];
+    uint32_t ulProtocolDataStructureSize;
+} USBCCIDState;
+
+/* Slot specific variables. We emulate a single slot card reader.
+ */
+
+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 */
+
+
+/* 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.
+ *
+ * This device implemented based on the spec and with an Athena Smart Card
+ * Reader as reference:
+ *   0dc3:1004 Athena Smartcard Solutions, Inc.
+ */
+
+static const uint8_t qemu_ccid_dev_descriptor[] = {
+        0x12,       /*  u8 bLength; */
+        USB_DT_DEVICE, /*  u8 bDescriptorType; Device */
+        0x10, 0x01, /*  u16 bcdUSB; v1.1 */
+
+        0x00,       /*  u8  bDeviceClass; */
+        0x00,       /*  u8  bDeviceSubClass; */
+        0x00,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+        0x40,       /*  u8  bMaxPacketSize0; 8 Bytes (valid: 8,16,32,64) */
+
+        /* Vendor and product id are arbitrary.  */
+                    /*  u16 idVendor  */
+        CCID_VENDOR_ID & 0xff, CCID_VENDOR_ID >> 8,
+                    /*  u16 idProduct */
+        CCID_PRODUCT_ID & 0xff, CCID_PRODUCT_ID >> 8,
+                    /*  u16 bcdDevice */
+        CCID_DEVICE_VERSION & 0xff, CCID_DEVICE_VERSION >> 8,
+        0x01,       /*  u8  iManufacturer; */
+        0x02,       /*  u8  iProduct; */
+        0x03,       /*  u8  iSerialNumber; */
+        0x01,       /*  u8  bNumConfigurations; */
+};
+
+static const uint8_t qemu_ccid_config_descriptor[] = {
+
+        /* one configuration */
+        0x09,       /*  u8  bLength; */
+        USB_DT_CONFIG, /*  u8  bDescriptorType; Configuration */
+        0x5d, 0x00, /*  u16 wTotalLength; 9+9+54+7+7+7 */
+        0x01,       /*  u8  bNumInterfaces; (1) */
+        0x01,       /*  u8  bConfigurationValue; */
+        0x00,       /*  u8  iConfiguration; */
+        0x80,       /*  u8  bmAttributes;
+                                 Bit 7: must be set,
+                                     6: Self-powered,
+                                     5: Remote wakeup,
+                                     4..0: resvd */
+        100/2,      /*  u8  MaxPower; 50 == 100mA */
+
+        /* one interface */
+        0x09,       /*  u8  if_bLength; */
+        USB_DT_INTERFACE, /*  u8  if_bDescriptorType; Interface */
+        0x00,       /*  u8  if_bInterfaceNumber; */
+        0x00,       /*  u8  if_bAlternateSetting; */
+        0x03,       /*  u8  if_bNumEndpoints; */
+        0x0b,       /*  u8  if_bInterfaceClass; Smart Card Device Class */
+        0x00,       /*  u8  if_bInterfaceSubClass; Subclass code */
+        0x00,       /*  u8  if_bInterfaceProtocol; Protocol code */
+        0x04,       /*  u8  if_iInterface; Index of string descriptor */
+
+        /* Smart Card Device Class Descriptor */
+        0x36,       /*  u8  bLength; */
+        0x21,       /*  u8  bDescriptorType; Functional */
+        0x10, 0x01, /*  u16 bcdCCID; CCID Specification Release Number. */
+        0x00,       /*  u8  bMaxSlotIndex; The index of the highest available
+                        slot on this device. All slots are consecutive starting
+                        at 00h. */
+        0x07,       /*  u8  bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */
+
+        0x03, 0x00, /*  u32 dwProtocols; RRRR PPPP. RRRR = 0000h.*/
+        0x00, 0x00, /*  PPPP: 0001h = Protocol T=0, 0002h = Protocol T=1 */
+                    /*  u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */
+        0xa0, 0x0f, 0x00, 0x00,
+                    /*  u32 dwMaximumClock; */
+        0x00, 0x00, 0x01, 0x00,
+        0x00,       /*  u8 bNumClockSupported; 0 means just the default and max. */
+                    /*  u32 dwDataRate ;bps. 9600 == 00002580h */
+        0x80, 0x25, 0x00, 0x00,
+                    /*  u32 dwMaxDataRate ; 11520 bps == 0001C200h */
+        0x00, 0xC2, 0x01, 0x00,
+        0x00,       /*  u8  bNumDataRatesSupported; 00 means all rates between
+                     *      default and max */
+                    /*  u32 dwMaxIFSD; maximum IFSD supported by CCID for protocol
+                     *      T=1 (Maximum seen from various cards) */
+        0xfe, 0x00, 0x00, 0x00,
+                    /*  u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */
+        0x00, 0x00, 0x00, 0x00,
+                    /*  u32 dwMechanical;  0 - no special characteristics. */
+        0x00, 0x00, 0x00, 0x00,
+                    /*  u32 dwFeatures;
+                     *  0 - No special characteristics
+                     *  + 2 Automatic parameter configuration based on ATR data
+                     *  + 4 Automatic activation of ICC on inserting
+                     *  + 8 Automatic ICC voltage selection
+                     *  + 10 Automatic ICC clock frequency change
+                     *  + 20 Automatic baud rate change
+                     *  + 40 Automatic parameters negotiation made by the CCID
+                     *  + 80 automatic PPS made by the CCID
+                     *  100 CCID can set ICC in clock stop mode
+                     *  200 NAD value other then 00 accepted (T=1 protocol)
+                     *  + 400 Automatic IFSD exchange as first exchange (T=1)
+                     *  One of the following only:
+                     *  + 10000 TPDU level exchanges with CCID
+                     *  20000 Short APDU level exchange with CCID
+                     *  40000 Short and Extended APDU level exchange with CCID
+                     *
+                     *  100000 USB Wake up signaling supported on card insertion
+                     *  and removal. Must set bit 5 in bmAttributes in Configuration
+                     *  descriptor if 100000 is set.*/
+        0xfe, 0x04, 0x01, 0x00,
+                    /*  u32 dwMaxCCIDMessageLength; For extended APDU in [261 + 10
+                     *  , 65544 + 10]. Otherwise the minimum is wMaxPacketSize of
+                     *  the Bulk-OUT endpoint */
+        0x12, 0x00, 0x01, 0x00,
+        0xFF,       /*  u8  bClassGetResponse; Significant only for CCID that
+                     *  offers an APDU level for exchanges. Indicates the default
+                     *  class value used by the CCID when it sends a Get Response
+                     *  command to perform the transportation of an APDU by T=0
+                     *  protocol
+                     *  FFh indicates that the CCID echos the class of the APDU.
+                     */
+        0xFF,       /*  u8  bClassEnvelope; EAPDU only. Envelope command for T=0 */
+        0x00, 0x00, /*  u16 wLcdLayout; XXYY Number of lines (XX) and chars per
+                     *  line for LCD display used for PIN entry. 0000 - no LCD */
+        0x01,       /*  u8  bPINSupport; 01h PIN Verification,
+                     *                   02h PIN Modification */
+        0x01,       /*  u8  bMaxCCIDBusySlots; */
+
+        /* Interrupt-IN endpoint */
+        0x07,       /*  u8  ep_bLength; */
+                    /*  u8  ep_bDescriptorType; Endpoint */
+        USB_DT_ENDPOINT,
+                    /*  u8  ep_bEndpointAddress; IN Endpoint 1 */
+        0x80 | CCID_INT_IN_EP,
+        0x03,       /*  u8  ep_bmAttributes; Interrupt */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0xff,       /*  u8  ep_bInterval; */
+
+        /* Bulk-In endpoint */
+        0x07,       /*  u8  ep_bLength; */
+                    /*  u8  ep_bDescriptorType; Endpoint */
+        USB_DT_ENDPOINT,
+                    /*  u8  ep_bEndpointAddress; IN Endpoint 2 */
+        0x80 | CCID_BULK_IN_EP,
+        0x02,       /*  u8  ep_bmAttributes; Bulk */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0x00,       /*  u8  ep_bInterval; */
+
+        /* Bulk-Out endpoint */
+        0x07,       /*  u8  ep_bLength; */
+                    /*  u8  ep_bDescriptorType; Endpoint */
+        USB_DT_ENDPOINT,
+                    /*  u8  ep_bEndpointAddress; OUT Endpoint 3 */
+        CCID_BULK_OUT_EP,
+        0x02,       /*  u8  ep_bmAttributes; Bulk */
+        0x40, 0x00, /*  u16 ep_wMaxPacketSize; */
+        0x00,       /*  u8  ep_bInterval; */
+
+};
+
+static bool ccid_has_pending_answers(USBCCIDState *s)
+{
+    return !QSIMPLEQ_EMPTY(&s->pending_answers);
+}
+
+static void ccid_clear_pending_answers(USBCCIDState *s)
+{
+    answer_t *answer;
+
+    QSIMPLEQ_FOREACH(answer, &s->pending_answers, entry) {
+        qemu_free(answer);
+    }
+    QSIMPLEQ_INIT(&s->pending_answers);
+}
+
+static void ccid_print_pending_answers(USBCCIDState *s)
+{
+#ifdef DEBUG_CCID
+    answer_t *answer;
+
+    printf("usb-ccid: pending answers:");
+    if (!ccid_has_pending_answers(s)) {
+        printf(" empty\n");
+        return;
+    }
+    QSIMPLEQ_FOREACH(answer, &s->pending_answers, entry) {
+        if (QSIMPLEQ_NEXT(answer, entry) == NULL) {
+            printf("%d:%d\n", answer->slot, answer->seq);
+        } else {
+            printf("%d:%d,", answer->slot, answer->seq);
+        }
+    }
+#endif
+}
+
+static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr)
+{
+    answer_t* answer;
+
+    answer = qemu_mallocz(sizeof(answer_t));
+    answer->slot = hdr->bSlot;
+    answer->seq = hdr->bSeq;
+    QSIMPLEQ_INSERT_TAIL(&s->pending_answers, answer, entry);
+    ccid_print_pending_answers(s);
+}
+
+static void ccid_remove_pending_answer(USBCCIDState *s,
+    uint8_t *slot, uint8_t *seq)
+{
+    answer_t *answer;
+
+    answer = QSIMPLEQ_FIRST(&s->pending_answers);
+    *slot = answer->slot;
+    *seq = answer->seq;
+    QSIMPLEQ_REMOVE_HEAD(&s->pending_answers, entry);
+    qemu_free(answer);
+    ccid_print_pending_answers(s);
+}
+
+static void ccid_bulk_in_clear(USBCCIDState *s)
+{
+    bulk_in_t* bulk_in;
+
+    while (!QSIMPLEQ_EMPTY(&s->bulk_in_pending)) {
+        bulk_in = QSIMPLEQ_FIRST(&s->bulk_in_pending);
+        QSIMPLEQ_REMOVE_HEAD(&s->bulk_in_pending, entry);
+        QSIMPLEQ_INSERT_TAIL(&s->bulk_in_free, bulk_in, entry);
+    }
+}
+
+static void ccid_reset(USBCCIDState *s)
+{
+    ccid_bulk_in_clear(s);
+    ccid_clear_pending_answers(s);
+}
+
+
+static void ccid_detach(USBCCIDState *s)
+{
+    ccid_reset(s);
+    if (s->auto_attach == 0 && s->dev.attached) {
+        usb_device_detach(&s->dev);
+    }
+}
+
+static void ccid_handle_reset(USBDevice *dev)
+{
+    USBCCIDState *s = (USBCCIDState *)dev;
+
+    DPRINTF("Reset\n");
+
+    ccid_reset(s);
+}
+
+static int ccid_handle_control(USBDevice *dev, int request, int value,
+                                  int index, int length, uint8_t *data)
+{
+    int ret = 0;
+
+    DPRINTF("got control %x, value %x\n",request, value);
+    switch (request) {
+    case DeviceRequest | USB_REQ_GET_STATUS:
+        data[0] = (0 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+        data[1] = 0x00;
+        ret = 2;
+        break;
+    case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 0;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_FEATURE:
+        if (value == USB_DEVICE_REMOTE_WAKEUP) {
+            dev->remote_wakeup = 1;
+        } else {
+            goto fail;
+        }
+        ret = 0;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+        dev->addr = value;
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+        switch(value >> 8) {
+        case USB_DT_DEVICE:
+            memcpy(data, qemu_ccid_dev_descriptor,
+                   sizeof(qemu_ccid_dev_descriptor));
+            ret = sizeof(qemu_ccid_dev_descriptor);
+            break;
+        case USB_DT_CONFIG:
+            memcpy(data, qemu_ccid_config_descriptor,
+                   sizeof(qemu_ccid_config_descriptor));
+            ret = sizeof(qemu_ccid_config_descriptor);
+            break;
+        case USB_DT_STRING:
+            switch(value & 0xff) {
+            case 0:
+                /* language ids */
+                data[0] = 4;
+                data[1] = 3;
+                data[2] = 0x09;
+                data[3] = 0x04;
+                ret = 4;
+                break;
+            case 1:
+                /* vendor description */
+                ret = set_usb_string(data, CCID_VENDOR_DESCRIPTION);
+                break;
+            case 2:
+                /* product description */
+                ret = set_usb_string(data, CCID_PRODUCT_DESCRIPTION);
+                break;
+            case 3:
+                /* serial number */
+                ret = set_usb_string(data, CCID_SERIAL_NUMBER_STRING);
+                break;
+            case 4:
+                /* interface name */
+                ret = set_usb_string(data, CCID_INTERFACE_NAME);
+                break;
+            default:
+                goto fail;
+            }
+            break;
+        default:
+            goto fail;
+        }
+        break;
+    case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+        data[0] = 1;
+        ret = 1;
+        break;
+    case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+        /* Only one configuration - we just ignore the request */
+        ret = 0;
+        break;
+    case DeviceRequest | USB_REQ_GET_INTERFACE:
+        data[0] = 0;
+        ret = 1;
+        break;
+    case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+        ret = 0;
+        break;
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        ret = 0;
+        break;
+
+        /* Class specific requests.  */
+    case DeviceOutVendor | CCID_CONTROL_ABORT:
+        DPRINTF("ccid_control abort UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    case DeviceOutVendor | CCID_CONTROL_GET_CLOCK_FREQUENCIES:
+        DPRINTF("ccid_control get clock frequencies UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    case DeviceOutVendor | CCID_CONTROL_GET_DATA_RATES:
+        DPRINTF("ccid_control get data rates UNIMPLEMENTED\n");
+        ret = USB_RET_STALL;
+        break;
+    default:
+    fail:
+        DPRINTF("got unsupported/bogus control %x, value %x\n", request, value);
+        ret = USB_RET_STALL;
+        break;
+    }
+    return ret;
+}
+
+static void ccid_bulk_in_release(USBCCIDState *s)
+{
+    s->current_bulk_in->pos = 0;
+    s->current_bulk_in->len = RECV_BUF;
+    QSIMPLEQ_INSERT_TAIL(&s->bulk_in_free, s->current_bulk_in, entry);
+    s->current_bulk_in = NULL;
+}
+
+static void ccid_bulk_in_get(USBCCIDState *s)
+{
+    if (s->current_bulk_in != NULL || QSIMPLEQ_EMPTY(&s->bulk_in_pending)) {
+        return;
+    }
+    s->current_bulk_in = QSIMPLEQ_FIRST(&s->bulk_in_pending);
+    QSIMPLEQ_REMOVE_HEAD(&s->bulk_in_pending, entry);
+}
+
+static uint8_t* ccid_reserve_recv_buf(USBCCIDState* s, uint16_t len)
+{
+    bulk_in_t* bulk_in;
+
+    DPRINTF("%s: QUEUE: reserve %d bytes\n", __func__, len);
+
+    /* look for an existing element */
+    if (len > RECV_BUF) {
+        printf("usb-ccid.c: %s: len larger then max (%d>%d). bailing out.\n",
+            __func__, len, RECV_BUF);
+        exit(-1);
+    }
+    if (QSIMPLEQ_EMPTY(&s->bulk_in_free)) {
+        printf("usb-ccid.c: %s: No buffers in bulk_in_free. discarding message.\n",
+                __func__);
+        return NULL;
+    }
+    bulk_in = QSIMPLEQ_FIRST(&s->bulk_in_free);
+    QSIMPLEQ_REMOVE_HEAD(&s->bulk_in_free, entry);
+    QSIMPLEQ_INSERT_TAIL(&s->bulk_in_pending, bulk_in, entry);
+    bulk_in->len = len;
+    return bulk_in->data;
+}
+
+static bool ccid_card_inserted(USBCCIDState *s)
+{
+    return s->bmSlotICCState & SLOT_0_STATE_MASK;
+}
+
+static uint8_t ccid_card_status(USBCCIDState *s)
+{
+    return ccid_card_inserted(s)
+            ? (s->powered ?
+                ICC_STATUS_PRESENT_ACTIVE
+              : ICC_STATUS_PRESENT_INACTIVE
+              )
+            : ICC_STATUS_NOT_PRESENT;
+}
+
+static uint8_t ccid_calc_status(USBCCIDState *s)
+{
+    /* page 55, 6.2.6, calculation of bStatus from bmICCStatus and
+       bmCommandStatus
+     */
+    uint8_t ret = ccid_card_status(s) | (s->bmCommandStatus << 6);
+#ifdef DEBUG_CCID
+    DPRINTF("status = %d\n", ret);
+#endif
+    return ret;
+}
+
+static void ccid_reset_error_status(USBCCIDState* s)
+{
+    s->bError = ERROR_CMD_NOT_SUPPORTED;
+    s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+}
+
+static void ccid_write_slot_status(USBCCIDState* s, CCID_Header* recv)
+{
+    CCID_SlotStatus *h = (CCID_SlotStatus*)ccid_reserve_recv_buf(s, sizeof(CCID_SlotStatus));
+    if (h == NULL) {
+        return;
+    }
+    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus;
+    h->b.hdr.dwLength = 0;
+    h->b.hdr.bSlot = recv->bSlot;
+    h->b.hdr.bSeq = recv->bSeq;
+    h->b.bStatus = ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bClockStatus = CLOCK_STATUS_RUNNING;
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_parameters(USBCCIDState* s, CCID_Header* recv)
+{
+    CCID_Parameter *h;
+    uint32_t len = s->ulProtocolDataStructureSize;
+
+    h = (CCID_Parameter *)ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + len);
+    if (h == NULL) {
+        return;
+    }
+    h->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_Parameters;
+    h->b.hdr.dwLength = 0;
+    h->b.hdr.bSlot = recv->bSlot;
+    h->b.hdr.bSeq = recv->bSeq;
+    h->b.bStatus = ccid_calc_status(s);
+    h->b.bError = s->bError;
+    h->bProtocolNum = s->bProtocolNum;
+    memcpy(h->abProtocolDataStructure, s->abProtocolDataStructure, len);
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_data_block(
+    USBCCIDState* s, uint8_t slot, uint8_t seq,
+    const uint8_t* data, uint32_t len)
+{
+    CCID_DataBlock *p;
+
+    p = (CCID_DataBlock*)ccid_reserve_recv_buf(s, sizeof(*p) + len);
+    if (p == NULL) {
+        return;
+    }
+    p->b.hdr.bMessageType = CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock;
+    p->b.hdr.dwLength = len;
+    p->b.hdr.bSlot = slot;
+    p->b.hdr.bSeq = seq;
+    p->b.bStatus = ccid_calc_status(s);
+    p->b.bError = s->bError;
+#ifdef DEBUG_CCID
+    if (p->b.bError) {
+        DPRINTF("error %d", p->b.bError);
+    }
+#endif
+    memcpy(p->abData, data, len);
+    ccid_reset_error_status(s);
+}
+
+static void ccid_write_data_block_answer(USBCCIDState* s,
+    const uint8_t* data, uint32_t len)
+{
+    uint8_t seq;
+    uint8_t slot;
+
+    if (!ccid_has_pending_answers(s)) {
+        abort();
+    }
+    ccid_remove_pending_answer(s, &slot, &seq);
+    ccid_write_data_block(s, slot, seq, data, len);
+}
+
+static void ccid_write_data_block_atr(USBCCIDState* s, CCID_Header* recv)
+{
+    ccid_write_data_block(s, recv->bSlot, recv->bSeq, s->atr, s->atr_length);
+}
+
+static void ccid_set_parameters(USBCCIDState *s, CCID_Header *recv)
+{
+    CCID_SetParameter *ph = (CCID_SetParameter *) recv;
+    uint32_t len = 0;
+    if (ph->bProtocolNum == 0) {
+        len = 5;
+    }
+    if (ph->bProtocolNum == 1) {
+        len = 7;
+    }
+    if (len == 0) {
+        s->bmCommandStatus = COMMAND_STATUS_FAILED;
+        s->bError = 7; /* Protocol invalid or not supported */
+        return;
+    }
+    s->bProtocolNum = ph->bProtocolNum;
+    memcpy(s->abProtocolDataStructure, ph->abProtocolDataStructure, len);
+    s->ulProtocolDataStructureSize = len;
+    DPRINTF("%s: using len %d\n", __func__, len);
+}
+
+/* must be 5 bytes for T=0, 7 bytes for T=1
+ * See page 52 */
+static const uint8_t abDefaultProtocolDataStructure[7] =
+    { 0x77, 0x00, 0x00, 0x00, 0x00, 0xfe /*IFSC*/, 0x00 /*NAD*/ };
+
+static void ccid_reset_parameters(USBCCIDState *s)
+{
+   uint32_t len = sizeof(abDefaultProtocolDataStructure);
+
+   s->bProtocolNum = 1; /* T=1 */
+   s->ulProtocolDataStructureSize = len;
+   memcpy(s->abProtocolDataStructure, abDefaultProtocolDataStructure, len);
+}
+
+/* NOTE: only a single slot is supported (SLOT_0)
+ */
+static void ccid_on_slot_change(USBCCIDState* s, bool full)
+{
+    /* RDR_to_PC_NotifySlotChange, 6.3.1 page 56
+     */
+    uint8_t current = s->bmSlotICCState;
+    if (full) {
+        s->bmSlotICCState |= SLOT_0_STATE_MASK;
+    } else {
+        s->bmSlotICCState &= ~SLOT_0_STATE_MASK;
+    }
+    if (current != s->bmSlotICCState) {
+        s->bmSlotICCState |= SLOT_0_CHANGED_MASK;
+    }
+    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_vscard_send_msg(
+    USBCCIDState *s, VSCMsgType type, reader_id_t reader_id,
+        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_vscard_send_apdu(
+    USBCCIDState *s, uint8_t* apdu, uint32_t length)
+{
+    ccid_vscard_send_msg(s, VSC_APDU, VSCARD_MINIMAL_READER_ID, apdu, length);
+}
+
+static void ccid_vscard_send_error(
+    USBCCIDState *s, reader_id_t reader_id, VSCErrorCode code)
+{
+    VSCMsgError msg = {.code=code};
+
+    ccid_vscard_send_msg(s, VSC_Error, reader_id, (uint8_t*)&msg, sizeof(msg));
+}
+
+static void ccid_vscard_send_init(USBCCIDState *s)
+{
+    VSCMsgInit msg = {.version=VSCARD_VERSION};
+
+    ccid_vscard_send_msg(s, VSC_Init, VSCARD_UNDEFINED_READER_ID,
+                         (uint8_t*)&msg, sizeof(msg));
+}
+
+static void ccid_on_apdu_from_guest(USBCCIDState *s, CCID_XferBlock *recv)
+{
+    if (!s->cs) {
+        printf("usb-ccid: discarding apdu length %d\n", recv->hdr.dwLength);
+        return;
+    }
+    if (ccid_card_status(s) != ICC_STATUS_PRESENT_ACTIVE) {
+        DPRINTF("usb-ccid: not sending apdu to client, no card connected\n");
+        ccid_write_data_block_error(s, recv->hdr.bSlot, recv->hdr.bSeq);
+        return;
+    }
+    DPRINTF("%s: seq %d, len %d\n", __FUNCTION__,
+                recv->hdr.bSeq, recv->hdr.dwLength);
+    ccid_add_pending_answer(s, (CCID_Header*)recv);
+    ccid_vscard_send_apdu(s, recv->abData, recv->hdr.dwLength);
+}
+
+static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p)
+{
+    CCID_Header* ccid_header;
+
+    if (p->len + s->bulk_out_pos > s->bulk_out_len) {
+        s->bulk_out_len = MAX(p->len + s->bulk_out_pos, s->bulk_out_len*2);
+        s->bulk_out_data = realloc(s->bulk_out_data, s->bulk_out_len);
+        assert(s->bulk_out_data != NULL);
+    }
+    ccid_header = (CCID_Header*)s->bulk_out_data;
+    memcpy(s->bulk_out_data + s->bulk_out_pos, p->data, p->len);
+    s->bulk_out_pos += p->len;
+    if (p->len == 64) {
+        DPRINTF("usb-ccid: bulk_in: expecting more packets (%d/%d)\n",
+            p->len, ccid_header->dwLength);
+        return;
+    }
+    if (s->bulk_out_pos < 10) {
+        DPRINTF("handle_data: bad USB_TOKEN_OUT length, should be at least 10 bytes\n");
+    } else {
+        DPRINTF("handle_data %x\n", ccid_header->bMessageType);
+        switch (ccid_header->bMessageType) {
+            case CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus:
+                ccid_write_slot_status(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn:
+                /* We need to send an ATR back */
+                DPRINTF("PowerOn: %d\n",
+                    ((CCID_IccPowerOn*)(ccid_header))->bPowerSelect);
+                s->powered = true;
+                ccid_write_data_block_atr(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff:
+                DPRINTF("PowerOff\n");
+                ccid_reset_error_status(s);
+                s->powered = false;
+                ccid_write_slot_status(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock:
+                ccid_on_apdu_from_guest(s, (CCID_XferBlock*)s->bulk_out_data);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters:
+                s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+                ccid_set_parameters(s, ccid_header);
+                ccid_write_parameters(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters:
+                s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+                ccid_reset_parameters(s);
+                ccid_write_parameters(s, ccid_header);
+                break;
+            case CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters:
+                s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+                ccid_write_parameters(s, ccid_header);
+                break;
+            default:
+                DPRINTF("handle_data: ERROR: unhandled message type %Xh\n",
+                    ccid_header->bMessageType);
+                /* the caller is expecting the device to respond, tell it we
+                 * do't support the operation */
+                s->bmCommandStatus = COMMAND_STATUS_FAILED;
+                s->bError = ERROR_CMD_NOT_SUPPORTED;
+                ccid_write_slot_status(s, ccid_header);
+                break;
+        }
+    }
+    s->bulk_out_pos = 0;
+}
+
+static int ccid_bulk_in_copy_out(USBCCIDState *s, uint8_t *data, int len)
+{
+    int ret = 0;
+
+    assert(len>0);
+    ccid_bulk_in_get(s);
+    if (s->current_bulk_in != NULL) {
+        ret = MIN(s->current_bulk_in->len - s->current_bulk_in->pos, len);
+        memcpy(data, s->current_bulk_in->data + s->current_bulk_in->pos, ret);
+        s->current_bulk_in->pos += ret;
+        if (s->current_bulk_in->pos == s->current_bulk_in->len) {
+            ccid_bulk_in_release(s);
+        }
+    } else {
+        ret = USB_RET_NAK;
+    }
+    DPRINTF("%s: %d/%d req/act to guest (BULK_IN)\n", __func__, len, ret);
+    return ret;
+}
+
+static int ccid_handle_data(USBDevice *dev, USBPacket *p)
+{
+    USBCCIDState *s = (USBCCIDState *)dev;
+    int ret = 0;
+    uint8_t *data = p->data;
+    int len = p->len;
+
+    switch (p->pid) {
+    case USB_TOKEN_OUT:
+        ccid_handle_bulk_out(s, p);
+        break;
+
+    case USB_TOKEN_IN:
+        switch (p->devep & 0xf) {
+            case CCID_BULK_IN_EP:
+                if (!len) {
+                    ret = USB_RET_NAK;
+                } else {
+                    ret = ccid_bulk_in_copy_out(s, data, len);
+                }
+                break;
+            case CCID_INT_IN_EP:
+                if (s->notify_slot_change) {
+                    /* page 56, RDR_to_PC_NotifySlotChange */
+                    data[0] = CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange;
+                    data[1] = s->bmSlotICCState;
+                    ret = 2;
+                    s->notify_slot_change = false;
+                    s->bmSlotICCState &= ~SLOT_0_CHANGED_MASK;
+                    DPRINTF("handle_data: int_in: notify_slot_change %X\n",
+                            s->bmSlotICCState);
+                }
+                break;
+            default:
+                DPRINTF("Bad endpoint\n");
+                break;
+        }
+        break;
+    default:
+        DPRINTF("Bad token\n");
+        ret = USB_RET_STALL;
+        break;
+    }
+
+    return ret;
+}
+
+static void ccid_handle_destroy(USBDevice *dev)
+{
+    USBCCIDState *s = (USBCCIDState *)dev;
+    bulk_in_t* bulk_in;
+
+    if (s->cs) {
+        qemu_chr_close(s->cs);
+    }
+    free(s->bulk_out_data);
+    free(s->vscard_in_data);
+    ccid_bulk_in_clear(s);
+    QSIMPLEQ_FOREACH(bulk_in, &s->bulk_in_free, entry) {
+        qemu_free(bulk_in->data);
+        qemu_free(bulk_in);
+    }
+}
+
+/* APDU chardev */
+
+static int ccid_vscard_can_read(void *opaque)
+{
+    return 65535;
+}
+
+static void ccid_vscard_handle_message(USBCCIDState *s,
+    VSCMsgHeader* scr_msg_header)
+{
+    uint8_t *data = (uint8_t*)&scr_msg_header[1];
+    answer_t *answer;
+
+    switch (scr_msg_header->type) {
+        case VSC_ATR:
+            DPRINTF("VSC_ATR %d\n", scr_msg_header->length);
+            assert(scr_msg_header->length <= MAX_ATR_SIZE);
+            memcpy(s->atr, data, scr_msg_header->length);
+            s->atr_length = scr_msg_header->length;
+            s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+            if (ccid_has_pending_answers(s)) {
+                ccid_write_data_block_answer(s, NULL, 0);
+            }
+            ccid_on_slot_change(s, true);
+            break;
+        case VSC_APDU:
+            if (!ccid_has_pending_answers(s)) {
+                DPRINTF("VSC_APDU: ERROR: got an APDU without pending answers\n");
+                break;
+            }
+            s->bmCommandStatus = COMMAND_STATUS_NO_ERROR;
+            answer = QSIMPLEQ_FIRST(&s->pending_answers);
+            if (answer == NULL) {
+                abort();
+            }
+            DPRINTF("VSC_APDU %d (answer seq %d, slot %d)\n",
+                scr_msg_header->length,
+                answer->seq, answer->slot);
+            ccid_write_data_block_answer(s, data, scr_msg_header->length);
+            break;
+        case VSC_CardRemove:
+            DPRINTF("VSC_CardRemove\n");
+            ccid_on_slot_change(s, false);
+            if (ccid_has_pending_answers(s)) {
+                ccid_write_data_block_answer(s, NULL, 0);
+            }
+            ccid_reset(s);
+            break;
+        case VSC_Init:
+            break;
+        case VSC_Error:
+            s->bmCommandStatus = COMMAND_STATUS_FAILED;
+            s->last_answer_error = *(uint64_t*)data;
+            DPRINTF("VSC_Error: %lX\n", s->last_answer_error);
+            ccid_write_data_block_answer(s, NULL, 0);
+            break;
+        case VSC_ReaderAdd:
+            DPRINTF("VSC_ReaderAdd\n");
+            if (s->auto_attach==0 && !s->dev.attached) {
+                usb_device_attach(&s->dev);
+                ccid_vscard_send_msg(s, VSC_ReaderAddResponse,
+                                     VSCARD_MINIMAL_READER_ID, NULL, 0);
+            } else {
+                ccid_vscard_send_error(s, VSCARD_UNDEFINED_READER_ID,
+                                          VSC_CANNOT_ADD_MORE_READERS);
+            }
+            break;
+        case VSC_ReaderRemove:
+            DPRINTF("VSC_ReaderRemove\n");
+            if (ccid_card_inserted(s)) {
+                ccid_on_slot_change(s, false);
+                ccid_clear_pending_answers(s);
+            }
+            if (s->auto_attach == 0 && s->dev.attached) {
+                usb_device_detach(&s->dev);
+            }
+            break;
+        default:
+            printf("usb-ccid: chardev: unexpected message of type %X\n",
+                   scr_msg_header->type);
+            ccid_vscard_send_error(s, scr_msg_header->reader_id,
+                VSC_GENERAL_ERROR);
+    }
+}
+
+static void ccid_vscard_read(void *opaque, const uint8_t *buf, int size)
+{
+    USBCCIDState *s = opaque;
+    VSCMsgHeader *hdr;
+
+    if (s->vscard_in_pos + size > s->vscard_in_len) {
+        s->vscard_in_len = MAX(s->vscard_in_pos + size, s->vscard_in_len * 2);
+        s->vscard_in_data = realloc(s->vscard_in_data, s->vscard_in_len);
+        assert(s->vscard_in_data);
+    }
+    memcpy(s->vscard_in_data + s->vscard_in_pos, buf, size);
+    s->vscard_in_pos += size;
+    hdr = (VSCMsgHeader*)(s->vscard_in_data + s->vscard_in_hdr);
+
+    while ((s->vscard_in_pos - s->vscard_in_hdr >= sizeof(VSCMsgHeader)) &&
+        (s->vscard_in_pos - s->vscard_in_hdr - sizeof(VSCMsgHeader) >=
+            hdr->length))
+    {
+        ccid_vscard_handle_message(s, hdr);
+        s->vscard_in_hdr += hdr->length + sizeof(VSCMsgHeader);
+        hdr = (VSCMsgHeader*)(s->vscard_in_data + s->vscard_in_hdr);
+    }
+    if (s->vscard_in_hdr == s->vscard_in_pos) {
+        s->vscard_in_pos = s->vscard_in_hdr = 0;
+    }
+}
+
+static void ccid_vscard_event(void *opaque, int event)
+{
+    USBCCIDState *s = opaque;
+    switch (event) {
+        case CHR_EVENT_BREAK:
+            break;
+        case CHR_EVENT_FOCUS:
+            break;
+        case CHR_EVENT_OPENED:
+            DPRINTF("%s: CHR_EVENT_OPENED\n", __func__);
+            ccid_detach(s);
+            break;
+    }
+}
+
+static int ccid_initfn(USBDevice *dev)
+{
+    USBCCIDState *s = DO_UPCAST(USBCCIDState, dev, dev);
+    int i;
+    bulk_in_t* bulk_in;
+
+    debug = s->debug;
+    dev->auto_attach = s->auto_attach;
+    s->dev.speed = USB_SPEED_FULL;
+    s->notify_slot_change = false;
+    s->powered = true;
+    QSIMPLEQ_INIT(&s->pending_answers);
+    s->last_answer_error = 0;
+    QSIMPLEQ_INIT(&s->bulk_in_pending);
+    QSIMPLEQ_INIT(&s->bulk_in_free);
+    for (i = 0 ; i < BULK_IN_FREE_START_NUM ; ++i) {
+        bulk_in = qemu_mallocz(sizeof(bulk_in_t));
+        bulk_in->data = qemu_mallocz(RECV_BUF);
+        bulk_in->len = RECV_BUF;
+        bulk_in->pos = 0;
+        QSIMPLEQ_INSERT_TAIL(&s->bulk_in_free, bulk_in, entry);
+    }
+    s->current_bulk_in = NULL;
+    ccid_reset_error_status(s);
+    assert(sizeof(DEFAULT_ATR) <= MAX_ATR_SIZE);
+    memcpy(s->atr, DEFAULT_ATR, sizeof(DEFAULT_ATR));
+    s->atr_length = sizeof(DEFAULT_ATR);
+    s->bulk_out_data = malloc(sizeof(VSCMsgHeader));
+    s->bulk_out_len = sizeof(VSCMsgHeader);
+    s->bulk_out_pos = 0;
+    s->vscard_in_data = NULL;
+    s->vscard_in_len = 0;
+    s->vscard_in_pos = 0;
+    s->vscard_in_hdr = 0;
+    if (s->cs) {
+        DPRINTF("initing chardev\n");
+        qemu_chr_add_handlers(s->cs,
+            ccid_vscard_can_read,
+            ccid_vscard_read,
+            ccid_vscard_event, s);
+#ifdef USB_CCID_USE_IOCTL
+        qemu_chr_ioctl(s->cs, CHR_IOCTL_VIRT_SERIAL_OPEN, NULL);
+#endif
+        ccid_vscard_send_init(s);
+    }
+    ccid_reset_parameters(s);
+    ccid_reset(s);
+    return 0;
+}
+
+static struct USBDeviceInfo ccid_info = {
+    .product_desc   = "QEMU USB CCID",
+    .qdev.name      = "usb-ccid",
+    .qdev.size      = sizeof(USBCCIDState),
+    .init           = ccid_initfn,
+    .handle_packet  = usb_generic_handle_packet,
+    .handle_reset   = ccid_handle_reset,
+    .handle_control = ccid_handle_control,
+    .handle_data    = ccid_handle_data,
+    .handle_destroy = ccid_handle_destroy,
+    .usbdevice_name = "ccid",
+    .qdev.props     = (Property[]) {
+        DEFINE_PROP_CHR("chardev",      USBCCIDState, cs),
+        DEFINE_PROP_UINT8("auto_attach", USBCCIDState, auto_attach, 0),
+        DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0),
+        DEFINE_PROP_END_OF_LIST(),
+    },
+};
+
+static void ccid_register_devices(void)
+{
+    usb_qdev_register(&ccid_info);
+}
+device_init(ccid_register_devices)
-- 
1.7.3.1

^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-05 21:32 [Qemu-devel] [PATCH 0/2] USB CCID device Alon Levy
  2010-10-05 21:32 ` [Qemu-devel] [PATCH 1/2] usb-ccid: add CCID device. add configure option Alon Levy
  2010-10-05 21:33 ` [Qemu-devel] [PATCH 2/2] usb-ccid: add CCID device (device itself) Alon Levy
@ 2010-10-05 23:02 ` Anthony Liguori
  2 siblings, 0 replies; 14+ messages in thread
From: Anthony Liguori @ 2010-10-05 23:02 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 10/05/2010 04:32 PM, Alon Levy wrote:
> This patch adds a new device, it is described in full in the second patch
> intro and also in the documentation in docs. In brief it provides a standard
> smart card reader device.
>
> The first patch is the configure change and docs.
> The second patch contains the actual device, I couldn't figure out a good
> way to split it to ease review, so if the first reviewer can suggest
> a good way to split it I would gladly do that.
>
> Alon Levy (2):
>    usb-ccid: add CCID device. add configure option.
>    usb-ccid: add CCID device (device itself)
>    

Does this work with live migration?  I can't see how it would.

Regards,

Anthony Liguori

>   Makefile.objs      |    1 +
>   configure          |   12 +
>   docs/usb-ccid.txt  |  115 +++++
>   hw/usb-ccid.c      | 1254 ++++++++++++++++++++++++++++++++++++++++++++++++++++
>   hw/vscard_common.h |  123 +++++
>   5 files changed, 1505 insertions(+), 0 deletions(-)
>   create mode 100644 docs/usb-ccid.txt
>   create mode 100644 hw/usb-ccid.c
>   create mode 100644 hw/vscard_common.h
>
>    

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
       [not found] <1820922650.529041286324880996.JavaMail.root@zmail06.collab.prod.int.phx2.redhat.com>
@ 2010-10-06  0:28 ` Alon Levy
  2010-10-06  8:55   ` Gerd Hoffmann
  2010-10-06 19:02   ` Anthony Liguori
  0 siblings, 2 replies; 14+ messages in thread
From: Alon Levy @ 2010-10-06  0:28 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel


----- "Anthony Liguori" <anthony@codemonkey.ws> wrote:

> On 10/05/2010 04:32 PM, Alon Levy wrote:
> > This patch adds a new device, it is described in full in the second
> patch
> > intro and also in the documentation in docs. In brief it provides a
> standard
> > smart card reader device.
> >
> > The first patch is the configure change and docs.
> > The second patch contains the actual device, I couldn't figure out a
> good
> > way to split it to ease review, so if the first reviewer can
> suggest
> > a good way to split it I would gladly do that.
> >
> > Alon Levy (2):
> >    usb-ccid: add CCID device. add configure option.
> >    usb-ccid: add CCID device (device itself)
> >    
> 
> Does this work with live migration?  I can't see how it would.
> 

No, it doesn't right now. It would require cooperation with the client,
to tell it to reconnect to the target qemu (kind of like spice).

> Regards,
> 
> Anthony Liguori
> 
> >   Makefile.objs      |    1 +
> >   configure          |   12 +
> >   docs/usb-ccid.txt  |  115 +++++
> >   hw/usb-ccid.c      | 1254
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
> >   hw/vscard_common.h |  123 +++++
> >   5 files changed, 1505 insertions(+), 0 deletions(-)
> >   create mode 100644 docs/usb-ccid.txt
> >   create mode 100644 hw/usb-ccid.c
> >   create mode 100644 hw/vscard_common.h
> >
> >

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06  0:28 ` Alon Levy
@ 2010-10-06  8:55   ` Gerd Hoffmann
  2010-10-06 19:00     ` Anthony Liguori
  2010-10-06 19:02   ` Anthony Liguori
  1 sibling, 1 reply; 14+ messages in thread
From: Gerd Hoffmann @ 2010-10-06  8:55 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 10/06/10 02:28, Alon Levy wrote:
>
>>
>> Does this work with live migration?  I can't see how it would.
>>
>
> No, it doesn't right now. It would require cooperation with the client,
> to tell it to reconnect to the target qemu (kind of like spice).

I think until we have this migration should have pretty much the same 
effect as a chardev disconnect, i.e. detach the usb device (which the 
guest will see as unplug).

Needs some code though, at minimum you'll have to xfer the connected 
state from the migration source and have some bits in post_load() which 
do attach/detach if needed.

cheers,
   Gerd

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06  8:55   ` Gerd Hoffmann
@ 2010-10-06 19:00     ` Anthony Liguori
  2010-10-07  7:14       ` Gerd Hoffmann
  2010-10-07  7:39       ` Alon Levy
  0 siblings, 2 replies; 14+ messages in thread
From: Anthony Liguori @ 2010-10-06 19:00 UTC (permalink / raw)
  To: Gerd Hoffmann; +Cc: Alon Levy, qemu-devel

On 10/06/2010 03:55 AM, Gerd Hoffmann wrote:
> On 10/06/10 02:28, Alon Levy wrote:
>>
>>>
>>> Does this work with live migration?  I can't see how it would.
>>>
>>
>> No, it doesn't right now. It would require cooperation with the client,
>> to tell it to reconnect to the target qemu (kind of like spice).
>
> I think until we have this migration should have pretty much the same 
> effect as a chardev disconnect, i.e. detach the usb device (which the 
> guest will see as unplug).

Better yet, mark the guest as unmigrateable and let the management tool 
unplug the usb device before migration and replug it after migration.

It's the same principle behind device assignment.

Regards,

Anthony Liguori

> Needs some code though, at minimum you'll have to xfer the connected 
> state from the migration source and have some bits in post_load() 
> which do attach/detach if needed.
>
> cheers,
>   Gerd
>

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06  0:28 ` Alon Levy
  2010-10-06  8:55   ` Gerd Hoffmann
@ 2010-10-06 19:02   ` Anthony Liguori
  2010-10-06 22:12     ` Alon Levy
  1 sibling, 1 reply; 14+ messages in thread
From: Anthony Liguori @ 2010-10-06 19:02 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 10/05/2010 07:28 PM, Alon Levy wrote:
> ----- "Anthony Liguori"<anthony@codemonkey.ws>  wrote:
>
>    
>> On 10/05/2010 04:32 PM, Alon Levy wrote:
>>      
>>> This patch adds a new device, it is described in full in the second
>>>        
>> patch
>>      
>>> intro and also in the documentation in docs. In brief it provides a
>>>        
>> standard
>>      
>>> smart card reader device.
>>>
>>> The first patch is the configure change and docs.
>>> The second patch contains the actual device, I couldn't figure out a
>>>        
>> good
>>      
>>> way to split it to ease review, so if the first reviewer can
>>>        
>> suggest
>>      
>>> a good way to split it I would gladly do that.
>>>
>>> Alon Levy (2):
>>>     usb-ccid: add CCID device. add configure option.
>>>     usb-ccid: add CCID device (device itself)
>>>
>>>        
>> Does this work with live migration?  I can't see how it would.
>>
>>      
> No, it doesn't right now. It would require cooperation with the client,
> to tell it to reconnect to the target qemu (kind of like spice).
>    

Is that because the device's logic is implemented in software outside of 
qemu or because it's somehow tied to hardware?

If it's the former, disconnect is not acceptable.  The logic for the 
software implementation should reside in qemu such that the device can 
be properly migrated.

If it's tied to hardware, it's just another form of device assignment 
and see my other mail.

Regards,

Anthony Liguori

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
       [not found] <668252848.649521286402747765.JavaMail.root@zmail06.collab.prod.int.phx2.redhat.com>
@ 2010-10-06 22:06 ` Alon Levy
  0 siblings, 0 replies; 14+ messages in thread
From: Alon Levy @ 2010-10-06 22:06 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Gerd Hoffmann


----- "Anthony Liguori" <anthony@codemonkey.ws> wrote:

> On 10/06/2010 03:55 AM, Gerd Hoffmann wrote:
> > On 10/06/10 02:28, Alon Levy wrote:
> >>
> >>>
> >>> Does this work with live migration?  I can't see how it would.
> >>>
> >>
> >> No, it doesn't right now. It would require cooperation with the
> client,
> >> to tell it to reconnect to the target qemu (kind of like spice).
> >
> > I think until we have this migration should have pretty much the
> same 
> > effect as a chardev disconnect, i.e. detach the usb device (which
> the 
> > guest will see as unplug).
> 
> Better yet, mark the guest as unmigrateable and let the management
> tool 
> unplug the usb device before migration and replug it after migration.
> 
> It's the same principle behind device assignment.
> 

ok, I think I'll go this way.

> Regards,
> 
> Anthony Liguori
> 
> > Needs some code though, at minimum you'll have to xfer the connected
> 
> > state from the migration source and have some bits in post_load() 
> > which do attach/detach if needed.
> >
> > cheers,
> >   Gerd
> >

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06 19:02   ` Anthony Liguori
@ 2010-10-06 22:12     ` Alon Levy
  2010-10-07 19:56       ` Anthony Liguori
  0 siblings, 1 reply; 14+ messages in thread
From: Alon Levy @ 2010-10-06 22:12 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel


----- "Anthony Liguori" <anthony@codemonkey.ws> wrote:

> On 10/05/2010 07:28 PM, Alon Levy wrote:
> > ----- "Anthony Liguori"<anthony@codemonkey.ws>  wrote:
> >
> >    
> >> On 10/05/2010 04:32 PM, Alon Levy wrote:
> >>      
> >>> This patch adds a new device, it is described in full in the
> second
> >>>        
> >> patch
> >>      
> >>> intro and also in the documentation in docs. In brief it provides
> a
> >>>        
> >> standard
> >>      
> >>> smart card reader device.
> >>>
> >>> The first patch is the configure change and docs.
> >>> The second patch contains the actual device, I couldn't figure out
> a
> >>>        
> >> good
> >>      
> >>> way to split it to ease review, so if the first reviewer can
> >>>        
> >> suggest
> >>      
> >>> a good way to split it I would gladly do that.
> >>>
> >>> Alon Levy (2):
> >>>     usb-ccid: add CCID device. add configure option.
> >>>     usb-ccid: add CCID device (device itself)
> >>>
> >>>        
> >> Does this work with live migration?  I can't see how it would.
> >>
> >>      
> > No, it doesn't right now. It would require cooperation with the
> client,
> > to tell it to reconnect to the target qemu (kind of like spice).
> >    
> 
> Is that because the device's logic is implemented in software outside
> of 
> qemu or because it's somehow tied to hardware?

Actually, both are possible - but the later is the interesting use case (the
former is mainly for debugging). To elaborate: the device is meant to allow
a hardware reader to be available to the guest while still being available to
the client (which is running on the computer with the real reader attached). So
the real card is what we are talking to. The other usage is to have a virtual
card, which would actually be more logical to put with the qemu device, but
It isn't my current focus (the focus being the real card, and the virtual card
being implemented in the client side is a testing measure).

> 
> If it's the former, disconnect is not acceptable.  The logic for the 
> software implementation should reside in qemu such that the device can
> 
> be properly migrated.
> 

Yes, I'd like to do that eventually, but it isn't the focus right now.

> If it's tied to hardware, it's just another form of device assignment
> 
> and see my other mail.
> 

I'll do this.

A side note: I tried migrating a QSIMPLEQ today - not a fun experience. I'm
wondering if this is a result of a mentality that "all devices should allocate
memory upfront" that makes using / migrating dynamic linked lists a non-starter,
or just an oversight (no one wrote VMSTATE_QSIMPLEQ yet). As it stands migrating
a QSIMPLEQ (or any other list) is much easier the old way without using VMSTATE.
</rant>.

> Regards,
> 
> Anthony Liguori

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06 19:00     ` Anthony Liguori
@ 2010-10-07  7:14       ` Gerd Hoffmann
  2010-10-07  7:39       ` Alon Levy
  1 sibling, 0 replies; 14+ messages in thread
From: Gerd Hoffmann @ 2010-10-07  7:14 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: Alon Levy, qemu-devel

On 10/06/10 21:00, Anthony Liguori wrote:
> Better yet, mark the guest as unmigrateable and let the management tool
> unplug the usb device before migration and replug it after migration.

Yep, that will work too.

cheers,
   Gerd

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06 19:00     ` Anthony Liguori
  2010-10-07  7:14       ` Gerd Hoffmann
@ 2010-10-07  7:39       ` Alon Levy
  2010-10-07 19:54         ` Anthony Liguori
  1 sibling, 1 reply; 14+ messages in thread
From: Alon Levy @ 2010-10-07  7:39 UTC (permalink / raw)
  To: Anthony Liguori; +Cc: qemu-devel, Gerd Hoffmann


----- "Anthony Liguori" <anthony@codemonkey.ws> wrote:

> On 10/06/2010 03:55 AM, Gerd Hoffmann wrote:
> > On 10/06/10 02:28, Alon Levy wrote:
> >>
> >>>
> >>> Does this work with live migration?  I can't see how it would.
> >>>
> >>
> >> No, it doesn't right now. It would require cooperation with the
> client,
> >> to tell it to reconnect to the target qemu (kind of like spice).
> >
> > I think until we have this migration should have pretty much the
> same 
> > effect as a chardev disconnect, i.e. detach the usb device (which
> the 
> > guest will see as unplug).
> 
> Better yet, mark the guest as unmigrateable and let the management
> tool 
> unplug the usb device before migration and replug it after migration.
> 
> It's the same principle behind device assignment.
> 

Is there any way to also get a pre_migrate callback with register_device_unmigratable?
I'd like to send a VSC_Reconnect message, then the guest sees an unplug, then migration,
then (no plug yet since the device is marked as auto_attach=0) client reconnects
(actually this happens before but to a paused machine waiting for migration), and then
causes attachement, same as a new machine.

> Regards,
> 
> Anthony Liguori
> 
> > Needs some code though, at minimum you'll have to xfer the connected
> 
> > state from the migration source and have some bits in post_load() 
> > which do attach/detach if needed.
> >
> > cheers,
> >   Gerd
> >

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-07  7:39       ` Alon Levy
@ 2010-10-07 19:54         ` Anthony Liguori
  0 siblings, 0 replies; 14+ messages in thread
From: Anthony Liguori @ 2010-10-07 19:54 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel, Gerd Hoffmann

On 10/07/2010 02:39 AM, Alon Levy wrote:
> ----- "Anthony Liguori"<anthony@codemonkey.ws>  wrote:
>
>    
>> On 10/06/2010 03:55 AM, Gerd Hoffmann wrote:
>>      
>>> On 10/06/10 02:28, Alon Levy wrote:
>>>        
>>>>          
>>>>> Does this work with live migration?  I can't see how it would.
>>>>>
>>>>>            
>>>> No, it doesn't right now. It would require cooperation with the
>>>>          
>> client,
>>      
>>>> to tell it to reconnect to the target qemu (kind of like spice).
>>>>          
>>> I think until we have this migration should have pretty much the
>>>        
>> same
>>      
>>> effect as a chardev disconnect, i.e. detach the usb device (which
>>>        
>> the
>>      
>>> guest will see as unplug).
>>>        
>> Better yet, mark the guest as unmigrateable and let the management
>> tool
>> unplug the usb device before migration and replug it after migration.
>>
>> It's the same principle behind device assignment.
>>
>>      
> Is there any way to also get a pre_migrate callback with register_device_unmigratable?
> I'd like to send a VSC_Reconnect message, then the guest sees an unplug, then migration,
>    

No.  The disconnect needs to happen in the management tooling layer.   
Same is true for any device doing hardware passthrough.

Automagic unplugging/plugging during migration is not a good idea 
universally so it's something that needs to happen as a policy at the 
management level.

Regards,

Anthony Liguori

> then (no plug yet since the device is marked as auto_attach=0) client reconnects
> (actually this happens before but to a paused machine waiting for migration), and then
> causes attachement, same as a new machine.
>
>    
>> Regards,
>>
>> Anthony Liguori
>>
>>      
>>> Needs some code though, at minimum you'll have to xfer the connected
>>>        
>>      
>>> state from the migration source and have some bits in post_load()
>>> which do attach/detach if needed.
>>>
>>> cheers,
>>>    Gerd
>>>
>>>        

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [Qemu-devel] [PATCH 0/2] USB CCID device
  2010-10-06 22:12     ` Alon Levy
@ 2010-10-07 19:56       ` Anthony Liguori
  0 siblings, 0 replies; 14+ messages in thread
From: Anthony Liguori @ 2010-10-07 19:56 UTC (permalink / raw)
  To: Alon Levy; +Cc: qemu-devel

On 10/06/2010 05:12 PM, Alon Levy wrote:
> Actually, both are possible - but the later is the interesting use case (the
> former is mainly for debugging). To elaborate: the device is meant to allow
> a hardware reader to be available to the guest while still being available to
> the client (which is running on the computer with the real reader attached). So
> the real card is what we are talking to. The other usage is to have a virtual
> card, which would actually be more logical to put with the qemu device, but
> It isn't my current focus (the focus being the real card, and the virtual card
> being implemented in the client side is a testing measure).
>    

Okay, that makes sense.

> I'll do this.
>
> A side note: I tried migrating a QSIMPLEQ today - not a fun experience. I'm
> wondering if this is a result of a mentality that "all devices should allocate
> memory upfront" that makes using / migrating dynamic linked lists a non-starter,
> or just an oversight (no one wrote VMSTATE_QSIMPLEQ yet). As it stands migrating
> a QSIMPLEQ (or any other list) is much easier the old way without using VMSTATE.
> </rant>.
>    

Yeah, complex types are not at all easy to migrate today.  Something we 
need to tackle with vmstate2 eventually.

Regards,

Anthony Liguori

>> Regards,
>>
>> Anthony Liguori
>>      

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2010-10-07 19:56 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-05 21:32 [Qemu-devel] [PATCH 0/2] USB CCID device Alon Levy
2010-10-05 21:32 ` [Qemu-devel] [PATCH 1/2] usb-ccid: add CCID device. add configure option Alon Levy
2010-10-05 21:33 ` [Qemu-devel] [PATCH 2/2] usb-ccid: add CCID device (device itself) Alon Levy
2010-10-05 23:02 ` [Qemu-devel] [PATCH 0/2] USB CCID device Anthony Liguori
     [not found] <1820922650.529041286324880996.JavaMail.root@zmail06.collab.prod.int.phx2.redhat.com>
2010-10-06  0:28 ` Alon Levy
2010-10-06  8:55   ` Gerd Hoffmann
2010-10-06 19:00     ` Anthony Liguori
2010-10-07  7:14       ` Gerd Hoffmann
2010-10-07  7:39       ` Alon Levy
2010-10-07 19:54         ` Anthony Liguori
2010-10-06 19:02   ` Anthony Liguori
2010-10-06 22:12     ` Alon Levy
2010-10-07 19:56       ` Anthony Liguori
     [not found] <668252848.649521286402747765.JavaMail.root@zmail06.collab.prod.int.phx2.redhat.com>
2010-10-06 22:06 ` Alon Levy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).