+static int
+on_host_init(VSCMsgHeader *mhHeader, VSCMsgInit *incoming)
+{
+ uint32_t *capabilities = (incoming->capabilities);
+ int num_capabilities =
+ 1 + ((mhHeader->length - sizeof(VSCMsgInit)) / sizeof(uint32_t));
+ int i;
+ QemuThread thread_id;
+
+ incoming->version = ntohl(incoming->version);
+ if (incoming->version != VSCARD_VERSION) {
+ if (verbose > 0) {
+ printf("warning: host has version %d, we have %d\n",
+ verbose, VSCARD_VERSION);
+ }
+ }
+ if (incoming->magic != VSCARD_MAGIC) {
+ printf("unexpected magic: got %d, expected %d\n",
+ incoming->magic, VSCARD_MAGIC);
+ return -1;
+ }
+ for (i = 0 ; i < num_capabilities; ++i) {
+ capabilities[i] = ntohl(capabilities[i]);
+ }
+ /* Future: check capabilities */
+ /* remove whatever reader might be left in qemu,
+ * in case of an unclean previous exit. */
+ send_msg(VSC_ReaderRemove, VSCARD_MINIMAL_READER_ID, NULL, 0);
+ /* launch the event_thread. This will trigger reader adds for all the
+ * existing readers */
+ qemu_thread_create(&thread_id, event_thread, NULL, 0);
+ return 0;
+}
+
+
+enum {
+ STATE_HEADER,
+ STATE_MESSAGE,
+};
+
+#define APDUBufSize 270
+
+static gboolean
+do_socket_read(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ int rv;
+ int dwSendLength;
+ int dwRecvLength;
+ uint8_t pbRecvBuffer[APDUBufSize];
+ static uint8_t pbSendBuffer[APDUBufSize];
+ VReaderStatus reader_status;
+ VReader *reader = NULL;
+ static VSCMsgHeader mhHeader;
+ VSCMsgError *error_msg;
+ GError *err = NULL;
+
+ static gchar *buf;
+ static gsize br, to_read;
+ static int state = STATE_HEADER;
+
+ if (state == STATE_HEADER && to_read == 0) {
+ buf = (gchar *)&mhHeader;
+ to_read = sizeof(mhHeader);
+ }
+
+ if (to_read > 0) {
+ g_io_channel_read_chars(source, (gchar *)buf, to_read, &br, &err);
+ if (err != NULL) {
+ g_error("error while reading: %s", err->message);
+ }
+ buf += br;
+ to_read -= br;
+ if (to_read != 0) {
+ return TRUE;
+ }
+ }
+
+ if (state == STATE_HEADER) {
+ mhHeader.type = ntohl(mhHeader.type);
+ mhHeader.reader_id = ntohl(mhHeader.reader_id);
+ mhHeader.length = ntohl(mhHeader.length);
+ if (verbose) {
+ printf("Header: type=%d, reader_id=%u length=%d (0x%x)\n",
+ mhHeader.type, mhHeader.reader_id, mhHeader.length,
+ mhHeader.length);
+ }
+ switch (mhHeader.type) {
+ case VSC_APDU:
+ case VSC_Flush:
+ case VSC_Error:
+ case VSC_Init:
+ buf = (gchar *)pbSendBuffer;
+ to_read = mhHeader.length;
+ state = STATE_MESSAGE;
+ return TRUE;
+ default:
+ fprintf(stderr, "Unexpected message of type 0x%X\n", mhHeader.type);
+ return FALSE;
+ }
+ }
+
+ if (state == STATE_MESSAGE) {
+ switch (mhHeader.type) {
+ case VSC_APDU:
+ if (verbose) {
+ printf(" recv APDU: ");
+ print_byte_array(pbSendBuffer, mhHeader.length);
+ }
+ /* Transmit received APDU */
+ dwSendLength = mhHeader.length;
+ dwRecvLength = sizeof(pbRecvBuffer);
+ reader = vreader_get_reader_by_id(mhHeader.reader_id);
+ reader_status = vreader_xfr_bytes(reader,
+ pbSendBuffer, dwSendLength,
+ pbRecvBuffer, &dwRecvLength);
+ if (reader_status == VREADER_OK) {
+ mhHeader.length = dwRecvLength;
+ if (verbose) {
+ printf(" send response: ");
+ print_byte_array(pbRecvBuffer, mhHeader.length);
+ }
+ send_msg(VSC_APDU, mhHeader.reader_id,
+ pbRecvBuffer, dwRecvLength);
+ } else {
+ rv = reader_status; /* warning: not meaningful */
+ send_msg(VSC_Error, mhHeader.reader_id, &rv, sizeof(uint32_t));
+ }
+ vreader_free(reader);
+ reader = NULL; /* we've freed it, don't use it by accident
+ again */
+ break;
+ case VSC_Flush:
+ /* TODO: actually flush */
+ send_msg(VSC_FlushComplete, mhHeader.reader_id, NULL, 0);
+ break;
+ case VSC_Error:
+ error_msg = (VSCMsgError *) pbSendBuffer;
+ if (error_msg->code == VSC_SUCCESS) {
+ qemu_mutex_lock(&pending_reader_lock);
+ if (pending_reader) {
+ vreader_set_id(pending_reader, mhHeader.reader_id);
+ vreader_free(pending_reader);
+ pending_reader = NULL;
+ qemu_cond_signal(&pending_reader_condition);
+ }
+ qemu_mutex_unlock(&pending_reader_lock);
+ break;
+ }
+ printf("warning: qemu refused to add reader\n");
+ if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
+ /* clear pending reader, qemu can't handle any more */
+ qemu_mutex_lock(&pending_reader_lock);
+ if (pending_reader) {
+ pending_reader = NULL;
+ /* make sure the event loop doesn't hang */
+ qemu_cond_signal(&pending_reader_condition);
+ }
+ qemu_mutex_unlock(&pending_reader_lock);
+ }
+ break;
+ case VSC_Init:
+ if (on_host_init(&mhHeader, (VSCMsgInit *)pbSendBuffer) < 0) {
+ return FALSE;
+ }
+ break;
+ default:
+ g_warn_if_reached();
+ return FALSE;
+ }
+
+ state = STATE_HEADER;
+ }
+
+
+ return TRUE;
+}
+
+static gboolean
+do_socket(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
+{
+ /* not sure if two watches work well with a single win32 sources */
+ if (condition & G_IO_OUT) {
+ if (!do_socket_send(source, condition, data)) {
+ return FALSE;
+ }
+ }
+
+ if (condition & G_IO_IN) {
+ if (!do_socket_read(source, condition, data)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+