xref: /openbmc/qemu/hw/usb/ccid-card-emulated.c (revision 2a8537cf)
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 "qemu/osdep.h"
30 #include <libcacard.h>
31 
32 #include "qemu/thread.h"
33 #include "qemu/lockable.h"
34 #include "qemu/main-loop.h"
35 #include "qemu/module.h"
36 #include "ccid.h"
37 #include "hw/qdev-properties.h"
38 #include "qapi/error.h"
39 #include "qom/object.h"
40 
41 #define DPRINTF(card, lvl, fmt, ...) \
42 do {\
43     if (lvl <= card->debug) {\
44         printf("ccid-card-emul: %s: " fmt , __func__, ## __VA_ARGS__);\
45     } \
46 } while (0)
47 
48 
49 #define TYPE_EMULATED_CCID "ccid-card-emulated"
50 typedef struct EmulatedState EmulatedState;
51 DECLARE_INSTANCE_CHECKER(EmulatedState, EMULATED_CCID_CARD,
52                          TYPE_EMULATED_CCID)
53 
54 #define BACKEND_NSS_EMULATED_NAME "nss-emulated"
55 #define BACKEND_CERTIFICATES_NAME "certificates"
56 
57 enum {
58     BACKEND_NSS_EMULATED = 1,
59     BACKEND_CERTIFICATES
60 };
61 
62 #define DEFAULT_BACKEND BACKEND_NSS_EMULATED
63 
64 
65 enum {
66     EMUL_READER_INSERT = 0,
67     EMUL_READER_REMOVE,
68     EMUL_CARD_INSERT,
69     EMUL_CARD_REMOVE,
70     EMUL_GUEST_APDU,
71     EMUL_RESPONSE_APDU,
72     EMUL_ERROR,
73 };
74 
emul_event_to_string(uint32_t emul_event)75 static const char *emul_event_to_string(uint32_t emul_event)
76 {
77     switch (emul_event) {
78     case EMUL_READER_INSERT:
79         return "EMUL_READER_INSERT";
80     case EMUL_READER_REMOVE:
81         return "EMUL_READER_REMOVE";
82     case EMUL_CARD_INSERT:
83         return "EMUL_CARD_INSERT";
84     case EMUL_CARD_REMOVE:
85         return "EMUL_CARD_REMOVE";
86     case EMUL_GUEST_APDU:
87         return "EMUL_GUEST_APDU";
88     case EMUL_RESPONSE_APDU:
89         return "EMUL_RESPONSE_APDU";
90     case EMUL_ERROR:
91         return "EMUL_ERROR";
92     }
93     return "UNKNOWN";
94 }
95 
96 typedef struct EmulEvent {
97     QSIMPLEQ_ENTRY(EmulEvent) entry;
98     union {
99         struct {
100             uint32_t type;
101         } gen;
102         struct {
103             uint32_t type;
104             uint64_t code;
105         } error;
106         struct {
107             uint32_t type;
108             uint32_t len;
109             uint8_t data[];
110         } data;
111     } p;
112 } EmulEvent;
113 
114 #define MAX_ATR_SIZE 40
115 struct EmulatedState {
116     CCIDCardState base;
117     uint8_t  debug;
118     char    *backend_str;
119     uint32_t backend;
120     char    *cert1;
121     char    *cert2;
122     char    *cert3;
123     char    *db;
124     uint8_t  atr[MAX_ATR_SIZE];
125     uint8_t  atr_length;
126     QSIMPLEQ_HEAD(, EmulEvent) event_list;
127     QemuMutex event_list_mutex;
128     QemuThread event_thread_id;
129     VReader *reader;
130     QSIMPLEQ_HEAD(, EmulEvent) guest_apdu_list;
131     QemuMutex vreader_mutex; /* and guest_apdu_list mutex */
132     QemuMutex handle_apdu_mutex;
133     QemuCond handle_apdu_cond;
134     EventNotifier notifier;
135     int      quit_apdu_thread;
136     QemuThread apdu_thread_id;
137 };
138 
emulated_apdu_from_guest(CCIDCardState * base,const uint8_t * apdu,uint32_t len)139 static void emulated_apdu_from_guest(CCIDCardState *base,
140     const uint8_t *apdu, uint32_t len)
141 {
142     EmulatedState *card = EMULATED_CCID_CARD(base);
143     EmulEvent *event = g_malloc(sizeof(EmulEvent) + len);
144 
145     assert(event);
146     event->p.data.type = EMUL_GUEST_APDU;
147     event->p.data.len = len;
148     memcpy(event->p.data.data, apdu, len);
149     qemu_mutex_lock(&card->vreader_mutex);
150     QSIMPLEQ_INSERT_TAIL(&card->guest_apdu_list, event, entry);
151     qemu_mutex_unlock(&card->vreader_mutex);
152     qemu_mutex_lock(&card->handle_apdu_mutex);
153     qemu_cond_signal(&card->handle_apdu_cond);
154     qemu_mutex_unlock(&card->handle_apdu_mutex);
155 }
156 
emulated_get_atr(CCIDCardState * base,uint32_t * len)157 static const uint8_t *emulated_get_atr(CCIDCardState *base, uint32_t *len)
158 {
159     EmulatedState *card = EMULATED_CCID_CARD(base);
160 
161     *len = card->atr_length;
162     return card->atr;
163 }
164 
emulated_push_event(EmulatedState * card,EmulEvent * event)165 static void emulated_push_event(EmulatedState *card, EmulEvent *event)
166 {
167     qemu_mutex_lock(&card->event_list_mutex);
168     QSIMPLEQ_INSERT_TAIL(&(card->event_list), event, entry);
169     qemu_mutex_unlock(&card->event_list_mutex);
170     event_notifier_set(&card->notifier);
171 }
172 
emulated_push_type(EmulatedState * card,uint32_t type)173 static void emulated_push_type(EmulatedState *card, uint32_t type)
174 {
175     EmulEvent *event = g_new(EmulEvent, 1);
176 
177     assert(event);
178     event->p.gen.type = type;
179     emulated_push_event(card, event);
180 }
181 
emulated_push_error(EmulatedState * card,uint64_t code)182 static void emulated_push_error(EmulatedState *card, uint64_t code)
183 {
184     EmulEvent *event = g_new(EmulEvent, 1);
185 
186     assert(event);
187     event->p.error.type = EMUL_ERROR;
188     event->p.error.code = code;
189     emulated_push_event(card, event);
190 }
191 
emulated_push_data_type(EmulatedState * card,uint32_t type,const uint8_t * data,uint32_t len)192 static void emulated_push_data_type(EmulatedState *card, uint32_t type,
193     const uint8_t *data, uint32_t len)
194 {
195     EmulEvent *event = (EmulEvent *)g_malloc(sizeof(EmulEvent) + len);
196 
197     assert(event);
198     event->p.data.type = type;
199     event->p.data.len = len;
200     memcpy(event->p.data.data, data, len);
201     emulated_push_event(card, event);
202 }
203 
emulated_push_reader_insert(EmulatedState * card)204 static void emulated_push_reader_insert(EmulatedState *card)
205 {
206     emulated_push_type(card, EMUL_READER_INSERT);
207 }
208 
emulated_push_reader_remove(EmulatedState * card)209 static void emulated_push_reader_remove(EmulatedState *card)
210 {
211     emulated_push_type(card, EMUL_READER_REMOVE);
212 }
213 
emulated_push_card_insert(EmulatedState * card,const uint8_t * atr,uint32_t len)214 static void emulated_push_card_insert(EmulatedState *card,
215     const uint8_t *atr, uint32_t len)
216 {
217     emulated_push_data_type(card, EMUL_CARD_INSERT, atr, len);
218 }
219 
emulated_push_card_remove(EmulatedState * card)220 static void emulated_push_card_remove(EmulatedState *card)
221 {
222     emulated_push_type(card, EMUL_CARD_REMOVE);
223 }
224 
emulated_push_response_apdu(EmulatedState * card,const uint8_t * apdu,uint32_t len)225 static void emulated_push_response_apdu(EmulatedState *card,
226     const uint8_t *apdu, uint32_t len)
227 {
228     emulated_push_data_type(card, EMUL_RESPONSE_APDU, apdu, len);
229 }
230 
231 #define APDU_BUF_SIZE 270
handle_apdu_thread(void * arg)232 static void *handle_apdu_thread(void* arg)
233 {
234     EmulatedState *card = arg;
235     uint8_t recv_data[APDU_BUF_SIZE];
236     int recv_len;
237     VReaderStatus reader_status;
238     EmulEvent *event;
239 
240     while (1) {
241         qemu_mutex_lock(&card->handle_apdu_mutex);
242         qemu_cond_wait(&card->handle_apdu_cond, &card->handle_apdu_mutex);
243         qemu_mutex_unlock(&card->handle_apdu_mutex);
244         if (card->quit_apdu_thread) {
245             card->quit_apdu_thread = 0; /* debugging */
246             break;
247         }
248         WITH_QEMU_LOCK_GUARD(&card->vreader_mutex) {
249             while (!QSIMPLEQ_EMPTY(&card->guest_apdu_list)) {
250                 event = QSIMPLEQ_FIRST(&card->guest_apdu_list);
251                 assert(event != NULL);
252                 QSIMPLEQ_REMOVE_HEAD(&card->guest_apdu_list, entry);
253                 if (event->p.data.type != EMUL_GUEST_APDU) {
254                     DPRINTF(card, 1, "unexpected message in handle_apdu_thread\n");
255                     g_free(event);
256                     continue;
257                 }
258                 if (card->reader == NULL) {
259                     DPRINTF(card, 1, "reader is NULL\n");
260                     g_free(event);
261                     continue;
262                 }
263                 recv_len = sizeof(recv_data);
264                 reader_status = vreader_xfr_bytes(card->reader,
265                         event->p.data.data, event->p.data.len,
266                         recv_data, &recv_len);
267                 DPRINTF(card, 2, "got back apdu of length %d\n", recv_len);
268                 if (reader_status == VREADER_OK) {
269                     emulated_push_response_apdu(card, recv_data, recv_len);
270                 } else {
271                     emulated_push_error(card, reader_status);
272                 }
273                 g_free(event);
274             }
275         }
276     }
277     return NULL;
278 }
279 
event_thread(void * arg)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: quitting 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         default:
356             break;
357         }
358         vevent_delete(event);
359     }
360     return NULL;
361 }
362 
card_event_handler(EventNotifier * notifier)363 static void card_event_handler(EventNotifier *notifier)
364 {
365     EmulatedState *card = container_of(notifier, EmulatedState, notifier);
366     EmulEvent *event, *next;
367 
368     event_notifier_test_and_clear(&card->notifier);
369     QEMU_LOCK_GUARD(&card->event_list_mutex);
370     QSIMPLEQ_FOREACH_SAFE(event, &card->event_list, entry, next) {
371         DPRINTF(card, 2, "event %s\n", emul_event_to_string(event->p.gen.type));
372         switch (event->p.gen.type) {
373         case EMUL_RESPONSE_APDU:
374             ccid_card_send_apdu_to_guest(&card->base, event->p.data.data,
375                 event->p.data.len);
376             break;
377         case EMUL_READER_INSERT:
378             ccid_card_ccid_attach(&card->base);
379             break;
380         case EMUL_READER_REMOVE:
381             ccid_card_ccid_detach(&card->base);
382             break;
383         case EMUL_CARD_INSERT:
384             assert(event->p.data.len <= MAX_ATR_SIZE);
385             card->atr_length = event->p.data.len;
386             memcpy(card->atr, event->p.data.data, card->atr_length);
387             ccid_card_card_inserted(&card->base);
388             break;
389         case EMUL_CARD_REMOVE:
390             ccid_card_card_removed(&card->base);
391             break;
392         case EMUL_ERROR:
393             ccid_card_card_error(&card->base, event->p.error.code);
394             break;
395         default:
396             DPRINTF(card, 2, "unexpected event\n");
397             break;
398         }
399         g_free(event);
400     }
401     QSIMPLEQ_INIT(&card->event_list);
402 }
403 
init_event_notifier(EmulatedState * card,Error ** errp)404 static int init_event_notifier(EmulatedState *card, Error **errp)
405 {
406     if (event_notifier_init(&card->notifier, false) < 0) {
407         error_setg(errp, "ccid-card-emul: event notifier creation failed");
408         return -1;
409     }
410     event_notifier_set_handler(&card->notifier, card_event_handler);
411     return 0;
412 }
413 
clean_event_notifier(EmulatedState * card)414 static void clean_event_notifier(EmulatedState *card)
415 {
416     event_notifier_set_handler(&card->notifier, NULL);
417     event_notifier_cleanup(&card->notifier);
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 
wrap_vcard_emul_init(VCardEmulOptions * options)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 
emulated_initialize_vcard_from_certificates(EmulatedState * card)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 static const EnumTable backend_enum_table[] = {
466     {BACKEND_NSS_EMULATED_NAME, BACKEND_NSS_EMULATED},
467     {BACKEND_CERTIFICATES_NAME, BACKEND_CERTIFICATES},
468     {NULL, 0},
469 };
470 
parse_enumeration(char * str,const EnumTable * table,uint32_t not_found_value)471 static uint32_t parse_enumeration(char *str,
472     const EnumTable *table, uint32_t not_found_value)
473 {
474     uint32_t ret = not_found_value;
475 
476     if (str == NULL)
477         return 0;
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 
emulated_realize(CCIDCardState * base,Error ** errp)489 static void emulated_realize(CCIDCardState *base, Error **errp)
490 {
491     EmulatedState *card = EMULATED_CCID_CARD(base);
492     VCardEmulError ret;
493     const EnumTable *ptable;
494 
495     QSIMPLEQ_INIT(&card->event_list);
496     QSIMPLEQ_INIT(&card->guest_apdu_list);
497     qemu_mutex_init(&card->event_list_mutex);
498     qemu_mutex_init(&card->vreader_mutex);
499     qemu_mutex_init(&card->handle_apdu_mutex);
500     qemu_cond_init(&card->handle_apdu_cond);
501     card->reader = NULL;
502     card->quit_apdu_thread = 0;
503     if (init_event_notifier(card, errp) < 0) {
504         goto out1;
505     }
506 
507     card->backend = 0;
508     if (card->backend_str) {
509         card->backend = parse_enumeration(card->backend_str,
510                                           backend_enum_table, 0);
511     }
512 
513     if (card->backend == 0) {
514         error_setg(errp, "backend must be one of:");
515         for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) {
516             error_append_hint(errp, "%s\n", ptable->name);
517         }
518         goto out2;
519     }
520 
521     /* TODO: a passthru backend that works on local machine. third card type?*/
522     if (card->backend == BACKEND_CERTIFICATES) {
523         if (card->cert1 != NULL && card->cert2 != NULL && card->cert3 != NULL) {
524             ret = emulated_initialize_vcard_from_certificates(card);
525         } else {
526             error_setg(errp, "%s: you must provide all three certs for"
527                        " certificates backend", TYPE_EMULATED_CCID);
528             goto out2;
529         }
530     } else {
531         if (card->backend != BACKEND_NSS_EMULATED) {
532             error_setg(errp, "%s: bad backend specified. The options are:%s"
533                        " (default), %s.", TYPE_EMULATED_CCID,
534                        BACKEND_NSS_EMULATED_NAME, BACKEND_CERTIFICATES_NAME);
535             goto out2;
536         }
537         if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) {
538             error_setg(errp, "%s: unexpected cert parameters to nss emulated "
539                        "backend", TYPE_EMULATED_CCID);
540             goto out2;
541         }
542         /* default to mirroring the local hardware readers */
543         ret = wrap_vcard_emul_init(NULL);
544     }
545     if (ret != VCARD_EMUL_OK) {
546         error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID);
547         goto out2;
548     }
549     qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread,
550                        card, QEMU_THREAD_JOINABLE);
551     qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread,
552                        card, QEMU_THREAD_JOINABLE);
553 
554     return;
555 
556 out2:
557     clean_event_notifier(card);
558 out1:
559     qemu_cond_destroy(&card->handle_apdu_cond);
560     qemu_mutex_destroy(&card->handle_apdu_mutex);
561     qemu_mutex_destroy(&card->vreader_mutex);
562     qemu_mutex_destroy(&card->event_list_mutex);
563 }
564 
emulated_unrealize(CCIDCardState * base)565 static void emulated_unrealize(CCIDCardState *base)
566 {
567     EmulatedState *card = EMULATED_CCID_CARD(base);
568     VEvent *vevent = vevent_new(VEVENT_LAST, NULL, NULL);
569 
570     vevent_queue_vevent(vevent); /* stop vevent thread */
571     qemu_thread_join(&card->event_thread_id);
572 
573     card->quit_apdu_thread = 1; /* stop handle_apdu thread */
574     qemu_cond_signal(&card->handle_apdu_cond);
575     qemu_thread_join(&card->apdu_thread_id);
576 
577     clean_event_notifier(card);
578     /* threads exited, can destroy all condvars/mutexes */
579     qemu_cond_destroy(&card->handle_apdu_cond);
580     qemu_mutex_destroy(&card->handle_apdu_mutex);
581     qemu_mutex_destroy(&card->vreader_mutex);
582     qemu_mutex_destroy(&card->event_list_mutex);
583 }
584 
585 static Property emulated_card_properties[] = {
586     DEFINE_PROP_STRING("backend", EmulatedState, backend_str),
587     DEFINE_PROP_STRING("cert1", EmulatedState, cert1),
588     DEFINE_PROP_STRING("cert2", EmulatedState, cert2),
589     DEFINE_PROP_STRING("cert3", EmulatedState, cert3),
590     DEFINE_PROP_STRING("db", EmulatedState, db),
591     DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0),
592     DEFINE_PROP_END_OF_LIST(),
593 };
594 
emulated_class_initfn(ObjectClass * klass,void * data)595 static void emulated_class_initfn(ObjectClass *klass, void *data)
596 {
597     DeviceClass *dc = DEVICE_CLASS(klass);
598     CCIDCardClass *cc = CCID_CARD_CLASS(klass);
599 
600     cc->realize = emulated_realize;
601     cc->unrealize = emulated_unrealize;
602     cc->get_atr = emulated_get_atr;
603     cc->apdu_from_guest = emulated_apdu_from_guest;
604     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
605     dc->desc = "emulated smartcard";
606     device_class_set_props(dc, emulated_card_properties);
607 }
608 
609 static const TypeInfo emulated_card_info = {
610     .name          = TYPE_EMULATED_CCID,
611     .parent        = TYPE_CCID_CARD,
612     .instance_size = sizeof(EmulatedState),
613     .class_init    = emulated_class_initfn,
614 };
615 module_obj(TYPE_EMULATED_CCID);
616 module_kconfig(USB);
617 
ccid_card_emulated_register_types(void)618 static void ccid_card_emulated_register_types(void)
619 {
620     type_register_static(&emulated_card_info);
621 }
622 
623 type_init(ccid_card_emulated_register_types)
624