root / hw / ccid-card-emulated.c @ 03a6b667
History | View | Annotate | Download (18.5 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 |
QemuThread event_thread_id; |
124 |
VReader *reader; |
125 |
QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; |
126 |
QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
|
127 |
QemuMutex handle_apdu_mutex; |
128 |
QemuCond handle_apdu_cond; |
129 |
int pipe[2]; |
130 |
int quit_apdu_thread;
|
131 |
QemuThread apdu_thread_id; |
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 *)g_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 *)g_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 *)g_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 *)g_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 |
g_free(event); |
253 |
continue;
|
254 |
} |
255 |
if (card->reader == NULL) { |
256 |
DPRINTF(card, 1, "reader is NULL\n"); |
257 |
g_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 |
g_free(event); |
271 |
} |
272 |
qemu_mutex_unlock(&card->vreader_mutex); |
273 |
} |
274 |
return NULL; |
275 |
} |
276 |
|
277 |
static void *event_thread(void *arg) |
278 |
{ |
279 |
int atr_len = MAX_ATR_SIZE;
|
280 |
uint8_t atr[MAX_ATR_SIZE]; |
281 |
VEvent *event = NULL;
|
282 |
EmulatedState *card = arg; |
283 |
|
284 |
while (1) { |
285 |
const char *reader_name; |
286 |
|
287 |
event = vevent_wait_next_vevent(); |
288 |
if (event == NULL || event->type == VEVENT_LAST) { |
289 |
break;
|
290 |
} |
291 |
if (event->type != VEVENT_READER_INSERT) {
|
292 |
if (card->reader == NULL && event->reader != NULL) { |
293 |
/* Happens after device_add followed by card remove or insert.
|
294 |
* XXX: create synthetic add_reader events if vcard_emul_init
|
295 |
* already called, which happens if device_del and device_add
|
296 |
* are called */
|
297 |
card->reader = vreader_reference(event->reader); |
298 |
} else {
|
299 |
if (event->reader != card->reader) {
|
300 |
fprintf(stderr, |
301 |
"ERROR: wrong reader: quiting event_thread\n");
|
302 |
break;
|
303 |
} |
304 |
} |
305 |
} |
306 |
switch (event->type) {
|
307 |
case VEVENT_READER_INSERT:
|
308 |
/* TODO: take a specific reader. i.e. track which reader
|
309 |
* we are seeing here, check it is the one we want (the first,
|
310 |
* or by a particular name), and ignore if we don't want it.
|
311 |
*/
|
312 |
reader_name = vreader_get_name(event->reader); |
313 |
if (card->reader != NULL) { |
314 |
DPRINTF(card, 2, "READER INSERT - replacing %s with %s\n", |
315 |
vreader_get_name(card->reader), reader_name); |
316 |
qemu_mutex_lock(&card->vreader_mutex); |
317 |
vreader_free(card->reader); |
318 |
qemu_mutex_unlock(&card->vreader_mutex); |
319 |
emulated_push_reader_remove(card); |
320 |
} |
321 |
qemu_mutex_lock(&card->vreader_mutex); |
322 |
DPRINTF(card, 2, "READER INSERT %s\n", reader_name); |
323 |
card->reader = vreader_reference(event->reader); |
324 |
qemu_mutex_unlock(&card->vreader_mutex); |
325 |
emulated_push_reader_insert(card); |
326 |
break;
|
327 |
case VEVENT_READER_REMOVE:
|
328 |
DPRINTF(card, 2, " READER REMOVE: %s\n", |
329 |
vreader_get_name(event->reader)); |
330 |
qemu_mutex_lock(&card->vreader_mutex); |
331 |
vreader_free(card->reader); |
332 |
card->reader = NULL;
|
333 |
qemu_mutex_unlock(&card->vreader_mutex); |
334 |
emulated_push_reader_remove(card); |
335 |
break;
|
336 |
case VEVENT_CARD_INSERT:
|
337 |
/* get the ATR (intended as a response to a power on from the
|
338 |
* reader */
|
339 |
atr_len = MAX_ATR_SIZE; |
340 |
vreader_power_on(event->reader, atr, &atr_len); |
341 |
card->atr_length = (uint8_t)atr_len; |
342 |
DPRINTF(card, 2, " CARD INSERT\n"); |
343 |
emulated_push_card_insert(card, atr, atr_len); |
344 |
break;
|
345 |
case VEVENT_CARD_REMOVE:
|
346 |
DPRINTF(card, 2, " CARD REMOVE\n"); |
347 |
emulated_push_card_remove(card); |
348 |
break;
|
349 |
case VEVENT_LAST: /* quit */ |
350 |
vevent_delete(event); |
351 |
return NULL; |
352 |
break;
|
353 |
default:
|
354 |
break;
|
355 |
} |
356 |
vevent_delete(event); |
357 |
} |
358 |
return NULL; |
359 |
} |
360 |
|
361 |
static void pipe_read(void *opaque) |
362 |
{ |
363 |
EmulatedState *card = opaque; |
364 |
EmulEvent *event, *next; |
365 |
char dummy;
|
366 |
int len;
|
367 |
|
368 |
do {
|
369 |
len = read(card->pipe[0], &dummy, sizeof(dummy)); |
370 |
} while (len == sizeof(dummy)); |
371 |
qemu_mutex_lock(&card->event_list_mutex); |
372 |
QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) { |
373 |
DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type)); |
374 |
switch (event->p.gen.type) {
|
375 |
case EMUL_RESPONSE_APDU:
|
376 |
ccid_card_send_apdu_to_guest(&card->base, event->p.data.data, |
377 |
event->p.data.len); |
378 |
break;
|
379 |
case EMUL_READER_INSERT:
|
380 |
ccid_card_ccid_attach(&card->base); |
381 |
break;
|
382 |
case EMUL_READER_REMOVE:
|
383 |
ccid_card_ccid_detach(&card->base); |
384 |
break;
|
385 |
case EMUL_CARD_INSERT:
|
386 |
assert(event->p.data.len <= MAX_ATR_SIZE); |
387 |
card->atr_length = event->p.data.len; |
388 |
memcpy(card->atr, event->p.data.data, card->atr_length); |
389 |
ccid_card_card_inserted(&card->base); |
390 |
break;
|
391 |
case EMUL_CARD_REMOVE:
|
392 |
ccid_card_card_removed(&card->base); |
393 |
break;
|
394 |
case EMUL_ERROR:
|
395 |
ccid_card_card_error(&card->base, event->p.error.code); |
396 |
break;
|
397 |
default:
|
398 |
DPRINTF(card, 2, "unexpected event\n"); |
399 |
break;
|
400 |
} |
401 |
g_free(event); |
402 |
} |
403 |
QSIMPLEQ_INIT(&card->event_list); |
404 |
qemu_mutex_unlock(&card->event_list_mutex); |
405 |
} |
406 |
|
407 |
static int init_pipe_signaling(EmulatedState *card) |
408 |
{ |
409 |
if (pipe(card->pipe) < 0) { |
410 |
DPRINTF(card, 2, "pipe creation failed\n"); |
411 |
return -1; |
412 |
} |
413 |
fcntl(card->pipe[0], F_SETFL, O_NONBLOCK);
|
414 |
fcntl(card->pipe[1], F_SETFL, O_NONBLOCK);
|
415 |
fcntl(card->pipe[0], F_SETOWN, getpid());
|
416 |
qemu_set_fd_handler(card->pipe[0], pipe_read, NULL, card); |
417 |
return 0; |
418 |
} |
419 |
|
420 |
#define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" |
421 |
#define CERTIFICATES_ARGS_TEMPLATE\
|
422 |
"db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)"
|
423 |
|
424 |
static int wrap_vcard_emul_init(VCardEmulOptions *options) |
425 |
{ |
426 |
static int called; |
427 |
static int options_was_null; |
428 |
|
429 |
if (called) {
|
430 |
if ((options == NULL) != options_was_null) { |
431 |
printf("%s: warning: running emulated with certificates"
|
432 |
" and emulated side by side is not supported\n",
|
433 |
__func__); |
434 |
return VCARD_EMUL_FAIL;
|
435 |
} |
436 |
vcard_emul_replay_insertion_events(); |
437 |
return VCARD_EMUL_OK;
|
438 |
} |
439 |
options_was_null = (options == NULL);
|
440 |
called = 1;
|
441 |
return vcard_emul_init(options);
|
442 |
} |
443 |
|
444 |
static int emulated_initialize_vcard_from_certificates(EmulatedState *card) |
445 |
{ |
446 |
char emul_args[200]; |
447 |
VCardEmulOptions *options = NULL;
|
448 |
|
449 |
snprintf(emul_args, sizeof(emul_args) - 1, CERTIFICATES_ARGS_TEMPLATE, |
450 |
card->db ? card->db : CERTIFICATES_DEFAULT_DB, |
451 |
card->cert1, card->cert2, card->cert3); |
452 |
options = vcard_emul_options(emul_args); |
453 |
if (options == NULL) { |
454 |
printf("%s: warning: not using certificates due to"
|
455 |
" initialization error\n", __func__);
|
456 |
} |
457 |
return wrap_vcard_emul_init(options);
|
458 |
} |
459 |
|
460 |
typedef struct EnumTable { |
461 |
const char *name; |
462 |
uint32_t value; |
463 |
} EnumTable; |
464 |
|
465 |
EnumTable backend_enum_table[] = { |
466 |
{BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED}, |
467 |
{BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES}, |
468 |
{NULL, 0}, |
469 |
}; |
470 |
|
471 |
static uint32_t parse_enumeration(char *str, |
472 |
EnumTable *table, uint32_t not_found_value) |
473 |
{ |
474 |
uint32_t ret = not_found_value; |
475 |
|
476 |
while (table->name != NULL) { |
477 |
if (strcmp(table->name, str) == 0) { |
478 |
ret = table->value; |
479 |
break;
|
480 |
} |
481 |
table++; |
482 |
} |
483 |
return ret;
|
484 |
} |
485 |
|
486 |
static int emulated_initfn(CCIDCardState *base) |
487 |
{ |
488 |
EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
489 |
VCardEmulError ret; |
490 |
EnumTable *ptable; |
491 |
|
492 |
QSIMPLEQ_INIT(&card->event_list); |
493 |
QSIMPLEQ_INIT(&card->guest_apdu_list); |
494 |
qemu_mutex_init(&card->event_list_mutex); |
495 |
qemu_mutex_init(&card->vreader_mutex); |
496 |
qemu_mutex_init(&card->handle_apdu_mutex); |
497 |
qemu_cond_init(&card->handle_apdu_cond); |
498 |
card->reader = NULL;
|
499 |
card->quit_apdu_thread = 0;
|
500 |
if (init_pipe_signaling(card) < 0) { |
501 |
return -1; |
502 |
} |
503 |
card->backend = parse_enumeration(card->backend_str, backend_enum_table, 0);
|
504 |
if (card->backend == 0) { |
505 |
printf("unknown backend, must be one of:\n");
|
506 |
for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { |
507 |
printf("%s\n", ptable->name);
|
508 |
} |
509 |
return -1; |
510 |
} |
511 |
|
512 |
/* TODO: a passthru backened that works on local machine. third card type?*/
|
513 |
if (card->backend == BACKEND_CERTIFICATES) {
|
514 |
if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) { |
515 |
ret = emulated_initialize_vcard_from_certificates(card); |
516 |
} else {
|
517 |
printf("%s: you must provide all three certs for"
|
518 |
" certificates backend\n", EMULATED_DEV_NAME);
|
519 |
return -1; |
520 |
} |
521 |
} else {
|
522 |
if (card->backend != BACKEND_NSS_EMULATED) {
|
523 |
printf("%s: bad backend specified. The options are:\n%s (default),"
|
524 |
" %s.\n", EMULATED_DEV_NAME, BACKEND_NSS_EMULATED_NAME,
|
525 |
BACKEND_CERTIFICATES_NAME); |
526 |
return -1; |
527 |
} |
528 |
if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { |
529 |
printf("%s: unexpected cert parameters to nss emulated backend\n",
|
530 |
EMULATED_DEV_NAME); |
531 |
return -1; |
532 |
} |
533 |
/* default to mirroring the local hardware readers */
|
534 |
ret = wrap_vcard_emul_init(NULL);
|
535 |
} |
536 |
if (ret != VCARD_EMUL_OK) {
|
537 |
printf("%s: failed to initialize vcard\n", EMULATED_DEV_NAME);
|
538 |
return -1; |
539 |
} |
540 |
qemu_thread_create(&card->event_thread_id, event_thread, card, |
541 |
QEMU_THREAD_JOINABLE); |
542 |
qemu_thread_create(&card->apdu_thread_id, handle_apdu_thread, card, |
543 |
QEMU_THREAD_JOINABLE); |
544 |
return 0; |
545 |
} |
546 |
|
547 |
static int emulated_exitfn(CCIDCardState *base) |
548 |
{ |
549 |
EmulatedState *card = DO_UPCAST(EmulatedState, base, base); |
550 |
VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL); |
551 |
|
552 |
vevent_queue_vevent(vevent); /* stop vevent thread */
|
553 |
qemu_thread_join(&card->event_thread_id); |
554 |
|
555 |
card->quit_apdu_thread = 1; /* stop handle_apdu thread */ |
556 |
qemu_cond_signal(&card->handle_apdu_cond); |
557 |
qemu_thread_join(&card->apdu_thread_id); |
558 |
|
559 |
/* threads exited, can destroy all condvars/mutexes */
|
560 |
qemu_cond_destroy(&card->handle_apdu_cond); |
561 |
qemu_mutex_destroy(&card->handle_apdu_mutex); |
562 |
qemu_mutex_destroy(&card->vreader_mutex); |
563 |
qemu_mutex_destroy(&card->event_list_mutex); |
564 |
return 0; |
565 |
} |
566 |
|
567 |
static Property emulated_card_properties[] = {
|
568 |
DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
|
569 |
DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
|
570 |
DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
|
571 |
DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
|
572 |
DEFINE_PROP_STRING("db", EmulatedState, db),
|
573 |
DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), |
574 |
DEFINE_PROP_END_OF_LIST(), |
575 |
}; |
576 |
|
577 |
static void emulated_class_initfn(ObjectClass *klass, void *data) |
578 |
{ |
579 |
DeviceClass *dc = DEVICE_CLASS(klass); |
580 |
CCIDCardClass *cc = CCID_CARD_CLASS(klass); |
581 |
|
582 |
cc->initfn = emulated_initfn; |
583 |
cc->exitfn = emulated_exitfn; |
584 |
cc->get_atr = emulated_get_atr; |
585 |
cc->apdu_from_guest = emulated_apdu_from_guest; |
586 |
dc->desc = "emulated smartcard";
|
587 |
dc->props = emulated_card_properties; |
588 |
} |
589 |
|
590 |
static TypeInfo emulated_card_info = {
|
591 |
.name = EMULATED_DEV_NAME, |
592 |
.parent = TYPE_CCID_CARD, |
593 |
.instance_size = sizeof(EmulatedState),
|
594 |
.class_init = emulated_class_initfn, |
595 |
}; |
596 |
|
597 |
static void ccid_card_emulated_register_types(void) |
598 |
{ |
599 |
type_register_static(&emulated_card_info); |
600 |
} |
601 |
|
602 |
type_init(ccid_card_emulated_register_types) |