From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from [140.186.70.92] (port=52940 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PM3bs-0006zt-Qk for qemu-devel@nongnu.org; Fri, 26 Nov 2010 14:05:31 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PM3bp-0006AI-Cu for qemu-devel@nongnu.org; Fri, 26 Nov 2010 14:05:28 -0500 Received: from mail-pw0-f45.google.com ([209.85.160.45]:37619) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PM3bo-0006A0-TR for qemu-devel@nongnu.org; Fri, 26 Nov 2010 14:05:25 -0500 Received: by pwj6 with SMTP id 6so506739pwj.4 for ; Fri, 26 Nov 2010 11:05:23 -0800 (PST) MIME-Version: 1.0 In-Reply-To: <1290702170-3775-2-git-send-email-alevy@redhat.com> References: <1290702170-3775-1-git-send-email-alevy@redhat.com> <1290702170-3775-2-git-send-email-alevy@redhat.com> From: Blue Swirl Date: Fri, 26 Nov 2010 19:05:02 +0000 Message-ID: Subject: Re: [Qemu-devel] [PATCH 1/6] usb-ccid: add CCID bus Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Alon Levy Cc: qemu-devel@nongnu.org On Thu, Nov 25, 2010 at 4:22 PM, Alon Levy wrote: > A CCID device is a smart card reader. It is a USB device, defined at [1]. > This patch introduces the usb-ccid device that is a ccid bus. Next patche= s will > introduce two card types to use it, a passthru card and an emulated card. > > =C2=A0[1] http://www.usb.org/developers/devclass_docs/DWG_Smart-Card_CCID= _Rev110. > > Signed-off-by: Alon Levy > --- > =C2=A0Makefile.objs | =C2=A0 =C2=A01 + > =C2=A0configure =C2=A0 =C2=A0 | =C2=A0 12 + > =C2=A0hw/ccid.h =C2=A0 =C2=A0 | =C2=A0 34 ++ > =C2=A0hw/usb-ccid.c | 1342 ++++++++++++++++++++++++++++++++++++++++++++++= +++++++++++ > =C2=A04 files changed, 1389 insertions(+), 0 deletions(-) > =C2=A0create mode 100644 hw/ccid.h > =C2=A0create mode 100644 hw/usb-ccid.c > > diff --git a/Makefile.objs b/Makefile.objs > index 23b17ce..713131f 100644 > --- a/Makefile.objs > +++ b/Makefile.objs > @@ -183,6 +183,7 @@ hw-obj-$(CONFIG_FDC) +=3D fdc.o > =C2=A0hw-obj-$(CONFIG_ACPI) +=3D acpi.o acpi_piix4.o > =C2=A0hw-obj-$(CONFIG_APM) +=3D pm_smbus.o apm.o > =C2=A0hw-obj-$(CONFIG_DMA) +=3D dma.o > +hw-obj-$(CONFIG_SMARTCARD) +=3D usb-ccid.o > > =C2=A0# PPC devices > =C2=A0hw-obj-$(CONFIG_OPENPIC) +=3D openpic.o > diff --git a/configure b/configure > index 2917874..fb9eac2 100755 > --- a/configure > +++ b/configure > @@ -332,6 +332,7 @@ zero_malloc=3D"" > =C2=A0trace_backend=3D"nop" > =C2=A0trace_file=3D"trace" > =C2=A0spice=3D"" > +smartcard=3D"no" > > =C2=A0# OS specific > =C2=A0if check_define __linux__ ; then > @@ -739,6 +740,10 @@ for opt do > =C2=A0 ;; > =C2=A0 --enable-vhost-net) vhost_net=3D"yes" > =C2=A0 ;; > + =C2=A0--disable-smartcard) smartcard=3D"no" > + =C2=A0;; > + =C2=A0--enable-smartcard) smartcard=3D"yes" > + =C2=A0;; > =C2=A0 --*dir) > =C2=A0 ;; > =C2=A0 *) echo "ERROR: unknown option $opt"; show_help=3D"yes" > @@ -934,6 +939,8 @@ echo " =C2=A0--trace-file=3DNAME =C2=A0 =C2=A0 =C2=A0= =C2=A0Full PATH,NAME of file to store traces" > =C2=A0echo " =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Default:trace-" > =C2=A0echo " =C2=A0--disable-spice =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0disa= ble spice" > =C2=A0echo " =C2=A0--enable-spice =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 enab= le spice" > +echo " =C2=A0--disable-smartcard =C2=A0 =C2=A0 =C2=A0disable smartcard s= upport" > +echo " =C2=A0--enable-smartcard =C2=A0 =C2=A0 =C2=A0 enable smartcard su= pport" > =C2=A0echo "" > =C2=A0echo "NOTE: The object files are built at the place where configure= is launched" > =C2=A0exit 1 > @@ -2354,6 +2361,7 @@ echo "vhost-net support $vhost_net" > =C2=A0echo "Trace backend =C2=A0 =C2=A0 $trace_backend" > =C2=A0echo "Trace output file $trace_file-" > =C2=A0echo "spice support =C2=A0 =C2=A0 $spice" > +echo "smartcard support $smartcard" > > =C2=A0if test $sdl_too_old =3D "yes"; then > =C2=A0echo "-> Your SDL version is too old - please upgrade to have SDL s= upport" > @@ -2617,6 +2625,10 @@ if test "$spice" =3D "yes" ; then > =C2=A0 echo "CONFIG_SPICE=3Dy" >> $config_host_mak > =C2=A0fi > > +if test "$smartcard" =3D "yes" ; then > + =C2=A0echo "CONFIG_SMARTCARD=3Dy" >> $config_host_mak > +fi > + > =C2=A0# XXX: suppress that > =C2=A0if [ "$bsd" =3D "yes" ] ; then > =C2=A0 echo "CONFIG_BSD=3Dy" >> $config_host_mak > diff --git a/hw/ccid.h b/hw/ccid.h > new file mode 100644 > index 0000000..a38f971 > --- /dev/null > +++ b/hw/ccid.h > @@ -0,0 +1,34 @@ > +#ifndef __CCID_H__ > +#define __CCID_H__ > + > +#include "qdev.h" > + > +typedef struct CCIDCardState CCIDCardState; > +typedef struct CCIDCardInfo CCIDCardInfo; > + > +struct CCIDCardState { > + =C2=A0 =C2=A0DeviceState qdev; > +}; > + > +struct CCIDCardInfo { > + =C2=A0 =C2=A0DeviceInfo qdev; > + =C2=A0 =C2=A0void (*print)(Monitor *mon, CCIDCardState *card, int inden= t); > + =C2=A0 =C2=A0const uint8_t *(*get_atr)(CCIDCardState *card, uint32_t *l= en); > + =C2=A0 =C2=A0void (*apdu_from_guest)(CCIDCardState *card, const uint8_t= *apdu, uint32_t len); > + =C2=A0 =C2=A0int (*exitfn)(CCIDCardState *card); > + =C2=A0 =C2=A0int (*initfn)(CCIDCardState *card); > +}; > + > +void ccid_card_send_apdu_to_guest(CCIDCardState *card, uint8_t* apdu, ui= nt32_t len); > +void ccid_card_card_removed(CCIDCardState *card); > +void ccid_card_card_inserted(CCIDCardState *card); > +void ccid_card_card_error(CCIDCardState *card, uint64_t error); > +void ccid_card_qdev_register(CCIDCardInfo *card); > + > +/* support guest visible insertion/removal of ccid devices based on actu= al > + * devices connected/removed. Called by card implementation (passthru, l= ocal) */ > +int ccid_card_ccid_attach(CCIDCardState *card); > +void ccid_card_ccid_detach(CCIDCardState *card); > + > +#endif // __CCID_H__ > + > diff --git a/hw/usb-ccid.c b/hw/usb-ccid.c > new file mode 100644 > index 0000000..66c1dba > --- /dev/null > +++ b/hw/usb-ccid.c > @@ -0,0 +1,1342 @@ > +/* > + * CCID Device emulation > + * > + * Based on usb-serial.c: > + * Copyright (c) 2006 CodeSourcery. > + * Copyright (c) 2008 Samuel Thibault > + * Written by Paul Brook, reused for FTDI by Samuel Thibault, > + * Reused for CCID by Alon Levy. > + * Contributed to by Robert Relyea > + * Copyright (c) 2010 Red Hat. > + * > + * This code is licenced under the LGPL. > + */ > + > +/* References: > + * > + * CCID Specification Revision 1.1 April 22nd 2005 > + * =C2=A0"Universal Serial Bus, Device Class: Smart Card" > + * =C2=A0Specification for Integrated Circuit(s) Cards Interface Devices > + * > + * KNOWN BUGS > + * 1. remove/insert can sometimes result in removed state instead of ins= erted. > + * This is a result of the following: > + * =C2=A0symptom: dmesg shows ERMOTEIO (-121), pcscd shows -99. This hap= pens > + * =C2=A0when we send a too short packet, seen in uhci-usb.c, resulting = from > + * =C2=A0a urb requesting SPD and us returning a smaller packet. > + * =C2=A0Not sure which messages trigger this. > + * > + */ > + > +#include "qemu-common.h" > +#include "qemu-error.h" > +#include "usb.h" > +#include "monitor.h" > + > +#include "hw/ccid.h" > + > +//#define DEBUG_CCID > + > +#define DPRINTF(s, lvl, fmt, ...) \ > +do { if (lvl <=3D s->debug) { printf("usb-ccid: " fmt , ## __VA_ARGS__);= } } while (0) > + > +#define CCID_DEV_NAME "usb-ccid" > + > +/* The two options for variable sized buffers: > + * make them constant size, for large enough constant, > + * or handle the migration complexity - VMState doesn't handle this case= . > + * sizes are expected never to be exceeded, unless guest misbehaves. */ > +#define BULK_OUT_DATA_SIZE 65536 > +#define PENDING_ANSWERS_NUM 128 > + > +#define BULK_IN_BUF_SIZE 384 > +#define BULK_IN_PENDING_NUM 8 > + > +#define InterfaceOutClass =C2=A0 =C2=A0((USB_DIR_OUT|USB_TYPE_CLASS|USB_= RECIP_INTERFACE)<<8) > +#define InterfaceInClass =C2=A0 =C2=A0 ((USB_DIR_IN |USB_TYPE_CLASS|USB_= RECIP_INTERFACE)<<8) > + > +#define CCID_CONTROL_ABORT =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A00x1 > +#define CCID_CONTROL_GET_CLOCK_FREQUENCIES =C2=A00x2 > +#define CCID_CONTROL_GET_DATA_RATES =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x3 > + > +#define CCID_PRODUCT_DESCRIPTION =C2=A0 =C2=A0 =C2=A0 =C2=A0"QEMU USB CC= ID" > +#define CCID_VENDOR_DESCRIPTION =C2=A0 =C2=A0 =C2=A0 =C2=A0 "QEMU " QEMU= _VERSION > +#define CCID_INTERFACE_NAME =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 "C= CID Interface" > +#define CCID_SERIAL_NUMBER_STRING =C2=A0 =C2=A0 =C2=A0 "1" > +/* Using Gemplus Vendor and Product id > + =C2=A0Effect on various drivers: > + =C2=A0* usbccid.sys (winxp, others untested) is a class driver so it do= esn't care. > + =C2=A0* linux has a number of class drivers, but openct filters based o= n > + =C2=A0 =C2=A0vendor/product (/etc/openct.conf under fedora), hence Gemp= lus. > + */ > +#define CCID_VENDOR_ID =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A00x08e6 > +#define CCID_PRODUCT_ID =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 0x4433 > +#define CCID_DEVICE_VERSION =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x= 0000 > + > +/* BULK_OUT messages from PC to Reader > + =C2=A0 Defined in CCID Rev 1.1 6.1 (page 26) > + */ > +#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOn =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A00x62 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_IccPowerOff =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 0x63 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_GetSlotStatus =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 0x65 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_XfrBlock =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A00x6f > +#define CCID_MESSAGE_TYPE_PC_to_RDR_GetParameters =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 0x6c > +#define CCID_MESSAGE_TYPE_PC_to_RDR_ResetParameters =C2=A0 =C2=A0 =C2=A0= =C2=A0 0x6d > +#define CCID_MESSAGE_TYPE_PC_to_RDR_SetParameters =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 0x61 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_Escape =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x6b > +#define CCID_MESSAGE_TYPE_PC_to_RDR_IccClock =C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0 =C2=A0 =C2=A0 =C2=A00x6e > +#define CCID_MESSAGE_TYPE_PC_to_RDR_T0APDU =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x6a > +#define CCID_MESSAGE_TYPE_PC_to_RDR_Secure =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x69 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_Mechanical =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A00x71 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_Abort =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 0x72 > +#define CCID_MESSAGE_TYPE_PC_to_RDR_SetDataRateAndClockFrequency 0x73 > + > +/* BULK_IN messages from Reader to PC > + =C2=A0 Defined in CCID Rev 1.1 6.2 (page 48) > + */ > +#define CCID_MESSAGE_TYPE_RDR_to_PC_DataBlock =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 0x80 > +#define CCID_MESSAGE_TYPE_RDR_to_PC_SlotStatus =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A00x81 > +#define CCID_MESSAGE_TYPE_RDR_to_PC_Parameters =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A00x82 > +#define CCID_MESSAGE_TYPE_RDR_to_PC_Escape =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A00x83 > +#define CCID_MESSAGE_TYPE_RDR_to_PC_DataRateAndClockFrequency 0x84 > + > +/* INTERRUPT_IN messages from Reader to PC > + =C2=A0 Defined in CCID Rev 1.1 6.3 (page 56) > + */ > +#define CCID_MESSAGE_TYPE_RDR_to_PC_NotifySlotChange =C2=A0 =C2=A0 =C2= =A0 =C2=A00x50 > +#define CCID_MESSAGE_TYPE_RDR_to_PC_HardwareError =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 0x51 > + > +/* Endpoints for CCID - addresses are up to us to decide. > + =C2=A0 To support slot insertion and removal we must have an interrupt = in ep > + =C2=A0 in addition we need a bulk in and bulk out ep > + =C2=A0 5.2, page 20 > + */ > +#define CCID_INT_IN_EP =C2=A0 =C2=A0 =C2=A0 1 > +#define CCID_BULK_IN_EP =C2=A0 =C2=A0 =C2=A02 > +#define CCID_BULK_OUT_EP =C2=A0 =C2=A0 3 > + > +/* bmSlotICCState masks */ > +#define SLOT_0_STATE_MASK =C2=A0 =C2=A01 > +#define SLOT_0_CHANGED_MASK =C2=A02 > + > +/* Status codes that go in bStatus (see 6.2.6) */ > +enum { > + =C2=A0 =C2=A0ICC_STATUS_PRESENT_ACTIVE =3D 0, > + =C2=A0 =C2=A0ICC_STATUS_PRESENT_INACTIVE, > + =C2=A0 =C2=A0ICC_STATUS_NOT_PRESENT > +}; > + > +enum { > + =C2=A0 =C2=A0COMMAND_STATUS_NO_ERROR =3D 0, > + =C2=A0 =C2=A0COMMAND_STATUS_FAILED, > + =C2=A0 =C2=A0COMMAND_STATUS_TIME_EXTENSION_REQUIRED > +}; > + > +/* Error codes that go in bError (see 6.2.6) > + */ > +enum { > + =C2=A0 =C2=A0ERROR_CMD_NOT_SUPPORTED =3D 0, > + =C2=A0 =C2=A0ERROR_CMD_ABORTED =C2=A0 =C2=A0 =C2=A0 =3D -1, > + =C2=A0 =C2=A0ERROR_ICC_MUTE =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D -2, > + =C2=A0 =C2=A0ERROR_XFR_PARITY_ERROR =C2=A0=3D -3, > + =C2=A0 =C2=A0ERROR_XFR_OVERRUN =C2=A0 =C2=A0 =C2=A0 =3D -4, > + =C2=A0 =C2=A0ERROR_HW_ERROR =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0=3D -5, > +}; > + > +/* 6.2.6 RDR_to_PC_SlotStatus definitions */ > +enum { > + =C2=A0 =C2=A0CLOCK_STATUS_RUNNING =3D 0, > + =C2=A0 =C2=A0/* 0 - Clock Running, 1 - Clock stopped in State L, 2 - H, > + =C2=A0 =C2=A0 =C2=A0 3 - unkonwn state. rest are RFU > + =C2=A0 =C2=A0 */ > +}; > + > +typedef struct __attribute__ ((__packed__)) { Please don't use anonymous structs. > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bMessageType; Why aHungarian nNotation? It's not QEMU style. > + =C2=A0 =C2=A0uint32_t =C2=A0 =C2=A0dwLength; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bSlot; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bSeq; > +} CCID_Header; Packed structure with unaligned fields. Will this work? > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_Header hdr; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bStatus; =C2=A0 =C2=A0 =C2=A0 =C2=A0= /* Only used in BULK_IN */ > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bError; =C2=A0 =C2=A0 =C2=A0 =C2=A0 = /* Only used in BULK_IN */ > +} CCID_BULK_IN; > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_BULK_IN b; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bClockStatus; > +} CCID_SlotStatus; > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_BULK_IN b; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bProtocolNum; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 abProtocolDataStructure[0]; > +} CCID_Parameter; > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_BULK_IN b; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 =C2=A0bChainParameter; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 =C2=A0abData[0]; > +} CCID_DataBlock; > + > +/* 6.1.4 PC_to_RDR_XfrBlock */ > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_Header =C2=A0hdr; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 =C2=A0bBWI; /* Block Waiting Timeout= */ > + =C2=A0 =C2=A0uint16_t =C2=A0 =C2=A0 wLevelParameter; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 =C2=A0abData[0]; > +} CCID_XferBlock; > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_Header hdr; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bPowerSelect; > + =C2=A0 =C2=A0uint16_t =C2=A0 =C2=A0abRFU; > +} CCID_IccPowerOn; > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_Header hdr; > + =C2=A0 =C2=A0uint16_t =C2=A0 =C2=A0abRFU; > +} CCID_IccPowerOff; > + > +typedef struct __attribute__ ((__packed__)) { > + =C2=A0 =C2=A0CCID_Header hdr; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bProtocolNum; > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0abProtocolDataStructure[0]; > +} CCID_SetParameter; > + > +typedef struct { > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bMessageType; /* CCID_MESSAGE_TYPE_R= DR_to_PC_NotifySlotChange */ > + =C2=A0 =C2=A0uint8_t =C2=A0 =C2=A0 bmSlotICCState; > +} CCID_Notify_Slot_Change; > + > +/* used for DataBlock response to XferBlock */ > +typedef struct answer_t { > + =C2=A0 =C2=A0uint8_t slot; > + =C2=A0 =C2=A0uint8_t seq; > +} answer_t; > + answer_t is not in line with CODING_STYLE. > +/* pending BULK_IN messages */ > +typedef struct bulk_in_t { > + =C2=A0 =C2=A0uint8_t =C2=A0data[BULK_IN_BUF_SIZE]; > + =C2=A0 =C2=A0uint32_t len; > + =C2=A0 =C2=A0uint32_t pos; > +} bulk_in_t; Neither is this. > + > +enum { > + =C2=A0 =C2=A0MIGRATION_NONE, > + =C2=A0 =C2=A0MIGRATION_MIGRATED, > +}; > + > +typedef struct CCIDBus CCIDBus; > +typedef struct USBCCIDState USBCCIDState; > + > +#define MAX_PROTOCOL_SIZE =C2=A0 7 > + > +/** > + * powered - defaults to true, changed by PowerOn/PowerOff messages > + */ > +struct USBCCIDState { > + =C2=A0 =C2=A0USBDevice dev; > + =C2=A0 =C2=A0CCIDBus *bus; > + =C2=A0 =C2=A0CCIDCardState *card; > + =C2=A0 =C2=A0CCIDCardInfo *cardinfo; /* caching the info pointer */ > + =C2=A0 =C2=A0uint8_t =C2=A0debug; > + =C2=A0 =C2=A0uint8_t =C2=A0auto_attach; > + =C2=A0 =C2=A0bulk_in_t bulk_in_pending[BULK_IN_PENDING_NUM]; /* circula= r */ > + =C2=A0 =C2=A0uint32_t bulk_in_pending_start; > + =C2=A0 =C2=A0uint32_t bulk_in_pending_end; /* first free */ > + =C2=A0 =C2=A0uint32_t bulk_in_pending_num; > + =C2=A0 =C2=A0bulk_in_t *current_bulk_in; > + =C2=A0 =C2=A0uint8_t =C2=A0bulk_out_data[BULK_OUT_DATA_SIZE]; > + =C2=A0 =C2=A0uint32_t bulk_out_pos; > + =C2=A0 =C2=A0uint8_t =C2=A0bmSlotICCState; > + =C2=A0 =C2=A0uint8_t =C2=A0powered; > + =C2=A0 =C2=A0uint8_t =C2=A0notify_slot_change; > + =C2=A0 =C2=A0uint64_t last_answer_error; > + =C2=A0 =C2=A0answer_t pending_answers[PENDING_ANSWERS_NUM]; > + =C2=A0 =C2=A0uint32_t pending_answers_start; > + =C2=A0 =C2=A0uint32_t pending_answers_end; > + =C2=A0 =C2=A0uint32_t pending_answers_num; > + =C2=A0 =C2=A0uint8_t =C2=A0bError; > + =C2=A0 =C2=A0uint8_t =C2=A0bmCommandStatus; > + =C2=A0 =C2=A0uint8_t =C2=A0bProtocolNum; > + =C2=A0 =C2=A0uint8_t =C2=A0abProtocolDataStructure[MAX_PROTOCOL_SIZE]; > + =C2=A0 =C2=A0uint32_t ulProtocolDataStructureSize; > + =C2=A0 =C2=A0uint8_t =C2=A0attached_vmstate; > + =C2=A0 =C2=A0uint32_t state_vmstate; > + =C2=A0 =C2=A0uint8_t =C2=A0migration_state; > + =C2=A0 =C2=A0uint32_t migration_target_ip; > + =C2=A0 =C2=A0uint16_t migration_target_port; > +}; > + > +/* Slot specific variables. We emulate a single slot card reader. > + */ > + > + > +/* CCID Spec chapter 4: CCID uses a standard device descriptor per Chapt= er 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 Ca= rd > + * Reader as reference: > + * =C2=A0 0dc3:1004 Athena Smartcard Solutions, Inc. > + */ > + > +static const uint8_t qemu_ccid_dev_descriptor[] =3D { > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x12, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 bLeng= th; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0USB_DT_DEVICE, /* =C2=A0u8 bDescriptorType; = Device */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x10, 0x01, /* =C2=A0u16 bcdUSB; v1.1 */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bDeviceClass; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bDeviceSubClass; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bDeviceProtocol; [ low/full speeds only ] */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x40, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bMaxPacketSize0; 8 Bytes (valid: 8,16,32,64) */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Vendor and product id are arbitrary. =C2= =A0*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u16 idVendor =C2=A0*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0CCID_VENDOR_ID & 0xff, CCID_VENDOR_ID >> 8, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u16 idProduct */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0CCID_PRODUCT_ID & 0xff, CCID_PRODUCT_ID >> 8= , > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u16 bcdDevice */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0CCID_DEVICE_VERSION & 0xff, CCID_DEVICE_VERS= ION >> 8, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0iManufacturer; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x02, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0iProduct; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x03, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0iSerialNumber; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bNumConfigurations; */ > +}; > + > +static const uint8_t qemu_ccid_config_descriptor[] =3D { > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* one configuration */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x09, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bLength; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0USB_DT_CONFIG, /* =C2=A0u8 =C2=A0bDescriptor= Type; Configuration */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x5d, 0x00, /* =C2=A0u16 wTotalLength; 9+9+5= 4+7+7+7 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bNumInterfaces; (1) */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bConfigurationValue; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0iConfiguration; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xe0, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bmAttributes; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 Bit 7: must be set, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 6: Self-powered, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 5: Remote wakeup, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 4..0: resvd */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0100/2, =C2=A0 =C2=A0 =C2=A0/* =C2=A0u8 =C2= =A0MaxPower; 50 =3D=3D 100mA */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* one interface */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x09, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bLength; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0USB_DT_INTERFACE, /* =C2=A0u8 =C2=A0if_bDesc= riptorType; Interface */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bInterfaceNumber; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bAlternateSetting; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x03, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bNumEndpoints; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x0b, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bInterfaceClass; Smart Card Device Class */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bInterfaceSubClass; Subclass code */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_bInterfaceProtocol; Protocol code */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x04, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0if_iInterface; Index of string descriptor */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Smart Card Device Class Descriptor */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x36, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bLength; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x21, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bDescriptorType; Functional */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x10, 0x01, /* =C2=A0u16 bcdCCID; CCID Speci= fication Release Number. */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bMaxSlotIndex; The index of the highest available > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0slot on this device. All slots are consecutive starting > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0at 00h. */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x07, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bVoltageSupport; 01h - 5.0v, 02h - 3.0, 03 - 1.8 */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x03, 0x00, /* =C2=A0u32 dwProtocols; RRRR P= PPP. RRRR =3D 0000h.*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, 0x00, /* =C2=A0PPPP: 0001h =3D Protoco= l T=3D0, 0002h =3D Protocol T=3D1 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwDefaultClock; in kHZ (0x0fa0 is 4 MHz) */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xa0, 0x0f, 0x00, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwMaximumClock; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, 0x00, 0x01, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 bNumC= lockSupported; 0 means just the default and max. */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwDataRate ;bps. 9600 =3D=3D 00002580h */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x80, 0x25, 0x00, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwMaxDataRate ; 11520 bps =3D=3D 0001C200h */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, 0xC2, 0x01, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bNumDataRatesSupported; 00 means all rates between > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0 =C2=A0 =C2=A0default and max */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwMaxIFSD; maximum IFSD supported by CCID for protocol > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0 =C2=A0 =C2=A0T=3D1 (Maximum seen from various cards) */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xfe, 0x00, 0x00, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwSyncProtocols; 1 - 2-wire, 2 - 3-wire, 4 - I2C */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, 0x00, 0x00, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwMechanical; =C2=A00 - no special characteristics. */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, 0x00, 0x00, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwFeatures; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A00 - No special characteristics > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 2 Automatic parameter configuration based on ATR data > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 4 Automatic activation of ICC on inserting > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 8 Automatic ICC voltage selection > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 10 Automatic ICC clock frequency change > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 20 Automatic baud rate change > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 40 Automatic parameters negotiation made by the CCID > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 80 automatic PPS made by the CCID > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0100 CCID can set ICC in clock stop mode > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0200 NAD value other then 00 accepted (T=3D1 protocol) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 400 Automatic IFSD exchange as first exchange (T=3D1) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0One of the following only: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 10000 TPDU level exchanges with CCID > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A020000 Short APDU level exchange with CCID > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A040000 Short and Extended APDU level exchange with CCID > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 * > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0+ 100000 USB Wake up signaling supported on card insertion > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0and removal. Must set bit 5 in bmAttributes in Configuration > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0descriptor if 100000 is set.*/ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xfe, 0x04, 0x11, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u32 dwMaxCCIDMessageLength; For extended APDU in [261 + 10 > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0, 65544 + 10]. Otherwise the minimum is wMaxPacketSize of > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0the Bulk-OUT endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x12, 0x00, 0x01, 0x00, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xFF, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bClassGetResponse; Significant only for CCID that > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0offers an APDU level for exchanges. Indicates the default > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0class value used by the CCID when it sends a Get Response > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0command to perform the transportation of an APDU by T=3D0 > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0protocol > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0FFh indicates that the CCID echos the class of the APDU. > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= / > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xFF, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bClassEnvelope; EAPDU only. Envelope command for T=3D0 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, 0x00, /* =C2=A0u16 wLcdLayout; XXYY Nu= mber of lines (XX) and chars per > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0line for LCD display used for PIN entry. 0000 - no LCD */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bPINSupport; 01h PIN Verification, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 *= =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 02h PIN Mod= ification */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x01, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0bMaxCCIDBusySlots; */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Interrupt-IN endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x07, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bLength; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u8 =C2=A0ep_bDescriptorType; Endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0USB_DT_ENDPOINT, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u8 =C2=A0ep_bEndpointAddress; IN Endpoint 1 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x80 | CCID_INT_IN_EP, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x03, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bmAttributes; Interrupt */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x40, 0x00, /* =C2=A0u16 ep_wMaxPacketSize; = */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00xff, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bInterval; */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Bulk-In endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x07, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bLength; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u8 =C2=A0ep_bDescriptorType; Endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0USB_DT_ENDPOINT, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u8 =C2=A0ep_bEndpointAddress; IN Endpoint 2 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x80 | CCID_BULK_IN_EP, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x02, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bmAttributes; Bulk */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x40, 0x00, /* =C2=A0u16 ep_wMaxPacketSize; = */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bInterval; */ > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Bulk-Out endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x07, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bLength; */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u8 =C2=A0ep_bDescriptorType; Endpoint */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0USB_DT_ENDPOINT, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/*= =C2=A0u8 =C2=A0ep_bEndpointAddress; OUT Endpoint 3 */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0CCID_BULK_OUT_EP, > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x02, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bmAttributes; Bulk */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x40, 0x00, /* =C2=A0u16 ep_wMaxPacketSize; = */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A00x00, =C2=A0 =C2=A0 =C2=A0 /* =C2=A0u8 =C2= =A0ep_bInterval; */ > + > +}; > + > +static bool ccid_has_pending_answers(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0return s->pending_answers_num > 0; > +} > + > +static void ccid_clear_pending_answers(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0s->pending_answers_num =3D 0; > + =C2=A0 =C2=A0s->pending_answers_start =3D 0; > + =C2=A0 =C2=A0s->pending_answers_end =3D 0; > +} > + > +static void ccid_print_pending_answers(USBCCIDState *s) > +{ > +#ifdef DEBUG_CCID > + =C2=A0 =C2=A0answer_t *answer; > + =C2=A0 =C2=A0int i, count; > + > + =C2=A0 =C2=A0printf("usb-ccid: pending answers:"); > + =C2=A0 =C2=A0if (!ccid_has_pending_answers(s)) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0printf(" empty\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0for (i =3D s->pending_answers_start, count=3Ds->pending_an= swers_num ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 count > 0; count--, i++) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0answer =3D &s->pending_answers[i % PENDING_A= NSWERS_NUM]; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (count =3D=3D 1) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("%d:%d\n", answer->slot= , answer->seq); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("%d:%d,", answer->slot,= answer->seq); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0} > +#endif > +} > + > +static void ccid_add_pending_answer(USBCCIDState *s, CCID_Header *hdr) > +{ > + =C2=A0 =C2=A0answer_t* answer; > + > + =C2=A0 =C2=A0assert(s->pending_answers_num++ < PENDING_ANSWERS_NUM); > + =C2=A0 =C2=A0answer =3D &s->pending_answers[(s->pending_answers_end++) = % PENDING_ANSWERS_NUM]; > + =C2=A0 =C2=A0answer->slot =3D hdr->bSlot; > + =C2=A0 =C2=A0answer->seq =3D hdr->bSeq; > + =C2=A0 =C2=A0ccid_print_pending_answers(s); > +} > + > +static void ccid_remove_pending_answer(USBCCIDState *s, > + =C2=A0 =C2=A0uint8_t *slot, uint8_t *seq) > +{ > + =C2=A0 =C2=A0answer_t *answer; > + > + =C2=A0 =C2=A0assert(s->pending_answers_num-- > 0); > + =C2=A0 =C2=A0answer =3D &s->pending_answers[(s->pending_answers_start++= ) % PENDING_ANSWERS_NUM]; > + =C2=A0 =C2=A0*slot =3D answer->slot; > + =C2=A0 =C2=A0*seq =3D answer->seq; > + =C2=A0 =C2=A0ccid_print_pending_answers(s); > +} > + > +static void ccid_bulk_in_clear(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0s->bulk_in_pending_start =3D 0; > + =C2=A0 =C2=A0s->bulk_in_pending_end =3D 0; > + =C2=A0 =C2=A0s->bulk_in_pending_num =3D 0; > +} > + > +static void ccid_bulk_in_release(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0assert(s->current_bulk_in !=3D NULL); > + =C2=A0 =C2=A0s->current_bulk_in->pos =3D 0; > + =C2=A0 =C2=A0s->current_bulk_in =3D NULL; > +} > + > +static void ccid_bulk_in_get(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0if (s->current_bulk_in !=3D NULL || s->bulk_in_pending_num= =3D=3D 0) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0assert(s->bulk_in_pending_num > 0); > + =C2=A0 =C2=A0s->bulk_in_pending_num--; > + =C2=A0 =C2=A0s->current_bulk_in =3D &s->bulk_in_pending[ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0(s->bulk_in_pending_start++) % BULK_IN_PENDI= NG_NUM]; > +} > + > +static void* ccid_reserve_recv_buf(USBCCIDState* s, uint16_t len) > +{ > + =C2=A0 =C2=A0bulk_in_t* bulk_in; > + > + =C2=A0 =C2=A0DPRINTF(s, 4, "%s: QUEUE: reserve %d bytes\n", __func__, l= en); > + > + =C2=A0 =C2=A0/* look for an existing element */ > + =C2=A0 =C2=A0if (len > BULK_IN_BUF_SIZE) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("usb-ccid.c: %s: len larger then max = (%d>%d). discarding message.\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0__func__, len, BULK_IN_BUF_SIZ= E); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return NULL; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0if (s->bulk_in_pending_num >=3D BULK_IN_PENDING_NUM) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0printf("usb-ccid.c: %s: No free bulk_in buff= ers. discarding message.\n", > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0__func__); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return NULL; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0bulk_in =3D &s->bulk_in_pending[(s->bulk_in_pending_end++)= % BULK_IN_PENDING_NUM]; > + =C2=A0 =C2=A0s->bulk_in_pending_num++; > + =C2=A0 =C2=A0bulk_in->len =3D len; > + =C2=A0 =C2=A0return bulk_in->data; > +} > + > +static void ccid_reset(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0ccid_bulk_in_clear(s); > + =C2=A0 =C2=A0ccid_clear_pending_answers(s); > +} > + > +static void ccid_detach(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0ccid_reset(s); > + =C2=A0 =C2=A0if (s->auto_attach =3D=3D 0 && s->dev.attached) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0usb_device_detach(&s->dev); > + =C2=A0 =C2=A0} > +} > + > +static void ccid_handle_reset(USBDevice *dev) > +{ > + =C2=A0 =C2=A0USBCCIDState *s =3D DO_UPCAST(USBCCIDState, dev, dev); > + > + =C2=A0 =C2=A0DPRINTF(s, 1, "Reset\n"); > + > + =C2=A0 =C2=A0ccid_reset(s); > +} > + > +static int ccid_handle_control(USBDevice *dev, int request, int value, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0int index, int length, uint= 8_t *data) > +{ > + =C2=A0 =C2=A0USBCCIDState *s =3D DO_UPCAST(USBCCIDState, dev, dev); > + =C2=A0 =C2=A0int ret =3D 0; > + > + =C2=A0 =C2=A0DPRINTF(s, 1, "got control %x, value %x\n",request, value)= ; > + =C2=A0 =C2=A0switch (request) { > + =C2=A0 =C2=A0case DeviceRequest | USB_REQ_GET_STATUS: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0data[0] =3D (0 << USB_DEVICE_SELF_POWERED) | > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0(dev->remote_wakeup << USB_DEV= ICE_REMOTE_WAKEUP); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0data[1] =3D 0x00; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 2; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceOutRequest | USB_REQ_CLEAR_FEATURE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (value =3D=3D USB_DEVICE_REMOTE_WAKEUP) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->remote_wakeup =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceOutRequest | USB_REQ_SET_FEATURE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0if (value =3D=3D USB_DEVICE_REMOTE_WAKEUP) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->remote_wakeup =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} else { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceOutRequest | USB_REQ_SET_ADDRESS: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0dev->addr =3D value; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceRequest | USB_REQ_GET_DESCRIPTOR: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0switch(value >> 8) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_DT_DEVICE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy(data, qemu_ccid_dev_des= criptor, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sizeof(q= emu_ccid_dev_descriptor)); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D sizeof(qemu_ccid_dev_d= escriptor); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_DT_CONFIG: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0memcpy(data, qemu_ccid_config_= descriptor, > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 sizeof(q= emu_ccid_config_descriptor)); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D sizeof(qemu_ccid_confi= g_descriptor); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0case USB_DT_STRING: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0switch(value & 0xff) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 0: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* language ids = */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0data[0] =3D 4; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0data[1] =3D 3; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0data[2] =3D 0x09= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0data[3] =3D 0x04= ; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 4; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 1: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* vendor descri= ption */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D set_usb_= string(data, CCID_VENDOR_DESCRIPTION); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 2: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* product descr= iption */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D set_usb_= string(data, CCID_PRODUCT_DESCRIPTION); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 3: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* serial number= */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D set_usb_= string(data, CCID_SERIAL_NUMBER_STRING); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0case 4: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0/* interface nam= e */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D set_usb_= string(data, CCID_INTERFACE_NAME); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0goto fail; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0} > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceRequest | USB_REQ_GET_CONFIGURATION: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0data[0] =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceOutRequest | USB_REQ_SET_CONFIGURATION: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Only one configuration - we just ignore t= he request */ > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case DeviceRequest | USB_REQ_GET_INTERFACE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0data[0] =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 1; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case InterfaceOutRequest | USB_REQ_SET_INTERFACE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D 0; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + > + =C2=A0 =C2=A0 =C2=A0 =C2=A0/* Class specific requests. =C2=A0*/ > + =C2=A0 =C2=A0case InterfaceOutClass | CCID_CONTROL_ABORT: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF(s, 1, "ccid_control abort UNIMPLEMEN= TED\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_STALL; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case InterfaceInClass | CCID_CONTROL_GET_CLOCK_FREQUENCIES= : > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF(s, 1, "ccid_control get clock freque= ncies UNIMPLEMENTED\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_STALL; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0case InterfaceInClass | CCID_CONTROL_GET_DATA_RATES: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF(s, 1, "ccid_control get data rates U= NIMPLEMENTED\n"); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_STALL; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0default: > + =C2=A0 =C2=A0fail: > + =C2=A0 =C2=A0 =C2=A0 =C2=A0DPRINTF(s, 1, "got unsupported/bogus control= %x, value %x\n", request, value); > + =C2=A0 =C2=A0 =C2=A0 =C2=A0ret =3D USB_RET_STALL; > + =C2=A0 =C2=A0 =C2=A0 =C2=A0break; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0return ret; > +} > + > +static bool ccid_card_inserted(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0return s->bmSlotICCState & SLOT_0_STATE_MASK; > +} > + > +static uint8_t ccid_card_status(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0return ccid_card_inserted(s) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0? (s->powered ? > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0ICC_STATUS_PRESE= NT_ACTIVE > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0: ICC_STATUS_PRESENT_IN= ACTIVE > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0) > + =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0: ICC_STATUS_NOT_PRESENT; > +} > + > +static uint8_t ccid_calc_status(USBCCIDState *s) > +{ > + =C2=A0 =C2=A0/* page 55, 6.2.6, calculation of bStatus from bmICCStatus= and > + =C2=A0 =C2=A0 =C2=A0 bmCommandStatus > + =C2=A0 =C2=A0 */ > + =C2=A0 =C2=A0uint8_t ret =3D ccid_card_status(s) | (s->bmCommandStatus = << 6); > + =C2=A0 =C2=A0DPRINTF(s, 4, "status =3D %d\n", ret); > + =C2=A0 =C2=A0return ret; > +} > + > +static void ccid_reset_error_status(USBCCIDState* s) > +{ > + =C2=A0 =C2=A0s->bError =3D ERROR_CMD_NOT_SUPPORTED; > + =C2=A0 =C2=A0s->bmCommandStatus =3D COMMAND_STATUS_NO_ERROR; > +} > + > +static void ccid_write_slot_status(USBCCIDState* s, CCID_Header* recv) > +{ > + =C2=A0 =C2=A0CCID_SlotStatus *h =3D ccid_reserve_recv_buf(s, sizeof(CCI= D_SlotStatus)); > + =C2=A0 =C2=A0if (h =3D=3D NULL) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0h->b.hdr.bMessageType =3D CCID_MESSAGE_TYPE_RDR_to_PC_Slot= Status; > + =C2=A0 =C2=A0h->b.hdr.dwLength =3D 0; > + =C2=A0 =C2=A0h->b.hdr.bSlot =3D recv->bSlot; > + =C2=A0 =C2=A0h->b.hdr.bSeq =3D recv->bSeq; > + =C2=A0 =C2=A0h->b.bStatus =3D ccid_calc_status(s); > + =C2=A0 =C2=A0h->b.bError =3D s->bError; > + =C2=A0 =C2=A0h->bClockStatus =3D CLOCK_STATUS_RUNNING; > + =C2=A0 =C2=A0ccid_reset_error_status(s); > +} > + > +static void ccid_write_parameters(USBCCIDState* s, CCID_Header* recv) > +{ > + =C2=A0 =C2=A0CCID_Parameter *h; > + =C2=A0 =C2=A0uint32_t len =3D s->ulProtocolDataStructureSize; > + > + =C2=A0 =C2=A0h =3D ccid_reserve_recv_buf(s, sizeof(CCID_Parameter) + le= n); > + =C2=A0 =C2=A0if (h =3D=3D NULL) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0h->b.hdr.bMessageType =3D CCID_MESSAGE_TYPE_RDR_to_PC_Para= meters; > + =C2=A0 =C2=A0h->b.hdr.dwLength =3D 0; > + =C2=A0 =C2=A0h->b.hdr.bSlot =3D recv->bSlot; > + =C2=A0 =C2=A0h->b.hdr.bSeq =3D recv->bSeq; > + =C2=A0 =C2=A0h->b.bStatus =3D ccid_calc_status(s); > + =C2=A0 =C2=A0h->b.bError =3D s->bError; > + =C2=A0 =C2=A0h->bProtocolNum =3D s->bProtocolNum; > + =C2=A0 =C2=A0memcpy(h->abProtocolDataStructure, s->abProtocolDataStruct= ure, len); > + =C2=A0 =C2=A0ccid_reset_error_status(s); > +} > + > +static void ccid_write_data_block( > + =C2=A0 =C2=A0USBCCIDState* s, uint8_t slot, uint8_t seq, > + =C2=A0 =C2=A0const uint8_t* data, uint32_t len) > +{ > + =C2=A0 =C2=A0CCID_DataBlock *p =3D ccid_reserve_recv_buf(s, sizeof(*p) = + len); > + > + =C2=A0 =C2=A0if (p =3D=3D NULL) { > + =C2=A0 =C2=A0 =C2=A0 =C2=A0return; > + =C2=A0 =C2=A0} > + =C2=A0 =C2=A0p->b.hdr.bMessageType =3D CCID_MESSAGE_TYPE_RDR_to_PC_Data= Block; > + =C2=A0 =C2=A0p->b.hdr.dwLength =3D len; This assumes that host endianess happens to match what USB uses.