spiceccid.c revision d514b0f3
1d514b0f3Smrg/*
2d514b0f3Smrg * Copyright (C) 2014 CodeWeavers, Inc.
3d514b0f3Smrg *
4d514b0f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5d514b0f3Smrg * copy of this software and associated documentation files (the "Software"),
6d514b0f3Smrg * to deal in the Software without restriction, including without limitation
7d514b0f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d514b0f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
9d514b0f3Smrg * Software is furnished to do so, subject to the following conditions:
10d514b0f3Smrg *
11d514b0f3Smrg * The above copyright notice and this permission notice (including the next
12d514b0f3Smrg * paragraph) shall be included in all copies or substantial portions of the
13d514b0f3Smrg * Software.
14d514b0f3Smrg *
15d514b0f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d514b0f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d514b0f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18d514b0f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19d514b0f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20d514b0f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21d514b0f3Smrg * SOFTWARE.
22d514b0f3Smrg *
23d514b0f3Smrg * Authors:
24d514b0f3Smrg *    Jeremy White <jwhite@codeweavers.com>
25d514b0f3Smrg */
26d514b0f3Smrg
27d514b0f3Smrg/*----------------------------------------------------------------------------
28d514b0f3Smrg  Chip/Smart Card Interface Devices driver for Spice
29d514b0f3Smrg
30d514b0f3Smrg    This driver is built to interface to pcsc-lite as a serial smartcard
31d514b0f3Smrg  device.
32d514b0f3Smrg    It translates the IFD (Interface device) ABI into the Spice protocol.
33d514b0f3Smrg----------------------------------------------------------------------------*/
34d514b0f3Smrg
35d514b0f3Smrg#include <stdio.h>
36d514b0f3Smrg#include <string.h>
37d514b0f3Smrg#include <stdlib.h>
38d514b0f3Smrg#include <sys/types.h>
39d514b0f3Smrg#include <sys/stat.h>
40d514b0f3Smrg#include <fcntl.h>
41d514b0f3Smrg#include <unistd.h>
42d514b0f3Smrg#include <pthread.h>
43d514b0f3Smrg#include <errno.h>
44d514b0f3Smrg#include <sys/socket.h>
45d514b0f3Smrg#include <sys/un.h>
46d514b0f3Smrg
47d514b0f3Smrg#include "vscard_common.h"
48d514b0f3Smrg#include "ifdhandler.h"
49d514b0f3Smrg#include <arpa/inet.h>
50d514b0f3Smrg
51d514b0f3Smrgtypedef struct apdu_list {
52d514b0f3Smrg    void *data;
53d514b0f3Smrg    int len;
54d514b0f3Smrg    struct apdu_list *next;
55d514b0f3Smrg} apdu_t;
56d514b0f3Smrg
57d514b0f3Smrg#define MAX_LUNS    2
58d514b0f3Smrgtypedef struct smartcard_ccid {
59d514b0f3Smrg    int fd;
60d514b0f3Smrg    int lun;
61d514b0f3Smrg    pthread_t tid;
62d514b0f3Smrg    int state;
63d514b0f3Smrg    char atr[36];
64d514b0f3Smrg    int  atr_len;
65d514b0f3Smrg    pthread_mutex_t apdu_lock;
66d514b0f3Smrg    apdu_t *apdu_list;
67d514b0f3Smrg} smartcard_ccid_t;
68d514b0f3Smrg
69d514b0f3Smrg#define STATE_OPEN                  1
70d514b0f3Smrg#define STATE_READER_ADDED          2
71d514b0f3Smrg#define STATE_READER_REMOVED        4
72d514b0f3Smrg
73d514b0f3Smrg#if ! defined(MIN)
74d514b0f3Smrg#define MIN(x, y) (((x) < (y)) ? (x) : (y))
75d514b0f3Smrg#endif
76d514b0f3Smrg
77d514b0f3Smrg
78d514b0f3Smrgsmartcard_ccid_t luns[MAX_LUNS] = { { -1 }, { -1 } };
79d514b0f3Smrg
80d514b0f3SmrgRESPONSECODE IFDHCloseChannel(DWORD Lun);
81d514b0f3Smrg
82d514b0f3Smrgstatic void push_apdu(smartcard_ccid_t *ccid, void *data, int len)
83d514b0f3Smrg{
84d514b0f3Smrg    apdu_t *a = malloc(sizeof(*a));
85d514b0f3Smrg    apdu_t **p;
86d514b0f3Smrg
87d514b0f3Smrg    a->data = malloc(len);
88d514b0f3Smrg    a->len = len;
89d514b0f3Smrg    a->next = NULL;
90d514b0f3Smrg    memcpy(a->data, data, len);
91d514b0f3Smrg
92d514b0f3Smrg    pthread_mutex_lock(&ccid->apdu_lock);
93d514b0f3Smrg    for (p = &ccid->apdu_list; *p; p = &(*p)->next)
94d514b0f3Smrg        ;
95d514b0f3Smrg    *p = a;
96d514b0f3Smrg
97d514b0f3Smrg    pthread_mutex_unlock(&ccid->apdu_lock);
98d514b0f3Smrg}
99d514b0f3Smrg
100d514b0f3Smrgstatic apdu_t * pop_apdu(smartcard_ccid_t *ccid)
101d514b0f3Smrg{
102d514b0f3Smrg    apdu_t *p;
103d514b0f3Smrg    pthread_mutex_lock(&ccid->apdu_lock);
104d514b0f3Smrg    p = ccid->apdu_list;
105d514b0f3Smrg    if (ccid->apdu_list)
106d514b0f3Smrg        ccid->apdu_list = p->next;
107d514b0f3Smrg    pthread_mutex_unlock(&ccid->apdu_lock);
108d514b0f3Smrg    return p;
109d514b0f3Smrg}
110d514b0f3Smrg
111d514b0f3Smrgstatic void free_apdu(apdu_t *a)
112d514b0f3Smrg{
113d514b0f3Smrg    free(a->data);
114d514b0f3Smrg    free(a);
115d514b0f3Smrg}
116d514b0f3Smrg
117d514b0f3Smrgstatic void send_reply(smartcard_ccid_t *ccid, uint32_t code)
118d514b0f3Smrg{
119d514b0f3Smrg    uint32_t reply[4];
120d514b0f3Smrg
121d514b0f3Smrg    reply[0] = htonl(VSC_Error);        // type
122d514b0f3Smrg    reply[1] = htonl(ccid->lun);        // reader id
123d514b0f3Smrg    reply[2] = htonl(sizeof(uint32_t)); // length
124d514b0f3Smrg    reply[3] = htonl(code);             // Error code
125d514b0f3Smrg
126d514b0f3Smrg    if (write(ccid->fd, (char *) reply, sizeof(reply)) != sizeof(reply)) {
127d514b0f3Smrg        fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno);
128d514b0f3Smrg        IFDHCloseChannel(ccid->lun);
129d514b0f3Smrg    }
130d514b0f3Smrg}
131d514b0f3Smrg
132d514b0f3Smrgstatic int send_tx_buffer(smartcard_ccid_t *ccid, void *data, int len)
133d514b0f3Smrg{
134d514b0f3Smrg    uint32_t *reply, *p;
135d514b0f3Smrg    int write_len = sizeof(*reply) * 3 + len;
136d514b0f3Smrg
137d514b0f3Smrg    reply = malloc(write_len);
138d514b0f3Smrg    p = reply;
139d514b0f3Smrg
140d514b0f3Smrg    *p++ = htonl(VSC_APDU);         // type
141d514b0f3Smrg    *p++ = htonl(ccid->lun);        // reader id
142d514b0f3Smrg    *p++ = htonl(len);
143d514b0f3Smrg    memcpy(p, data, len);
144d514b0f3Smrg
145d514b0f3Smrg    if (write(ccid->fd, (char *) reply, write_len) != write_len) {
146d514b0f3Smrg        fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno);
147d514b0f3Smrg        IFDHCloseChannel(ccid->lun);
148d514b0f3Smrg        free(reply);
149d514b0f3Smrg        return 0;
150d514b0f3Smrg    }
151d514b0f3Smrg    free(reply);
152d514b0f3Smrg    return 1;
153d514b0f3Smrg}
154d514b0f3Smrg
155d514b0f3Smrgstatic void process_reader_add(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data)
156d514b0f3Smrg{
157d514b0f3Smrg    if (ccid->state & STATE_READER_ADDED) {
158d514b0f3Smrg        send_reply(ccid, VSC_GENERAL_ERROR);
159d514b0f3Smrg        return;
160d514b0f3Smrg    }
161d514b0f3Smrg
162d514b0f3Smrg    ccid->state |= STATE_READER_ADDED;
163d514b0f3Smrg    ccid->state &= ~STATE_READER_REMOVED;
164d514b0f3Smrg
165d514b0f3Smrg    pthread_mutex_init(&ccid->apdu_lock, NULL);
166d514b0f3Smrg    ccid->apdu_list = NULL;
167d514b0f3Smrg
168d514b0f3Smrg    send_reply(ccid, VSC_SUCCESS);
169d514b0f3Smrg}
170d514b0f3Smrg
171d514b0f3Smrgstatic void process_reader_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h)
172d514b0f3Smrg{
173d514b0f3Smrg    apdu_t *p;
174d514b0f3Smrg
175d514b0f3Smrg    if (ccid->state & STATE_READER_REMOVED) {
176d514b0f3Smrg        send_reply(ccid, VSC_GENERAL_ERROR);
177d514b0f3Smrg        return;
178d514b0f3Smrg    }
179d514b0f3Smrg
180d514b0f3Smrg    ccid->state |= STATE_READER_REMOVED;
181d514b0f3Smrg    ccid->state &= ~STATE_READER_ADDED;
182d514b0f3Smrg
183d514b0f3Smrg    while (p = pop_apdu(ccid))
184d514b0f3Smrg        free_apdu(p);
185d514b0f3Smrg
186d514b0f3Smrg    pthread_mutex_destroy(&ccid->apdu_lock);
187d514b0f3Smrg
188d514b0f3Smrg    send_reply(ccid, VSC_SUCCESS);
189d514b0f3Smrg}
190d514b0f3Smrg
191d514b0f3Smrgstatic void process_atr(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data)
192d514b0f3Smrg{
193d514b0f3Smrg    ccid->atr_len = h->length;
194d514b0f3Smrg    if (h->length > sizeof(ccid->atr)) {
195d514b0f3Smrg        fprintf(stderr, "Supplied ATR of length %d exceeds %d maximum\n",
196d514b0f3Smrg            h->length, sizeof(ccid->atr));
197d514b0f3Smrg        send_reply(ccid, VSC_GENERAL_ERROR);
198d514b0f3Smrg        return;
199d514b0f3Smrg    }
200d514b0f3Smrg
201d514b0f3Smrg    memset(ccid->atr, 0, sizeof(ccid->atr));
202d514b0f3Smrg    memcpy(ccid->atr, data, ccid->atr_len);
203d514b0f3Smrg
204d514b0f3Smrg    send_reply(ccid, VSC_SUCCESS);
205d514b0f3Smrg}
206d514b0f3Smrg
207d514b0f3Smrgstatic void process_apdu(smartcard_ccid_t *ccid, VSCMsgHeader *h, char *data)
208d514b0f3Smrg{
209d514b0f3Smrg    if (ccid->state & STATE_READER_ADDED)
210d514b0f3Smrg        push_apdu(ccid, data, h->length);
211d514b0f3Smrg    else
212d514b0f3Smrg        fprintf(stderr, "apdu of length %d discarded; inactive reader\n", h->length);
213d514b0f3Smrg}
214d514b0f3Smrg
215d514b0f3Smrgstatic void process_card_remove(smartcard_ccid_t *ccid, VSCMsgHeader *h)
216d514b0f3Smrg{
217d514b0f3Smrg    ccid->atr_len = 0;
218d514b0f3Smrg    memset(ccid->atr, 0, sizeof(ccid->atr));
219d514b0f3Smrg    send_reply(ccid, VSC_SUCCESS);
220d514b0f3Smrg}
221d514b0f3Smrg
222d514b0f3Smrgstatic int process_message(smartcard_ccid_t *ccid, char *buf, int len)
223d514b0f3Smrg{
224d514b0f3Smrg    VSCMsgHeader h;
225d514b0f3Smrg    uint32_t *p = (uint32_t *) buf;
226d514b0f3Smrg
227d514b0f3Smrg    h.type = ntohl(*p++);
228d514b0f3Smrg    h.reader_id = ntohl(*p++);
229d514b0f3Smrg    h.length = ntohl(*p++);
230d514b0f3Smrg
231d514b0f3Smrg    if (len < sizeof(h) || len < sizeof(h) + h.length)
232d514b0f3Smrg        return 0;
233d514b0f3Smrg
234d514b0f3Smrg    switch (h.type) {
235d514b0f3Smrg        case VSC_ReaderAdd:
236d514b0f3Smrg            process_reader_add(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL);
237d514b0f3Smrg            break;
238d514b0f3Smrg
239d514b0f3Smrg        case VSC_ReaderRemove:
240d514b0f3Smrg            process_reader_remove(ccid, &h);
241d514b0f3Smrg            break;
242d514b0f3Smrg
243d514b0f3Smrg        case VSC_ATR:
244d514b0f3Smrg            process_atr(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL);
245d514b0f3Smrg            break;
246d514b0f3Smrg
247d514b0f3Smrg        case VSC_CardRemove:
248d514b0f3Smrg            process_card_remove(ccid, &h);
249d514b0f3Smrg            break;
250d514b0f3Smrg
251d514b0f3Smrg        case VSC_APDU:
252d514b0f3Smrg            process_apdu(ccid, &h, h.length > 0 ? buf + sizeof(h) : NULL);
253d514b0f3Smrg            break;
254d514b0f3Smrg
255d514b0f3Smrg        default:
256d514b0f3Smrg            fprintf(stderr, "spiceccid %s: unknown smartcard message %d / %d\n", __FUNCTION__, h.type, sizeof(h) + h.length);
257d514b0f3Smrg
258d514b0f3Smrg    }
259d514b0f3Smrg
260d514b0f3Smrg    return(h.length + sizeof(h));
261d514b0f3Smrg}
262d514b0f3Smrg
263d514b0f3Smrgstatic void * lun_thread(void *arg)
264d514b0f3Smrg{
265d514b0f3Smrg    char buf[8096];
266d514b0f3Smrg    int pos = 0;
267d514b0f3Smrg    smartcard_ccid_t *ccid = (smartcard_ccid_t *) arg;
268d514b0f3Smrg    int rc;
269d514b0f3Smrg
270d514b0f3Smrg    while (1) {
271d514b0f3Smrg        rc = read(ccid->fd, buf + pos, sizeof(buf) - pos);
272d514b0f3Smrg        if (rc == -1)
273d514b0f3Smrg            if (errno == EINTR)
274d514b0f3Smrg                continue;
275d514b0f3Smrg            else
276d514b0f3Smrg                break;
277d514b0f3Smrg
278d514b0f3Smrg        if (rc == 0)
279d514b0f3Smrg            break;
280d514b0f3Smrg
281d514b0f3Smrg        pos += rc;
282d514b0f3Smrg
283d514b0f3Smrg        do {
284d514b0f3Smrg            rc = process_message(ccid, buf, pos);
285d514b0f3Smrg            pos -= rc;
286d514b0f3Smrg
287d514b0f3Smrg            if (rc > 0 && pos > 0)
288d514b0f3Smrg                memmove(buf, buf + rc, pos);
289d514b0f3Smrg        } while (rc > 0 && pos > 0);
290d514b0f3Smrg    }
291d514b0f3Smrg
292d514b0f3Smrg    fprintf(stderr, "LUN %d thread exiting: %s\n", ccid->lun,
293d514b0f3Smrg            rc == 0 ? "normally" : strerror(errno));
294d514b0f3Smrg    close(ccid->fd);
295d514b0f3Smrg    ccid->fd = -1;
296d514b0f3Smrg    ccid->lun = 0;
297d514b0f3Smrg    ccid->atr_len = 0;
298d514b0f3Smrg    ccid->state &= ~STATE_OPEN;
299d514b0f3Smrg
300d514b0f3Smrg    return NULL;
301d514b0f3Smrg}
302d514b0f3Smrg
303d514b0f3Smrg
304d514b0f3Smrgstatic void send_init(smartcard_ccid_t *ccid)
305d514b0f3Smrg{
306d514b0f3Smrg    uint32_t msg[6];
307d514b0f3Smrg
308d514b0f3Smrg    msg[0] = htonl(VSC_Init);               // type
309d514b0f3Smrg    msg[1] = htonl(ccid->lun);              // reader id
310d514b0f3Smrg    msg[2] = htonl(sizeof(uint32_t) * 3);   // length
311d514b0f3Smrg    msg[3] = htonl(VSCARD_MAGIC);           // VSCD
312d514b0f3Smrg    msg[4] = htonl(VSCARD_VERSION);         // VSCD
313d514b0f3Smrg    msg[5] = 0;                             // capabilities
314d514b0f3Smrg
315d514b0f3Smrg    if (write(ccid->fd, (char *) msg, sizeof(msg)) != sizeof(msg)) {
316d514b0f3Smrg        fprintf(stderr, "Error: lun %d fd %d write failed; errno %d\n", ccid->lun, ccid->fd, errno);
317d514b0f3Smrg        IFDHCloseChannel(ccid->lun);
318d514b0f3Smrg    }
319d514b0f3Smrg}
320d514b0f3Smrg
321d514b0f3Smrg/*----------------------------------------------------------------------------
322d514b0f3Smrg    IFDHCreateChannelByName
323d514b0f3Smrg        The pcsc daemon should invoke this function passing in the path name
324d514b0f3Smrg    configured in reader.conf.
325d514b0f3Smrg*/
326d514b0f3SmrgRESPONSECODE IFDHCreateChannelByName(DWORD Lun, LPSTR DeviceName)
327d514b0f3Smrg{
328d514b0f3Smrg    int i;
329d514b0f3Smrg    struct sockaddr_un addr;
330d514b0f3Smrg
331d514b0f3Smrg    for (i = 0; i < MAX_LUNS; i++)
332d514b0f3Smrg        if (luns[i].fd != -1 && luns[i].lun == Lun)
333d514b0f3Smrg            return IFD_COMMUNICATION_ERROR;
334d514b0f3Smrg
335d514b0f3Smrg    for (i = 0; i < MAX_LUNS; i++)
336d514b0f3Smrg        if (luns[i].fd == -1)
337d514b0f3Smrg            break;
338d514b0f3Smrg
339d514b0f3Smrg    if (i >= MAX_LUNS)
340d514b0f3Smrg        return IFD_COMMUNICATION_ERROR;
341d514b0f3Smrg
342d514b0f3Smrg    luns[i].fd = socket(AF_UNIX, SOCK_STREAM, 0);
343d514b0f3Smrg    if (luns[i].fd < 0)
344d514b0f3Smrg        return IFD_NO_SUCH_DEVICE;
345d514b0f3Smrg
346d514b0f3Smrg    memset(&addr, 0, sizeof(addr));
347d514b0f3Smrg    addr.sun_family = AF_UNIX;
348d514b0f3Smrg    strncpy(addr.sun_path, DeviceName, sizeof(addr.sun_path) - 1);
349d514b0f3Smrg    if (connect(luns[i].fd, (struct sockaddr *) &addr, sizeof(addr))) {
350d514b0f3Smrg        close(luns[i].fd);
351d514b0f3Smrg        return IFD_COMMUNICATION_ERROR;
352d514b0f3Smrg    }
353d514b0f3Smrg
354d514b0f3Smrg    if (pthread_create(&luns[i].tid, NULL, &lun_thread, &luns[i])) {
355d514b0f3Smrg        close(luns[i].fd);
356d514b0f3Smrg        return IFD_COMMUNICATION_ERROR;
357d514b0f3Smrg    }
358d514b0f3Smrg
359d514b0f3Smrg    luns[i].lun = Lun;
360d514b0f3Smrg    luns[i].state = STATE_OPEN;
361d514b0f3Smrg
362d514b0f3Smrg    return IFD_SUCCESS;
363d514b0f3Smrg}
364d514b0f3Smrg
365d514b0f3SmrgRESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
366d514b0f3Smrg{
367d514b0f3Smrg    fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Channel %ld\n", __FUNCTION__, Lun, Channel);
368d514b0f3Smrg    return IFD_ERROR_NOT_SUPPORTED;
369d514b0f3Smrg}
370d514b0f3Smrg
371d514b0f3SmrgRESPONSECODE IFDHCloseChannel(DWORD Lun)
372d514b0f3Smrg{
373d514b0f3Smrg    int i;
374d514b0f3Smrg
375d514b0f3Smrg    for (i = 0; i < MAX_LUNS; i++) {
376d514b0f3Smrg        if (luns[i].fd != -1 && luns[i].lun == Lun) {
377d514b0f3Smrg            pthread_cancel(luns[i].tid);
378d514b0f3Smrg            close(luns[i].fd);
379d514b0f3Smrg            luns[i].fd = -1;
380d514b0f3Smrg            luns[i].lun = 0;
381d514b0f3Smrg            luns[i].atr_len = 0;
382d514b0f3Smrg            luns[i].state &= ~STATE_OPEN;
383d514b0f3Smrg            break;
384d514b0f3Smrg        }
385d514b0f3Smrg    }
386d514b0f3Smrg
387d514b0f3Smrg    if (i == MAX_LUNS)
388d514b0f3Smrg        return IFD_NO_SUCH_DEVICE;
389d514b0f3Smrg
390d514b0f3Smrg    return IFD_SUCCESS;
391d514b0f3Smrg}
392d514b0f3Smrg
393d514b0f3SmrgRESPONSECODE IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
394d514b0f3Smrg{
395d514b0f3Smrg    fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Tag %ld, Length %ld, Value %p\n", __FUNCTION__, Lun, Tag, *Length, Value);
396d514b0f3Smrg    /* TODO - explore supporting TAG_IFD_POLLING_THREAD */
397d514b0f3Smrg    return IFD_ERROR_NOT_SUPPORTED;
398d514b0f3Smrg}
399d514b0f3Smrg
400d514b0f3SmrgRESPONSECODE IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
401d514b0f3Smrg{
402d514b0f3Smrg    return IFD_ERROR_NOT_SUPPORTED;
403d514b0f3Smrg}
404d514b0f3Smrg
405d514b0f3SmrgRESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
406d514b0f3Smrg{
407d514b0f3Smrg    int i;
408d514b0f3Smrg
409d514b0f3Smrg    for (i = 0; i < MAX_LUNS; i++)
410d514b0f3Smrg        if (luns[i].fd != -1 && luns[i].lun == Lun)
411d514b0f3Smrg            if (Action == IFD_POWER_UP || Action == IFD_RESET) {
412d514b0f3Smrg                if (*AtrLength >= luns[i].atr_len) {
413d514b0f3Smrg                    memcpy(Atr, luns[i].atr, luns[i].atr_len);
414d514b0f3Smrg                    *AtrLength = luns[i].atr_len;
415d514b0f3Smrg                }
416d514b0f3Smrg                send_init(&luns[i]);
417d514b0f3Smrg                return IFD_SUCCESS;
418d514b0f3Smrg            }
419d514b0f3Smrg
420d514b0f3Smrg    fprintf(stderr, "spiceccid %s unsupported: Lun %ld, Action %ld\n", __FUNCTION__, Lun, Action);
421d514b0f3Smrg    return IFD_ERROR_NOT_SUPPORTED;
422d514b0f3Smrg}
423d514b0f3Smrg
424d514b0f3Smrg#define TX_MAX_SLEEP 5000
425d514b0f3Smrg#define TX_SLEEP_INTERVAL 1000
426d514b0f3SmrgRESPONSECODE IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,
427d514b0f3Smrg    PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, PDWORD
428d514b0f3Smrg    RxLength, PSCARD_IO_HEADER RecvPci)
429d514b0f3Smrg{
430d514b0f3Smrg    apdu_t *p;
431d514b0f3Smrg    int i, j;
432d514b0f3Smrg
433d514b0f3Smrg    for (i = 0; i < MAX_LUNS; i++)
434d514b0f3Smrg        if (luns[i].fd != -1 && luns[i].lun == Lun) {
435d514b0f3Smrg            while (p = pop_apdu(&luns[i]))
436d514b0f3Smrg                free_apdu(p);
437d514b0f3Smrg
438d514b0f3Smrg            if (send_tx_buffer(&luns[i], TxBuffer, TxLength)) {
439d514b0f3Smrg                for (j = 0; j < TX_MAX_SLEEP; j++)
440d514b0f3Smrg                    if (p = pop_apdu(&luns[i]))
441d514b0f3Smrg                        break;
442d514b0f3Smrg                    else
443d514b0f3Smrg                        usleep(TX_SLEEP_INTERVAL);
444d514b0f3Smrg
445d514b0f3Smrg                if (p) {
446d514b0f3Smrg                    memcpy(RxBuffer, p->data, MIN(p->len, *RxLength));
447d514b0f3Smrg                    *RxLength = MIN(p->len, *RxLength);
448d514b0f3Smrg                    free_apdu(p);
449d514b0f3Smrg                    return IFD_SUCCESS;
450d514b0f3Smrg                }
451d514b0f3Smrg
452d514b0f3Smrg                return IFD_RESPONSE_TIMEOUT;
453d514b0f3Smrg            }
454d514b0f3Smrg        }
455d514b0f3Smrg    return IFD_NO_SUCH_DEVICE;
456d514b0f3Smrg}
457d514b0f3Smrg
458d514b0f3SmrgRESPONSECODE IFDHICCPresence(DWORD Lun)
459d514b0f3Smrg{
460d514b0f3Smrg    int i;
461d514b0f3Smrg
462d514b0f3Smrg    for (i = 0; i < MAX_LUNS; i++)
463d514b0f3Smrg        if (luns[i].fd != -1 && luns[i].lun == Lun) {
464d514b0f3Smrg            if (luns[i].atr_len > 0 && luns[i].state & STATE_READER_ADDED)
465d514b0f3Smrg                return IFD_SUCCESS;
466d514b0f3Smrg
467d514b0f3Smrg            return IFD_ICC_NOT_PRESENT;
468d514b0f3Smrg        }
469d514b0f3Smrg
470d514b0f3Smrg    return IFD_NO_SUCH_DEVICE;
471d514b0f3Smrg}
472d514b0f3Smrg
473d514b0f3SmrgRESPONSECODE IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol, UCHAR Flags,
474d514b0f3Smrg    UCHAR PTS1, UCHAR PTS2, UCHAR PTS3)
475d514b0f3Smrg{
476d514b0f3Smrg    if (Protocol == SCARD_PROTOCOL_T1)
477d514b0f3Smrg        return IFD_SUCCESS;
478d514b0f3Smrg
479d514b0f3Smrg    return IFD_NOT_SUPPORTED;
480d514b0f3Smrg}
481d514b0f3Smrg
482d514b0f3SmrgRESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode, PUCHAR
483d514b0f3Smrg    TxBuffer, DWORD TxLength, PUCHAR RxBuffer, DWORD RxLength,
484d514b0f3Smrg    LPDWORD pdwBytesReturned)
485d514b0f3Smrg{
486d514b0f3Smrg    fprintf(stderr, "spiceccid %s unsupported: Lun %ld\n", __FUNCTION__, Lun);
487d514b0f3Smrg    return IFD_ERROR_NOT_SUPPORTED;
488d514b0f3Smrg}
489