The spice bits looks good to me, the pcsc ccid module builds fine, and looks ok except the read loop (see below) and I haven't tried it. I would have added a couple more pre-conditions, for example if the client sends VSC_ReaderAdd several times without VCS_ReaderRemove I guess it crashes or leaks, perhaps this could be handled a bit better? On Thu, Dec 11, 2014 at 10:04 PM, Jeremy White <jwhite@xxxxxxxxxxxxxxx> wrote: > This is done by creating a Unix domain socket to which smartcard > messages are transferred, using the vscard protocol. > > A further system library, spiceccid, is used to provide an interface into > pcsc-lite, specifically the pcsc-lite daemon, so that regular Unix applications > can access the passed through smartcard information. > > Signed-off-by: Jeremy White <jwhite@xxxxxxxxxxxxxxx> > --- > configure.ac | 21 ++ > examples/spiceqxl.xorg.conf.example | 3 + > src/Makefile.am | 3 +- > src/qxl.h | 2 + > src/qxl_driver.c | 14 +- > src/spiceccid/Makefile.am | 29 ++ > src/spiceccid/spice.pcsc.conf.template | 7 + > src/spiceccid/spiceccid.c | 455 ++++++++++++++++++++++++++++++++ > src/spiceqxl_smartcard.c | 193 ++++++++++++++ > src/spiceqxl_smartcard.h | 31 +++ > 10 files changed, 756 insertions(+), 2 deletions(-) > create mode 100644 src/spiceccid/Makefile.am > create mode 100644 src/spiceccid/spice.pcsc.conf.template > create mode 100644 src/spiceccid/spiceccid.c > create mode 100644 src/spiceqxl_smartcard.c > create mode 100644 src/spiceqxl_smartcard.h > > diff --git a/configure.ac b/configure.ac > index 14e0597..d9da852 100644 > --- a/configure.ac > +++ b/configure.ac > @@ -137,8 +137,27 @@ if test "x$enable_xspice" = "xyes"; then > else > enable_xspice=no > fi > + > +AC_ARG_ENABLE([ccid], > + [AS_HELP_STRING([--enable-ccid], > + [Build the spiceccid SmartCard driver (default is no)])], > + [enable_ccid=$enableval], > + [enable_ccid=no]) > +AC_ARG_WITH(ccid-module-dir, > + [AS_HELP_STRING([--with-ccid-module-dir=DIR ], > + [Specify the install path for spiceccid driver (default is $libdir/pcsc/drivers/serial)])], > + [ cciddir="$withval" ], > + [ cciddir="$libdir/pcsc/drivers/serial" ]) > +AC_SUBST(cciddir) > +if test "x$enable_ccid" != "xno"; then > + PKG_CHECK_MODULES(LIBPCSCLITE, [libpcsclite]) > + PKG_CHECK_MODULES(LIBCACARD, [libcacard]) > +fi > + > + > AM_CONDITIONAL(BUILD_XSPICE, test "x$enable_xspice" = "xyes") > AM_CONDITIONAL(BUILD_QXL, test "x$enable_qxl" = "xyes") > +AM_CONDITIONAL(BUILD_SPICECCID, test "x$enable_ccid" = "xyes") > > AC_ARG_ENABLE([udev], > AS_HELP_STRING([--disable-udev], [Disable libudev support [default=auto]]), > @@ -168,6 +187,7 @@ fi > AC_CONFIG_FILES([ > Makefile > src/Makefile > + src/spiceccid/Makefile > src/uxa/Makefile > scripts/Makefile > examples/Makefile > @@ -187,4 +207,5 @@ echo " > KMS: ${DRM_MODE} > Build qxl: ${enable_qxl} > Build xspice: ${enable_xspice} > + Build spiceccid: ${enable_ccid} > " > diff --git a/examples/spiceqxl.xorg.conf.example b/examples/spiceqxl.xorg.conf.example > index 597a5bd..d15f7f2 100644 > --- a/examples/spiceqxl.xorg.conf.example > +++ b/examples/spiceqxl.xorg.conf.example > @@ -143,6 +143,9 @@ Section "Device" > # to the client. Default is no mixing. > #Option "SpicePlaybackFIFODir" "/tmp/" > > + # A unix domain name for a unix domain socket > + # to communicate with a spiceccid smartcard driver > + #Option "SpiceSmartCardFile" "/tmp/spice.pcsc.comm" > EndSection > > Section "InputDevice" > diff --git a/src/Makefile.am b/src/Makefile.am > index bf50ae1..6c72bbd 100644 > --- a/src/Makefile.am > +++ b/src/Makefile.am > @@ -25,7 +25,7 @@ > # _ladir passes a dummy rpath to libtool so the thing will actually link > # TODO: -nostdlib/-Bstatic/-lgcc platform magic, not installing the .a, etc. > > -SUBDIRS=uxa > +SUBDIRS=uxa spiceccid > > AM_CFLAGS = $(SPICE_PROTOCOL_CFLAGS) $(XORG_CFLAGS) $(PCIACCESS_CFLAGS) $(CWARNFLAGS) $(DRM_CFLAGS) @LIBUDEV_CFLAGS@ > > @@ -96,6 +96,7 @@ spiceqxl_drv_la_SOURCES = \ > spiceqxl_uinput.c \ > spiceqxl_uinput.h \ > spiceqxl_audio.c \ > + spiceqxl_smartcard.c \ > spiceqxl_audio.h \ > spiceqxl_inputs.c \ > spiceqxl_inputs.h \ > diff --git a/src/qxl.h b/src/qxl.h > index 603faca..54995cf 100644 > --- a/src/qxl.h > +++ b/src/qxl.h > @@ -157,6 +157,7 @@ enum { > OPTION_FRAME_BUFFER_SIZE, > OPTION_SURFACE_BUFFER_SIZE, > OPTION_COMMAND_BUFFER_SIZE, > + OPTION_SPICE_SMARTCARD_FILE, > #endif > OPTION_COUNT, > }; > @@ -352,6 +353,7 @@ struct _qxl_screen_t > > char playback_fifo_dir[PATH_MAX]; > void *playback_opaque; > + char smartcard_file[PATH_MAX]; > #endif /* XSPICE */ > > uint32_t deferred_fps; > diff --git a/src/qxl_driver.c b/src/qxl_driver.c > index 165f468..9ad8921 100644 > --- a/src/qxl_driver.c > +++ b/src/qxl_driver.c > @@ -55,6 +55,7 @@ > #include "spiceqxl_io_port.h" > #include "spiceqxl_spice_server.h" > #include "spiceqxl_audio.h" > +#include "spiceqxl_smartcard.h" > #include "spiceqxl_vdagent.h" > #endif /* XSPICE */ > > @@ -152,8 +153,10 @@ const OptionInfoRec DefaultOptions[] = > "SurfaceBufferSize", OPTV_INTEGER, {DEFAULT_SURFACE_BUFFER_SIZE}, FALSE}, > { OPTION_COMMAND_BUFFER_SIZE, > "CommandBufferSize", OPTV_INTEGER, {DEFAULT_COMMAND_BUFFER_SIZE}, FALSE}, > + { OPTION_SPICE_SMARTCARD_FILE, > + "SpiceSmartcardFile", OPTV_STRING, {0}, FALSE}, > #endif > - > + > { -1, NULL, OPTV_NONE, {0}, FALSE } > }; > > @@ -659,6 +662,7 @@ spiceqxl_screen_init (ScrnInfoPtr pScrn, qxl_screen_t *qxl) > } > qxl_add_spice_display_interface (qxl); > qxl_add_spice_playback_interface (qxl); > + qxl_add_spice_smartcard_interface (qxl); > spiceqxl_vdagent_init (qxl); > } > else > @@ -1034,6 +1038,7 @@ qxl_pre_init (ScrnInfoPtr pScrn, int flags) > unsigned int max_x, max_y; > #ifdef XSPICE > const char *playback_fifo_dir; > + const char *smartcard_file; > #endif > > /* In X server 1.7.5, Xorg -configure will cause this > @@ -1089,6 +1094,13 @@ qxl_pre_init (ScrnInfoPtr pScrn, int flags) > else > qxl->playback_fifo_dir[0] = '\0'; > > + smartcard_file = get_str_option(qxl->options, OPTION_SPICE_SMARTCARD_FILE, > + "XSPICE_SMARTCARD_FILE"); > + if (smartcard_file) > + strncpy(qxl->smartcard_file, smartcard_file, sizeof(qxl->smartcard_file)); > + else > + qxl->smartcard_file[0] = '\0'; > + > qxl->surface0_size = > get_int_option (qxl->options, OPTION_FRAME_BUFFER_SIZE, "QXL_FRAME_BUFFER_SIZE") << 20L; > qxl->vram_size = > diff --git a/src/spiceccid/Makefile.am b/src/spiceccid/Makefile.am > new file mode 100644 > index 0000000..437e992 > --- /dev/null > +++ b/src/spiceccid/Makefile.am > @@ -0,0 +1,29 @@ > +# Copyright 2014 Jeremy White for CodeWeavers, Inc. > +# > +# Permission is hereby granted, free of charge, to any person obtaining a > +# copy of this software and associated documentation files (the "Software"), > +# to deal in the Software without restriction, including without limitation > +# on the rights to use, copy, modify, merge, publish, distribute, sub > +# license, and/or sell copies of the Software, and to permit persons to whom > +# the Software is furnished to do so, subject to the following conditions: > +# > +# The above copyright notice and this permission notice (including the next > +# paragraph) shall be included in all copies or substantial portions of the > +# Software. > +# > +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > +# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL > +# THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER > +# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN > +# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + > + > +AM_CFLAGS = $(LIBPCSCLITE_CFLAGS) > + > +if BUILD_SPICECCID > +libspiceccid_la_LTLIBRARIES = libspiceccid.la > +libspiceccid_la_LDFLAGS = $(LIBPCSCLITE_LDFLAGS) > +libspiceccid_la_SOURCES = spiceccid.c > +libspiceccid_ladir = @cciddir@/ > +endif > diff --git a/src/spiceccid/spice.pcsc.conf.template b/src/spiceccid/spice.pcsc.conf.template > new file mode 100644 > index 0000000..345cdf5 > --- /dev/null > +++ b/src/spiceccid/spice.pcsc.conf.template > @@ -0,0 +1,7 @@ > +# Spice CCID Reader > +# This configuration file is the format required by the pcscd deamon for > +# serial devices; so the qxl driver looks like a serial device to pcscd. > +FRIENDLYNAME "Spice ccid" > +DEVICENAME /tmp/spice.pcsc.comm > +LIBPATH /usr/lib/pcsc/drivers/serial/libspiceccid.so > +CHANNELID 1 > diff --git a/src/spiceccid/spiceccid.c b/src/spiceccid/spiceccid.c > new file mode 100644 > index 0000000..ef9c931 > --- /dev/null > +++ b/src/spiceccid/spiceccid.c > @@ -0,0 +1,455 @@ > +/* > + * Copyright (C) 2014 CodeWeavers, Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * the rights to use, copy, modify, merge, publish, distribute, sublicense, > + * and/or sell copies of the Software, and to permit persons to whom the > + * Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE > + * SOFTWARE. > + * > + * Authors: > + * Jeremy White <jwhite@xxxxxxxxxxxxxxx> > + */ > + > +/*---------------------------------------------------------------------------- > + Chip/Smart Card Interface Devices driver for Spice > + > + This driver is built to interface to pcsc-lite as a serial smartcard > + device. > + It translates the IFD (Interface device) ABI into the Spice protocol. > +----------------------------------------------------------------------------*/ > + > +#include <stdio.h> > +#include <string.h> > +#include <stdlib.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <pthread.h> > +#include <errno.h> > +#include <sys/socket.h> > +#include <sys/un.h> > + > +#include "cacard/vscard_common.h" > +#include "ifdhandler.h" > +#include <arpa/inet.h> > + > +typedef struct apdu_list { > + void *data; > + int len; > + struct apdu_list *next; > +} apdu_t; > + > +#define MAX_LUNS 2 > +typedef struct smartcard_ccid { > + int fd; > + int lun; > + pthread_t tid; > + int state; > + char atr[36]; > + int atr_len; > + pthread_mutex_t apdu_lock; > + apdu_t *apdu_list; > +} smartcard_ccid_t; > + > +#define STATE_OPEN 1 > +#define STATE_READER_ADDED 2 > +#define STATE_READER_REMOVED 4 > + > +#if ! defined(MIN) > +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) > +#endif > + > + > +smartcard_ccid_t luns[MAX_LUNS] = { { -1 }, { -1 } }; > + > +RESPONSECODE IFDHCloseChannel(DWORD Lun); > + > +static void push_apdu(smartcard_ccid_t *ccid, void *data, int len) > +{ > + apdu_t *a = malloc(sizeof(*a)); > + apdu_t **p; > + > + a->data = malloc(len); > + a->len = len; > + a->next = NULL; > + memcpy(a->data, data, len); > + > + pthread_mutex_lock(&ccid->apdu_lock); > + for (p = &ccid->apdu_list; *p; p = &(*p)->next) > + ; > + *p = a; > + > + pthread_mutex_unlock(&ccid->apdu_lock); > +} > + > +static apdu_t * pop_apdu(smartcard_ccid_t *ccid) > +{ > + apdu_t *p; > + pthread_mutex_lock(&ccid->apdu_lock); > + p = ccid->apdu_list; > + if (ccid->apdu_list) > + ccid->apdu_list = p->next; > + pthread_mutex_unlock(&ccid->apdu_lock); > + return p; > +} > + > +static void free_apdu(apdu_t *a) > +{ > + free(a->data); > + free(a); > +} > + > +static void send_reply(smartcard_ccid_t *ccid, uint32_t code) > +{ > + uint32_t reply[4]; > + > + reply[0] = htonl(VSC_Error); // type > + reply[1] = htonl(ccid->lun); // reader id > + reply[2] = htonl(sizeof(uint32_t)); // length > + reply[3] = htonl(code); // Error code > + > + if (write(ccid->fd, (char *) reply, sizeof(reply)) != sizeof(reply)) { > + fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); > + IFDHCloseChannel(ccid->lun); > + } > +} > + > +static int send_tx_buffer(smartcard_ccid_t *ccid, void *data, int len) > +{ > + uint32_t *reply, *p; > + int write_len = sizeof(*reply) * 3 + len; > + > + reply = malloc(write_len); > + p = reply; > + > + *p++ = htonl(VSC_APDU); // type > + *p++ = htonl(ccid->lun); // reader id > + *p++ = htonl(len); > + memcpy(p, data, len); > + > + if (write(ccid->fd, (char *) reply, write_len) != write_len) { > + fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); > + IFDHCloseChannel(ccid->lun); > + free(reply); > + return 0; > + } > + free(reply); > + return 1; > +} > + > +static void process_reader_add(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) > +{ > + ccid->state |= STATE_READER_ADDED; > + > + pthread_mutex_init(&ccid->apdu_lock, NULL); > + ccid->apdu_list = NULL; > + > + send_reply(ccid, VSC_SUCCESS); > +} > + > +static void process_reader_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h) > +{ > + apdu_t *p; > + > + ccid->state |= STATE_READER_REMOVED; > + ccid->state &= ~STATE_READER_ADDED; > + > + while (p = pop_apdu(ccid)) > + free_apdu(p); > + > + pthread_mutex_destroy(&ccid->apdu_lock); > + > + send_reply(ccid, VSC_SUCCESS); > +} > + > +static void process_atr(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) > +{ > + ccid->atr_len = MIN(h->length, sizeof(ccid->atr)); > + ATR length truncation could use a warning, > + memset(ccid->atr, 0, sizeof(ccid->atr)); > + memcpy(ccid->atr, data, ccid->atr_len); > + > + send_reply(ccid, VSC_SUCCESS); > +} > + > +static void process_apdu(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data) > +{ > + push_apdu(ccid, data, h->length); > +} > + > +static void process_card_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h) > +{ > + ccid->atr_len = 0; > + memset(ccid->atr, 0, sizeof(ccid->atr)); > + send_reply(ccid, VSC_SUCCESS); > +} > + > +static int process_message(smartcard_ccid_t *ccid, char *buf, int len) > +{ > + VSCMsgHeader h; > + uint32_t *p = (uint32_t *) buf; > + > + h.type = ntohl(*p++); > + h.reader_id = ntohl(*p++); > + h.length = ntohl(*p++); > + > + if (len < sizeof(h) || len < sizeof(h) + h.length) > + return 0; > + > + switch (h.type) { > + case VSC_ReaderAdd: > + process_reader_add(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); > + break; > + > + case VSC_ReaderRemove: > + process_reader_remove(ccid, &h); > + break; > + > + case VSC_ATR: > + process_atr(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); > + break; > + > + case VSC_CardRemove: > + process_card_remove(ccid, &h); > + break; > + > + case VSC_APDU: > + process_apdu(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL); > + break; > + > + default: > + fprintf(stderr, "spiceccid %s: unknown smartcard message %d / %d\n", __FUNCTION__, h.type, sizeof(h) + h.length); > + > + } > + > + return(MIN(len, h.length + sizeof(h))); > +} > + > +static void * lun_thread(void *arg) > +{ > + char buf[8096]; > + int pos = 0; > + smartcard_ccid_t *ccid = (smartcard_ccid_t *) arg; > + int rc; > + > + while (1) { > + rc = read(ccid->fd, buf + pos, sizeof(buf) - pos); > + if (rc == -1) > + if (errno == EINTR) > + continue; > + else > + break; > + > + if (rc == 0) > + break; > + > + pos += rc; > + > + do { > + rc = process_message(ccid, buf, pos); > + pos -= rc; So you assume that messages are not sent "in batch" and you won't override previous message? I guess you could assert() that pos == rc. And then this loop is useless right? Or you need to increment buf? hmm, I think you should rather memcpy the remaining bits if any > + } while (rc > 0); I guess it could warn if pos == sizeof(buf) here > + } > + > + return NULL; > +} > + > + > +static void send_init(smartcard_ccid_t *ccid) > +{ > + uint32_t msg[6]; > + > + msg[0] = htonl(VSC_Init); // type > + msg[1] = htonl(ccid->lun); // reader id > + msg[2] = htonl(sizeof(uint32_t) * 3); // length > + msg[3] = htonl(VSCARD_MAGIC); // VSCD > + msg[4] = htonl(VSCARD_VERSION); // VSCD > + msg[5] = 0; // capabilities > + > + if (write(ccid->fd, (char *) msg, sizeof(msg)) != sizeof(msg)) { > + fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno); > + IFDHCloseChannel(ccid->lun); > + } > +} > + > +/*---------------------------------------------------------------------------- > + IFDHCreateChannelByName > + The pcsc daemon should invoke this function passing in the path name > + configured in reader.conf. > +*/ > +RESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName) > +{ > + int i; > + struct sockaddr_un addr; > + > + for (i = 0; i < MAX_LUNS; i++) > + if (luns[i].fd != -1 && luns[i].lun == Lun) > + return IFD_COMMUNICATION_ERROR; > + > + for (i = 0; i < MAX_LUNS; i++) > + if (luns[i].fd == -1) > + break; > + > + if (i >= MAX_LUNS) > + return IFD_COMMUNICATION_ERROR; > + > + luns[i].fd = socket(AF_UNIX, SOCK_STREAM, 0); > + if (luns[i].fd < 0) > + return IFD_NO_SUCH_DEVICE; > + > + memset(&addr, 0, sizeof(addr)); > + addr.sun_family = AF_UNIX; > + strncpy(addr.sun_path, DeviceName, sizeof(addr.sun_path) - 1); > + if (connect(luns[i].fd, (struct sockaddr *) &addr, sizeof(addr))) { > + close(luns[i].fd); > + return IFD_COMMUNICATION_ERROR; > + } > + > + if (pthread_create(&luns[i].tid, NULL, &lun_thread, &luns[i])) { > + close(luns[i].fd); > + return IFD_COMMUNICATION_ERROR; > + } > + > + luns[i].lun = Lun; > + luns[i].state = STATE_OPEN; > + > + return IFD_SUCCESS; > +} > + > +RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel) > +{ > + fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Channel %ld\n", __FUNCTION__, Lun, Channel); > + return IFD_ERROR_NOT_SUPPORTED; > +} > + > +RESPONSECODE IFDHCloseChannel(DWORD Lun) > +{ > + int i; > + > + for (i = 0; i < MAX_LUNS; i++) { > + if (luns[i].fd != -1 && luns[i].lun == Lun) { > + pthread_cancel(luns[i].tid); > + close(luns[i].fd); > + luns[i].fd = -1; > + luns[i].lun = 0; > + luns[i].atr_len = 0; > + break; > + } > + } > + > + if (i == MAX_LUNS) > + return IFD_NO_SUCH_DEVICE; > + > + return IFD_SUCCESS; > +} > + > +RESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) > +{ > + fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Tag %ld, Length %ld, Value %p\n", __FUNCTION__, Lun, Tag, *Length, Value); > + /* TODO - explore supporting TAG_IFD_POLLING_THREAD */ > + return IFD_ERROR_NOT_SUPPORTED; > +} > + > +RESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value) > +{ > + return IFD_ERROR_NOT_SUPPORTED; > +} > + > +RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) > +{ > + int i; > + > + for (i = 0; i < MAX_LUNS; i++) > + if (luns[i].fd != -1 && luns[i].lun == Lun) > + if (Action == IFD_POWER_UP) { > + if (*AtrLength >= luns[i].atr_len) { > + memcpy(Atr, luns[i].atr, luns[i].atr_len); > + *AtrLength = luns[i].atr_len; > + } > + send_init(&luns[i]); > + return IFD_SUCCESS; > + } > + > + return IFD_ERROR_NOT_SUPPORTED; > +} > + > +#define TX_MAX_SLEEP 5000 > +#define TX_SLEEP_INTERVAL 1000 > +RESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci, > + PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD > + RxLength, PSCARD_IO_HEADER RecvPci) > +{ > + apdu_t *p; > + int i, j; > + > + for (i = 0; i < MAX_LUNS; i++) > + if (luns[i].fd != -1 && luns[i].lun == Lun) { > + while (p = pop_apdu(&luns[i])) > + free_apdu(p); > + > + if (send_tx_buffer(&luns[i], TxBuffer, TxLength)) { > + for (j = 0; j < TX_MAX_SLEEP; j++) > + if (p = pop_apdu(&luns[i])) > + break; > + else > + usleep(TX_SLEEP_INTERVAL); > + > + if (p) { > + memcpy(RxBuffer, p->data, MIN(p->len, *RxLength)); > + *RxLength = MIN(p->len, *RxLength); > + free_apdu(p); > + return IFD_SUCCESS; > + } > + > + return IFD_RESPONSE_TIMEOUT; > + } > + } > + return IFD_NO_SUCH_DEVICE; > +} > + > +RESPONSECODE IFDHICCPresence(DWORD Lun) > +{ > + int i; > + > + for (i = 0; i < MAX_LUNS; i++) > + if (luns[i].fd != -1 && luns[i].lun == Lun) { > + if (luns[i].atr_len > 0 && luns[i].state & STATE_READER_ADDED) > + return IFD_SUCCESS; > + > + return IFD_ICC_NOT_PRESENT; > + } > + > + return IFD_NO_SUCH_DEVICE; > +} > + > +RESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags, > + UCHAR PTS1, UCHAR PTS2, UCHAR PTS3) > +{ > + if (Protocol == SCARD_PROTOCOL_T1) > + return IFD_SUCCESS; > + > + return IFD_NOT_SUPPORTED; > +} > + > +RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR > + TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength, > + LPDWORD pdwBytesReturned) > +{ > + fprintf(stderr, "spiceccid %s unsupported: Lun %ld\n", __FUNCTION__, Lun); > + return IFD_ERROR_NOT_SUPPORTED; > +} > diff --git a/src/spiceqxl_smartcard.c b/src/spiceqxl_smartcard.c > new file mode 100644 > index 0000000..f2ec2e2 > --- /dev/null > +++ b/src/spiceqxl_smartcard.c > @@ -0,0 +1,193 @@ > +/* > + * Copyright 2014 Jeremy White for CodeWeavers Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * on the rights to use, copy, modify, merge, publish, distribute, sub > + * license, and/or sell copies of the Software, and to permit persons to whom > + * the Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER > + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN > + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#ifdef HAVE_CONFIG_H > +#include "config.h" > +#endif > + > +#include <errno.h> > +#include <sys/types.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <unistd.h> > +#include <sys/socket.h> > +#include <sys/un.h> > + > + > +#include "spiceqxl_smartcard.h" > + > +typedef struct XSpiceSmartcardCharDeviceInstance { > + SpiceCharDeviceInstance base; > + qxl_screen_t *qxl; > + int listen_fd; > + int fd; > + SpiceWatch *listen_watch; > + SpiceWatch *watch; > +} XSpiceSmartcardCharDeviceInstance; > + > +XSpiceSmartcardCharDeviceInstance smartcard_sin = { > + .base = { > + .subtype = "smartcard" > + } > +}; > + > +static int smartcard_write(SpiceCharDeviceInstance *sin, const uint8_t *buf, int len) > +{ > + int written; > + > + if (smartcard_sin.fd == -1) > + return 0; > + > + written = write(smartcard_sin.fd, buf, len); > + if (written != len) > + ErrorF("%s: ERROR: short write to smartcard socket - TODO buffering\n", __FUNCTION__); > + > + return written; > +} > + > +static int smartcard_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) > +{ > + int rc; > + > + if (smartcard_sin.fd == -1) > + return 0; > + > + rc = read(smartcard_sin.fd, buf, len); > + if (rc <= 0) { > + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) { > + return 0; > + } > + ErrorF("smartcard socket died: %s\n", strerror(errno)); > + > + smartcard_sin.qxl->core->watch_remove(smartcard_sin.watch); > + close(smartcard_sin.fd); > + smartcard_sin.fd = -1; > + smartcard_sin.watch = NULL; > + } > + > + return rc; > +} > + > +static void on_read_available(int fd, int event, void *opaque) > +{ > + spice_server_char_device_wakeup(&smartcard_sin.base); > +} > + > +static void on_accept_available(int fd, int event, void *opaque) > +{ > + qxl_screen_t *qxl = (qxl_screen_t *) opaque; > + int flags; > + int client_fd; > + > + client_fd = accept(fd, NULL, NULL); > + if (client_fd < 0) > + return; > + > + if (smartcard_sin.fd != -1) { > + ErrorF("smartcard error: a new connection came in while an old one was active.\n"); > + close(client_fd); > + return; > + } > + > + flags = fcntl(client_fd, F_GETFL, 0); > + if (flags < 0) > + flags = 0; > + flags |= O_NONBLOCK; > + fcntl(client_fd, F_SETFL, flags); > + > + smartcard_sin.fd = client_fd; > + smartcard_sin.watch = qxl->core->watch_add(smartcard_sin.fd, SPICE_WATCH_EVENT_READ, on_read_available, qxl); > + > +} > + > + > +#if SPICE_SERVER_VERSION >= 0x000c02 > +static void smartcard_event(SpiceCharDeviceInstance *sin, uint8_t event) > +{ > + ErrorF("%s: unimplemented; event is %d\n", __FUNCTION__, event); > +} > +#endif > + > +static void smartcard_state(SpiceCharDeviceInstance *sin, int connected) > +{ > + ErrorF("%s: unimplemented; connected is %d\n", __FUNCTION__, connected); > +} > + > +static SpiceCharDeviceInterface smartcard_interface = { > + .base.type = SPICE_INTERFACE_CHAR_DEVICE, > + .base.description = "Xspice virtual channel char device", > + .base.major_version = SPICE_INTERFACE_CHAR_DEVICE_MAJOR, > + .base.minor_version = SPICE_INTERFACE_CHAR_DEVICE_MINOR, > + .state = smartcard_state, > + .write = smartcard_write, > + .read = smartcard_read, > +#if SPICE_SERVER_VERSION >= 0x000c02 > + .event = smartcard_event, > +#endif > +}; > + > +int > +qxl_add_spice_smartcard_interface (qxl_screen_t *qxl) > +{ > + int rc; > + struct sockaddr_un addr; > + > + if (qxl->smartcard_file[0] == 0) { > + xf86DrvMsg(qxl->pScrn->scrnIndex, X_INFO, "smartcard: no file given, smartcard is disabled\n"); > + return 0; > + } > + > + smartcard_sin.fd = -1; > + smartcard_sin.listen_fd = socket(AF_UNIX, SOCK_STREAM, 0); > + if (smartcard_sin.listen_fd < 0) { > + ErrorF("smartcard: unable to open socket: %s\n", strerror(errno)); > + return errno; > + } > + > + memset(&addr, 0, sizeof(addr)); > + addr.sun_family = AF_UNIX; > + strncpy(addr.sun_path, qxl->smartcard_file, sizeof(addr.sun_path) - 1); > + unlink(qxl->smartcard_file); > + > + if (bind(smartcard_sin.listen_fd, (struct sockaddr *) &addr, sizeof(addr))) { > + ErrorF("smartcard: unable to bind to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno)); > + close(smartcard_sin.listen_fd); > + return errno; > + } > + > + if (listen(smartcard_sin.listen_fd, 1)) { > + ErrorF("smartcard: unable to listen to unix domain %s: %s\n", qxl->smartcard_file, strerror(errno)); > + close(smartcard_sin.listen_fd); > + return errno; > + } > + > + smartcard_sin.listen_watch = qxl->core->watch_add(smartcard_sin.listen_fd, SPICE_WATCH_EVENT_READ, on_accept_available, qxl); > + > + smartcard_sin.base.base.sif = &smartcard_interface.base; > + smartcard_sin.qxl = qxl; > + > + rc = spice_server_add_interface(qxl->spice_server, &smartcard_sin.base.base); > + if (rc < 0) > + return errno; > + > + return 0; > +} > diff --git a/src/spiceqxl_smartcard.h b/src/spiceqxl_smartcard.h > new file mode 100644 > index 0000000..62df5a8 > --- /dev/null > +++ b/src/spiceqxl_smartcard.h > @@ -0,0 +1,31 @@ > +/* > + * Copyright 2014 Jeremy White for CodeWeavers Inc. > + * > + * Permission is hereby granted, free of charge, to any person obtaining a > + * copy of this software and associated documentation files (the "Software"), > + * to deal in the Software without restriction, including without limitation > + * on the rights to use, copy, modify, merge, publish, distribute, sub > + * license, and/or sell copies of the Software, and to permit persons to whom > + * the Software is furnished to do so, subject to the following conditions: > + * > + * The above copyright notice and this permission notice (including the next > + * paragraph) shall be included in all copies or substantial portions of the > + * Software. > + * > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL > + * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER > + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN > + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. > + */ > + > +#ifndef QXL_SPICE_SMARTCARD_H > +#define QXL_SPICE_SMARTCARD_H > + > +#include "qxl.h" > +#include <spice.h> > + > +int qxl_add_spice_smartcard_interface(qxl_screen_t *qxl); > + > +#endif > -- > 1.7.10.4 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel -- Marc-André Lureau _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel