 7267c0947d
			
		
	
	
		7267c0947d
		
	
	
	
	
		
			
			qemu_malloc/qemu_free no longer exist after this commit. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
		
			
				
	
	
		
			514 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			514 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * emulate the reader
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 | |
|  * See the COPYING.LIB file in the top-level directory.
 | |
|  */
 | |
| 
 | |
| #include "qemu-common.h"
 | |
| #include "qemu-thread.h"
 | |
| 
 | |
| #include "vcard.h"
 | |
| #include "vcard_emul.h"
 | |
| #include "card_7816.h"
 | |
| #include "vreader.h"
 | |
| #include "vevent.h"
 | |
| 
 | |
| struct VReaderStruct {
 | |
|     int    reference_count;
 | |
|     VCard *card;
 | |
|     char *name;
 | |
|     vreader_id_t id;
 | |
|     QemuMutex lock;
 | |
|     VReaderEmul  *reader_private;
 | |
|     VReaderEmulFree reader_private_free;
 | |
| };
 | |
| 
 | |
| /* manage locking */
 | |
| static inline void
 | |
| vreader_lock(VReader *reader)
 | |
| {
 | |
|     qemu_mutex_lock(&reader->lock);
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| vreader_unlock(VReader *reader)
 | |
| {
 | |
|     qemu_mutex_unlock(&reader->lock);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * vreader constructor
 | |
|  */
 | |
| VReader *
 | |
| vreader_new(const char *name, VReaderEmul *private,
 | |
|             VReaderEmulFree private_free)
 | |
| {
 | |
|     VReader *reader;
 | |
| 
 | |
|     reader = (VReader *)g_malloc(sizeof(VReader));
 | |
|     qemu_mutex_init(&reader->lock);
 | |
|     reader->reference_count = 1;
 | |
|     reader->name = name ? strdup(name) : NULL;
 | |
|     reader->card = NULL;
 | |
|     reader->id = (vreader_id_t)-1;
 | |
|     reader->reader_private = private;
 | |
|     reader->reader_private_free = private_free;
 | |
|     return reader;
 | |
| }
 | |
| 
 | |
| /* get a reference */
 | |
| VReader*
 | |
| vreader_reference(VReader *reader)
 | |
| {
 | |
|     if (reader == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     vreader_lock(reader);
 | |
|     reader->reference_count++;
 | |
|     vreader_unlock(reader);
 | |
|     return reader;
 | |
| }
 | |
| 
 | |
| /* free a reference */
 | |
| void
 | |
| vreader_free(VReader *reader)
 | |
| {
 | |
|     if (reader == NULL) {
 | |
|         return;
 | |
|     }
 | |
|     vreader_lock(reader);
 | |
|     if (reader->reference_count-- > 1) {
 | |
|         vreader_unlock(reader);
 | |
|         return;
 | |
|     }
 | |
|     vreader_unlock(reader);
 | |
|     if (reader->card) {
 | |
|         vcard_free(reader->card);
 | |
|     }
 | |
|     if (reader->name) {
 | |
|         g_free(reader->name);
 | |
|     }
 | |
|     if (reader->reader_private_free) {
 | |
|         reader->reader_private_free(reader->reader_private);
 | |
|     }
 | |
|     g_free(reader);
 | |
|     return;
 | |
| }
 | |
| 
 | |
| static VCard *
 | |
| vreader_get_card(VReader *reader)
 | |
| {
 | |
|     VCard *card;
 | |
| 
 | |
|     vreader_lock(reader);
 | |
|     card = vcard_reference(reader->card);
 | |
|     vreader_unlock(reader);
 | |
|     return card;
 | |
| }
 | |
| 
 | |
| VReaderStatus
 | |
| vreader_card_is_present(VReader *reader)
 | |
| {
 | |
|     VCard *card = vreader_get_card(reader);
 | |
| 
 | |
|     if (card == NULL) {
 | |
|         return VREADER_NO_CARD;
 | |
|     }
 | |
|     vcard_free(card);
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| vreader_id_t
 | |
| vreader_get_id(VReader *reader)
 | |
| {
 | |
|     if (reader == NULL) {
 | |
|         return (vreader_id_t)-1;
 | |
|     }
 | |
|     return reader->id;
 | |
| }
 | |
| 
 | |
| VReaderStatus
 | |
| vreader_set_id(VReader *reader, vreader_id_t id)
 | |
| {
 | |
|     if (reader == NULL) {
 | |
|         return VREADER_NO_CARD;
 | |
|     }
 | |
|     reader->id = id;
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| const char *
 | |
| vreader_get_name(VReader *reader)
 | |
| {
 | |
|     if (reader == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     return reader->name;
 | |
| }
 | |
| 
 | |
| VReaderEmul *
 | |
| vreader_get_private(VReader *reader)
 | |
| {
 | |
|     return reader->reader_private;
 | |
| }
 | |
| 
 | |
| static VReaderStatus
 | |
| vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
 | |
| {
 | |
|     VCard *card = vreader_get_card(reader);
 | |
| 
 | |
|     if (card == NULL) {
 | |
|         return VREADER_NO_CARD;
 | |
|     }
 | |
|     /*
 | |
|      * clean up our state
 | |
|      */
 | |
|     vcard_reset(card, power);
 | |
|     if (atr) {
 | |
|         vcard_get_atr(card, atr, len);
 | |
|     }
 | |
|     vcard_free(card); /* free our reference */
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| VReaderStatus
 | |
| vreader_power_on(VReader *reader, unsigned char *atr, int *len)
 | |
| {
 | |
|     return vreader_reset(reader, VCARD_POWER_ON, atr, len);
 | |
| }
 | |
| 
 | |
| VReaderStatus
 | |
| vreader_power_off(VReader *reader)
 | |
| {
 | |
|     return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| VReaderStatus
 | |
| vreader_xfr_bytes(VReader *reader,
 | |
|                   unsigned char *send_buf, int send_buf_len,
 | |
|                   unsigned char *receive_buf, int *receive_buf_len)
 | |
| {
 | |
|     VCardAPDU *apdu;
 | |
|     VCardResponse *response = NULL;
 | |
|     VCardStatus card_status;
 | |
|     unsigned short status;
 | |
|     VCard *card = vreader_get_card(reader);
 | |
| 
 | |
|     if (card == NULL) {
 | |
|         return VREADER_NO_CARD;
 | |
|     }
 | |
| 
 | |
|     apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
 | |
|     if (apdu == NULL) {
 | |
|         response = vcard_make_response(status);
 | |
|         card_status = VCARD_DONE;
 | |
|     } else {
 | |
|         card_status = vcard_process_apdu(card, apdu, &response);
 | |
|     }
 | |
|     assert(card_status == VCARD_DONE);
 | |
|     if (card_status == VCARD_DONE) {
 | |
|         int size = MIN(*receive_buf_len, response->b_total_len);
 | |
|         memcpy(receive_buf, response->b_data, size);
 | |
|         *receive_buf_len = size;
 | |
|     }
 | |
|     vcard_response_delete(response);
 | |
|     vcard_apdu_delete(apdu);
 | |
|     vcard_free(card); /* free our reference */
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| struct VReaderListStruct {
 | |
|     VReaderListEntry *head;
 | |
|     VReaderListEntry *tail;
 | |
| };
 | |
| 
 | |
| struct VReaderListEntryStruct {
 | |
|     VReaderListEntry *next;
 | |
|     VReaderListEntry *prev;
 | |
|     VReader *reader;
 | |
| };
 | |
| 
 | |
| 
 | |
| static VReaderListEntry *
 | |
| vreader_list_entry_new(VReader *reader)
 | |
| {
 | |
|     VReaderListEntry *new_reader_list_entry;
 | |
| 
 | |
|     new_reader_list_entry = (VReaderListEntry *)
 | |
|                                g_malloc(sizeof(VReaderListEntry));
 | |
|     new_reader_list_entry->next = NULL;
 | |
|     new_reader_list_entry->prev = NULL;
 | |
|     new_reader_list_entry->reader = vreader_reference(reader);
 | |
|     return new_reader_list_entry;
 | |
| }
 | |
| 
 | |
| static void
 | |
| vreader_list_entry_delete(VReaderListEntry *entry)
 | |
| {
 | |
|     if (entry == NULL) {
 | |
|         return;
 | |
|     }
 | |
|     vreader_free(entry->reader);
 | |
|     g_free(entry);
 | |
| }
 | |
| 
 | |
| 
 | |
| static VReaderList *
 | |
| vreader_list_new(void)
 | |
| {
 | |
|     VReaderList *new_reader_list;
 | |
| 
 | |
|     new_reader_list = (VReaderList *)g_malloc(sizeof(VReaderList));
 | |
|     new_reader_list->head = NULL;
 | |
|     new_reader_list->tail = NULL;
 | |
|     return new_reader_list;
 | |
| }
 | |
| 
 | |
| void
 | |
| vreader_list_delete(VReaderList *list)
 | |
| {
 | |
|     VReaderListEntry *current_entry;
 | |
|     VReaderListEntry *next_entry = NULL;
 | |
|     for (current_entry = vreader_list_get_first(list); current_entry;
 | |
|          current_entry = next_entry) {
 | |
|         next_entry = vreader_list_get_next(current_entry);
 | |
|         vreader_list_entry_delete(current_entry);
 | |
|     }
 | |
|     list->head = NULL;
 | |
|     list->tail = NULL;
 | |
|     g_free(list);
 | |
| }
 | |
| 
 | |
| 
 | |
| VReaderListEntry *
 | |
| vreader_list_get_first(VReaderList *list)
 | |
| {
 | |
|     return list ? list->head : NULL;
 | |
| }
 | |
| 
 | |
| VReaderListEntry *
 | |
| vreader_list_get_next(VReaderListEntry *current)
 | |
| {
 | |
|     return current ? current->next : NULL;
 | |
| }
 | |
| 
 | |
| VReader *
 | |
| vreader_list_get_reader(VReaderListEntry *entry)
 | |
| {
 | |
|     return entry ? vreader_reference(entry->reader) : NULL;
 | |
| }
 | |
| 
 | |
| static void
 | |
| vreader_queue(VReaderList *list, VReaderListEntry *entry)
 | |
| {
 | |
|     if (entry == NULL) {
 | |
|         return;
 | |
|     }
 | |
|     entry->next = NULL;
 | |
|     entry->prev = list->tail;
 | |
|     if (list->head) {
 | |
|         list->tail->next = entry;
 | |
|     } else {
 | |
|         list->head = entry;
 | |
|     }
 | |
|     list->tail = entry;
 | |
| }
 | |
| 
 | |
| static void
 | |
| vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
 | |
| {
 | |
|     if (entry == NULL) {
 | |
|         return;
 | |
|     }
 | |
|     if (entry->next == NULL) {
 | |
|         list->tail = entry->prev;
 | |
|     } else if (entry->prev == NULL) {
 | |
|         list->head = entry->next;
 | |
|     } else {
 | |
|         entry->prev->next = entry->next;
 | |
|         entry->next->prev = entry->prev;
 | |
|     }
 | |
|     if ((list->tail == NULL) || (list->head == NULL)) {
 | |
|         list->head = list->tail = NULL;
 | |
|     }
 | |
|     entry->next = entry->prev = NULL;
 | |
| }
 | |
| 
 | |
| static VReaderList *vreader_list;
 | |
| static QemuMutex vreader_list_mutex;
 | |
| 
 | |
| static void
 | |
| vreader_list_init(void)
 | |
| {
 | |
|     vreader_list = vreader_list_new();
 | |
|     qemu_mutex_init(&vreader_list_mutex);
 | |
| }
 | |
| 
 | |
| static void
 | |
| vreader_list_lock(void)
 | |
| {
 | |
|     qemu_mutex_lock(&vreader_list_mutex);
 | |
| }
 | |
| 
 | |
| static void
 | |
| vreader_list_unlock(void)
 | |
| {
 | |
|     qemu_mutex_unlock(&vreader_list_mutex);
 | |
| }
 | |
| 
 | |
| static VReaderList *
 | |
| vreader_copy_list(VReaderList *list)
 | |
| {
 | |
|     VReaderList *new_list = NULL;
 | |
|     VReaderListEntry *current_entry = NULL;
 | |
| 
 | |
|     new_list = vreader_list_new();
 | |
|     if (new_list == NULL) {
 | |
|         return NULL;
 | |
|     }
 | |
|     for (current_entry = vreader_list_get_first(list); current_entry;
 | |
|          current_entry = vreader_list_get_next(current_entry)) {
 | |
|         VReader *reader = vreader_list_get_reader(current_entry);
 | |
|         VReaderListEntry *new_entry = vreader_list_entry_new(reader);
 | |
| 
 | |
|         vreader_free(reader);
 | |
|         vreader_queue(new_list, new_entry);
 | |
|     }
 | |
|     return new_list;
 | |
| }
 | |
| 
 | |
| VReaderList *
 | |
| vreader_get_reader_list(void)
 | |
| {
 | |
|     VReaderList *new_reader_list;
 | |
| 
 | |
|     vreader_list_lock();
 | |
|     new_reader_list = vreader_copy_list(vreader_list);
 | |
|     vreader_list_unlock();
 | |
|     return new_reader_list;
 | |
| }
 | |
| 
 | |
| VReader *
 | |
| vreader_get_reader_by_id(vreader_id_t id)
 | |
| {
 | |
|     VReader *reader = NULL;
 | |
|     VReaderListEntry *current_entry = NULL;
 | |
| 
 | |
|     if (id == (vreader_id_t) -1) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     vreader_list_lock();
 | |
|     for (current_entry = vreader_list_get_first(vreader_list); current_entry;
 | |
|             current_entry = vreader_list_get_next(current_entry)) {
 | |
|         VReader *creader = vreader_list_get_reader(current_entry);
 | |
|         if (creader->id == id) {
 | |
|             reader = creader;
 | |
|             break;
 | |
|         }
 | |
|         vreader_free(creader);
 | |
|     }
 | |
|     vreader_list_unlock();
 | |
|     return reader;
 | |
| }
 | |
| 
 | |
| VReader *
 | |
| vreader_get_reader_by_name(const char *name)
 | |
| {
 | |
|     VReader *reader = NULL;
 | |
|     VReaderListEntry *current_entry = NULL;
 | |
| 
 | |
|     vreader_list_lock();
 | |
|     for (current_entry = vreader_list_get_first(vreader_list); current_entry;
 | |
|             current_entry = vreader_list_get_next(current_entry)) {
 | |
|         VReader *creader = vreader_list_get_reader(current_entry);
 | |
|         if (strcmp(creader->name, name) == 0) {
 | |
|             reader = creader;
 | |
|             break;
 | |
|         }
 | |
|         vreader_free(creader);
 | |
|     }
 | |
|     vreader_list_unlock();
 | |
|     return reader;
 | |
| }
 | |
| 
 | |
| /* called from card_emul to initialize the readers */
 | |
| VReaderStatus
 | |
| vreader_add_reader(VReader *reader)
 | |
| {
 | |
|     VReaderListEntry *reader_entry;
 | |
| 
 | |
|     reader_entry = vreader_list_entry_new(reader);
 | |
|     if (reader_entry == NULL) {
 | |
|         return VREADER_OUT_OF_MEMORY;
 | |
|     }
 | |
|     vreader_list_lock();
 | |
|     vreader_queue(vreader_list, reader_entry);
 | |
|     vreader_list_unlock();
 | |
|     vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| VReaderStatus
 | |
| vreader_remove_reader(VReader *reader)
 | |
| {
 | |
|     VReaderListEntry *current_entry;
 | |
| 
 | |
|     vreader_list_lock();
 | |
|     for (current_entry = vreader_list_get_first(vreader_list); current_entry;
 | |
|          current_entry = vreader_list_get_next(current_entry)) {
 | |
|         if (current_entry->reader == reader) {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
|     vreader_dequeue(vreader_list, current_entry);
 | |
|     vreader_list_unlock();
 | |
|     vreader_list_entry_delete(current_entry);
 | |
|     vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Generate VEVENT_CARD_INSERT or VEVENT_CARD_REMOVE based on vreader
 | |
|  * state. Separated from vreader_insert_card to allow replaying events
 | |
|  * for a given state.
 | |
|  */
 | |
| void
 | |
| vreader_queue_card_event(VReader *reader)
 | |
| {
 | |
|     vevent_queue_vevent(vevent_new(
 | |
|         reader->card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader,
 | |
|         reader->card));
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * insert/remove a new card. for removal, card == NULL
 | |
|  */
 | |
| VReaderStatus
 | |
| vreader_insert_card(VReader *reader, VCard *card)
 | |
| {
 | |
|     vreader_lock(reader);
 | |
|     if (reader->card) {
 | |
|         /* decrement reference count */
 | |
|         vcard_free(reader->card);
 | |
|         reader->card = NULL;
 | |
|     }
 | |
|     reader->card = vcard_reference(card);
 | |
|     vreader_unlock(reader);
 | |
|     vreader_queue_card_event(reader);
 | |
|     return VREADER_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * initialize all the static reader structures
 | |
|  */
 | |
| void
 | |
| vreader_init(void)
 | |
| {
 | |
|     vreader_list_init();
 | |
| }
 | |
| 
 |