Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 1 | /* |
| 2 | * emulate the reader |
| 3 | * |
| 4 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. |
| 5 | * See the COPYING.LIB file in the top-level directory. |
| 6 | */ |
| 7 | |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 8 | #ifdef G_LOG_DOMAIN |
| 9 | #undef G_LOG_DOMAIN |
| 10 | #endif |
| 11 | #define G_LOG_DOMAIN "libcacard" |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 12 | |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 13 | #include "qemu-common.h" |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 14 | |
| 15 | #include "vcard.h" |
| 16 | #include "vcard_emul.h" |
| 17 | #include "card_7816.h" |
| 18 | #include "vreader.h" |
| 19 | #include "vevent.h" |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 20 | #include "cac.h" /* just for debugging defines */ |
| 21 | |
| 22 | #define LIBCACARD_LOG_DOMAIN "libcacard" |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 23 | |
| 24 | struct VReaderStruct { |
| 25 | int reference_count; |
| 26 | VCard *card; |
| 27 | char *name; |
| 28 | vreader_id_t id; |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 29 | CompatGMutex lock; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 30 | VReaderEmul *reader_private; |
| 31 | VReaderEmulFree reader_private_free; |
| 32 | }; |
| 33 | |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 34 | /* |
| 35 | * Debug helpers |
| 36 | */ |
| 37 | |
| 38 | static const char * |
| 39 | apdu_ins_to_string(int ins) |
| 40 | { |
| 41 | switch (ins) { |
| 42 | case VCARD7816_INS_MANAGE_CHANNEL: |
| 43 | return "manage channel"; |
| 44 | case VCARD7816_INS_EXTERNAL_AUTHENTICATE: |
| 45 | return "external authenticate"; |
| 46 | case VCARD7816_INS_GET_CHALLENGE: |
| 47 | return "get challenge"; |
| 48 | case VCARD7816_INS_INTERNAL_AUTHENTICATE: |
| 49 | return "internal authenticate"; |
| 50 | case VCARD7816_INS_ERASE_BINARY: |
| 51 | return "erase binary"; |
| 52 | case VCARD7816_INS_READ_BINARY: |
| 53 | return "read binary"; |
| 54 | case VCARD7816_INS_WRITE_BINARY: |
| 55 | return "write binary"; |
| 56 | case VCARD7816_INS_UPDATE_BINARY: |
| 57 | return "update binary"; |
| 58 | case VCARD7816_INS_READ_RECORD: |
| 59 | return "read record"; |
| 60 | case VCARD7816_INS_WRITE_RECORD: |
| 61 | return "write record"; |
| 62 | case VCARD7816_INS_UPDATE_RECORD: |
| 63 | return "update record"; |
| 64 | case VCARD7816_INS_APPEND_RECORD: |
| 65 | return "append record"; |
| 66 | case VCARD7816_INS_ENVELOPE: |
| 67 | return "envelope"; |
| 68 | case VCARD7816_INS_PUT_DATA: |
| 69 | return "put data"; |
| 70 | case VCARD7816_INS_GET_DATA: |
| 71 | return "get data"; |
| 72 | case VCARD7816_INS_SELECT_FILE: |
| 73 | return "select file"; |
| 74 | case VCARD7816_INS_VERIFY: |
| 75 | return "verify"; |
| 76 | case VCARD7816_INS_GET_RESPONSE: |
| 77 | return "get response"; |
| 78 | case CAC_GET_PROPERTIES: |
| 79 | return "get properties"; |
| 80 | case CAC_GET_ACR: |
| 81 | return "get acr"; |
| 82 | case CAC_READ_BUFFER: |
| 83 | return "read buffer"; |
| 84 | case CAC_UPDATE_BUFFER: |
| 85 | return "update buffer"; |
| 86 | case CAC_SIGN_DECRYPT: |
| 87 | return "sign decrypt"; |
| 88 | case CAC_GET_CERTIFICATE: |
| 89 | return "get certificate"; |
| 90 | } |
| 91 | return "unknown"; |
| 92 | } |
| 93 | |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 94 | /* manage locking */ |
| 95 | static inline void |
| 96 | vreader_lock(VReader *reader) |
| 97 | { |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 98 | g_mutex_lock(&reader->lock); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | static inline void |
| 102 | vreader_unlock(VReader *reader) |
| 103 | { |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 104 | g_mutex_unlock(&reader->lock); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | /* |
| 108 | * vreader constructor |
| 109 | */ |
| 110 | VReader * |
| 111 | vreader_new(const char *name, VReaderEmul *private, |
| 112 | VReaderEmulFree private_free) |
| 113 | { |
| 114 | VReader *reader; |
| 115 | |
Michael Tokarev | 78a4b8d | 2014-05-08 19:51:01 +0400 | [diff] [blame] | 116 | reader = g_new(VReader, 1); |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 117 | g_mutex_init(&reader->lock); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 118 | reader->reference_count = 1; |
Markus Armbruster | be168af | 2013-01-22 11:08:04 +0100 | [diff] [blame] | 119 | reader->name = g_strdup(name); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 120 | reader->card = NULL; |
| 121 | reader->id = (vreader_id_t)-1; |
| 122 | reader->reader_private = private; |
| 123 | reader->reader_private_free = private_free; |
| 124 | return reader; |
| 125 | } |
| 126 | |
| 127 | /* get a reference */ |
| 128 | VReader* |
| 129 | vreader_reference(VReader *reader) |
| 130 | { |
| 131 | if (reader == NULL) { |
| 132 | return NULL; |
| 133 | } |
| 134 | vreader_lock(reader); |
| 135 | reader->reference_count++; |
| 136 | vreader_unlock(reader); |
| 137 | return reader; |
| 138 | } |
| 139 | |
| 140 | /* free a reference */ |
| 141 | void |
| 142 | vreader_free(VReader *reader) |
| 143 | { |
| 144 | if (reader == NULL) { |
| 145 | return; |
| 146 | } |
| 147 | vreader_lock(reader); |
| 148 | if (reader->reference_count-- > 1) { |
| 149 | vreader_unlock(reader); |
| 150 | return; |
| 151 | } |
| 152 | vreader_unlock(reader); |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 153 | g_mutex_clear(&reader->lock); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 154 | if (reader->card) { |
| 155 | vcard_free(reader->card); |
| 156 | } |
Markus Armbruster | ec15993 | 2014-06-06 18:32:08 +0200 | [diff] [blame] | 157 | g_free(reader->name); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 158 | if (reader->reader_private_free) { |
| 159 | reader->reader_private_free(reader->reader_private); |
| 160 | } |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 161 | g_free(reader); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 162 | } |
| 163 | |
| 164 | static VCard * |
| 165 | vreader_get_card(VReader *reader) |
| 166 | { |
| 167 | VCard *card; |
| 168 | |
| 169 | vreader_lock(reader); |
| 170 | card = vcard_reference(reader->card); |
| 171 | vreader_unlock(reader); |
| 172 | return card; |
| 173 | } |
| 174 | |
| 175 | VReaderStatus |
| 176 | vreader_card_is_present(VReader *reader) |
| 177 | { |
| 178 | VCard *card = vreader_get_card(reader); |
| 179 | |
| 180 | if (card == NULL) { |
| 181 | return VREADER_NO_CARD; |
| 182 | } |
| 183 | vcard_free(card); |
| 184 | return VREADER_OK; |
| 185 | } |
| 186 | |
| 187 | vreader_id_t |
| 188 | vreader_get_id(VReader *reader) |
| 189 | { |
| 190 | if (reader == NULL) { |
| 191 | return (vreader_id_t)-1; |
| 192 | } |
| 193 | return reader->id; |
| 194 | } |
| 195 | |
| 196 | VReaderStatus |
| 197 | vreader_set_id(VReader *reader, vreader_id_t id) |
| 198 | { |
| 199 | if (reader == NULL) { |
| 200 | return VREADER_NO_CARD; |
| 201 | } |
| 202 | reader->id = id; |
| 203 | return VREADER_OK; |
| 204 | } |
| 205 | |
| 206 | const char * |
| 207 | vreader_get_name(VReader *reader) |
| 208 | { |
| 209 | if (reader == NULL) { |
| 210 | return NULL; |
| 211 | } |
| 212 | return reader->name; |
| 213 | } |
| 214 | |
| 215 | VReaderEmul * |
| 216 | vreader_get_private(VReader *reader) |
| 217 | { |
| 218 | return reader->reader_private; |
| 219 | } |
| 220 | |
| 221 | static VReaderStatus |
| 222 | vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len) |
| 223 | { |
| 224 | VCard *card = vreader_get_card(reader); |
| 225 | |
| 226 | if (card == NULL) { |
| 227 | return VREADER_NO_CARD; |
| 228 | } |
| 229 | /* |
| 230 | * clean up our state |
| 231 | */ |
| 232 | vcard_reset(card, power); |
| 233 | if (atr) { |
| 234 | vcard_get_atr(card, atr, len); |
| 235 | } |
| 236 | vcard_free(card); /* free our reference */ |
| 237 | return VREADER_OK; |
| 238 | } |
| 239 | |
| 240 | VReaderStatus |
| 241 | vreader_power_on(VReader *reader, unsigned char *atr, int *len) |
| 242 | { |
| 243 | return vreader_reset(reader, VCARD_POWER_ON, atr, len); |
| 244 | } |
| 245 | |
| 246 | VReaderStatus |
| 247 | vreader_power_off(VReader *reader) |
| 248 | { |
| 249 | return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0); |
| 250 | } |
| 251 | |
| 252 | |
| 253 | VReaderStatus |
| 254 | vreader_xfr_bytes(VReader *reader, |
| 255 | unsigned char *send_buf, int send_buf_len, |
| 256 | unsigned char *receive_buf, int *receive_buf_len) |
| 257 | { |
| 258 | VCardAPDU *apdu; |
| 259 | VCardResponse *response = NULL; |
| 260 | VCardStatus card_status; |
| 261 | unsigned short status; |
| 262 | VCard *card = vreader_get_card(reader); |
| 263 | |
| 264 | if (card == NULL) { |
| 265 | return VREADER_NO_CARD; |
| 266 | } |
| 267 | |
| 268 | apdu = vcard_apdu_new(send_buf, send_buf_len, &status); |
| 269 | if (apdu == NULL) { |
| 270 | response = vcard_make_response(status); |
| 271 | card_status = VCARD_DONE; |
| 272 | } else { |
Alon Levy | 8e25c27 | 2014-05-05 17:41:32 +0300 | [diff] [blame] | 273 | g_debug("%s: CLS=0x%x,INS=0x%x,P1=0x%x,P2=0x%x,Lc=%d,Le=%d %s", |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 274 | __func__, apdu->a_cla, apdu->a_ins, apdu->a_p1, apdu->a_p2, |
| 275 | apdu->a_Lc, apdu->a_Le, apdu_ins_to_string(apdu->a_ins)); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 276 | card_status = vcard_process_apdu(card, apdu, &response); |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 277 | if (response) { |
Alon Levy | 8e25c27 | 2014-05-05 17:41:32 +0300 | [diff] [blame] | 278 | g_debug("%s: status=%d sw1=0x%x sw2=0x%x len=%d (total=%d)", |
Alon Levy | 7a68589 | 2013-03-05 15:32:19 +0200 | [diff] [blame] | 279 | __func__, response->b_status, response->b_sw1, |
| 280 | response->b_sw2, response->b_len, response->b_total_len); |
| 281 | } |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 282 | } |
Markus Armbruster | f33a984 | 2014-05-23 13:24:36 +0200 | [diff] [blame] | 283 | assert(card_status == VCARD_DONE && response); |
Markus Armbruster | fa5912a | 2014-05-23 13:24:35 +0200 | [diff] [blame] | 284 | int size = MIN(*receive_buf_len, response->b_total_len); |
| 285 | memcpy(receive_buf, response->b_data, size); |
| 286 | *receive_buf_len = size; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 287 | vcard_response_delete(response); |
| 288 | vcard_apdu_delete(apdu); |
| 289 | vcard_free(card); /* free our reference */ |
| 290 | return VREADER_OK; |
| 291 | } |
| 292 | |
| 293 | struct VReaderListStruct { |
| 294 | VReaderListEntry *head; |
| 295 | VReaderListEntry *tail; |
| 296 | }; |
| 297 | |
| 298 | struct VReaderListEntryStruct { |
| 299 | VReaderListEntry *next; |
| 300 | VReaderListEntry *prev; |
| 301 | VReader *reader; |
| 302 | }; |
| 303 | |
| 304 | |
| 305 | static VReaderListEntry * |
| 306 | vreader_list_entry_new(VReader *reader) |
| 307 | { |
| 308 | VReaderListEntry *new_reader_list_entry; |
| 309 | |
Michael Tokarev | 78a4b8d | 2014-05-08 19:51:01 +0400 | [diff] [blame] | 310 | new_reader_list_entry = g_new0(VReaderListEntry, 1); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 311 | new_reader_list_entry->reader = vreader_reference(reader); |
| 312 | return new_reader_list_entry; |
| 313 | } |
| 314 | |
| 315 | static void |
| 316 | vreader_list_entry_delete(VReaderListEntry *entry) |
| 317 | { |
| 318 | if (entry == NULL) { |
| 319 | return; |
| 320 | } |
| 321 | vreader_free(entry->reader); |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 322 | g_free(entry); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 323 | } |
| 324 | |
| 325 | |
| 326 | static VReaderList * |
| 327 | vreader_list_new(void) |
| 328 | { |
| 329 | VReaderList *new_reader_list; |
| 330 | |
Michael Tokarev | 78a4b8d | 2014-05-08 19:51:01 +0400 | [diff] [blame] | 331 | new_reader_list = g_new0(VReaderList, 1); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 332 | return new_reader_list; |
| 333 | } |
| 334 | |
| 335 | void |
| 336 | vreader_list_delete(VReaderList *list) |
| 337 | { |
| 338 | VReaderListEntry *current_entry; |
Michael Tokarev | 1687a08 | 2014-05-08 21:17:38 +0400 | [diff] [blame] | 339 | VReaderListEntry *next_entry; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 340 | for (current_entry = vreader_list_get_first(list); current_entry; |
| 341 | current_entry = next_entry) { |
| 342 | next_entry = vreader_list_get_next(current_entry); |
| 343 | vreader_list_entry_delete(current_entry); |
| 344 | } |
Anthony Liguori | 7267c09 | 2011-08-20 22:09:37 -0500 | [diff] [blame] | 345 | g_free(list); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 346 | } |
| 347 | |
| 348 | |
| 349 | VReaderListEntry * |
| 350 | vreader_list_get_first(VReaderList *list) |
| 351 | { |
| 352 | return list ? list->head : NULL; |
| 353 | } |
| 354 | |
| 355 | VReaderListEntry * |
| 356 | vreader_list_get_next(VReaderListEntry *current) |
| 357 | { |
| 358 | return current ? current->next : NULL; |
| 359 | } |
| 360 | |
| 361 | VReader * |
| 362 | vreader_list_get_reader(VReaderListEntry *entry) |
| 363 | { |
| 364 | return entry ? vreader_reference(entry->reader) : NULL; |
| 365 | } |
| 366 | |
| 367 | static void |
| 368 | vreader_queue(VReaderList *list, VReaderListEntry *entry) |
| 369 | { |
| 370 | if (entry == NULL) { |
| 371 | return; |
| 372 | } |
| 373 | entry->next = NULL; |
| 374 | entry->prev = list->tail; |
| 375 | if (list->head) { |
| 376 | list->tail->next = entry; |
| 377 | } else { |
| 378 | list->head = entry; |
| 379 | } |
| 380 | list->tail = entry; |
| 381 | } |
| 382 | |
| 383 | static void |
| 384 | vreader_dequeue(VReaderList *list, VReaderListEntry *entry) |
| 385 | { |
| 386 | if (entry == NULL) { |
| 387 | return; |
| 388 | } |
| 389 | if (entry->next == NULL) { |
| 390 | list->tail = entry->prev; |
| 391 | } else if (entry->prev == NULL) { |
| 392 | list->head = entry->next; |
| 393 | } else { |
| 394 | entry->prev->next = entry->next; |
| 395 | entry->next->prev = entry->prev; |
| 396 | } |
| 397 | if ((list->tail == NULL) || (list->head == NULL)) { |
| 398 | list->head = list->tail = NULL; |
| 399 | } |
| 400 | entry->next = entry->prev = NULL; |
| 401 | } |
| 402 | |
| 403 | static VReaderList *vreader_list; |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 404 | static CompatGMutex vreader_list_mutex; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 405 | |
| 406 | static void |
| 407 | vreader_list_init(void) |
| 408 | { |
| 409 | vreader_list = vreader_list_new(); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 410 | } |
| 411 | |
| 412 | static void |
| 413 | vreader_list_lock(void) |
| 414 | { |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 415 | g_mutex_lock(&vreader_list_mutex); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 416 | } |
| 417 | |
| 418 | static void |
| 419 | vreader_list_unlock(void) |
| 420 | { |
Michael Tokarev | fd25c0e | 2014-05-08 12:30:48 +0400 | [diff] [blame] | 421 | g_mutex_unlock(&vreader_list_mutex); |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 422 | } |
| 423 | |
| 424 | static VReaderList * |
| 425 | vreader_copy_list(VReaderList *list) |
| 426 | { |
Michael Tokarev | 1687a08 | 2014-05-08 21:17:38 +0400 | [diff] [blame] | 427 | VReaderList *new_list; |
| 428 | VReaderListEntry *current_entry; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 429 | |
| 430 | new_list = vreader_list_new(); |
| 431 | if (new_list == NULL) { |
| 432 | return NULL; |
| 433 | } |
| 434 | for (current_entry = vreader_list_get_first(list); current_entry; |
| 435 | current_entry = vreader_list_get_next(current_entry)) { |
| 436 | VReader *reader = vreader_list_get_reader(current_entry); |
| 437 | VReaderListEntry *new_entry = vreader_list_entry_new(reader); |
| 438 | |
| 439 | vreader_free(reader); |
| 440 | vreader_queue(new_list, new_entry); |
| 441 | } |
| 442 | return new_list; |
| 443 | } |
| 444 | |
| 445 | VReaderList * |
| 446 | vreader_get_reader_list(void) |
| 447 | { |
| 448 | VReaderList *new_reader_list; |
| 449 | |
| 450 | vreader_list_lock(); |
| 451 | new_reader_list = vreader_copy_list(vreader_list); |
| 452 | vreader_list_unlock(); |
| 453 | return new_reader_list; |
| 454 | } |
| 455 | |
| 456 | VReader * |
| 457 | vreader_get_reader_by_id(vreader_id_t id) |
| 458 | { |
| 459 | VReader *reader = NULL; |
Michael Tokarev | 1687a08 | 2014-05-08 21:17:38 +0400 | [diff] [blame] | 460 | VReaderListEntry *current_entry; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 461 | |
| 462 | if (id == (vreader_id_t) -1) { |
| 463 | return NULL; |
| 464 | } |
| 465 | |
| 466 | vreader_list_lock(); |
| 467 | for (current_entry = vreader_list_get_first(vreader_list); current_entry; |
| 468 | current_entry = vreader_list_get_next(current_entry)) { |
| 469 | VReader *creader = vreader_list_get_reader(current_entry); |
| 470 | if (creader->id == id) { |
| 471 | reader = creader; |
| 472 | break; |
| 473 | } |
| 474 | vreader_free(creader); |
| 475 | } |
| 476 | vreader_list_unlock(); |
| 477 | return reader; |
| 478 | } |
| 479 | |
| 480 | VReader * |
| 481 | vreader_get_reader_by_name(const char *name) |
| 482 | { |
| 483 | VReader *reader = NULL; |
Michael Tokarev | 1687a08 | 2014-05-08 21:17:38 +0400 | [diff] [blame] | 484 | VReaderListEntry *current_entry; |
Robert Relyea | 111a38b | 2010-11-28 16:36:38 +0200 | [diff] [blame] | 485 | |
| 486 | vreader_list_lock(); |
| 487 | for (current_entry = vreader_list_get_first(vreader_list); current_entry; |
| 488 | current_entry = vreader_list_get_next(current_entry)) { |
| 489 | VReader *creader = vreader_list_get_reader(current_entry); |
| 490 | if (strcmp(creader->name, name) == 0) { |
| 491 | reader = creader; |
| 492 | break; |
| 493 | } |
| 494 | vreader_free(creader); |
| 495 | } |
| 496 | vreader_list_unlock(); |
| 497 | return reader; |
| 498 | } |
| 499 | |
| 500 | /* called from card_emul to initialize the readers */ |
| 501 | VReaderStatus |
| 502 | vreader_add_reader(VReader *reader) |
| 503 | { |
| 504 | VReaderListEntry *reader_entry; |
| 505 | |
| 506 | reader_entry = vreader_list_entry_new(reader); |
| 507 | if (reader_entry == NULL) { |
| 508 | return VREADER_OUT_OF_MEMORY; |
| 509 | } |
| 510 | vreader_list_lock(); |
| 511 | vreader_queue(vreader_list, reader_entry); |
| 512 | vreader_list_unlock(); |
| 513 | vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL)); |
| 514 | return VREADER_OK; |
| 515 | } |
| 516 | |
| 517 | |
| 518 | VReaderStatus |
| 519 | vreader_remove_reader(VReader *reader) |
| 520 | { |
| 521 | VReaderListEntry *current_entry; |
| 522 | |
| 523 | vreader_list_lock(); |
| 524 | for (current_entry = vreader_list_get_first(vreader_list); current_entry; |
| 525 | current_entry = vreader_list_get_next(current_entry)) { |
| 526 | if (current_entry->reader == reader) { |
| 527 | break; |
| 528 | } |
| 529 | } |
| 530 | vreader_dequeue(vreader_list, current_entry); |
| 531 | vreader_list_unlock(); |
| 532 | vreader_list_entry_delete(current_entry); |
| 533 | vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL)); |
| 534 | return VREADER_OK; |
| 535 | } |
| 536 | |
| 537 | /* |
| 538 | * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader |
| 539 | * state. Separated from vreader_insert_card to allow replaying events |
| 540 | * for a given state. |
| 541 | */ |
| 542 | void |
| 543 | vreader_queue_card_event(VReader *reader) |
| 544 | { |
| 545 | vevent_queue_vevent(vevent_new( |
| 546 | reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, |
| 547 | reader->card)); |
| 548 | } |
| 549 | |
| 550 | /* |
| 551 | * insert/remove a new card. for removal, card == NULL |
| 552 | */ |
| 553 | VReaderStatus |
| 554 | vreader_insert_card(VReader *reader, VCard *card) |
| 555 | { |
| 556 | vreader_lock(reader); |
| 557 | if (reader->card) { |
| 558 | /* decrement reference count */ |
| 559 | vcard_free(reader->card); |
| 560 | reader->card = NULL; |
| 561 | } |
| 562 | reader->card = vcard_reference(card); |
| 563 | vreader_unlock(reader); |
| 564 | vreader_queue_card_event(reader); |
| 565 | return VREADER_OK; |
| 566 | } |
| 567 | |
| 568 | /* |
| 569 | * initialize all the static reader structures |
| 570 | */ |
| 571 | void |
| 572 | vreader_init(void) |
| 573 | { |
| 574 | vreader_list_init(); |
| 575 | } |
| 576 | |