root / hw / ccid-card-emulated.c @ 8e31bf38
History | View | Annotate | Download (18.6 kB)
1 |
/*
|
---|---|
2 |
* CCID Card Device. Emulated card.
|
3 |
*
|
4 |
* Copyright (c) 2011 Red Hat.
|
5 |
* Written by Alon Levy.
|
6 |
*
|
7 |
* This code is licensed under the GNU LGPL, version 2 or later.
|
8 |
*/
|
9 |
|
10 |
/*
|
11 |
* It can be used to provide access to the local hardware in a non exclusive
|
12 |
* way, or it can use certificates. It requires the usb-ccid bus.
|
13 |
*
|
14 |
* Usage 1: standard, mirror hardware reader+card:
|
15 |
* qemu .. -usb -device usb-ccid -device ccid-card-emulated
|
16 |
*
|
17 |
* Usage 2: use certificates, no hardware required
|
18 |
* one time: create the certificates:
|
19 |
* for i in 1 2 3; do
|
20 |
* certutil -d /etc/pki/nssdb -x -t "CT,CT,CT" -S -s "CN=user$i" -n user$i
|
21 |
* done
|
22 |
* qemu .. -usb -device usb-ccid \
|
23 |
* -device ccid-card-emulated,cert1=user1,cert2=user2,cert3=user3
|
24 |
*
|
25 |
* If you use a non default db for the certificates you can specify it using
|
26 |
* the db parameter.
|
27 |
*/
|
28 |
|
29 |
#include <eventt.h> |
30 |
#include <vevent.h> |
31 |
#include <vreader.h> |
32 |
#include <vcard_emul.h> |
33 |
|
34 |
#include "qemu-thread.h" |
35 |
#include "qemu-char.h" |
36 |
#include "monitor.h" |
37 |
#include "hw/ccid.h" |
38 |
|
39 |
#define DPRINTF(card, lvl, fmt, ...) \
|
40 |
do {\
|
41 |
if (lvl <= card->debug) {\
|
42 |
printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\ |
43 |
} \ |
44 |
} while (0) |
45 |
|
46 |
#define EMULATED_DEV_NAME "ccid-card-emulated" |
47 |
|
48 |
#define BACKEND_NSS_EMULATED_NAME "nss-emulated" |
49 |
#define BACKEND_CERTIFICATES_NAME "certificates" |
50 |
|
51 |
enum {
|
52 |
BACKEND_NSS_EMULATED = 1,
|
53 |
BACKEND_CERTIFICATES |
54 |
}; |
55 |
|
56 |
#define DEFAULT_BACKEND BACKEND_NSS_EMULATED
|
57 |
|
58 |
typedef struct EmulatedState EmulatedState; |
59 |
|
60 |
enum {
|
61 |
EMUL_READER_INSERT = 0,
|
62 |
EMUL_READER_REMOVE, |
63 |
EMUL_CARD_INSERT, |
64 |
EMUL_CARD_REMOVE, |
65 |
EMUL_GUEST_APDU, |
66 |
EMUL_RESPONSE_APDU, |
67 |
EMUL_ERROR, |
68 |
}; |
69 |
|
70 |
static const char *emul_event_to_string(uint32_t emul_event) |
71 |
{ |
72 |
switch (emul_event) {
|
73 |
case EMUL_READER_INSERT:
|
74 |
return "EMUL_READER_INSERT"; |
75 |
case EMUL_READER_REMOVE:
|
76 |
return "EMUL_READER_REMOVE"; |
77 |
case EMUL_CARD_INSERT:
|
78 |
return "EMUL_CARD_INSERT"; |
79 |
case EMUL_CARD_REMOVE:
|
80 |
return "EMUL_CARD_REMOVE"; |
81 |
case EMUL_GUEST_APDU:
|
82 |
return "EMUL_GUEST_APDU"; |
83 |
case EMUL_RESPONSE_APDU:
|
84 |
return "EMUL_RESPONSE_APDU"; |
85 |
case EMUL_ERROR:
|
86 |
return "EMUL_ERROR"; |
87 |
} |
88 |
return "UNKNOWN"; |
89 |
} |
90 |
|
91 |
typedef struct EmulEvent { |
92 |
QSIMPLEQ_ENTRY(EmulEvent) entry; |
93 |
union {
|
94 |
struct {
|
95 |
uint32_t type; |
96 |
} gen; |
97 |
struct {
|
98 |
uint32_t type; |
99 |
uint64_t code; |
100 |
} error; |
101 |
struct {
|
102 |
uint32_t type; |
103 |
uint32_t len; |
104 |
uint8_t data[]; |
105 |
} data; |
106 |
} p; |
107 |
} EmulEvent; |
108 |
|
109 |
#define MAX_ATR_SIZE 40 |
110 |
struct EmulatedState {
|
111 |
CCIDCardState base; |
112 |
uint8_t debug; |
113 |
char *backend_str;
|
114 |
uint32_t backend; |
115 |
char *cert1;
|
116 |
char *cert2;
|
117 |
char *cert3;
|
118 |
char *db;
|
119 |
uint8_t atr[MAX_ATR_SIZE]; |
120 |
uint8_t atr_length; |
121 |
QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; |
122 |
QemuMutex event_list_mutex; |
123 |
VReader *reader; |
124 |
QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; |
125 |
QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
|
126 |
QemuMutex handle_apdu_mutex; |
127 |
QemuCond handle_apdu_cond; |
128 |
int pipe[2]; |
129 |
int quit_apdu_thread;
|
130 |
QemuMutex apdu_thread_quit_mutex; |
131 |
QemuCond apdu_thread_quit_cond; |
132 |
}; |
133 |
|
134 |
static void emulated_apdu_from_guest(CCIDCardState *base, |
135 |
const uint8_t *apdu, uint32_t len)
|
136 |
{ |
137 |
EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
138 |
EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent) + len);
|
139 |
|
140 |
assert(event); |
141 |
event->p.data.type = EMUL_GUEST_APDU; |
142 |
event->p.data.len = len; |
143 |
memcpy(event->p.data.data, apdu, len); |
144 |
qemu_mutex_lock(&card->vreader_mutex); |
145 |
QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry); |
146 |
qemu_mutex_unlock(&card->vreader_mutex); |
147 |
qemu_mutex_lock(&card->handle_apdu_mutex); |
148 |
qemu_cond_signal(&card->handle_apdu_cond); |
149 |
qemu_mutex_unlock(&card->handle_apdu_mutex); |
150 |
} |
151 |
|
152 |
static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len) |
153 |
{ |
154 |
EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
155 |
|
156 |
*len = card->atr_length; |
157 |
return card->atr;
|
158 |
} |
159 |
|
160 |
static void emulated_push_event(EmulatedState *card, EmulEvent *event) |
161 |
{ |
162 |
qemu_mutex_lock(&card->event_list_mutex); |
163 |
QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry); |
164 |
qemu_mutex_unlock(&card->event_list_mutex); |
165 |
if (write(card->pipe[1], card, 1) != 1) { |
166 |
DPRINTF(card, 1, "write to pipe failed\n"); |
167 |
} |
168 |
} |
169 |
|
170 |
static void emulated_push_type(EmulatedState *card, uint32_t type) |
171 |
{ |
172 |
EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent));
|
173 |
|
174 |
assert(event); |
175 |
event->p.gen.type = type; |
176 |
emulated_push_event(card, event); |
177 |
} |
178 |
|
179 |
static void emulated_push_error(EmulatedState *card, uint64_t code) |
180 |
{ |
181 |
EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent));
|
182 |
|
183 |
assert(event); |
184 |
event->p.error.type = EMUL_ERROR; |
185 |
event->p.error.code = code; |
186 |
emulated_push_event(card, event); |
187 |
} |
188 |
|
189 |
static void emulated_push_data_type(EmulatedState *card, uint32_t type, |
190 |
const uint8_t *data, uint32_t len)
|
191 |
{ |
192 |
EmulEvent *event = (EmulEvent *)qemu_malloc(sizeof(EmulEvent) + len);
|
193 |
|
194 |
assert(event); |
195 |
event->p.data.type = type; |
196 |
event->p.data.len = len; |
197 |
memcpy(event->p.data.data, data, len); |
198 |
emulated_push_event(card, event); |
199 |
} |
200 |
|
201 |
static void emulated_push_reader_insert(EmulatedState *card) |
202 |
{ |
203 |
emulated_push_type(card, EMUL_READER_INSERT); |
204 |
} |
205 |
|
206 |
static void emulated_push_reader_remove(EmulatedState *card) |
207 |
{ |
208 |
emulated_push_type(card, EMUL_READER_REMOVE); |
209 |
} |
210 |
|
211 |
static void emulated_push_card_insert(EmulatedState *card, |
212 |
const uint8_t *atr, uint32_t len)
|
213 |
{ |
214 |
emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len); |
215 |
} |
216 |
|
217 |
static void emulated_push_card_remove(EmulatedState *card) |
218 |
{ |
219 |
emulated_push_type(card, EMUL_CARD_REMOVE); |
220 |
} |
221 |
|
222 |
static void emulated_push_response_apdu(EmulatedState *card, |
223 |
const uint8_t *apdu, uint32_t len)
|
224 |
{ |
225 |
emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len); |
226 |
} |
227 |
|
228 |
#define APDU_BUF_SIZE 270 |
229 |
static void *handle_apdu_thread(void* arg) |
230 |
{ |
231 |
EmulatedState *card = arg; |
232 |
uint8_t recv_data[APDU_BUF_SIZE]; |
233 |
int recv_len;
|
234 |
VReaderStatus reader_status; |
235 |
EmulEvent *event; |
236 |
|
237 |
while (1) { |
238 |
qemu_mutex_lock(&card->handle_apdu_mutex); |
239 |
qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex); |
240 |
qemu_mutex_unlock(&card->handle_apdu_mutex); |
241 |
if (card->quit_apdu_thread) {
|
242 |
card->quit_apdu_thread = 0; /* debugging */ |
243 |
break;
|
244 |
} |
245 |
qemu_mutex_lock(&card->vreader_mutex); |
246 |
while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
|
247 |
event = QSIMPLEQ_FIRST(&card->guest_apdu_list); |
248 |
assert((unsigned long)event > 1000); |
249 |
QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry); |
250 |
if (event->p.data.type != EMUL_GUEST_APDU) {
|
251 |
DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n"); |
252 |
qemu_free(event); |
253 |
continue;
|
254 |
} |
255 |
if (card->reader == NULL) { |
256 |
DPRINTF(card, 1, "reader is NULL\n"); |
257 |
qemu_free(event); |
258 |
continue;
|
259 |
} |
260 |
recv_len = sizeof(recv_data);
|
261 |
reader_status = vreader_xfr_bytes(card->reader, |
262 |
event->p.data.data, event->p.data.len, |
263 |
recv_data, &recv_len); |
264 |
DPRINTF(card, 2, "got back apdu of length %d\n", recv_len); |
265 |
if (reader_status == VREADER_OK) {
|
266 |
emulated_push_response_apdu(card, recv_data, recv_len); |
267 |
} else {
|
268 |
emulated_push_error(card, reader_status); |
269 |
} |
270 |
qemu_free(event); |
271 |
} |
272 |
qemu_mutex_unlock(&card->vreader_mutex); |
273 |
} |
274 |
qemu_mutex_lock(&card->apdu_thread_quit_mutex); |
275 |
qemu_cond_signal(&card->apdu_thread_quit_cond); |
276 |
qemu_mutex_unlock(&card->apdu_thread_quit_mutex); |
277 |
return NULL; |
278 |
} |
279 |
|
280 |
static void *event_thread(void *arg) |
281 |
{ |
282 |
int atr_len = MAX_ATR_SIZE;
|
283 |
uint8_t atr[MAX_ATR_SIZE]; |
284 |
VEvent *event = NULL;
|
285 |
EmulatedState *card = arg; |
286 |
|
287 |
while (1) { |
288 |
const char *reader_name; |
289 |
|
290 |
event = vevent_wait_next_vevent(); |
291 |
if (event == NULL || event->type == VEVENT_LAST) { |
292 |
break;
|
293 |
} |
294 |
if (event->type != VEVENT_READER_INSERT) {
|
295 |
if (card->reader == NULL && event->reader != NULL) { |
296 |
/* Happens after device_add followed by card remove or insert.
|
297 |
* XXX: create synthetic add_reader events if vcard_emul_init
|
298 |
* already called, which happens if device_del and device_add
|
299 |
* are called */
|
300 |
card->reader = vreader_reference(event->reader); |
301 |
} else {
|
302 |
if (event->reader != card->reader) {
|
303 |
fprintf(stderr, |
304 |
"ERROR: wrong reader: quiting event_thread\n");
|
305 |
break;
|
306 |
} |
307 |
} |
308 |
} |
309 |
switch (event->type) {
|
310 |
case VEVENT_READER_INSERT:
|
311 |
/* TODO: take a specific reader. i.e. track which reader
|
312 |
* we are seeing here, check it is the one we want (the first,
|
313 |
* or by a particular name), and ignore if we don't want it.
|
314 |
*/
|
315 |
reader_name = vreader_get_name(event->reader); |
316 |
if (card->reader != NULL) { |
317 |
DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", |
318 |
vreader_get_name(card->reader), reader_name); |
319 |
qemu_mutex_lock(&card->vreader_mutex); |
320 |
vreader_free(card->reader); |
321 |
qemu_mutex_unlock(&card->vreader_mutex); |
322 |
emulated_push_reader_remove(card); |
323 |
} |
324 |
qemu_mutex_lock(&card->vreader_mutex); |
325 |
DPRINTF(card, 2, "READER INSERT %s\n", reader_name); |
326 |
card->reader = vreader_reference(event->reader); |
327 |
qemu_mutex_unlock(&card->vreader_mutex); |
328 |
emulated_push_reader_insert(card); |
329 |
break;
|
330 |
case VEVENT_READER_REMOVE:
|
331 |
DPRINTF(card, 2, " READER REMOVE: %s\n", |
332 |
vreader_get_name(event->reader)); |
333 |
qemu_mutex_lock(&card->vreader_mutex); |
334 |
vreader_free(card->reader); |
335 |
card->reader = NULL;
|
336 |
qemu_mutex_unlock(&card->vreader_mutex); |
337 |
emulated_push_reader_remove(card); |
338 |
break;
|
339 |
case VEVENT_CARD_INSERT:
|
340 |
/* get the ATR (intended as a response to a power on from the
|
341 |
* reader */
|
342 |
atr_len = MAX_ATR_SIZE; |
343 |
vreader_power_on(event->reader, atr, &atr_len); |
344 |
card->atr_length = (uint8_t)atr_len; |
345 |
DPRINTF(card, 2, " CARD INSERT\n"); |
346 |
emulated_push_card_insert(card, atr, atr_len); |
347 |
break;
|
348 |
case VEVENT_CARD_REMOVE:
|
349 |
DPRINTF(card, 2, " CARD REMOVE\n"); |
350 |
emulated_push_card_remove(card); |
351 |
break;
|
352 |
case VEVENT_LAST: /* quit */ |
353 |
vevent_delete(event); |
354 |
return NULL; |
355 |
break;
|
356 |
default:
|
357 |
break;
|
358 |
} |
359 |
vevent_delete(event); |
360 |
} |
361 |
return NULL; |
362 |
} |
363 |
|
364 |
static void pipe_read(void *opaque) |
365 |
{ |
366 |
EmulatedState *card = opaque; |
367 |
EmulEvent *event, *next; |
368 |
char dummy;
|
369 |
int len;
|
370 |
|
371 |
do {
|
372 |
len = read(card->pipe[0], &dummy, sizeof(dummy)); |
373 |
} while (len == sizeof(dummy)); |
374 |
qemu_mutex_lock(&card->event_list_mutex); |
375 |
QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { |
376 |
DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); |
377 |
switch (event->p.gen.type) {
|
378 |
case EMUL_RESPONSE_APDU:
|
379 |
ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, |
380 |
event->p.data.len); |
381 |
break;
|
382 |
case EMUL_READER_INSERT:
|
383 |
ccid_card_ccid_attach(&card->base); |
384 |
break;
|
385 |
case EMUL_READER_REMOVE:
|
386 |
ccid_card_ccid_detach(&card->base); |
387 |
break;
|
388 |
case EMUL_CARD_INSERT:
|
389 |
assert(event->p.data.len <= MAX_ATR_SIZE); |
390 |
card->atr_length = event->p.data.len; |
391 |
memcpy(card->atr, event->p.data.data, card->atr_length); |
392 |
ccid_card_card_inserted(&card->base); |
393 |
break;
|
394 |
case EMUL_CARD_REMOVE:
|
395 |
ccid_card_card_removed(&card->base); |
396 |
break;
|
397 |
case EMUL_ERROR:
|
398 |
ccid_card_card_error(&card->base, event->p.error.code); |
399 |
break;
|
400 |
default:
|
401 |
DPRINTF(card, 2, "unexpected event\n"); |
402 |
break;
|
403 |
} |
404 |
qemu_free(event); |
405 |
} |
406 |
QSIMPLEQ_INIT(&card->event_list); |
407 |
qemu_mutex_unlock(&card->event_list_mutex); |
408 |
} |
409 |
|
410 |
static int init_pipe_signaling(EmulatedState *card) |
411 |
{ |
412 |
if (pipe(card->pipe) < 0) { |
413 |
DPRINTF(card, 2, "pipe creation failed\n"); |
414 |
return -1; |
415 |
} |
416 |
fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
|
417 |
fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
|
418 |
fcntl(card->pipe[0], F_SETOWN, getpid());
|
419 |
qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card); |
420 |
return 0; |
421 |
} |
422 |
|
423 |
#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" |
424 |
#define CERTIFICATES_ARGS_TEMPLATE\
|
425 |
"db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
|
426 |
|
427 |
static int wrap_vcard_emul_init(VCardEmulOptions *options) |
428 |
{ |
429 |
static int called; |
430 |
static int options_was_null; |
431 |
|
432 |
if (called) {
|
433 |
if ((options == NULL) != options_was_null) { |
434 |
printf("%s: warning: running emulated with certificates"
|
435 |
" and emulated side by side is not supported\n",
|
436 |
__func__); |
437 |
return VCARD_EMUL_FAIL;
|
438 |
} |
439 |
vcard_emul_replay_insertion_events(); |
440 |
return VCARD_EMUL_OK;
|
441 |
} |
442 |
options_was_null = (options == NULL);
|
443 |
called = 1;
|
444 |
return vcard_emul_init(options);
|
445 |
} |
446 |
|
447 |
static int emulated_initialize_vcard_from_certificates(EmulatedState *card) |
448 |
{ |
449 |
char emul_args[200]; |
450 |
VCardEmulOptions *options = NULL;
|
451 |
|
452 |
snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, |
453 |
card->db ? card->db : CERTIFICATES_DEFAULT_DB, |
454 |
card->cert1, card->cert2, card->cert3); |
455 |
options = vcard_emul_options(emul_args); |
456 |
if (options == NULL) { |
457 |
printf("%s: warning: not using certificates due to"
|
458 |
" initialization error\n", __func__);
|
459 |
} |
460 |
return wrap_vcard_emul_init(options);
|
461 |
} |
462 |
|
463 |
typedef struct EnumTable { |
464 |
const char *name; |
465 |
uint32_t value; |
466 |
} EnumTable; |
467 |
|
468 |
EnumTable backend_enum_table[] = { |
469 |
{BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, |
470 |
{BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, |
471 |
{NULL, 0}, |
472 |
}; |
473 |
|
474 |
static uint32_t parse_enumeration(char *str, |
475 |
EnumTable *table, uint32_t not_found_value) |
476 |
{ |
477 |
uint32_t ret = not_found_value; |
478 |
|
479 |
while (table->name != NULL) { |
480 |
if (strcmp(table->name, str) == 0) { |
481 |
ret = table->value; |
482 |
break;
|
483 |
} |
484 |
table++; |
485 |
} |
486 |
return ret;
|
487 |
} |
488 |
|
489 |
static int emulated_initfn(CCIDCardState *base) |
490 |
{ |
491 |
EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
492 |
QemuThread thread_id; |
493 |
VCardEmulError ret; |
494 |
EnumTable *ptable; |
495 |
|
496 |
QSIMPLEQ_INIT(&card->event_list); |
497 |
QSIMPLEQ_INIT(&card->guest_apdu_list); |
498 |
qemu_mutex_init(&card->event_list_mutex); |
499 |
qemu_mutex_init(&card->vreader_mutex); |
500 |
qemu_mutex_init(&card->handle_apdu_mutex); |
501 |
qemu_cond_init(&card->handle_apdu_cond); |
502 |
card->reader = NULL;
|
503 |
card->quit_apdu_thread = 0;
|
504 |
if (init_pipe_signaling(card) < 0) { |
505 |
return -1; |
506 |
} |
507 |
card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
|
508 |
if (card->backend == 0) { |
509 |
printf("unknown backend, must be one of:\n");
|
510 |
for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { |
511 |
printf("%s\n", ptable->name);
|
512 |
} |
513 |
return -1; |
514 |
} |
515 |
|
516 |
/* TODO: a passthru backened that works on local machine. third card type?*/
|
517 |
if (card->backend == BACKEND_CERTIFICATES) {
|
518 |
if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { |
519 |
ret = emulated_initialize_vcard_from_certificates(card); |
520 |
} else {
|
521 |
printf("%s: you must provide all three certs for"
|
522 |
" certificates backend\n", EMULATED_DEV_NAME);
|
523 |
return -1; |
524 |
} |
525 |
} else {
|
526 |
if (card->backend != BACKEND_NSS_EMULATED) {
|
527 |
printf("%s: bad backend specified. The options are:\n%s (default),"
|
528 |
" %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
|
529 |
BACKEND_CERTIFICATES_NAME); |
530 |
return -1; |
531 |
} |
532 |
if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { |
533 |
printf("%s: unexpected cert parameters to nss emulated backend\n",
|
534 |
EMULATED_DEV_NAME); |
535 |
return -1; |
536 |
} |
537 |
/* default to mirroring the local hardware readers */
|
538 |
ret = wrap_vcard_emul_init(NULL);
|
539 |
} |
540 |
if (ret != VCARD_EMUL_OK) {
|
541 |
printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
|
542 |
return -1; |
543 |
} |
544 |
qemu_thread_create(&thread_id, event_thread, card); |
545 |
qemu_thread_create(&thread_id, handle_apdu_thread, card); |
546 |
return 0; |
547 |
} |
548 |
|
549 |
static int emulated_exitfn(CCIDCardState *base) |
550 |
{ |
551 |
EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
552 |
VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); |
553 |
|
554 |
vevent_queue_vevent(vevent); /* stop vevent thread */
|
555 |
qemu_mutex_lock(&card->apdu_thread_quit_mutex); |
556 |
card->quit_apdu_thread = 1; /* stop handle_apdu thread */ |
557 |
qemu_cond_signal(&card->handle_apdu_cond); |
558 |
qemu_cond_wait(&card->apdu_thread_quit_cond, |
559 |
&card->apdu_thread_quit_mutex); |
560 |
/* handle_apdu thread stopped, can destroy all of it's mutexes */
|
561 |
qemu_cond_destroy(&card->handle_apdu_cond); |
562 |
qemu_cond_destroy(&card->apdu_thread_quit_cond); |
563 |
qemu_mutex_destroy(&card->apdu_thread_quit_mutex); |
564 |
qemu_mutex_destroy(&card->handle_apdu_mutex); |
565 |
qemu_mutex_destroy(&card->vreader_mutex); |
566 |
qemu_mutex_destroy(&card->event_list_mutex); |
567 |
return 0; |
568 |
} |
569 |
|
570 |
static CCIDCardInfo emulated_card_info = {
|
571 |
.qdev.name = EMULATED_DEV_NAME, |
572 |
.qdev.desc = "emulated smartcard",
|
573 |
.qdev.size = sizeof(EmulatedState),
|
574 |
.initfn = emulated_initfn, |
575 |
.exitfn = emulated_exitfn, |
576 |
.get_atr = emulated_get_atr, |
577 |
.apdu_from_guest = emulated_apdu_from_guest, |
578 |
.qdev.unplug = qdev_simple_unplug_cb, |
579 |
.qdev.props = (Property[]) { |
580 |
DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
|
581 |
DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
|
582 |
DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
|
583 |
DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
|
584 |
DEFINE_PROP_STRING("db", EmulatedState, db),
|
585 |
DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), |
586 |
DEFINE_PROP_END_OF_LIST(), |
587 |
}, |
588 |
}; |
589 |
|
590 |
static void ccid_card_emulated_register_devices(void) |
591 |
{ |
592 |
ccid_card_qdev_register(&emulated_card_info); |
593 |
} |
594 |
|
595 |
device_init(ccid_card_emulated_register_devices) |