[Spice-commits] 14 commits - server/char_device.c server/char_device.h server/main_channel.c server/main_channel.h server/Makefile.am server/reds.c server/reds.h server/smartcard.c server/smartcard.h server/spicevmc.c

Yonit Halperin yhalperi at kemper.freedesktop.org
Tue Jul 3 10:14:22 PDT 2012


 server/Makefile.am    |    1 
 server/char_device.c  |  775 ++++++++++++++++++++++++++++++++++++++++++++++
 server/char_device.h  |  183 ++++++++++
 server/main_channel.c |   94 ++---
 server/main_channel.h |    6 
 server/reds.c         |  842 ++++++++++++++++++--------------------------------
 server/reds.h         |    5 
 server/smartcard.c    |  367 +++++++++++++++------
 server/smartcard.h    |    6 
 server/spicevmc.c     |  193 ++++++++---
 10 files changed, 1713 insertions(+), 759 deletions(-)

New commits:
commit bf91bdce5cb5b391b9d3f86900845831da80a5c5
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Jun 25 09:34:57 2012 +0300

    char_device: move SpiceCharDeviceState from the header
    
    In addition, I also removed the no longer used wakeup callback

diff --git a/server/char_device.c b/server/char_device.c
index f493956..e87c029 100644
--- a/server/char_device.c
+++ b/server/char_device.c
@@ -43,6 +43,29 @@ struct SpiceCharDeviceClientState {
     uint32_t max_send_queue_size;
 };
 
+struct SpiceCharDeviceState {
+    int running;
+    uint32_t refs;
+
+    Ring write_queue;
+    Ring write_bufs_pool;
+    SpiceCharDeviceWriteBuffer *cur_write_buf;
+    uint8_t *cur_write_buf_pos;
+    SpiceTimer *write_to_dev_timer;
+    uint64_t num_self_tokens;
+
+    Ring clients; /* list of SpiceCharDeviceClientState */
+    uint32_t num_clients;
+
+    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
+    SpiceCharDeviceInstance *sin;
+
+    int during_read_from_device;
+
+    SpiceCharDeviceCallbacks cbs;
+    void *opaque;
+};
+
 enum {
     WRITE_BUFFER_ORIGIN_NONE,
     WRITE_BUFFER_ORIGIN_CLIENT,
diff --git a/server/char_device.h b/server/char_device.h
index 72f8abd..4a70075 100644
--- a/server/char_device.h
+++ b/server/char_device.h
@@ -109,34 +109,6 @@ typedef struct SpiceCharDeviceCallbacks {
 
 typedef struct SpiceCharDeviceState SpiceCharDeviceState;
 
-struct SpiceCharDeviceState {
-    int running;
-    uint32_t refs;
-
-    Ring write_queue;
-    Ring write_bufs_pool;
-    SpiceCharDeviceWriteBuffer *cur_write_buf;
-    uint8_t *cur_write_buf_pos;
-    SpiceTimer *write_to_dev_timer;
-    uint64_t num_self_tokens;
-
-    Ring clients;
-    uint32_t num_clients;
-
-    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
-    SpiceCharDeviceInstance *sin;
-
-    int during_read_from_device;
-
-    SpiceCharDeviceCallbacks cbs;
-    void *opaque;
-    /* tmp till all spice char devices will employ the new SpiceCharDeviceState
-     * implementation. Then, SpiceCharDeviceState will be moved to char_device.c and
-     * this callback will be removed */
-    void (*wakeup)(SpiceCharDeviceInstance *sin);
-};
-
-
 SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
                                                      uint32_t client_tokens_interval,
                                                      uint32_t self_tokens,
diff --git a/server/reds.c b/server/reds.c
index 911564b..9488237 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3158,11 +3158,11 @@ static SpiceCharDeviceState *attach_to_red_agent(SpiceCharDeviceInstance *sin)
 
 SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
 {
-    if (sin->st->wakeup) {
-        sin->st->wakeup(sin);
-    } else {
-        spice_char_device_wakeup(sin->st);
+    if (!sin->st) {
+        spice_error("no SpiceCharDeviceState attached to instance %p", sin);
+        return;
     }
+    spice_char_device_wakeup(sin->st);
 }
 
 #define SUBTYPE_VDAGENT "vdagent"
commit 8bb444ec7ac3a64058138b24241149ae116653a4
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Jun 25 09:19:38 2012 +0300

    smartcard: use SpiceCharDeviceState for writing to the guest device
    
    With SpiceCharDeviceState, the smartcard code now supports partial writes,
    and storing data that is received from the client after the device is
    stopped, instead of attempting to write it to the guest.

diff --git a/server/smartcard.c b/server/smartcard.c
index 0a2ab75..c844a32 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -44,6 +44,12 @@ typedef struct SmartCardDeviceState SmartCardDeviceState;
 typedef struct SmartCardChannelClient {
     RedChannelClient base;
     SmartCardDeviceState *smartcard_state;
+
+    /* read_from_client/write_to_device buffer.
+     * The beginning of the buffer should always be VSCMsgHeader*/
+    SpiceCharDeviceWriteBuffer *write_buf;
+    int msg_in_write_buf; /* was the client msg received into a SpiceCharDeviceWriteBuffer
+                           * or was it explicitly malloced */
 } SmartCardChannelClient;
 
 typedef struct SmartCardDeviceState {
@@ -96,7 +102,7 @@ static SpiceCharDeviceInstance* smartcard_readers_get(uint32_t reader_id);
 static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *sin);
 static void smartcard_char_device_attach(
     SpiceCharDeviceInstance *char_device, SmartCardChannelClient *scc);
-static void smartcard_channel_write_to_reader(VSCMsgHeader *vheader);
+static void smartcard_channel_write_to_reader(SpiceCharDeviceWriteBuffer *write_buf);
 
 static MsgItem *smartcard_char_device_on_message_from_device(
     SmartCardDeviceState *state, VSCMsgHeader *header);
@@ -306,7 +312,8 @@ static void smartcard_char_device_attach(SpiceCharDeviceInstance *char_device,
                                          SmartCardChannelClient *scc)
 {
     SmartCardDeviceState *st = spice_char_device_state_opaque_get(char_device->st);
-
+    SpiceCharDeviceWriteBuffer *write_buf;
+    VSCMsgHeader *vheader;
 
     spice_assert(!scc->smartcard_state);
     if (st->attached == TRUE) {
@@ -321,14 +328,23 @@ static void smartcard_char_device_attach(SpiceCharDeviceInstance *char_device,
                                  ~0,
                                  ~0);
     scc->smartcard_state = st;
-    VSCMsgHeader vheader = {.type = VSC_ReaderAdd, .reader_id=st->reader_id,
-        .length=0};
-    smartcard_channel_write_to_reader(&vheader);
+    write_buf = spice_char_device_write_buffer_get(st->chardev_st, NULL, sizeof(vheader));
+    if (!write_buf) {
+        spice_error("failed to allocate write buffer");
+        return;
+    }
+    vheader = (VSCMsgHeader *)write_buf->buf;
+    vheader->type = VSC_ReaderAdd;
+    vheader->reader_id = st->reader_id;
+    vheader->length = 0;
+    smartcard_channel_write_to_reader(write_buf);
 }
 
 static void smartcard_char_device_detach_client(SmartCardChannelClient *scc)
 {
     SmartCardDeviceState *st;
+    SpiceCharDeviceWriteBuffer *write_buf;
+    VSCMsgHeader *vheader;
 
     if (!scc->smartcard_state) {
         return;
@@ -339,9 +355,16 @@ static void smartcard_char_device_detach_client(SmartCardChannelClient *scc)
     scc->smartcard_state = NULL;
     st->attached = FALSE;
     st->scc = NULL;
-    VSCMsgHeader vheader = {.type = VSC_ReaderRemove, .reader_id=st->reader_id,
-        .length=0};
-    smartcard_channel_write_to_reader(&vheader);
+    write_buf = spice_char_device_write_buffer_get(st->chardev_st, NULL, sizeof(vheader));
+    if (!write_buf) {
+        spice_error("failed to allocate write buffer");
+        return;
+    }
+    vheader = (VSCMsgHeader *)write_buf->buf;
+    vheader->type = VSC_ReaderRemove;
+    vheader->reader_id = st->reader_id;
+    vheader->length = 0;
+    smartcard_channel_write_to_reader(write_buf);
 }
 
 static int smartcard_channel_client_config_socket(RedChannelClient *rcc)
@@ -353,7 +376,30 @@ static uint8_t *smartcard_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
                                                     uint16_t type,
                                                     uint32_t size)
 {
-    return spice_malloc(size);
+    SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base);
+
+    /* todo: only one reader is actually supported. When we fix the code to support
+     * multiple readers, we will porbably associate different devices to
+     * differenc channels */
+    if (!scc->smartcard_state) {
+        scc->msg_in_write_buf = FALSE;
+        return spice_malloc(size);
+    } else {
+        SmartCardDeviceState *st;
+
+        spice_assert(g_smartcard_readers.num == 1);
+        st = scc->smartcard_state;
+        spice_assert(st->scc || scc->smartcard_state);
+        spice_assert(!scc->write_buf);
+        scc->write_buf = spice_char_device_write_buffer_get(st->chardev_st, rcc->client, size);
+
+        if (!scc->write_buf) {
+            spice_error("failed to allocate write buffer");
+            return NULL;
+        }
+        scc->msg_in_write_buf = TRUE;
+        return scc->write_buf->buf;
+    }
 }
 
 static void smartcard_channel_release_msg_rcv_buf(RedChannelClient *rcc,
@@ -361,8 +407,24 @@ static void smartcard_channel_release_msg_rcv_buf(RedChannelClient *rcc,
                                                   uint32_t size,
                                                   uint8_t *msg)
 {
-    spice_printerr("freeing %d bytes", size);
-    free(msg);
+    SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base);
+
+    /* todo: only one reader is actually supported. When we fix the code to support
+     * multiple readers, we will porbably associate different devices to
+     * differenc channels */
+
+    if (!scc->msg_in_write_buf) {
+        spice_assert(!scc->write_buf);
+        free(msg);
+    } else {
+        SpiceCharDeviceState *dev_st;
+        if (scc->write_buf) { /* msg hasn't been pushed to the guest */
+            spice_assert(scc->write_buf->buf == msg);
+            dev_st = scc->smartcard_state ? scc->smartcard_state->chardev_st : NULL;
+            spice_char_device_write_buffer_release(dev_st, scc->write_buf);
+            scc->write_buf = NULL;
+        }
+    }
 }
 
 static void smartcard_channel_send_data(RedChannelClient *rcc, SpiceMarshaller *m,
@@ -407,7 +469,6 @@ static void smartcard_channel_send_item(RedChannelClient *rcc, PipeItem *item)
     }
 }
 
-
 static void smartcard_channel_release_pipe_item(RedChannelClient *rcc,
                                       PipeItem *item, int item_pushed)
 {
@@ -509,24 +570,31 @@ static void smartcard_add_reader(SmartCardChannelClient *scc, uint8_t *name)
     }
 }
 
-static void smartcard_channel_write_to_reader(VSCMsgHeader *vheader)
+static void smartcard_channel_write_to_reader(SpiceCharDeviceWriteBuffer *write_buf)
 {
     SpiceCharDeviceInstance *sin;
-    SpiceCharDeviceInterface *sif;
-    uint32_t n;
-    uint32_t actual_length = vheader->length;
+    SmartCardDeviceState *st;
+    VSCMsgHeader *vheader;
+    uint32_t actual_length;
+
+    vheader = (VSCMsgHeader *)write_buf->buf;
+    actual_length = vheader->length;
 
     spice_assert(vheader->reader_id <= g_smartcard_readers.num);
     sin = g_smartcard_readers.sin[vheader->reader_id];
-    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+    st = (SmartCardDeviceState *)spice_char_device_state_opaque_get(sin->st);
+    spice_assert(!st->attached || st == st->scc->smartcard_state);
     /* protocol requires messages to be in network endianess */
     vheader->type = htonl(vheader->type);
     vheader->length = htonl(vheader->length);
     vheader->reader_id = htonl(vheader->reader_id);
-    n = sif->write(sin, (uint8_t*)vheader,
-                   actual_length + sizeof(VSCMsgHeader));
-    // TODO - add ring
-    spice_assert(n == actual_length + sizeof(VSCMsgHeader));
+    write_buf->buf_used = actual_length + sizeof(VSCMsgHeader);
+    /* pushing the buffer to the write queue; It will be released
+     * when it will be fully consumed by the device */
+    spice_char_device_write_buffer_add(sin->st, write_buf);
+    if (st->attached && write_buf == st->scc->write_buf) {
+        st->scc->write_buf = NULL;
+    }
 }
 
 static int smartcard_channel_handle_message(RedChannelClient *rcc,
@@ -566,12 +634,15 @@ static int smartcard_channel_handle_message(RedChannelClient *rcc,
             return TRUE;
     }
 
+    /* todo: fix */
     if (vheader->reader_id >= g_smartcard_readers.num) {
         spice_printerr("ERROR: received message for non existent reader: %d, %d, %d", vheader->reader_id,
             vheader->type, vheader->length);
         return FALSE;
     }
-    smartcard_channel_write_to_reader(vheader);
+    spice_assert(scc->write_buf->buf == msg);
+    smartcard_channel_write_to_reader(scc->write_buf);
+
     return TRUE;
 }
 
commit 6071c1c8c507785a04b988e9b40efe3f31c043df
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Wed Jun 27 15:04:49 2012 +0300

    smartcard: creating SmartCardChannelClient type
    
    The lifetime of the channel is not necessarily correlated to the life
    time of the device. In the next patch, we need to keep a reference
    to SpiceCharDeviceWriteBuffer, which might be in use even if the
    SpiceCharDeviceState is destroyed, but the channel is still connected.
    The next patch keeps this reference inside SmartCardChannelClient.
    
    This patch also removes the routine smartcard_readers_detach_all(rcc), which
    is unnecessary since we don't support multiple readers; even when
    we do support them, each channel client should be associated with only
    one reader (i.e., we will have different channels for different
    readers).

diff --git a/server/smartcard.c b/server/smartcard.c
index 3b326da..0a2ab75 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -39,6 +39,13 @@
  */
 #define SMARTCARD_MAX_READERS 10
 
+typedef struct SmartCardDeviceState SmartCardDeviceState;
+
+typedef struct SmartCardChannelClient {
+    RedChannelClient base;
+    SmartCardDeviceState *smartcard_state;
+} SmartCardChannelClient;
+
 typedef struct SmartCardDeviceState {
     SpiceCharDeviceState *chardev_st;
     uint32_t             reader_id;
@@ -48,7 +55,8 @@ typedef struct SmartCardDeviceState {
     uint32_t             buf_size;
     uint8_t             *buf_pos;
     uint32_t             buf_used;
-    RedChannelClient    *rcc; // client providing the remote card
+
+    SmartCardChannelClient    *scc; // client providing the remote card
 } SmartCardDeviceState;
 
 enum {
@@ -87,8 +95,7 @@ static SpiceCharDeviceInstance* smartcard_readers_get_unattached(void);
 static SpiceCharDeviceInstance* smartcard_readers_get(uint32_t reader_id);
 static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *sin);
 static void smartcard_char_device_attach(
-    SpiceCharDeviceInstance *char_device, RedChannelClient *rcc);
-static void smartcard_char_device_detach(SpiceCharDeviceInstance *char_device);
+    SpiceCharDeviceInstance *char_device, SmartCardChannelClient *scc);
 static void smartcard_channel_write_to_reader(VSCMsgHeader *vheader);
 
 static MsgItem *smartcard_char_device_on_message_from_device(
@@ -155,9 +162,8 @@ static void smartcard_send_msg_to_client(SpiceCharDeviceMsgToClient *msg,
                                          void *opaque)
 {
     SmartCardDeviceState *dev = opaque;
-
-    spice_assert(dev->rcc && dev->rcc->client == client);
-    smartcard_channel_client_pipe_add_push(dev->rcc, &((MsgItem *)msg)->base);
+    spice_assert(dev->scc && dev->scc->base.client == client);
+    smartcard_channel_client_pipe_add_push(&dev->scc->base, &((MsgItem *)msg)->base);
 
 }
 
@@ -171,8 +177,8 @@ static void smartcard_remove_client(RedClient *client, void *opaque)
     SmartCardDeviceState *dev = opaque;
 
     spice_printerr("smartcard  state %p, client %p", dev, client);
-    spice_assert(dev->rcc && dev->rcc->client == client);
-    red_channel_client_shutdown(dev->rcc);
+    spice_assert(dev->scc && dev->scc->base.client == client);
+    red_channel_client_shutdown(&dev->scc->base);
 }
 
 MsgItem *smartcard_char_device_on_message_from_device(SmartCardDeviceState *state,
@@ -194,30 +200,16 @@ MsgItem *smartcard_char_device_on_message_from_device(SmartCardDeviceState *stat
     if (state->reader_id == VSCARD_UNDEFINED_READER_ID && vheader->type != VSC_Init) {
         spice_printerr("error: reader_id not assigned for message of type %d", vheader->type);
     }
-    if (state->rcc) {
+    if (state->scc) {
         sent_header = spice_memdup(vheader, sizeof(*vheader) + vheader->length);
         /* We patch the reader_id, since the device only knows about itself, and
          * we know about the sum of readers. */
         sent_header->reader_id = state->reader_id;
-        return smartcard_get_vsc_msg_item(state->rcc, sent_header);
+        return smartcard_get_vsc_msg_item(&state->scc->base, sent_header);
     }
     return NULL;
 }
 
-static void smartcard_readers_detach_all(RedChannelClient *rcc)
-{
-    int i;
-    SmartCardDeviceState *st;
-    // TODO - can track rcc->{sin}
-
-    for (i = 0 ; i < g_smartcard_readers.num; ++i) {
-        st = spice_char_device_state_opaque_get(g_smartcard_readers.sin[i]->st);
-        if (!rcc || st->rcc == rcc) {
-            smartcard_char_device_detach(g_smartcard_readers.sin[i]);
-        }
-    }
-}
-
 static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *char_device)
 {
     SmartCardDeviceState *state = spice_char_device_state_opaque_get(char_device->st);
@@ -237,6 +229,8 @@ static SpiceCharDeviceInstance *smartcard_readers_get(uint32_t reader_id)
     return g_smartcard_readers.sin[reader_id];
 }
 
+/* TODO: fix implementation for multiple readers. Each reader should have a separated
+ * channel */
 static SpiceCharDeviceInstance *smartcard_readers_get_unattached(void)
 {
     int i;
@@ -275,12 +269,15 @@ static SmartCardDeviceState *smartcard_device_state_new(SpiceCharDeviceInstance
     st->buf = spice_malloc(st->buf_size);
     st->buf_pos = st->buf;
     st->buf_used = 0;
-    st->rcc = NULL;
+    st->scc = NULL;
     return st;
 }
 
 static void smartcard_device_state_free(SmartCardDeviceState* st)
 {
+    if (st->scc) {
+        st->scc->smartcard_state = NULL;
+    }
     free(st->buf);
     spice_char_device_state_destroy(st->chardev_st);
     free(st);
@@ -306,38 +303,42 @@ SpiceCharDeviceState *smartcard_device_connect(SpiceCharDeviceInstance *char_dev
 }
 
 static void smartcard_char_device_attach(SpiceCharDeviceInstance *char_device,
-                                         RedChannelClient *rcc)
+                                         SmartCardChannelClient *scc)
 {
     SmartCardDeviceState *st = spice_char_device_state_opaque_get(char_device->st);
 
 
+    spice_assert(!scc->smartcard_state);
     if (st->attached == TRUE) {
         return;
     }
     st->attached = TRUE;
-    st->rcc = rcc;
+    st->scc = scc;
     spice_char_device_client_add(st->chardev_st,
-                                 rcc->client,
+                                 scc->base.client,
                                  FALSE, /* no flow control yet */
                                  0, /* send queue size */
                                  ~0,
                                  ~0);
+    scc->smartcard_state = st;
     VSCMsgHeader vheader = {.type = VSC_ReaderAdd, .reader_id=st->reader_id,
         .length=0};
     smartcard_channel_write_to_reader(&vheader);
 }
 
-static void smartcard_char_device_detach(SpiceCharDeviceInstance *char_device)
+static void smartcard_char_device_detach_client(SmartCardChannelClient *scc)
 {
-    SmartCardDeviceState *st = spice_char_device_state_opaque_get(char_device->st);
+    SmartCardDeviceState *st;
 
-    if (st->attached == FALSE) {
+    if (!scc->smartcard_state) {
         return;
     }
-    spice_assert(st->rcc);
-    spice_char_device_client_remove(st->chardev_st, st->rcc->client);
+    st = scc->smartcard_state;
+    spice_assert(st->scc == scc);
+    spice_char_device_client_remove(st->chardev_st, scc->base.client);
+    scc->smartcard_state = NULL;
     st->attached = FALSE;
-    st->rcc = NULL;
+    st->scc = NULL;
     VSCMsgHeader vheader = {.type = VSC_ReaderRemove, .reader_id=st->reader_id,
         .length=0};
     smartcard_channel_write_to_reader(&vheader);
@@ -419,7 +420,7 @@ static void smartcard_channel_release_pipe_item(RedChannelClient *rcc,
 
 static void smartcard_channel_on_disconnect(RedChannelClient *rcc)
 {
-    smartcard_readers_detach_all(rcc);
+    smartcard_char_device_detach_client(SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base));
 }
 
 /* this is called from both device input and client input. since the device is
@@ -470,39 +471,40 @@ static void smartcard_unref_vsc_msg_item(MsgItem *item)
     }
 }
 
-static void smartcard_remove_reader(RedChannelClient *rcc, uint32_t reader_id)
+static void smartcard_remove_reader(SmartCardChannelClient *scc, uint32_t reader_id)
 {
     SpiceCharDeviceInstance *char_device = smartcard_readers_get(reader_id);
     SmartCardDeviceState *state;
 
     if (char_device == NULL) {
-        smartcard_push_error(rcc, reader_id,
+        smartcard_push_error(&scc->base, reader_id,
             VSC_GENERAL_ERROR);
         return;
     }
 
     state = spice_char_device_state_opaque_get(char_device->st);
     if (state->attached == FALSE) {
-        smartcard_push_error(rcc, reader_id,
+        smartcard_push_error(&scc->base, reader_id,
             VSC_GENERAL_ERROR);
         return;
     }
-    smartcard_char_device_detach(char_device);
+    spice_assert(scc->smartcard_state == state);
+    smartcard_char_device_detach_client(scc);
 }
 
-static void smartcard_add_reader(RedChannelClient *rcc, uint8_t *name)
+static void smartcard_add_reader(SmartCardChannelClient *scc, uint8_t *name)
 {
     // TODO - save name somewhere
     SpiceCharDeviceInstance *char_device =
             smartcard_readers_get_unattached();
 
     if (char_device != NULL) {
-        smartcard_char_device_attach(char_device, rcc);
+        smartcard_char_device_attach(char_device, scc);
         // The device sends a VSC_Error message, we will let it through, no
         // need to send our own. We already set the correct reader_id, from
         // our SmartCardDeviceState.
     } else {
-        smartcard_push_error(rcc, VSCARD_UNDEFINED_READER_ID,
+        smartcard_push_error(&scc->base, VSCARD_UNDEFINED_READER_ID,
             VSC_CANNOT_ADD_MORE_READERS);
     }
 }
@@ -533,6 +535,7 @@ static int smartcard_channel_handle_message(RedChannelClient *rcc,
                                             uint8_t *msg)
 {
     VSCMsgHeader* vheader = (VSCMsgHeader*)msg;
+    SmartCardChannelClient *scc = SPICE_CONTAINEROF(rcc, SmartCardChannelClient, base);
 
     if (type != SPICE_MSGC_SMARTCARD_DATA) {
         /* handle ack's, spicy sends them while spicec does not */
@@ -542,11 +545,11 @@ static int smartcard_channel_handle_message(RedChannelClient *rcc,
     spice_assert(size == vheader->length + sizeof(VSCMsgHeader));
     switch (vheader->type) {
         case VSC_ReaderAdd:
-            smartcard_add_reader(rcc, msg + sizeof(VSCMsgHeader));
+            smartcard_add_reader(scc, msg + sizeof(VSCMsgHeader));
             return TRUE;
             break;
         case VSC_ReaderRemove:
-            smartcard_remove_reader(rcc, vheader->reader_id);
+            smartcard_remove_reader(scc, vheader->reader_id);
             return TRUE;
             break;
         case VSC_Init:
@@ -581,15 +584,18 @@ static void smartcard_connect(RedChannel *channel, RedClient *client,
                         int num_common_caps, uint32_t *common_caps,
                         int num_caps, uint32_t *caps)
 {
-    RedChannelClient *rcc;
+    SmartCardChannelClient *scc;
 
-    rcc = red_channel_client_create(sizeof(RedChannelClient), channel, client, stream,
-                                    num_common_caps, common_caps,
-                                    num_caps, caps);
-    if (!rcc) {
+    scc = (SmartCardChannelClient *)red_channel_client_create(sizeof(SmartCardChannelClient),
+                                                              channel,
+                                                              client,
+                                                              stream,
+                                                              num_common_caps, common_caps,
+                                                              num_caps, caps);
+    if (!scc) {
         return;
     }
-    red_channel_client_ack_zero_messages_window(rcc);
+    red_channel_client_ack_zero_messages_window(&scc->base);
 }
 
 static void smartcard_migrate(RedChannelClient *rcc)
commit c514a1d903dc967dcb5f430098db410ed37b68e4
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Jun 25 17:47:36 2012 +0300

    smartcard: use SpiceCharDeviceState for managing reading from the device
    
    This patch and the following one do not introduce tokening to the smartcard
    channel. But this can be done easily later, by setting the appropriate
    variables in SpiceCharDeviceState (after adding the appropriate protocol messages,
    and implementing this in the client side).

diff --git a/server/reds.c b/server/reds.c
index 42fad0c..911564b 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3200,7 +3200,7 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
     }
 #ifdef USE_SMARTCARD
     else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) {
-        if (smartcard_device_connect(char_device) == -1) {
+        if (!(dev_state = smartcard_device_connect(char_device))) {
             return -1;
         }
     }
@@ -3213,6 +3213,8 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
         /* setting the char_device state to "started" for backward compatibily with
          * qemu releases that don't call spice api for start/stop (not implemented yet) */
         spice_char_device_start(char_device->st);
+    } else {
+        spice_error("failed to create device state for %s", char_device->subtype);
     }
     return 0;
 }
diff --git a/server/smartcard.c b/server/smartcard.c
index fcf210f..3b326da 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -27,12 +27,23 @@
 #include "red_channel.h"
 #include "smartcard.h"
 
+/*
+ * TODO: the code doesn't really support multiple readers.
+ * For example: smartcard_char_device_add_to_readers calls smartcard_init,
+ * which can be called only once.
+ * We should allow different readers, at least one reader per client.
+ * In addition the implementation should be changed: instead of one channel for
+ * all readers, we need to have different channles for different readers,
+ * similarly to spicevmc.
+ *
+ */
 #define SMARTCARD_MAX_READERS 10
 
 typedef struct SmartCardDeviceState {
-    SpiceCharDeviceState base;
+    SpiceCharDeviceState *chardev_st;
     uint32_t             reader_id;
     uint32_t             attached;
+    /* read_from_device buffer */
     uint8_t             *buf;
     uint32_t             buf_size;
     uint8_t             *buf_pos;
@@ -53,9 +64,16 @@ typedef struct ErrorItem {
 
 typedef struct MsgItem {
     PipeItem base;
+    uint32_t refs;
+
     VSCMsgHeader* vheader;
 } MsgItem;
 
+static MsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, VSCMsgHeader *vheader);
+static MsgItem *smartcard_ref_vsc_msg_item(MsgItem *item);
+static void smartcard_unref_vsc_msg_item(MsgItem *item);
+static void smartcard_channel_client_pipe_add_push(RedChannelClient *rcc, PipeItem *item);
+
 typedef struct SmartCardChannel {
     RedChannel base;
 } SmartCardChannel;
@@ -73,18 +91,16 @@ static void smartcard_char_device_attach(
 static void smartcard_char_device_detach(SpiceCharDeviceInstance *char_device);
 static void smartcard_channel_write_to_reader(VSCMsgHeader *vheader);
 
-static void smartcard_char_device_on_message_from_device(
+static MsgItem *smartcard_char_device_on_message_from_device(
     SmartCardDeviceState *state, VSCMsgHeader *header);
-static void smartcard_on_message_from_device(
-    RedChannelClient *rcc, VSCMsgHeader *vheader);
-static SmartCardDeviceState* smartcard_device_state_new(void);
+static SmartCardDeviceState *smartcard_device_state_new(SpiceCharDeviceInstance *sin);
 static void smartcard_device_state_free(SmartCardDeviceState* st);
 static void smartcard_init(void);
 
-void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin)
+SpiceCharDeviceMsgToClient *smartcard_read_msg_from_device(SpiceCharDeviceInstance *sin,
+                                                           void *opaque)
 {
-    SmartCardDeviceState* state = SPICE_CONTAINEROF(
-                            sin->st, SmartCardDeviceState, base);
+    SmartCardDeviceState *state = opaque;
     SpiceCharDeviceInterface *sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
     VSCMsgHeader *vheader = (VSCMsgHeader*)state->buf;
     int n;
@@ -92,6 +108,8 @@ void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin)
     int actual_length;
 
     while ((n = sif->read(sin, state->buf_pos, state->buf_size - state->buf_used)) > 0) {
+        MsgItem *msg_to_client;
+
         state->buf_pos += n;
         state->buf_used += n;
         if (state->buf_used < sizeof(VSCMsgHeader)) {
@@ -106,19 +124,59 @@ void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin)
         if (state->buf_used - sizeof(VSCMsgHeader) < actual_length) {
             continue;
         }
-        smartcard_char_device_on_message_from_device(state, vheader);
+        msg_to_client = smartcard_char_device_on_message_from_device(state, vheader);
         remaining = state->buf_used - sizeof(VSCMsgHeader) - actual_length;
         if (remaining > 0) {
             memcpy(state->buf, state->buf_pos, remaining);
         }
         state->buf_pos = state->buf;
         state->buf_used = remaining;
+        if (msg_to_client) {
+            return msg_to_client;
+        }
     }
+    return NULL;
+}
+
+static SpiceCharDeviceMsgToClient *smartcard_ref_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                                               void *opaque)
+{
+    return smartcard_ref_vsc_msg_item((MsgItem *)msg);
+}
+
+static void smartcard_unref_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                          void *opaque)
+{
+    smartcard_unref_vsc_msg_item((MsgItem *)msg);
+}
+
+static void smartcard_send_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                         RedClient *client,
+                                         void *opaque)
+{
+    SmartCardDeviceState *dev = opaque;
+
+    spice_assert(dev->rcc && dev->rcc->client == client);
+    smartcard_channel_client_pipe_add_push(dev->rcc, &((MsgItem *)msg)->base);
+
+}
+
+static void smartcard_send_tokens_to_client(RedClient *client, uint32_t tokens, void *opaque)
+{
+    spice_error("not implemented");
 }
 
-void smartcard_char_device_on_message_from_device(
-    SmartCardDeviceState *state,
-    VSCMsgHeader *vheader)
+static void smartcard_remove_client(RedClient *client, void *opaque)
+{
+    SmartCardDeviceState *dev = opaque;
+
+    spice_printerr("smartcard  state %p, client %p", dev, client);
+    spice_assert(dev->rcc && dev->rcc->client == client);
+    red_channel_client_shutdown(dev->rcc);
+}
+
+MsgItem *smartcard_char_device_on_message_from_device(SmartCardDeviceState *state,
+                                                      VSCMsgHeader *vheader)
 {
     VSCMsgHeader *sent_header;
 
@@ -128,8 +186,7 @@ void smartcard_char_device_on_message_from_device(
 
     switch (vheader->type) {
         case VSC_Init:
-            return;
-            break;
+            return NULL;
         default:
             break;
     }
@@ -142,8 +199,9 @@ void smartcard_char_device_on_message_from_device(
         /* We patch the reader_id, since the device only knows about itself, and
          * we know about the sum of readers. */
         sent_header->reader_id = state->reader_id;
-        smartcard_on_message_from_device(state->rcc, sent_header);
+        return smartcard_get_vsc_msg_item(state->rcc, sent_header);
     }
+    return NULL;
 }
 
 static void smartcard_readers_detach_all(RedChannelClient *rcc)
@@ -153,7 +211,7 @@ static void smartcard_readers_detach_all(RedChannelClient *rcc)
     // TODO - can track rcc->{sin}
 
     for (i = 0 ; i < g_smartcard_readers.num; ++i) {
-        st = SPICE_CONTAINEROF(g_smartcard_readers.sin[i]->st, SmartCardDeviceState, base);
+        st = spice_char_device_state_opaque_get(g_smartcard_readers.sin[i]->st);
         if (!rcc || st->rcc == rcc) {
             smartcard_char_device_detach(g_smartcard_readers.sin[i]);
         }
@@ -162,8 +220,7 @@ static void smartcard_readers_detach_all(RedChannelClient *rcc)
 
 static int smartcard_char_device_add_to_readers(SpiceCharDeviceInstance *char_device)
 {
-    SmartCardDeviceState* state = SPICE_CONTAINEROF(
-                            char_device->st, SmartCardDeviceState, base);
+    SmartCardDeviceState *state = spice_char_device_state_opaque_get(char_device->st);
 
     if (g_smartcard_readers.num >= SMARTCARD_MAX_READERS) {
         return -1;
@@ -186,8 +243,7 @@ static SpiceCharDeviceInstance *smartcard_readers_get_unattached(void)
     SmartCardDeviceState* state;
 
     for (i = 0; i < g_smartcard_readers.num; ++i) {
-        state = SPICE_CONTAINEROF(g_smartcard_readers.sin[i]->st,
-                                  SmartCardDeviceState, base);
+        state = spice_char_device_state_opaque_get(g_smartcard_readers.sin[i]->st);
         if (!state->attached) {
             return g_smartcard_readers.sin[i];
         }
@@ -195,12 +251,24 @@ static SpiceCharDeviceInstance *smartcard_readers_get_unattached(void)
     return NULL;
 }
 
-static SmartCardDeviceState* smartcard_device_state_new(void)
+static SmartCardDeviceState *smartcard_device_state_new(SpiceCharDeviceInstance *sin)
 {
     SmartCardDeviceState *st;
+    SpiceCharDeviceCallbacks chardev_cbs = { NULL, };
+
+    chardev_cbs.read_one_msg_from_device = smartcard_read_msg_from_device;
+    chardev_cbs.ref_msg_to_client = smartcard_ref_msg_to_client;
+    chardev_cbs.unref_msg_to_client = smartcard_unref_msg_to_client;
+    chardev_cbs.send_msg_to_client = smartcard_send_msg_to_client;
+    chardev_cbs.send_tokens_to_client = smartcard_send_tokens_to_client;
+    chardev_cbs.remove_client = smartcard_remove_client;
 
     st = spice_new0(SmartCardDeviceState, 1);
-    st->base.wakeup = smartcard_char_device_wakeup;
+    st->chardev_st = spice_char_device_state_create(sin,
+                                                    0, /* tokens interval */
+                                                    ~0, /* self tokens */
+                                                    &chardev_cbs,
+                                                    st);
     st->reader_id = VSCARD_UNDEFINED_READER_ID;
     st->attached = FALSE;
     st->buf_size = APDUBufSize + sizeof(VSCMsgHeader);
@@ -214,40 +282,46 @@ static SmartCardDeviceState* smartcard_device_state_new(void)
 static void smartcard_device_state_free(SmartCardDeviceState* st)
 {
     free(st->buf);
+    spice_char_device_state_destroy(st->chardev_st);
     free(st);
 }
 
 void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device)
 {
-    SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st,
-        SmartCardDeviceState, base);
+    SmartCardDeviceState *st = spice_char_device_state_opaque_get(char_device->st);
 
     smartcard_device_state_free(st);
 }
 
-int smartcard_device_connect(SpiceCharDeviceInstance *char_device)
+SpiceCharDeviceState *smartcard_device_connect(SpiceCharDeviceInstance *char_device)
 {
     SmartCardDeviceState *st;
 
-    st = smartcard_device_state_new();
-    char_device->st = &st->base;
+    st = smartcard_device_state_new(char_device);
     if (smartcard_char_device_add_to_readers(char_device) == -1) {
         smartcard_device_state_free(st);
-        return -1;
+        return NULL;
     }
-    return 0;
+    return st->chardev_st;
 }
 
-static void smartcard_char_device_attach(
-    SpiceCharDeviceInstance *char_device, RedChannelClient *rcc)
+static void smartcard_char_device_attach(SpiceCharDeviceInstance *char_device,
+                                         RedChannelClient *rcc)
 {
-    SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+    SmartCardDeviceState *st = spice_char_device_state_opaque_get(char_device->st);
+
 
     if (st->attached == TRUE) {
         return;
     }
     st->attached = TRUE;
     st->rcc = rcc;
+    spice_char_device_client_add(st->chardev_st,
+                                 rcc->client,
+                                 FALSE, /* no flow control yet */
+                                 0, /* send queue size */
+                                 ~0,
+                                 ~0);
     VSCMsgHeader vheader = {.type = VSC_ReaderAdd, .reader_id=st->reader_id,
         .length=0};
     smartcard_channel_write_to_reader(&vheader);
@@ -255,11 +329,13 @@ static void smartcard_char_device_attach(
 
 static void smartcard_char_device_detach(SpiceCharDeviceInstance *char_device)
 {
-    SmartCardDeviceState *st = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+    SmartCardDeviceState *st = spice_char_device_state_opaque_get(char_device->st);
 
     if (st->attached == FALSE) {
         return;
     }
+    spice_assert(st->rcc);
+    spice_char_device_client_remove(st->chardev_st, st->rcc->client);
     st->attached = FALSE;
     st->rcc = NULL;
     VSCMsgHeader vheader = {.type = VSC_ReaderRemove, .reader_id=st->reader_id,
@@ -335,10 +411,10 @@ static void smartcard_channel_release_pipe_item(RedChannelClient *rcc,
                                       PipeItem *item, int item_pushed)
 {
     if (item->type == PIPE_ITEM_TYPE_MSG) {
-        MsgItem *mi = (MsgItem *)item;
-        free(mi->vheader);
+        smartcard_unref_vsc_msg_item((MsgItem *)item);
+    } else {
+        free(item);
     }
-    free(item);
 }
 
 static void smartcard_channel_on_disconnect(RedChannelClient *rcc)
@@ -369,19 +445,29 @@ static void smartcard_push_error(RedChannelClient *rcc, uint32_t reader_id, VSCE
     smartcard_channel_client_pipe_add_push(rcc, &error_item->base);
 }
 
-static void smartcard_push_vscmsg(RedChannelClient *rcc, VSCMsgHeader *vheader)
+static MsgItem *smartcard_get_vsc_msg_item(RedChannelClient *rcc, VSCMsgHeader *vheader)
 {
     MsgItem *msg_item = spice_new0(MsgItem, 1);
 
     red_channel_pipe_item_init(rcc->channel, &msg_item->base,
                                PIPE_ITEM_TYPE_MSG);
+    msg_item->refs = 1;
     msg_item->vheader = vheader;
-    smartcard_channel_client_pipe_add_push(rcc, &msg_item->base);
+    return msg_item;
 }
 
-void smartcard_on_message_from_device(RedChannelClient *rcc, VSCMsgHeader* vheader)
+static MsgItem *smartcard_ref_vsc_msg_item(MsgItem *item)
 {
-    smartcard_push_vscmsg(rcc, vheader);
+    item->refs++;
+    return item;
+}
+
+static void smartcard_unref_vsc_msg_item(MsgItem *item)
+{
+    if (!--item->refs) {
+        free(item->vheader);
+        free(item);
+    }
 }
 
 static void smartcard_remove_reader(RedChannelClient *rcc, uint32_t reader_id)
@@ -395,7 +481,7 @@ static void smartcard_remove_reader(RedChannelClient *rcc, uint32_t reader_id)
         return;
     }
 
-    state = SPICE_CONTAINEROF(char_device->st, SmartCardDeviceState, base);
+    state = spice_char_device_state_opaque_get(char_device->st);
     if (state->attached == FALSE) {
         smartcard_push_error(rcc, reader_id,
             VSC_GENERAL_ERROR);
diff --git a/server/smartcard.h b/server/smartcard.h
index 7881e1f..221c777 100644
--- a/server/smartcard.h
+++ b/server/smartcard.h
@@ -23,10 +23,10 @@
 // Maximal length of APDU
 #define APDUBufSize 270
 
-/** connect to smartcard interface, used by smartcard channel
- * returns -1 if failed, 0 if successfull
+/*
+ * connect to smartcard interface, used by smartcard channel
  */
-int smartcard_device_connect(SpiceCharDeviceInstance *char_device);
+SpiceCharDeviceState *smartcard_device_connect(SpiceCharDeviceInstance *char_device);
 void smartcard_device_disconnect(SpiceCharDeviceInstance *char_device);
 
 #endif // __SMART_CARD_H__
commit 7305a4b06f6e846bfecc70d79ac2cef99781cddd
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 15:02:35 2012 +0300

    spicevmc: use SpiceCharDeviceState for writing to the guest device
    
    With SpiceCharDeviceState, spicevmc now supports partial writes,
    and storing data that is received from the client after the device is
    stopped, instead of attempting to write it to the guest.

diff --git a/server/spicevmc.c b/server/spicevmc.c
index 628a900..b96bf9f 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -32,6 +32,10 @@
 #include "red_channel.h"
 #include "reds.h"
 
+/* todo: add flow control. i.e.,
+ * (a) limit the tokens available for the client
+ * (b) limit the tokens available for the server
+ */
 /* 64K should be enough for all but the largest writes + 32 bytes hdr */
 #define BUF_SIZE (64 * 1024 + 32)
 
@@ -50,9 +54,7 @@ typedef struct SpiceVmcState {
     SpiceCharDeviceState *chardev_st;
     SpiceCharDeviceInstance *chardev_sin;
     SpiceVmcPipeItem *pipe_item;
-    uint8_t *rcv_buf;
-    uint32_t rcv_buf_size;
-    int rcv_buf_in_use;
+    SpiceCharDeviceWriteBuffer *recv_from_client_buf;
 } SpiceVmcState;
 
 static SpiceVmcPipeItem *spicevmc_pipe_item_ref(SpiceVmcPipeItem *item)
@@ -204,23 +206,17 @@ static int spicevmc_red_channel_client_handle_message(RedChannelClient *rcc,
                                                       uint8_t *msg)
 {
     SpiceVmcState *state;
-    SpiceCharDeviceInstance *sin;
-    SpiceCharDeviceInterface *sif;
 
     state = SPICE_CONTAINEROF(rcc->channel, SpiceVmcState, channel);
-    sin = state->chardev_sin;
-    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
 
     if (type != SPICE_MSGC_SPICEVMC_DATA) {
         return red_channel_client_handle_message(rcc, size, type, msg);
     }
 
-    /*
-     * qemu spicevmc will consume everything we give it, no need for
-     * flow control checks (or to use a pipe).
-     */
-    sif->write(sin, msg, size);
-
+    spice_assert(state->recv_from_client_buf->buf == msg);
+    state->recv_from_client_buf->buf_used = size;
+    spice_char_device_write_buffer_add(state->chardev_st, state->recv_from_client_buf);
+    state->recv_from_client_buf = NULL;
     return TRUE;
 }
 
@@ -232,16 +228,16 @@ static uint8_t *spicevmc_red_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
 
     state = SPICE_CONTAINEROF(rcc->channel, SpiceVmcState, channel);
 
-    assert(!state->rcv_buf_in_use);
+    assert(!state->recv_from_client_buf);
 
-    if (size > state->rcv_buf_size) {
-        state->rcv_buf = spice_realloc(state->rcv_buf, size);
-        state->rcv_buf_size = size;
+    state->recv_from_client_buf = spice_char_device_write_buffer_get(state->chardev_st,
+                                                                     rcc->client,
+                                                                     size);
+    if (!state->recv_from_client_buf) {
+        spice_error("failed to allocate write buffer");
+        return NULL;
     }
-
-    state->rcv_buf_in_use = 1;
-
-    return state->rcv_buf;
+    return state->recv_from_client_buf->buf;
 }
 
 static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
@@ -253,8 +249,10 @@ static void spicevmc_red_channel_release_msg_rcv_buf(RedChannelClient *rcc,
 
     state = SPICE_CONTAINEROF(rcc->channel, SpiceVmcState, channel);
 
-    /* NOOP, we re-use the buffer every time and only free it on destruction */
-    state->rcv_buf_in_use = 0;
+    if (state->recv_from_client_buf) { /* buffer wasn't pushed to device */
+        spice_char_device_write_buffer_release(state->chardev_st, state->recv_from_client_buf);
+        state->recv_from_client_buf = NULL;
+    }
 }
 
 static void spicevmc_red_channel_hold_pipe_item(RedChannelClient *rcc,
@@ -377,12 +375,13 @@ void spicevmc_device_disconnect(SpiceCharDeviceInstance *sin)
 
     state = (SpiceVmcState *)spice_char_device_state_opaque_get(sin->st);
 
+    if (state->recv_from_client_buf) {
+        spice_char_device_write_buffer_release(state->chardev_st, state->recv_from_client_buf);
+    }
     spice_char_device_state_destroy(sin->st);
     state->chardev_st = NULL;
 
     reds_unregister_channel(&state->channel);
-
     free(state->pipe_item);
-    free(state->rcv_buf);
     red_channel_destroy(&state->channel);
 }
commit da7114d751fdc4d3cfae1f97b3d973bbb0e03584
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 14:58:27 2012 +0300

    spicevmc: employ SpiceCharDeviceState for managing reading from the guest device
    
    This patch and the following one do not introduce tokening to the
    spicevmc channel. But this can be done easily later, by setting the appropriate
    variables in SpiceCharDeviceState (after adding
    the appropriate protocol messages, and implementing this in the client
    side).

diff --git a/server/char_device.h b/server/char_device.h
index db27bec..72f8abd 100644
--- a/server/char_device.h
+++ b/server/char_device.h
@@ -208,8 +208,8 @@ void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
 
 /* api for specific char devices */
 
-void spicevmc_device_connect(SpiceCharDeviceInstance *sin,
-                             uint8_t channel_type);
+SpiceCharDeviceState *spicevmc_device_connect(SpiceCharDeviceInstance *sin,
+                                              uint8_t channel_type);
 void spicevmc_device_disconnect(SpiceCharDeviceInstance *char_device);
 
 #endif // __CHAR_DEVICE_H__
diff --git a/server/reds.c b/server/reds.c
index 5ca0d21..42fad0c 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3206,7 +3206,7 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
     }
 #endif
     else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
-        spicevmc_device_connect(char_device, SPICE_CHANNEL_USBREDIR);
+        dev_state = spicevmc_device_connect(char_device, SPICE_CHANNEL_USBREDIR);
     }
     if (dev_state) {
         spice_assert(char_device->st);
diff --git a/server/spicevmc.c b/server/spicevmc.c
index 27123d4..628a900 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -37,6 +37,8 @@
 
 typedef struct SpiceVmcPipeItem {
     PipeItem base;
+    uint32_t refs;
+
     /* writes which don't fit this will get split, this is not a problem */
     uint8_t buf[BUF_SIZE];
     uint32_t buf_used;
@@ -45,7 +47,7 @@ typedef struct SpiceVmcPipeItem {
 typedef struct SpiceVmcState {
     RedChannel channel; /* Must be the first item */
     RedChannelClient *rcc;
-    SpiceCharDeviceState chardev_st;
+    SpiceCharDeviceState *chardev_st;
     SpiceCharDeviceInstance *chardev_sin;
     SpiceVmcPipeItem *pipe_item;
     uint8_t *rcv_buf;
@@ -53,35 +55,95 @@ typedef struct SpiceVmcState {
     int rcv_buf_in_use;
 } SpiceVmcState;
 
-static void spicevmc_chardev_wakeup(SpiceCharDeviceInstance *sin)
+static SpiceVmcPipeItem *spicevmc_pipe_item_ref(SpiceVmcPipeItem *item)
 {
-    SpiceVmcState *state;
+    item->refs++;
+    return item;
+}
+
+static void spicevmc_pipe_item_unref(SpiceVmcPipeItem *item)
+{
+    if (!--item->refs) {
+        free(item);
+    }
+}
+
+SpiceCharDeviceMsgToClient *spicevmc_chardev_ref_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                                               void *opaque)
+{
+    return spicevmc_pipe_item_ref((SpiceVmcPipeItem *)msg);
+}
+
+static void spicevmc_chardev_unref_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                                 void *opaque)
+{
+    spicevmc_pipe_item_unref((SpiceVmcPipeItem *)msg);
+}
+
+static SpiceCharDeviceMsgToClient *spicevmc_chardev_read_msg_from_dev(SpiceCharDeviceInstance *sin,
+                                                                      void *opaque)
+{
+    SpiceVmcState *state = opaque;
     SpiceCharDeviceInterface *sif;
+    SpiceVmcPipeItem *msg_item;
     int n;
 
-    state = SPICE_CONTAINEROF(sin->st, SpiceVmcState, chardev_st);
     sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
 
     if (!state->rcc) {
-        return;
+        return NULL;
     }
 
-    do {
-        if (!state->pipe_item) {
-            state->pipe_item = spice_malloc(sizeof(SpiceVmcPipeItem));
-            red_channel_pipe_item_init(&state->channel,
-                                       &state->pipe_item->base, 0);
-        }
+    if (!state->pipe_item) {
+        msg_item = spice_new0(SpiceVmcPipeItem, 1);
+        msg_item->refs = 1;
+        red_channel_pipe_item_init(&state->channel,
+                                       &msg_item->base, 0);
+    } else {
+        spice_assert(state->pipe_item->buf_used == 0);
+        msg_item = state->pipe_item;
+        state->pipe_item = NULL;
+    }
 
-        n = sif->read(sin, state->pipe_item->buf,
-                      sizeof(state->pipe_item->buf));
-        if (n > 0) {
-            state->pipe_item->buf_used = n;
-            red_channel_client_pipe_add_push(state->rcc,
-                                             &state->pipe_item->base);
-            state->pipe_item = NULL;
-        }
-    } while (n > 0);
+    n = sif->read(sin, msg_item->buf,
+                  sizeof(msg_item->buf));
+    if (n > 0) {
+        spice_debug("read from dev %d", n);
+        msg_item->buf_used = n;
+        return msg_item;
+    } else {
+        state->pipe_item = msg_item;
+        return NULL;
+    }
+}
+
+static void spicevmc_chardev_send_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                                 RedClient *client,
+                                                 void *opaque)
+{
+    SpiceVmcState *state = opaque;
+    SpiceVmcPipeItem *vmc_msg = msg;
+
+    spice_assert(state->rcc->client == client);
+    spicevmc_pipe_item_ref(vmc_msg);
+    red_channel_client_pipe_add_push(state->rcc, &vmc_msg->base);
+}
+
+static void spicevmc_char_dev_send_tokens_to_client(RedClient *client,
+                                                    uint32_t tokens,
+                                                    void *opaque)
+{
+    spice_printerr("Not implemented!");
+}
+
+static void spicevmc_char_dev_remove_client(RedClient *client, void *opaque)
+{
+    SpiceVmcState *state = opaque;
+
+    spice_printerr("vmc state %p, client %p", state, client);
+    spice_assert(state->rcc && state->rcc->client == client);
+
+    red_channel_client_shutdown(state->rcc);
 }
 
 static int spicevmc_red_channel_client_config_socket(RedChannelClient *rcc)
@@ -116,6 +178,15 @@ static void spicevmc_red_channel_client_on_disconnect(RedChannelClient *rcc)
     sin = state->chardev_sin;
     sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
 
+    if (state->chardev_st) {
+        if (spice_char_device_client_exists(state->chardev_st, rcc->client)) {
+            spice_char_device_client_remove(state->chardev_st, rcc->client);
+        } else {
+            spice_printerr("client %p have already been removed from char dev %p",
+                           rcc->client, state->chardev_st);
+        }
+    }
+
     /* Don't destroy the rcc if it is already being destroyed, as then
        red_client_destroy/red_channel_client_destroy will already do this! */
     if (!rcc->destroying)
@@ -206,7 +277,7 @@ static void spicevmc_red_channel_send_item(RedChannelClient *rcc,
 static void spicevmc_red_channel_release_pipe_item(RedChannelClient *rcc,
     PipeItem *item, int item_pushed)
 {
-    free(item);
+    spicevmc_pipe_item_unref((SpiceVmcPipeItem *)item);
 }
 
 static void spicevmc_connect(RedChannel *channel, RedClient *client,
@@ -240,6 +311,8 @@ static void spicevmc_connect(RedChannel *channel, RedClient *client,
     state->rcc = rcc;
     red_channel_client_ack_zero_messages_window(rcc);
 
+    spice_char_device_client_add(state->chardev_st, client, FALSE, 0, ~0, ~0);
+
     if (sif->state) {
         sif->state(sin, 1);
     }
@@ -250,13 +323,14 @@ static void spicevmc_migrate(RedChannelClient *rcc)
     /* NOOP */
 }
 
-void spicevmc_device_connect(SpiceCharDeviceInstance *sin,
-    uint8_t channel_type)
+SpiceCharDeviceState *spicevmc_device_connect(SpiceCharDeviceInstance *sin,
+                                              uint8_t channel_type)
 {
     static uint8_t id[256] = { 0, };
     SpiceVmcState *state;
     ChannelCbs channel_cbs = { NULL, };
     ClientCbs client_cbs = { NULL, };
+    SpiceCharDeviceCallbacks char_dev_cbs = {NULL, };
 
     channel_cbs.config_socket = spicevmc_red_channel_client_config_socket;
     channel_cbs.on_disconnect = spicevmc_red_channel_client_on_disconnect;
@@ -273,18 +347,27 @@ void spicevmc_device_connect(SpiceCharDeviceInstance *sin,
                                    spicevmc_red_channel_client_handle_message,
                                    &channel_cbs);
     red_channel_init_outgoing_messages_window(&state->channel);
-    state->chardev_st.wakeup = spicevmc_chardev_wakeup;
-    state->chardev_sin = sin;
-    state->rcv_buf = spice_malloc(BUF_SIZE);
-    state->rcv_buf_size = BUF_SIZE;
 
     client_cbs.connect = spicevmc_connect;
     client_cbs.migrate = spicevmc_migrate;
     red_channel_register_client_cbs(&state->channel, &client_cbs);
 
-    sin->st = &state->chardev_st;
+    char_dev_cbs.read_one_msg_from_device = spicevmc_chardev_read_msg_from_dev;
+    char_dev_cbs.ref_msg_to_client = spicevmc_chardev_ref_msg_to_client;
+    char_dev_cbs.unref_msg_to_client = spicevmc_chardev_unref_msg_to_client;
+    char_dev_cbs.send_msg_to_client = spicevmc_chardev_send_msg_to_client;
+    char_dev_cbs.send_tokens_to_client = spicevmc_char_dev_send_tokens_to_client;
+    char_dev_cbs.remove_client = spicevmc_char_dev_remove_client;
+
+    state->chardev_st = spice_char_device_state_create(sin,
+                                                       0, /* tokens interval */
+                                                       ~0, /* self tokens */
+                                                       &char_dev_cbs,
+                                                       state);
+    state->chardev_sin = sin;
 
     reds_register_channel(&state->channel);
+    return state->chardev_st;
 }
 
 /* Must be called from RedClient handling thread. */
@@ -292,7 +375,10 @@ void spicevmc_device_disconnect(SpiceCharDeviceInstance *sin)
 {
     SpiceVmcState *state;
 
-    state = SPICE_CONTAINEROF(sin->st, SpiceVmcState, chardev_st);
+    state = (SpiceVmcState *)spice_char_device_state_opaque_get(sin->st);
+
+    spice_char_device_state_destroy(sin->st);
+    state->chardev_st = NULL;
 
     reds_unregister_channel(&state->channel);
 
commit bc93d5455cf6cff575e1e40cec9ff0f161f06d20
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 14:36:51 2012 +0300

    agent: employ SpiceCharDeviceState for writing to the device

diff --git a/server/reds.c b/server/reds.c
index 8abf19e..5ca0d21 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -167,11 +167,6 @@ typedef struct VDIPortState {
     uint32_t plug_generation;
 
     /* write to agent */
-    uint32_t num_tokens;
-    uint32_t num_client_tokens;
-    Ring external_bufs;
-    Ring internal_bufs;
-    Ring write_queue;
     SpiceCharDeviceWriteBuffer *recv_from_client_buf;
     int recv_from_client_buf_pushed;
     AgentMsgFilter write_filter;
@@ -188,6 +183,16 @@ typedef struct VDIPortState {
     VDIChunkHeader vdi_chunk_header;
 } VDIPortState;
 
+/* messages that are addressed to the agent and are created in the server */
+typedef struct __attribute__ ((__packed__)) VDInternalBuf {
+    VDIChunkHeader chunk_header;
+    VDAgentMessage header;
+    union {
+        VDAgentMouseState mouse_state;
+    }
+    u;
+} VDInternalBuf;
+
 #ifdef RED_STATISTICS
 
 #define REDS_MAX_STAT_NODES 100
@@ -241,8 +246,6 @@ typedef struct RedsState {
     MonitorMode monitor_mode;
     SpiceTimer *mig_timer;
     SpiceTimer *mm_timer;
-    SpiceTimer *vdi_port_write_timer;
-    int vdi_port_write_timer_started;
 
     SSL_CTX *ctx;
 
@@ -278,31 +281,6 @@ typedef struct RedLinkInfo {
     int skip_auth;
 } RedLinkInfo;
 
-typedef struct VDIPortBuf VDIPortBuf;
-struct  __attribute__ ((__packed__)) VDIPortBuf {
-    RingItem link;
-    uint8_t *now;
-    int write_len;
-    void (*free)(VDIPortBuf *buf);
-    VDIChunkHeader chunk_header; //start send from &chunk_header
-};
-
-typedef struct __attribute__ ((__packed__)) VDAgentExtBuf {
-    VDIPortBuf base;
-    uint8_t buf[SPICE_AGENT_MAX_DATA_SIZE];
-    VDIChunkHeader migrate_overflow;
-} VDAgentExtBuf;
-
-typedef struct __attribute__ ((__packed__)) VDInternalBuf {
-    VDIPortBuf base;
-    VDAgentMessage header;
-    union {
-        VDAgentMouseState mouse_state;
-    }
-    u;
-    VDIChunkHeader migrate_overflow;
-} VDInternalBuf;
-
 typedef struct RedSSLParameters {
     char keyfile_password[256];
     char certs_file[256];
@@ -607,15 +585,6 @@ static void reds_reset_vdp(void)
     VDIPortState *state = &reds->agent_state;
     SpiceCharDeviceInterface *sif;
 
-    while (!ring_is_empty(&state->write_queue)) {
-        VDIPortBuf *buf;
-        RingItem *item;
-
-        item = ring_get_tail(&state->write_queue);
-        ring_remove(item);
-        buf = (VDIPortBuf *)item;
-        buf->free(buf);
-    }
     state->read_state = VDI_PORT_READ_STATE_READ_HADER;
     state->recive_pos = (uint8_t *)&state->vdi_chunk_header;
     state->recive_len = sizeof(state->vdi_chunk_header);
@@ -786,73 +755,6 @@ static void reds_agent_remove(void)
     }
 }
 
-/* this will be fixed to handle multiple clients
-   in following patches */
-static void reds_push_tokens(MainChannelClient *mcc)
-{
-    reds->agent_state.num_client_tokens += reds->agent_state.num_tokens;
-    spice_assert(reds->agent_state.num_client_tokens <= REDS_AGENT_WINDOW_SIZE);
-    main_channel_client_push_agent_tokens(mcc, reds->agent_state.num_tokens);
-    reds->agent_state.num_tokens = 0;
-}
-
-static int write_to_vdi_port(void);
-
-static void vdi_port_write_timer_start(void)
-{
-    if (reds->vdi_port_write_timer_started) {
-        return;
-    }
-    reds->vdi_port_write_timer_started = TRUE;
-    core->timer_start(reds->vdi_port_write_timer,
-                      VDI_PORT_WRITE_RETRY_TIMEOUT);
-}
-
-static void vdi_port_write_retry(void *opaque)
-{
-    reds->vdi_port_write_timer_started = FALSE;
-    write_to_vdi_port();
-}
-
-static int write_to_vdi_port(void)
-{
-    VDIPortState *state = &reds->agent_state;
-    SpiceCharDeviceInterface *sif;
-    RingItem *ring_item;
-    VDIPortBuf *buf;
-    int total = 0;
-    int n;
-
-    if (!vdagent) {
-        return 0;
-    }
-
-    sif = SPICE_CONTAINEROF(vdagent->base.sif, SpiceCharDeviceInterface, base);
-    while (vdagent) {
-        if (!(ring_item = ring_get_tail(&state->write_queue))) {
-            break;
-        }
-        buf = (VDIPortBuf *)ring_item;
-        n = sif->write(vdagent, buf->now, buf->write_len);
-        if (n == 0) {
-            break;
-        }
-        total += n;
-        buf->write_len -= n;
-        if (!buf->write_len) {
-            ring_remove(ring_item);
-            buf->free(buf);
-            continue;
-        }
-        buf->now += n;
-    }
-    // Workaround for lack of proper sif write_possible callback (RHBZ 616772)
-    if (ring_item != NULL) {
-        vdi_port_write_timer_start();
-    }
-    return total;
-}
-
 /*******************************
  * Char device state callbacks *
  * *****************************/
@@ -1052,37 +954,38 @@ int reds_has_vdagent(void)
 
 void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state)
 {
-    RingItem *ring_item;
-    VDInternalBuf *buf;
+    SpiceCharDeviceWriteBuffer *char_dev_buf;
+    VDInternalBuf *internal_buf;
+    uint32_t total_msg_size;
 
-    if (!inputs_inited()) {
+    if (!inputs_inited() || !reds->agent_state.base) {
         return;
     }
-    if (!(ring_item = ring_get_head(&reds->agent_state.internal_bufs))) {
+
+    total_msg_size = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) +
+                     sizeof(VDAgentMouseState);
+    char_dev_buf = spice_char_device_write_buffer_get(reds->agent_state.base,
+                                                      NULL,
+                                                      total_msg_size);
+
+    if (!char_dev_buf) {
         reds->pending_mouse_event = TRUE;
-        vdi_port_write_timer_start();
+
         return;
     }
     reds->pending_mouse_event = FALSE;
-    ring_remove(ring_item);
-    buf = (VDInternalBuf *)ring_item;
-    buf->base.now = (uint8_t *)&buf->base.chunk_header;
-    buf->base.write_len = sizeof(VDIChunkHeader) + sizeof(VDAgentMessage) +
-                          sizeof(VDAgentMouseState);
-    buf->u.mouse_state = *mouse_state;
-    ring_add(&reds->agent_state.write_queue, &buf->base.link);
-    write_to_vdi_port();
-}
 
-static void add_token(MainChannelClient *mcc)
-{
-    VDIPortState *state = &reds->agent_state;
+    internal_buf = (VDInternalBuf *)char_dev_buf->buf;
+    internal_buf->chunk_header.port = VDP_SERVER_PORT;
+    internal_buf->chunk_header.size = sizeof(VDAgentMessage) + sizeof(VDAgentMouseState);
+    internal_buf->header.protocol = VD_AGENT_PROTOCOL;
+    internal_buf->header.type = VD_AGENT_MOUSE_STATE;
+    internal_buf->header.opaque = 0;
+    internal_buf->header.size = sizeof(VDAgentMouseState);
+    internal_buf->u.mouse_state = *mouse_state;
 
-    /* this will be fixed to handle multiple clients
-       in following patches */
-    if (++state->num_tokens == REDS_TOKENS_TO_SEND) {
-        reds_push_tokens(mcc);
-    }
+    char_dev_buf->buf_used = total_msg_size;
+    spice_char_device_write_buffer_add(reds->agent_state.base, char_dev_buf);
 }
 
 int reds_num_of_channels(void)
@@ -1215,18 +1118,11 @@ void reds_release_agent_data_buffer(uint8_t *buf)
 
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
 {
-    // TODO - use mcc (and start tracking agent data per channel. probably just move the whole
-    // tokens accounting to mainchannel.
-    RingItem *ring_item;
-    VDAgentExtBuf *buf;
+    VDIPortState *dev_state = &reds->agent_state;
+    VDIChunkHeader *header;
     int res;
 
-    if (!reds->agent_state.num_client_tokens) {
-        spice_printerr("token violation");
-        reds_disconnect();
-        return;
-    }
-    --reds->agent_state.num_client_tokens;
+    spice_assert(message == reds->agent_state.recv_from_client_buf->buf + sizeof(VDIChunkHeader));
 
     res = agent_msg_filter_process_data(&reds->agent_state.write_filter,
                                         message, size);
@@ -1234,26 +1130,20 @@ void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
     case AGENT_MSG_FILTER_OK:
         break;
     case AGENT_MSG_FILTER_DISCARD:
-        add_token(mcc);
         return;
     case AGENT_MSG_FILTER_PROTO_ERROR:
         reds_disconnect();
         return;
     }
 
-    if (!(ring_item = ring_get_head(&reds->agent_state.external_bufs))) {
-        spice_printerr("no agent free bufs");
-        reds_disconnect();
-        return;
-    }
-    ring_remove(ring_item);
-    buf = (VDAgentExtBuf *)ring_item;
-    buf->base.now = (uint8_t *)&buf->base.chunk_header.port;
-    buf->base.write_len = size + sizeof(VDIChunkHeader);
-    buf->base.chunk_header.size = size;
-    memcpy(buf->buf, message, size);
-    ring_add(&reds->agent_state.write_queue, ring_item);
-    write_to_vdi_port();
+    // TODO - start tracking agent data per channel
+    header =  (VDIChunkHeader *)dev_state->recv_from_client_buf->buf;
+    header->port = VDP_CLIENT_PORT;
+    header->size = size;
+    reds->agent_state.recv_from_client_buf->buf_used = sizeof(VDIChunkHeader) + size;
+
+    dev_state->recv_from_client_buf_pushed = TRUE;
+    spice_char_device_write_buffer_add(reds->agent_state.base, dev_state->recv_from_client_buf);
 }
 
 void reds_on_main_migrate_connected(void)
@@ -1526,7 +1416,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
     if (link_mess->connection_id == 0) {
         reds_send_link_result(link, SPICE_LINK_ERR_OK);
         while((connection_id = rand()) == 0);
-        reds->agent_state.num_tokens = 0;
         mig_target = FALSE;
     } else {
         // TODO: make sure link_mess->connection_id is the same
@@ -1564,8 +1453,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
         reds->agent_state.plug_generation++;
     }
 
-    reds->agent_state.num_client_tokens = REDS_AGENT_WINDOW_SIZE;
-
     if (!mig_target) {
         main_channel_push_init(mcc, red_dispatcher_count(),
             reds->mouse_mode, reds->is_client_mouse_allowed,
@@ -1577,7 +1464,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
             main_channel_push_uuid(mcc, spice_uuid);
 
         main_channel_client_start_net_test(mcc);
-        /* Now that we have a client, forward any pending agent data */
     } else {
         reds_mig_target_client_add(client);
     }
@@ -3501,38 +3387,11 @@ SPICE_GNUC_VISIBLE int spice_server_remove_interface(SpiceBaseInstance *sin)
     return 0;
 }
 
-static void free_external_agent_buff(VDIPortBuf *in_buf)
-{
-    VDIPortState *state = &reds->agent_state;
-    RedClient *random_client;
-
-    ring_add(&state->external_bufs, &in_buf->link);
-    /* this will be fixed to handle multiple clients
-       in following patches */
-    random_client = SPICE_CONTAINEROF(ring_get_tail(&reds->clients),
-                                      RedClient,
-                                      link);
-    add_token(red_client_get_main(random_client));
-}
-
-static void free_internal_agent_buff(VDIPortBuf *in_buf)
-{
-    VDIPortState *state = &reds->agent_state;
-
-    ring_add(&state->internal_bufs, &in_buf->link);
-    if (inputs_inited() && reds->pending_mouse_event) {
-        reds_handle_agent_mouse_event(inputs_get_mouse_state());
-    }
-}
-
 static void init_vd_agent_resources(void)
 {
     VDIPortState *state = &reds->agent_state;
     int i;
 
-    ring_init(&state->external_bufs);
-    ring_init(&state->internal_bufs);
-    ring_init(&state->write_queue);
     ring_init(&state->read_bufs);
     agent_msg_filter_init(&state->write_filter, agent_copypaste, TRUE);
     agent_msg_filter_init(&state->read_filter, agent_copypaste, TRUE);
@@ -3541,27 +3400,6 @@ static void init_vd_agent_resources(void)
     state->recive_pos = (uint8_t *)&state->vdi_chunk_header;
     state->recive_len = sizeof(state->vdi_chunk_header);
 
-    for (i = 0; i < REDS_AGENT_WINDOW_SIZE; i++) {
-        VDAgentExtBuf *buf = spice_new0(VDAgentExtBuf, 1);
-        ring_item_init(&buf->base.link);
-        buf->base.chunk_header.port = VDP_CLIENT_PORT;
-        buf->base.free = free_external_agent_buff;
-        ring_add(&reds->agent_state.external_bufs, &buf->base.link);
-    }
-
-    for (i = 0; i < REDS_NUM_INTERNAL_AGENT_MESSAGES; i++) {
-        VDInternalBuf *buf = spice_new0(VDInternalBuf, 1);
-        ring_item_init(&buf->base.link);
-        buf->base.free = free_internal_agent_buff;
-        buf->base.chunk_header.port = VDP_SERVER_PORT;
-        buf->base.chunk_header.size = sizeof(VDAgentMessage) + sizeof(VDAgentMouseState);
-        buf->header.protocol = VD_AGENT_PROTOCOL;
-        buf->header.type = VD_AGENT_MOUSE_STATE;
-        buf->header.opaque = 0;
-        buf->header.size = sizeof(VDAgentMouseState);
-        ring_add(&reds->agent_state.internal_bufs, &buf->base.link);
-    }
-
     for (i = 0; i < REDS_VDI_PORT_NUM_RECEIVE_BUFFS; i++) {
         VDIReadBuf *buf = spice_new0(VDIReadBuf, 1);
         ring_item_init(&buf->link);
@@ -3592,11 +3430,6 @@ static int do_spice_init(SpiceCoreInterface *core_interface)
     if (!(reds->mig_timer = core->timer_add(migrate_timeout, NULL))) {
         spice_error("migration timer create failed");
     }
-    if (!(reds->vdi_port_write_timer = core->timer_add(vdi_port_write_retry, NULL)))
-    {
-        spice_error("vdi port write timer create failed");
-    }
-    reds->vdi_port_write_timer_started = FALSE;
 
 #ifdef RED_STATISTICS
     int shm_name_len;
commit e88f03f16b34505ba2b8510c0670775928f5c0eb
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 14:16:59 2012 +0300

    agent: use SpiceCharDeviceWriteBuffer for agent data from the client
    
    This is an intermediate patch. The next patch will actually
    push the buffer to the device, instead of copying it.

diff --git a/server/main_channel.c b/server/main_channel.c
index f9492b0..00a6b0f 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -902,8 +902,13 @@ static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
                                                uint32_t size)
 {
     MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
 
-    return main_chan->recv_buf;
+    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
+        return reds_get_agent_data_buffer(mcc, size);
+    } else {
+        return main_chan->recv_buf;
+    }
 }
 
 static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
@@ -911,6 +916,9 @@ static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
                                                uint32_t size,
                                                uint8_t *msg)
 {
+    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
+        reds_release_agent_data_buffer(msg);
+    }
 }
 
 static int main_channel_config_socket(RedChannelClient *rcc)
diff --git a/server/reds.c b/server/reds.c
index 17b8f7e..8abf19e 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -172,6 +172,8 @@ typedef struct VDIPortState {
     Ring external_bufs;
     Ring internal_bufs;
     Ring write_queue;
+    SpiceCharDeviceWriteBuffer *recv_from_client_buf;
+    int recv_from_client_buf_pushed;
     AgentMsgFilter write_filter;
 
     /* read from agent */
@@ -1179,6 +1181,38 @@ void reds_on_main_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
                                                 num_tokens);
 }
 
+uint8_t *reds_get_agent_data_buffer(MainChannelClient *mcc, size_t size)
+{
+    VDIPortState *dev_state = &reds->agent_state;
+    RedClient *client;
+
+    if (!dev_state->base) {
+        return NULL;
+    }
+
+    spice_assert(dev_state->recv_from_client_buf == NULL);
+    client = main_channel_client_get_base(mcc)->client;
+    dev_state->recv_from_client_buf = spice_char_device_write_buffer_get(dev_state->base,
+                                                                         client,
+                                                                         size + sizeof(VDIChunkHeader));
+    dev_state->recv_from_client_buf_pushed = FALSE;
+    return dev_state->recv_from_client_buf->buf + sizeof(VDIChunkHeader);
+}
+
+void reds_release_agent_data_buffer(uint8_t *buf)
+{
+    VDIPortState *dev_state = &reds->agent_state;
+
+    spice_assert(buf == dev_state->recv_from_client_buf->buf + sizeof(VDIChunkHeader));
+
+    if (!dev_state->recv_from_client_buf_pushed) {
+        spice_char_device_write_buffer_release(reds->agent_state.base,
+                                               dev_state->recv_from_client_buf);
+    }
+    dev_state->recv_from_client_buf = NULL;
+    dev_state->recv_from_client_buf_pushed = FALSE;
+}
+
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
 {
     // TODO - use mcc (and start tracking agent data per channel. probably just move the whole
diff --git a/server/reds.h b/server/reds.h
index 674052a..f3d2ffa 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -147,6 +147,8 @@ void reds_update_stat_value(uint32_t value);
 
 void reds_on_main_agent_start(MainChannelClient *mcc, uint32_t num_tokens);
 void reds_on_main_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
+uint8_t *reds_get_agent_data_buffer(MainChannelClient *mcc, size_t size);
+void reds_release_agent_data_buffer(uint8_t *buf);
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
 void reds_on_main_migrate_connected(void); //should be called when all the clients
                                            // are connected to the target
commit bf1d9007b40d233369a3e202895837c60fe2f87b
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 13:22:36 2012 +0300

    agent: employ SpiceCharDeviceState for managing reading from the device

diff --git a/server/main_channel.c b/server/main_channel.c
index ce467f8..f9492b0 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -81,13 +81,8 @@ typedef struct TokensPipeItem {
     int tokens;
 } TokensPipeItem;
 
-typedef struct AgentDataPipeItemRefs {
-    int refs;
-} AgentDataPipeItemRefs;
-
 typedef struct AgentDataPipeItem {
     PipeItem base;
-    AgentDataPipeItemRefs *refs;
     uint8_t* data;
     size_t len;
     spice_marshaller_item_free_func free_data;
@@ -232,26 +227,18 @@ static PipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_
     return &item->base;
 }
 
-typedef struct MainAgentDataItemInfo {
-    uint8_t* data;
-    size_t len;
-    spice_marshaller_item_free_func free_data;
-    void *opaque;
-    AgentDataPipeItemRefs *refs;
-} MainAgentDataItemInfo;
-
-static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, void *data, int num)
+static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
+                                          spice_marshaller_item_free_func free_data,
+                                          void *opaque)
 {
-    MainAgentDataItemInfo *info = data;
     AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
 
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_AGENT_DATA);
-    item->refs = info->refs;
-    item->data = info->data;
-    item->len = info->len;
-    item->free_data = info->free_data;
-    item->opaque = info->opaque;
+    item->data = data;
+    item->len = len;
+    item->free_data = free_data;
+    item->opaque = opaque;
     return &item->base;
 }
 
@@ -432,20 +419,13 @@ static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens
     spice_marshall_msg_main_agent_token(m, &tokens);
 }
 
-void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque)
 {
-    MainAgentDataItemInfo info = {
-        .data = data,
-        .len = len,
-        .free_data = free_data,
-        .opaque = opaque,
-        .refs = spice_malloc(sizeof(AgentDataPipeItemRefs)),
-    };
+    PipeItem *item;
 
-    info.refs->refs = main_chan->base.clients_num;
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_agent_data_item_new, &info);
+    item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
+    red_channel_client_pipe_add_push(&mcc->base, item);
 }
 
 static void main_channel_marshall_agent_data(SpiceMarshaller *m,
@@ -760,14 +740,10 @@ static void main_channel_release_pipe_item(RedChannelClient *rcc,
 {
     switch (base->type) {
         case SPICE_MSG_MAIN_AGENT_DATA: {
-            AgentDataPipeItem *data = (AgentDataPipeItem*)base;
-            if (!--data->refs->refs) {
-                spice_debug("SPICE_MSG_MAIN_AGENT_DATA %p %p, %d",
-                            data, data->refs, data->refs->refs);
-                free(data->refs);
+                AgentDataPipeItem *data = (AgentDataPipeItem *)base;
+
                 data->free_data(data->data, data->opaque);
-            }
-            break;
+                break;
         }
         default:
             break;
diff --git a/server/main_channel.h b/server/main_channel.h
index 7f620bb..b69dcfe 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -73,8 +73,8 @@ void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int
 void main_channel_push_agent_connected(MainChannel *main_chan);
 void main_channel_push_agent_disconnected(MainChannel *main_chan);
 void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
-void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
-           spice_marshaller_item_free_func free_data, void *opaque);
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+                                         spice_marshaller_item_free_func free_data, void *opaque);
 void main_channel_client_start_net_test(MainChannelClient *mcc);
 // TODO: huge. Consider making a reds_* interface for these functions
 // and calling from main.
diff --git a/server/reds.c b/server/reds.c
index 8b179f8..17b8f7e 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -146,24 +146,27 @@ typedef struct MonitorMode {
 
 typedef struct VDIReadBuf {
     RingItem link;
+    uint32_t refs;
+
     int len;
     uint8_t data[SPICE_AGENT_MAX_DATA_SIZE];
 } VDIReadBuf;
 
+static VDIReadBuf *vdi_port_read_buf_get(void);
+static VDIReadBuf *vdi_port_read_buf_ref(VDIReadBuf *buf);
+static void vdi_port_read_buf_unref(VDIReadBuf *buf);
+
 enum {
     VDI_PORT_READ_STATE_READ_HADER,
     VDI_PORT_READ_STATE_GET_BUFF,
     VDI_PORT_READ_STATE_READ_DATA,
 };
 
-void vdagent_char_device_wakeup(SpiceCharDeviceInstance *sin);
-struct SpiceCharDeviceState vdagent_char_device_state = {
-    .wakeup = &vdagent_char_device_wakeup,
-};
-
 typedef struct VDIPortState {
+    SpiceCharDeviceState *base;
     uint32_t plug_generation;
 
+    /* write to agent */
     uint32_t num_tokens;
     uint32_t num_client_tokens;
     Ring external_bufs;
@@ -171,6 +174,7 @@ typedef struct VDIPortState {
     Ring write_queue;
     AgentMsgFilter write_filter;
 
+    /* read from agent */
     Ring read_bufs;
     uint32_t read_state;
     uint32_t message_recive_len;
@@ -615,7 +619,7 @@ static void reds_reset_vdp(void)
     state->recive_len = sizeof(state->vdi_chunk_header);
     state->message_recive_len = 0;
     if (state->current_read_buf) {
-        ring_add(&state->read_bufs, &state->current_read_buf->link);
+        vdi_port_read_buf_unref(state->current_read_buf);
         state->current_read_buf = NULL;
     }
     /* Reset read filter to start with clean state when the agent reconnects */
@@ -625,6 +629,21 @@ static void reds_reset_vdp(void)
     state->write_filter.result = AGENT_MSG_FILTER_DISCARD;
     state->write_filter.discard_all = TRUE;
 
+    /* reseting and not destroying the state as a workaround for a bad
+     * tokens management in the vdagent protocol:
+     *  The client tokens' are set only once, when the main channel is initialized.
+     *  Instead, it would have been more appropriate to reset them upon AGEN_CONNECT.
+     *  The client tokens are tracked as part of the SpiceCharDeviceClientState. Thus,
+     *  in order to be backwartd compatible with the client, we need to track the tokens
+     *  event when the agent is detached. We don't destroy the the char_device state, and
+     *  instead we just reset it.
+     *  In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it
+     *  overrides the amount of tokens, instead of adding the given amount.
+     *
+     *  TODO: change AGENT_CONNECT msg to contain tokens count.
+     */
+    spice_char_device_reset(state->base);
+
     sif = SPICE_CONTAINEROF(vdagent->base.sif, SpiceCharDeviceInterface, base);
     if (sif->state) {
         sif->state(vdagent, 0);
@@ -658,6 +677,15 @@ void reds_client_disconnect(RedClient *client)
     if (mig_client) {
         reds_mig_target_client_free(mig_client);
     }
+
+    if (reds->agent_state.base) {
+        /* note that vdagent might be NULL, if the vdagent was once
+         * up and than was removed */
+        if (spice_char_device_client_exists(reds->agent_state.base, client)) {
+            spice_char_device_client_remove(reds->agent_state.base, client);
+        }
+    }
+
     ring_remove(&client->link);
     reds->num_clients--;
     red_client_destroy(client);
@@ -823,22 +851,19 @@ static int write_to_vdi_port(void)
     return total;
 }
 
-static int read_from_vdi_port(void);
+/*******************************
+ * Char device state callbacks *
+ * *****************************/
 
-static void vdi_read_buf_release(uint8_t *data, void *opaque)
+static void vdi_port_read_buf_release(uint8_t *data, void *opaque)
 {
     VDIReadBuf *buf = (VDIReadBuf *)opaque;
 
-    ring_add(&reds->agent_state.read_bufs, &buf->link);
-    /* read_from_vdi_port() may have never completed because the read_bufs
-       ring was empty. So we call it again so it can complete its work if
-       necessary. Note since we can be called from read_from_vdi_port ourselves
-       this can cause recursion, read_from_vdi_port() contains code protecting
-       it against this. */
-    while (read_from_vdi_port());
+    vdi_port_read_buf_unref(buf);
 }
 
-static void dispatch_vdi_port_data(int port, VDIReadBuf *buf)
+/* returns TRUE if the buffer can be forwarded */
+static int vdi_port_read_buf_process(int port, VDIReadBuf *buf)
 {
     VDIPortState *state = &reds->agent_state;
     int res;
@@ -849,88 +874,91 @@ static void dispatch_vdi_port_data(int port, VDIReadBuf *buf)
                                             buf->data, buf->len);
         switch (res) {
         case AGENT_MSG_FILTER_OK:
-            break;
+            return TRUE;
         case AGENT_MSG_FILTER_DISCARD:
-            ring_add(&state->read_bufs, &buf->link);
-            return;
+            return FALSE;
         case AGENT_MSG_FILTER_PROTO_ERROR:
-            ring_add(&state->read_bufs, &buf->link);
             reds_agent_remove();
-            return;
+            return FALSE;
         }
-        main_channel_push_agent_data(reds->main_channel, buf->data, buf->len,
-                                     vdi_read_buf_release, buf);
-        break;
     }
     case VDP_SERVER_PORT:
-        ring_add(&state->read_bufs, &buf->link);
-        break;
+        return FALSE;
     default:
-        ring_add(&state->read_bufs, &buf->link);
         spice_printerr("invalid port");
         reds_agent_remove();
+        return FALSE;
     }
 }
 
-/* Note this function MUST always be called in a while loop until it
-   returns 0. This is needed because it can cause new data available events
-   and its recursion protection causes those to get lost. Calling it until
-   it returns 0 ensures that all data has been consumed. */
-static int read_from_vdi_port(void)
+static VDIReadBuf *vdi_port_read_buf_get(void)
+{
+    VDIPortState *state = &reds->agent_state;
+    RingItem *item;
+    VDIReadBuf *buf;
+
+    if (!(item = ring_get_head(&state->read_bufs))) {
+        return NULL;
+    }
+
+    ring_remove(item);
+    buf = SPICE_CONTAINEROF(item, VDIReadBuf, link);
+
+    buf->refs = 1;
+    return buf;
+}
+
+static VDIReadBuf* vdi_port_read_buf_ref(VDIReadBuf *buf)
+{
+    buf->refs++;
+    return buf;
+}
+
+static void vdi_port_read_buf_unref(VDIReadBuf *buf)
+{
+    if (!--buf->refs) {
+        ring_add(&reds->agent_state.read_bufs, &buf->link);
+
+        /* read_one_msg_from_vdi_port may have never completed because the read_bufs
+        ring was empty. So we call it again so it can complete its work if
+        necessary. Note that since we can be called from spice_char_device_wakeup
+        this can cause recursion, but we have protection for that */
+        spice_char_device_wakeup(reds->agent_state.base);
+    }
+}
+
+/* reads from the device till completes reading a message that is addressed to the client,
+ * or otherwise, when reading from the device fails */
+static SpiceCharDeviceMsgToClient *vdi_port_read_one_msg_from_device(SpiceCharDeviceInstance *sin,
+                                                                     void *opaque)
 {
-    /* There are 2 scenarios where we can get called recursively:
-       1) spice-vmc vmc_read triggering flush of throttled data, recalling us
-       2) the buf we push to the client may be send immediately without
-          blocking, in which case its free function will recall us
-       This messes up the state machine, so ignore recursive calls.
-       This is why we always must be called in a loop. */
-    static int inside_call = 0;
-    int quit_loop = 0;
     VDIPortState *state = &reds->agent_state;
     SpiceCharDeviceInterface *sif;
     VDIReadBuf *dispatch_buf;
-    int total = 0;
     int n;
 
-    if (inside_call) {
-        return 0;
-    }
-    inside_call = 1;
-
     if (!vdagent) {
-        // discard data only if we are migrating (?) or vdagent has not been
-        // initialized.
-        inside_call = 0;
-        return 0;
+        return NULL;
     }
-
+    spice_assert(vdagent == sin);
     sif = SPICE_CONTAINEROF(vdagent->base.sif, SpiceCharDeviceInterface, base);
-    while (!quit_loop && vdagent) {
+    while (vdagent) {
         switch (state->read_state) {
         case VDI_PORT_READ_STATE_READ_HADER:
             n = sif->read(vdagent, state->recive_pos, state->recive_len);
             if (!n) {
-                quit_loop = 1;
-                break;
+                return NULL;
             }
-            total += n;
             if ((state->recive_len -= n)) {
                 state->recive_pos += n;
-                quit_loop = 1;
-                break;
+                return NULL;
             }
             state->message_recive_len = state->vdi_chunk_header.size;
             state->read_state = VDI_PORT_READ_STATE_GET_BUFF;
         case VDI_PORT_READ_STATE_GET_BUFF: {
-            RingItem *item;
-
-            if (!(item = ring_get_head(&state->read_bufs))) {
-                quit_loop = 1;
-                break;
+            if (!(state->current_read_buf = vdi_port_read_buf_get())) {
+                return NULL;
             }
-
-            ring_remove(item);
-            state->current_read_buf = (VDIReadBuf *)item;
             state->recive_pos = state->current_read_buf->data;
             state->recive_len = MIN(state->message_recive_len,
                                     sizeof(state->current_read_buf->data));
@@ -941,10 +969,8 @@ static int read_from_vdi_port(void)
         case VDI_PORT_READ_STATE_READ_DATA:
             n = sif->read(vdagent, state->recive_pos, state->recive_len);
             if (!n) {
-                quit_loop = 1;
-                break;
+                return NULL;
             }
-            total += n;
             if ((state->recive_len -= n)) {
                 state->recive_pos += n;
                 break;
@@ -959,18 +985,64 @@ static int read_from_vdi_port(void)
             } else {
                 state->read_state = VDI_PORT_READ_STATE_GET_BUFF;
             }
-            dispatch_vdi_port_data(state->vdi_chunk_header.port, dispatch_buf);
-        }
+            if (vdi_port_read_buf_process(state->vdi_chunk_header.port, dispatch_buf)) {
+                return dispatch_buf;
+            } else {
+                vdi_port_read_buf_unref(dispatch_buf);
+            }
+        } /* END switch */
+    } /* END while */
+    return NULL;
+}
+
+static SpiceCharDeviceMsgToClient *vdi_port_ref_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                                       void *opaque)
+{
+    return vdi_port_read_buf_ref(msg);
+}
+
+static void vdi_port_unref_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                  void *opaque)
+{
+    vdi_port_read_buf_unref(msg);
+}
+
+/* after calling this, we unref the message, and the ref is in the instance side */
+static void vdi_port_send_msg_to_client(SpiceCharDeviceMsgToClient *msg,
+                                        RedClient *client,
+                                        void *opaque)
+{
+    VDIReadBuf *agent_data_buf = msg;
+
+    main_channel_client_push_agent_data(red_client_get_main(client),
+                                        agent_data_buf->data,
+                                        agent_data_buf->len,
+                                        vdi_port_read_buf_release,
+                                        vdi_port_read_buf_ref(agent_data_buf));
+}
+
+static void vdi_port_send_tokens_to_client(RedClient *client, uint32_t tokens, void *opaque)
+{
+    main_channel_client_push_agent_tokens(red_client_get_main(client),
+                                          tokens);
+}
+
+static void vdi_port_on_free_self_token(void *opaque)
+{
+
+    if (inputs_inited() && reds->pending_mouse_event) {
+        spice_debug("pending mouse event");
+        reds_handle_agent_mouse_event(inputs_get_mouse_state());
     }
-    inside_call = 0;
-    return total;
 }
 
-void vdagent_char_device_wakeup(SpiceCharDeviceInstance *sin)
+static void vdi_port_remove_client(RedClient *client, void *opaque)
 {
-    while (read_from_vdi_port());
+    reds_client_disconnect(client);
 }
 
+/****************************************************************************/
+
 int reds_has_vdagent(void)
 {
     return !!vdagent;
@@ -1066,9 +1138,33 @@ void reds_fill_channels(SpiceMsgChannels *channels_info)
 
 void reds_on_main_agent_start(MainChannelClient *mcc, uint32_t num_tokens)
 {
+    SpiceCharDeviceState *dev_state = reds->agent_state.base;
+    RedClient *client;
+
     if (!vdagent) {
         return;
     }
+    spice_assert(vdagent->st && vdagent->st == dev_state);
+    client = main_channel_client_get_base(mcc)->client;
+    /*
+     * Note that in older releases, send_tokens were set to ~0 on both client
+     * and server. The server ignored the client given tokens.
+     * Thanks to that, when an old client is connected to a new server,
+     * and vice versa, the sending from the server to the client won't have
+     * flow control, but will have no other problem.
+     */
+    if (!spice_char_device_client_exists(dev_state, client)) {
+        spice_char_device_client_add(dev_state,
+                                     client,
+                                     TRUE, /* flow control */
+                                     REDS_VDI_PORT_NUM_RECEIVE_BUFFS,
+                                     REDS_AGENT_WINDOW_SIZE,
+                                     num_tokens);
+    } else {
+        spice_char_device_send_to_client_tokens_set(dev_state,
+                                                    client,
+                                                    num_tokens);
+    }
     reds->agent_state.write_filter.discard_all = FALSE;
 }
 
@@ -1077,7 +1173,10 @@ void reds_on_main_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
     if (!vdagent) {
         return;
     }
-    spice_printerr("to be implemented");
+    spice_assert(vdagent->st);
+    spice_char_device_send_to_client_tokens_add(vdagent->st,
+                                                main_channel_client_get_base(mcc)->client,
+                                                num_tokens);
 }
 
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
@@ -1445,7 +1544,6 @@ static void reds_handle_main_link(RedLinkInfo *link)
 
         main_channel_client_start_net_test(mcc);
         /* Now that we have a client, forward any pending agent data */
-        while (read_from_vdi_port());
     } else {
         reds_mig_target_client_add(client);
     }
@@ -1548,9 +1646,6 @@ void reds_on_client_migrate_complete(RedClient *client)
     }
 
     reds_mig_target_client_free(mig_client);
-
-    /* Now that we have a client, forward any pending agent data */
-    while (read_from_vdi_port());
 }
 
 static void reds_handle_other_links(RedLinkInfo *link)
@@ -3096,10 +3191,29 @@ static void mm_timer_proc(void *opaque)
     core->timer_start(reds->mm_timer, MM_TIMER_GRANULARITY_MS);
 }
 
-static void attach_to_red_agent(SpiceCharDeviceInstance *sin)
+static SpiceCharDeviceState *attach_to_red_agent(SpiceCharDeviceInstance *sin)
 {
     VDIPortState *state = &reds->agent_state;
     SpiceCharDeviceInterface *sif;
+    SpiceCharDeviceCallbacks char_dev_state_cbs;
+
+    if (!state->base) {
+        char_dev_state_cbs.read_one_msg_from_device = vdi_port_read_one_msg_from_device;
+        char_dev_state_cbs.ref_msg_to_client = vdi_port_ref_msg_to_client;
+        char_dev_state_cbs.unref_msg_to_client = vdi_port_unref_msg_to_client;
+        char_dev_state_cbs.send_msg_to_client = vdi_port_send_msg_to_client;
+        char_dev_state_cbs.send_tokens_to_client = vdi_port_send_tokens_to_client;
+        char_dev_state_cbs.remove_client = vdi_port_remove_client;
+        char_dev_state_cbs.on_free_self_token = vdi_port_on_free_self_token;
+
+        state->base = spice_char_device_state_create(sin,
+                                                     REDS_TOKENS_TO_SEND,
+                                                     REDS_NUM_INTERNAL_AGENT_MESSAGES,
+                                                     &char_dev_state_cbs,
+                                                     NULL);
+    } else {
+        spice_char_device_state_reset_dev_instance(state->base, sin);
+    }
 
     vdagent = sin;
     reds_update_mouse_mode();
@@ -3110,18 +3224,25 @@ static void attach_to_red_agent(SpiceCharDeviceInstance *sin)
     }
 
     if (!reds_main_channel_connected()) {
-        return;
+        return state->base;
     }
 
     state->read_filter.discard_all = FALSE;
     reds->agent_state.plug_generation++;
 
+    /* we will assoicate the client with the char device, upon reds_on_main_agent_start,
+     * in response to MSGC_AGENT_START */
     main_channel_push_agent_connected(reds->main_channel);
+    return state->base;
 }
 
 SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance* sin)
 {
-    (*sin->st->wakeup)(sin);
+    if (sin->st->wakeup) {
+        sin->st->wakeup(sin);
+    } else {
+        spice_char_device_wakeup(sin->st);
+    }
 }
 
 #define SUBTYPE_VDAGENT "vdagent"
@@ -3147,6 +3268,7 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
 {
     SpiceCharDeviceInstance* char_device =
             SPICE_CONTAINEROF(sin, SpiceCharDeviceInstance, base);
+    SpiceCharDeviceState *dev_state = NULL;
 
     spice_printerr("CHAR_DEVICE %s", char_device->subtype);
     if (strcmp(char_device->subtype, SUBTYPE_VDAGENT) == 0) {
@@ -3154,8 +3276,7 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
             spice_printerr("vdagent already attached");
             return -1;
         }
-        char_device->st = &vdagent_char_device_state;
-        attach_to_red_agent(char_device);
+        dev_state = attach_to_red_agent(char_device);
     }
 #ifdef USE_SMARTCARD
     else if (strcmp(char_device->subtype, SUBTYPE_SMARTCARD) == 0) {
@@ -3167,6 +3288,12 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
     else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
         spicevmc_device_connect(char_device, SPICE_CHANNEL_USBREDIR);
     }
+    if (dev_state) {
+        spice_assert(char_device->st);
+        /* setting the char_device state to "started" for backward compatibily with
+         * qemu releases that don't call spice api for start/stop (not implemented yet) */
+        spice_char_device_start(char_device->st);
+    }
     return 0;
 }
 
@@ -3189,6 +3316,7 @@ static void spice_server_char_device_remove_interface(SpiceBaseInstance *sin)
     else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
         spicevmc_device_disconnect(char_device);
     }
+    char_device->st = NULL;
 }
 
 SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *s,
commit 80145817ae8ceae382d01d8a3090f5ddc748166b
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 13:18:20 2012 +0300

    agent: remove save/restore migration data code
    
    This code is never called, it was relevant for seamless migration.
    Most of the data that needs to be migrated was moved to
    SpiceCharDeviceState. When we implement seamless migration,
    we will have routines in char_device.c for migrating the relevant data.

diff --git a/server/reds.c b/server/reds.c
index 9da8958..8b179f8 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -1157,260 +1157,12 @@ typedef struct WriteQueueInfo {
 
 void reds_marshall_migrate_data_item(SpiceMarshaller *m, MainMigrateData *data)
 {
-    VDIPortState *state = &reds->agent_state;
-    int buf_index;
-    RingItem *now;
-
-    data->version = MAIN_CHANNEL_MIG_DATA_VERSION;
-
-    data->agent_connected = !!vdagent;
-    data->client_agent_started = !state->write_filter.discard_all;
-    data->num_client_tokens = state->num_client_tokens;
-    data->send_tokens = ~0;
-
-    data->read_state = state->read_state;
-    data->vdi_chunk_header = state->vdi_chunk_header;
-    data->recive_len = state->recive_len;
-    data->message_recive_len = state->message_recive_len;
-
-    if (state->current_read_buf) {
-        data->read_buf_len = state->current_read_buf->len;
-
-        if (data->read_buf_len - data->recive_len) {
-            spice_marshaller_add_ref(m,
-                                     state->current_read_buf->data,
-                                     data->read_buf_len - data->recive_len);
-        }
-    } else {
-        data->read_buf_len = 0;
-    }
-
-    now = &state->write_queue;
-    data->write_queue_size = 0;
-    while ((now = ring_prev(&state->write_queue, now))) {
-        data->write_queue_size++;
-    }
-    if (data->write_queue_size) {
-        WriteQueueInfo *queue_info;
-
-        queue_info = (WriteQueueInfo *)
-            spice_marshaller_reserve_space(m,
-                                           data->write_queue_size * sizeof(queue_info[0]));
-
-        buf_index = 0;
-        now = &state->write_queue;
-        while ((now = ring_prev(&state->write_queue, now))) {
-            VDIPortBuf *buf = (VDIPortBuf *)now;
-            queue_info[buf_index].port = buf->chunk_header.port;
-            queue_info[buf_index++].len = buf->write_len;
-            spice_marshaller_add_ref(m, buf->now, buf->write_len);
-        }
-    }
-}
-
-
-static int reds_main_channel_restore_vdi_read_state(MainMigrateData *data, uint8_t **in_pos,
-                                               uint8_t *end)
-{
-    VDIPortState *state = &reds->agent_state;
-    uint8_t *pos = *in_pos;
-    RingItem *ring_item;
-
-    state->read_state = data->read_state;
-    state->vdi_chunk_header = data->vdi_chunk_header;
-    state->recive_len = data->recive_len;
-    state->message_recive_len = data->message_recive_len;
-
-    switch (state->read_state) {
-    case VDI_PORT_READ_STATE_READ_HADER:
-        if (data->read_buf_len) {
-            spice_printerr("unexpected receive buf");
-            reds_disconnect();
-            return FALSE;
-        }
-        state->recive_pos = (uint8_t *)(&state->vdi_chunk_header + 1) - state->recive_len;
-        break;
-    case VDI_PORT_READ_STATE_GET_BUFF:
-        if (state->message_recive_len > state->vdi_chunk_header.size) {
-            spice_printerr("invalid message receive len");
-            reds_disconnect();
-            return FALSE;
-        }
-
-        if (data->read_buf_len) {
-            spice_printerr("unexpected receive buf");
-            reds_disconnect();
-            return FALSE;
-        }
-        break;
-    case VDI_PORT_READ_STATE_READ_DATA: {
-        VDIReadBuf *buff;
-        uint32_t n;
-
-        if (!data->read_buf_len) {
-            spice_printerr("read state and read_buf_len == 0");
-            reds_disconnect();
-            return FALSE;
-        }
-
-        if (state->message_recive_len > state->vdi_chunk_header.size) {
-            spice_printerr("invalid message receive len");
-            reds_disconnect();
-            return FALSE;
-        }
-
-
-        if (!(ring_item = ring_get_head(&state->read_bufs))) {
-            spice_printerr("get read buf failed");
-            reds_disconnect();
-            return FALSE;
-        }
-
-        ring_remove(ring_item);
-        buff = state->current_read_buf = (VDIReadBuf *)ring_item;
-        buff->len = data->read_buf_len;
-        n = buff->len - state->recive_len;
-        if (buff->len > SPICE_AGENT_MAX_DATA_SIZE || n > SPICE_AGENT_MAX_DATA_SIZE) {
-            spice_printerr("bad read position");
-            reds_disconnect();
-            return FALSE;
-        }
-        memcpy(buff->data, pos, n);
-        pos += n;
-        state->recive_pos = buff->data + n;
-        break;
-    }
-    default:
-        spice_printerr("invalid read state");
-        reds_disconnect();
-        return FALSE;
-    }
-    *in_pos = pos;
-    return TRUE;
-}
-
-static void free_tmp_internal_buf(VDIPortBuf *buf)
-{
-    free(buf);
-}
-
-static int reds_main_channel_restore_vdi_wqueue(MainMigrateData *data, uint8_t *pos, uint8_t *end)
-{
-    VDIPortState *state = &reds->agent_state;
-    WriteQueueInfo *inf;
-    WriteQueueInfo *inf_end;
-    RingItem *ring_item;
-
-    if (!data->write_queue_size) {
-        return TRUE;
-    }
-
-    inf = (WriteQueueInfo *)pos;
-    inf_end = inf + data->write_queue_size;
-    pos = (uint8_t *)inf_end;
-    if (pos > end) {
-        spice_printerr("access violation");
-        reds_disconnect();
-        return FALSE;
-    }
-
-    for (; inf < inf_end; inf++) {
-        if (pos + inf->len > end) {
-            spice_printerr("access violation");
-            reds_disconnect();
-            return FALSE;
-        }
-        if (inf->port == VDP_SERVER_PORT) {
-            VDInternalBuf *buf;
-
-            if (inf->len > sizeof(*buf) - SPICE_OFFSETOF(VDInternalBuf, header)) {
-                spice_printerr("bad buffer len");
-                reds_disconnect();
-                return FALSE;
-            }
-            buf = spice_new(VDInternalBuf, 1);
-            ring_item_init(&buf->base.link);
-            buf->base.free = free_tmp_internal_buf;
-            buf->base.now = (uint8_t *)&buf->base.chunk_header;
-            buf->base.write_len = inf->len;
-            memcpy(buf->base.now, pos, buf->base.write_len);
-            ring_add(&reds->agent_state.write_queue, &buf->base.link);
-        } else if (inf->port == VDP_CLIENT_PORT) {
-            VDAgentExtBuf *buf;
-
-            state->num_tokens--;
-            if (inf->len > sizeof(*buf) - SPICE_OFFSETOF(VDAgentExtBuf, buf)) {
-                spice_printerr("bad buffer len");
-                reds_disconnect();
-                return FALSE;
-            }
-            if (!(ring_item = ring_get_head(&reds->agent_state.external_bufs))) {
-                spice_printerr("no external buff");
-                reds_disconnect();
-                return FALSE;
-            }
-            ring_remove(ring_item);
-            buf = (VDAgentExtBuf *)ring_item;
-            memcpy(&buf->buf, pos, inf->len);
-            buf->base.now = (uint8_t *)buf->buf;
-            buf->base.write_len = inf->len;
-            ring_add(&reds->agent_state.write_queue, &buf->base.link);
-        } else {
-            spice_printerr("invalid data");
-            reds_disconnect();
-            return FALSE;
-        }
-        pos += inf->len;
-    }
-    return TRUE;
+    spice_error("not implemented");
 }
 
 void reds_on_main_receive_migrate_data(MainMigrateData *data, uint8_t *end)
 {
-    VDIPortState *state = &reds->agent_state;
-    uint8_t *pos;
-
-    if (data->version != MAIN_CHANNEL_MIG_DATA_VERSION) {
-        spice_printerr("version mismatch");
-        reds_disconnect();
-        return;
-    }
-
-    state->num_client_tokens = data->num_client_tokens;
-    spice_assert(state->num_client_tokens + data->write_queue_size <= REDS_AGENT_WINDOW_SIZE +
-                                                                REDS_NUM_INTERNAL_AGENT_MESSAGES);
-    state->num_tokens = REDS_AGENT_WINDOW_SIZE - state->num_client_tokens;
-
-    if (!data->agent_connected) {
-        if (vdagent) {
-            main_channel_push_agent_connected(reds->main_channel);
-        }
-        return;
-    }
-
-    if (!vdagent) {
-        main_channel_push_agent_disconnected(reds->main_channel);
-        return;
-    }
-
-    if (state->plug_generation > 1) {
-        main_channel_push_agent_disconnected(reds->main_channel);
-        main_channel_push_agent_connected(reds->main_channel);
-        return;
-    }
-
-    state->write_filter.discard_all = !data->client_agent_started;
-
-    pos = (uint8_t *)(data + 1);
-
-    if (!reds_main_channel_restore_vdi_read_state(data, &pos, end)) {
-        return;
-    }
-
-    reds_main_channel_restore_vdi_wqueue(data, pos, end);
-    spice_assert(state->num_client_tokens + state->num_tokens == REDS_AGENT_WINDOW_SIZE);
-
-    while (write_to_vdi_port() || read_from_vdi_port());
+    spice_error("not implemented");
 }
 
 static int sync_write(RedsStream *stream, const void *in_buf, size_t n)
commit fffa38672ce53c07a3341798e2bd66d806e8cf0d
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 12:07:09 2012 +0300

    agent: Fix tokens handling in main_channel
    
    - Allow sending tokens to a specific client.
    - Do not ignore tokens that are sent from the client to the server.
    
    The tokens support for multiple clients and for server side tokens
    is still broken in reds. It will be fixed in following patches, when
    the server-side agent code will use the SpiceCharDeviceState api.
    
    Notice that ignoring the server-side tokens didn't introduce a problem
    since both the client and the server set it to ~0.

diff --git a/server/main_channel.c b/server/main_channel.c
index ace24ff..ce467f8 100644
--- a/server/main_channel.c
+++ b/server/main_channel.c
@@ -222,18 +222,13 @@ static PipeItem *main_ping_item_new(MainChannelClient *mcc, int size)
     return &item->base;
 }
 
-typedef struct MainTokensItemInfo {
-    uint32_t num_tokens;
-} MainTokensItemInfo;
-
-static PipeItem *main_tokens_item_new(RedChannelClient *rcc, void *data, int num)
+static PipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
 {
     TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
-    MainTokensItemInfo *init = data;
 
     red_channel_pipe_item_init(rcc->channel, &item->base,
                                SPICE_MSG_MAIN_AGENT_TOKEN);
-    item->tokens = init->num_tokens;
+    item->tokens = num_tokens;
     return &item->base;
 }
 
@@ -422,13 +417,11 @@ static void main_channel_marshall_agent_disconnected(SpiceMarshaller *m)
     spice_marshall_msg_main_agent_disconnected(m, &disconnect);
 }
 
-// TODO: make this targeted (requires change to agent token accounting)
-void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens)
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
 {
-    MainTokensItemInfo init = {.num_tokens = num_tokens};
+    PipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
 
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_tokens_item_new, &init);
+    red_channel_client_pipe_add_push(&mcc->base, item);
 }
 
 static void main_channel_marshall_tokens(SpiceMarshaller *m, uint32_t num_tokens)
@@ -828,19 +821,28 @@ static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint
     MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
 
     switch (type) {
-    case SPICE_MSGC_MAIN_AGENT_START:
+    case SPICE_MSGC_MAIN_AGENT_START: {
+        SpiceMsgcMainAgentStart *tokens;
+
         spice_printerr("agent start");
         if (!main_chan) {
             return FALSE;
         }
-        reds_on_main_agent_start();
+        tokens = (SpiceMsgcMainAgentStart *)message;
+        reds_on_main_agent_start(mcc, tokens->num_tokens);
         break;
+    }
     case SPICE_MSGC_MAIN_AGENT_DATA: {
         reds_on_main_agent_data(mcc, message, size);
         break;
     }
-    case SPICE_MSGC_MAIN_AGENT_TOKEN:
+    case SPICE_MSGC_MAIN_AGENT_TOKEN: {
+        SpiceMsgcMainAgentTokens *tokens;
+
+        tokens = (SpiceMsgcMainAgentTokens *)message;
+        reds_on_main_agent_tokens(mcc, tokens->num_tokens);
         break;
+    }
     case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
         main_channel_push_channels(mcc);
         break;
diff --git a/server/main_channel.h b/server/main_channel.h
index afff313..7f620bb 100644
--- a/server/main_channel.h
+++ b/server/main_channel.h
@@ -72,7 +72,7 @@ void main_channel_close(MainChannel *main_chan); // not destroy, just socket clo
 void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
 void main_channel_push_agent_connected(MainChannel *main_chan);
 void main_channel_push_agent_disconnected(MainChannel *main_chan);
-void main_channel_push_tokens(MainChannel *main_chan, uint32_t num_tokens);
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
 void main_channel_push_agent_data(MainChannel *main_chan, uint8_t* data, size_t len,
            spice_marshaller_item_free_func free_data, void *opaque);
 void main_channel_client_start_net_test(MainChannelClient *mcc);
diff --git a/server/reds.c b/server/reds.c
index 26d6f89..9da8958 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -756,11 +756,13 @@ static void reds_agent_remove(void)
     }
 }
 
-static void reds_push_tokens(void)
+/* this will be fixed to handle multiple clients
+   in following patches */
+static void reds_push_tokens(MainChannelClient *mcc)
 {
     reds->agent_state.num_client_tokens += reds->agent_state.num_tokens;
     spice_assert(reds->agent_state.num_client_tokens <= REDS_AGENT_WINDOW_SIZE);
-    main_channel_push_tokens(reds->main_channel, reds->agent_state.num_tokens);
+    main_channel_client_push_agent_tokens(mcc, reds->agent_state.num_tokens);
     reds->agent_state.num_tokens = 0;
 }
 
@@ -998,12 +1000,14 @@ void reds_handle_agent_mouse_event(const VDAgentMouseState *mouse_state)
     write_to_vdi_port();
 }
 
-static void add_token(void)
+static void add_token(MainChannelClient *mcc)
 {
     VDIPortState *state = &reds->agent_state;
 
+    /* this will be fixed to handle multiple clients
+       in following patches */
     if (++state->num_tokens == REDS_TOKENS_TO_SEND) {
-        reds_push_tokens();
+        reds_push_tokens(mcc);
     }
 }
 
@@ -1012,6 +1016,7 @@ int reds_num_of_channels(void)
     return reds ? reds->num_of_channels : 0;
 }
 
+
 int reds_num_of_clients(void)
 {
     return reds ? reds->num_clients : 0;
@@ -1059,7 +1064,7 @@ void reds_fill_channels(SpiceMsgChannels *channels_info)
     }
 }
 
-void reds_on_main_agent_start(void)
+void reds_on_main_agent_start(MainChannelClient *mcc, uint32_t num_tokens)
 {
     if (!vdagent) {
         return;
@@ -1067,6 +1072,14 @@ void reds_on_main_agent_start(void)
     reds->agent_state.write_filter.discard_all = FALSE;
 }
 
+void reds_on_main_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
+{
+    if (!vdagent) {
+        return;
+    }
+    spice_printerr("to be implemented");
+}
+
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
 {
     // TODO - use mcc (and start tracking agent data per channel. probably just move the whole
@@ -1088,7 +1101,7 @@ void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size)
     case AGENT_MSG_FILTER_OK:
         break;
     case AGENT_MSG_FILTER_DISCARD:
-        add_token();
+        add_token(mcc);
         return;
     case AGENT_MSG_FILTER_PROTO_ERROR:
         reds_disconnect();
@@ -3577,9 +3590,15 @@ SPICE_GNUC_VISIBLE int spice_server_remove_interface(SpiceBaseInstance *sin)
 static void free_external_agent_buff(VDIPortBuf *in_buf)
 {
     VDIPortState *state = &reds->agent_state;
+    RedClient *random_client;
 
     ring_add(&state->external_bufs, &in_buf->link);
-    add_token();
+    /* this will be fixed to handle multiple clients
+       in following patches */
+    random_client = SPICE_CONTAINEROF(ring_get_tail(&reds->clients),
+                                      RedClient,
+                                      link);
+    add_token(red_client_get_main(random_client));
 }
 
 static void free_internal_agent_buff(VDIPortBuf *in_buf)
diff --git a/server/reds.h b/server/reds.h
index 1c59e68..674052a 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -145,7 +145,8 @@ void reds_update_stat_value(uint32_t value);
 
 /* callbacks from main channel messages */
 
-void reds_on_main_agent_start(void);
+void reds_on_main_agent_start(MainChannelClient *mcc, uint32_t num_tokens);
+void reds_on_main_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
 void reds_on_main_agent_data(MainChannelClient *mcc, void *message, size_t size);
 void reds_on_main_migrate_connected(void); //should be called when all the clients
                                            // are connected to the target
commit 50e3af6a995eb8aeee8bd8fb82a5cf6aa93f21cf
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 08:33:21 2012 +0300

    char_device: Introducing shared flow control code for char devices.
    
    SpiceCharDeviceState manages the (1) write-to-device queue
    (2) wakeup and reading from the device (3) client tokens (4)
    sending messages from the device to the client/s, considering the
    available tokens.
    SpiceCharDeviceState can be also stopped and started. When the device
    is stopped, no reading or writing is done from/to the device. Messages
    addressed from the client to the device are being queued.
    Later, an api for stop/start will be added to spice.h and it should
    be called from qemu.
    
    This patch does not yet remove the wakeup callback from
    SpiceCharDeviceState, but once all the char devices (agent/spicevmc/smartcard)
    code will switch to the new implementation, SpiceCharDeviceState
    will be moved to the c file and its reference to the wakeup callback will be removed.

diff --git a/server/Makefile.am b/server/Makefile.am
index 47b3c10..e7b4977 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -43,6 +43,7 @@ libspice_server_la_LIBADD =						\
 libspice_server_la_SOURCES =			\
 	agent-msg-filter.c			\
 	agent-msg-filter.h			\
+	char_device.c				\
 	char_device.h				\
 	demarshallers.h				\
 	glz_encoder.c				\
diff --git a/server/char_device.c b/server/char_device.c
new file mode 100644
index 0000000..f493956
--- /dev/null
+++ b/server/char_device.c
@@ -0,0 +1,752 @@
+/* spice-server char device flow control code
+
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Yonit Halperin <yhalperi at redhat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http:www.gnu.org/licenses/>.
+*/
+
+
+#include "char_device.h"
+#include "red_channel.h"
+#include "reds.h"
+
+#define CHAR_DEVICE_WRITE_TO_TIMEOUT 100
+#define SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT 30000
+
+typedef struct SpiceCharDeviceClientState SpiceCharDeviceClientState;
+struct SpiceCharDeviceClientState {
+    RingItem link;
+    SpiceCharDeviceState *dev;
+    RedClient *client;
+    int do_flow_control;
+    uint64_t num_client_tokens;
+    uint64_t num_client_tokens_free; /* client messages that were consumed by the device */
+    uint64_t num_send_tokens; /* send to client */
+    SpiceTimer *wait_for_tokens_timer;
+    int wait_for_tokens_started;
+    Ring send_queue;
+    uint32_t send_queue_size;
+    uint32_t max_send_queue_size;
+};
+
+enum {
+    WRITE_BUFFER_ORIGIN_NONE,
+    WRITE_BUFFER_ORIGIN_CLIENT,
+    WRITE_BUFFER_ORIGIN_SERVER,
+};
+
+/* Holding references for avoiding access violation if the char device was
+ * destroyed during a callback */
+static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev);
+static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev);
+
+static void spice_char_dev_write_retry(void *opaque);
+
+typedef struct SpiceCharDeviceMsgToClientItem {
+    RingItem link;
+    SpiceCharDeviceMsgToClient *msg;
+} SpiceCharDeviceMsgToClientItem;
+
+static void write_buffers_queue_free(Ring *write_queue)
+{
+    while (!ring_is_empty(write_queue)) {
+        RingItem *item = ring_get_tail(write_queue);
+        SpiceCharDeviceWriteBuffer *buf;
+
+        ring_remove(item);
+        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        free(buf->buf);
+        free(buf);
+    }
+}
+
+static void spice_char_device_write_buffer_pool_add(SpiceCharDeviceState *dev,
+                                                    SpiceCharDeviceWriteBuffer *buf)
+{
+    buf->buf_used = 0;
+    buf->origin = WRITE_BUFFER_ORIGIN_NONE;
+    buf->client = NULL;
+    ring_add(&dev->write_bufs_pool, &buf->link);
+}
+
+static void spice_char_device_client_send_queue_free(SpiceCharDeviceState *dev,
+                                                     SpiceCharDeviceClientState *dev_client)
+{
+    spice_debug("send_queue_empty %d", ring_is_empty(&dev_client->send_queue));
+    while (!ring_is_empty(&dev_client->send_queue)) {
+        RingItem *item = ring_get_tail(&dev_client->send_queue);
+        SpiceCharDeviceMsgToClientItem *msg_item = SPICE_CONTAINEROF(item,
+                                                                     SpiceCharDeviceMsgToClientItem,
+                                                                     link);
+
+        ring_remove(item);
+        dev->cbs.unref_msg_to_client(msg_item->msg, dev->opaque);
+        free(msg_item);
+    }
+    dev_client->num_send_tokens += dev_client->send_queue_size;
+    dev_client->send_queue_size = 0;
+}
+
+static void spice_char_device_client_free(SpiceCharDeviceState *dev,
+                                          SpiceCharDeviceClientState *dev_client)
+{
+    RingItem *item, *next;
+
+    if (dev_client->wait_for_tokens_timer) {
+        core->timer_remove(dev_client->wait_for_tokens_timer);
+    }
+
+    spice_char_device_client_send_queue_free(dev, dev_client);
+
+    /* remove write buffers that are associated with the client */
+    spice_debug("write_queue_is_empty %d", ring_is_empty(&dev->write_queue) && !dev->cur_write_buf);
+    RING_FOREACH_SAFE(item, next, &dev->write_queue) {
+        SpiceCharDeviceWriteBuffer *write_buf;
+
+        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
+            write_buf->client == dev_client->client) {
+            ring_remove(item);
+            spice_char_device_write_buffer_pool_add(dev, write_buf);
+        }
+    }
+
+    if (dev->cur_write_buf && dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
+        dev->cur_write_buf->client == dev_client->client) {
+        dev->cur_write_buf->origin = WRITE_BUFFER_ORIGIN_NONE;
+        dev->cur_write_buf->client = NULL;
+    }
+
+    dev->num_clients--;
+    ring_remove(&dev_client->link);
+    free(dev_client);
+}
+
+static void spice_char_device_handle_client_overflow(SpiceCharDeviceClientState *dev_client)
+{
+    SpiceCharDeviceState *dev = dev_client->dev;
+    spice_printerr("dev %p client %p ", dev, dev_client);
+    dev->cbs.remove_client(dev_client->client, dev->opaque);
+}
+
+static SpiceCharDeviceClientState *spice_char_device_client_find(SpiceCharDeviceState *dev,
+                                                                 RedClient *client)
+{
+    RingItem *item;
+
+    RING_FOREACH(item, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+        if (dev_client->client == client) {
+            return dev_client;
+        }
+    }
+    return NULL;
+}
+
+/***************************
+ * Reading from the device *
+ **************************/
+
+static void device_client_wait_for_tokens_timeout(void *opaque)
+{
+    SpiceCharDeviceClientState *dev_client = opaque;
+
+    spice_char_device_handle_client_overflow(dev_client);
+}
+
+static int spice_char_device_can_send_to_client(SpiceCharDeviceClientState *dev_client)
+{
+    return !dev_client->do_flow_control || dev_client->num_send_tokens;
+}
+
+static uint64_t spice_char_device_max_send_tokens(SpiceCharDeviceState *dev)
+{
+    RingItem *item;
+    uint64_t max = 0;
+
+    RING_FOREACH(item, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+
+        if (!dev_client->do_flow_control) {
+            max = ~0;
+            break;
+        }
+
+        if (dev_client->num_send_tokens > max) {
+            max = dev_client->num_send_tokens;
+        }
+    }
+    return max;
+}
+
+static void spice_char_device_add_msg_to_client_queue(SpiceCharDeviceClientState *dev_client,
+                                                      SpiceCharDeviceMsgToClient *msg)
+{
+    SpiceCharDeviceState *dev = dev_client->dev;
+    SpiceCharDeviceMsgToClientItem *msg_item;
+
+    if (dev_client->send_queue_size >= dev_client->max_send_queue_size) {
+        spice_char_device_handle_client_overflow(dev_client);
+        return;
+    }
+
+    msg_item = spice_new0(SpiceCharDeviceMsgToClientItem, 1);
+    msg_item->msg = dev->cbs.ref_msg_to_client(msg, dev->opaque);
+    ring_add(&dev_client->send_queue, &msg_item->link);
+    dev_client->send_queue_size++;
+    if (!dev_client->wait_for_tokens_started) {
+        core->timer_start(dev_client->wait_for_tokens_timer,
+                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
+        dev_client->wait_for_tokens_started = TRUE;
+    }
+}
+
+static void spice_char_device_send_msg_to_clients(SpiceCharDeviceState *dev,
+                                                  SpiceCharDeviceMsgToClient *msg)
+{
+    RingItem *item, *next;
+
+    RING_FOREACH_SAFE(item, next, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+        if (spice_char_device_can_send_to_client(dev_client)) {
+            dev_client->num_send_tokens--;
+            spice_assert(ring_is_empty(&dev_client->send_queue));
+            dev->cbs.send_msg_to_client(msg, dev_client->client, dev->opaque);
+
+            /* don't refer to dev_client anymore, it may have been released */
+        } else {
+            spice_char_device_add_msg_to_client_queue(dev_client, msg);
+        }
+    }
+}
+
+static int spice_char_device_read_from_device(SpiceCharDeviceState *dev)
+{
+    uint64_t max_send_tokens;
+    int did_read = FALSE;
+
+    if (!dev->running) {
+        return FALSE;
+    }
+
+    /* There are 2 scenarios where we can get called recursively:
+     * 1) spice-vmc vmc_read triggering flush of throttled data, recalling wakeup
+     * (virtio)
+     * 2) in case of sending messages to the client, and unreferencing the
+     * msg, we trigger another read.
+     */
+    if (dev->during_read_from_device++ > 0) {
+        return FALSE;
+    }
+
+    max_send_tokens = spice_char_device_max_send_tokens(dev);
+    spice_char_device_state_ref(dev);
+    /*
+     * Reading from the device only in case at least one of the clients have a free token.
+     * All messages will be discarded if no client is attached to the device
+     */
+    while ((max_send_tokens || ring_is_empty(&dev->clients)) && dev->running) {
+        SpiceCharDeviceMsgToClient *msg;
+
+        msg = dev->cbs.read_one_msg_from_device(dev->sin, dev->opaque);
+        if (!msg) {
+            if (dev->during_read_from_device > 1) {
+                dev->during_read_from_device = 1;
+                continue; /* a wakeup might have been called during the read -
+                             make sure it doesn't get lost */
+            }
+            break;
+        }
+        did_read = TRUE;
+        spice_char_device_send_msg_to_clients(dev, msg);
+        dev->cbs.unref_msg_to_client(msg, dev->opaque);
+        max_send_tokens--;
+    }
+    dev->during_read_from_device = 0;
+    spice_char_device_state_unref(dev);
+    return did_read;
+}
+
+static void spice_char_device_client_send_queue_push(SpiceCharDeviceClientState *dev_client)
+{
+    RingItem *item;
+    while ((item = ring_get_tail(&dev_client->send_queue)) &&
+           spice_char_device_can_send_to_client(dev_client)) {
+        SpiceCharDeviceMsgToClientItem *msg_item;
+
+        msg_item = SPICE_CONTAINEROF(item, SpiceCharDeviceMsgToClientItem, link);
+        ring_remove(item);
+
+        dev_client->num_send_tokens--;
+        dev_client->dev->cbs.send_msg_to_client(msg_item->msg,
+                                           dev_client->client,
+                                           dev_client->dev->opaque);
+        dev_client->dev->cbs.unref_msg_to_client(msg_item->msg, dev_client->dev->opaque);
+        dev_client->send_queue_size--;
+        free(msg_item);
+    }
+}
+
+static void spice_char_device_send_to_client_tokens_absorb(SpiceCharDeviceClientState *dev_client,
+                                                           uint32_t tokens)
+{
+    dev_client->num_send_tokens += tokens;
+
+    if (dev_client->send_queue_size) {
+        spice_assert(dev_client->num_send_tokens == tokens);
+        spice_char_device_client_send_queue_push(dev_client);
+    }
+
+    if (spice_char_device_can_send_to_client(dev_client)) {
+        core->timer_cancel(dev_client->wait_for_tokens_timer);
+        dev_client->wait_for_tokens_started = FALSE;
+        spice_char_device_read_from_device(dev_client->dev);
+    } else if (dev_client->send_queue_size) {
+        core->timer_start(dev_client->wait_for_tokens_timer,
+                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
+        dev_client->wait_for_tokens_started = TRUE;
+    }
+}
+
+void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    dev_client = spice_char_device_client_find(dev, client);
+
+    if (!dev_client) {
+        spice_error("client wasn't found dev %p client %p", dev, client);
+        return;
+    }
+    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
+}
+
+void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    dev_client = spice_char_device_client_find(dev, client);
+
+    if (!dev_client) {
+        spice_error("client wasn't found dev %p client %p", dev, client);
+        return;
+    }
+
+    dev_client->num_send_tokens = 0;
+    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
+}
+
+/**************************
+ * Writing to the device  *
+***************************/
+
+static void spice_char_device_client_token_add(SpiceCharDeviceState *dev,
+                                               SpiceCharDeviceClientState *dev_client)
+{
+    if (!dev_client->do_flow_control) {
+        return;
+    }
+    if (++dev_client->num_client_tokens_free == dev->client_tokens_interval) {
+        dev_client->num_client_tokens += dev->client_tokens_interval;
+        dev_client->num_client_tokens_free = 0;
+        dev->cbs.send_tokens_to_client(dev_client->client,
+                                       dev->client_tokens_interval,
+                                       dev->opaque);
+    }
+}
+
+static int spice_char_device_write_to_device(SpiceCharDeviceState *dev)
+{
+    SpiceCharDeviceInterface *sif;
+    int total = 0;
+    int n;
+
+    if (!dev->running) {
+        return 0;
+    }
+
+    spice_char_device_state_ref(dev);
+    core->timer_cancel(dev->write_to_dev_timer);
+
+    sif = SPICE_CONTAINEROF(dev->sin->base.sif, SpiceCharDeviceInterface, base);
+    while (1) {
+        uint32_t write_len;
+
+        if (!dev->cur_write_buf) {
+            RingItem *item = ring_get_tail(&dev->write_queue);
+            if (!item) {
+                break;
+            }
+            dev->cur_write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+            dev->cur_write_buf_pos = dev->cur_write_buf->buf;
+            ring_remove(item);
+        }
+
+        write_len = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
+                    dev->cur_write_buf_pos;
+        n = sif->write(dev->sin, dev->cur_write_buf_pos, write_len);
+        if (!dev->running) {
+            break;
+        }
+        if (n <= 0) {
+            break;
+        }
+        total += n;
+        write_len -= n;
+        if (!write_len) {
+            SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
+            dev->cur_write_buf = NULL;
+            spice_char_device_write_buffer_release(dev, release_buf);
+            continue;
+        }
+        dev->cur_write_buf_pos += n;
+    }
+    /* retry writing as long as the write queue is not empty */
+    if (dev->cur_write_buf) {
+        core->timer_start(dev->write_to_dev_timer, CHAR_DEVICE_WRITE_TO_TIMEOUT);
+    } else {
+        spice_assert(ring_is_empty(&dev->write_queue));
+    }
+    spice_char_device_state_unref(dev);
+    return total;
+}
+
+static void spice_char_dev_write_retry(void *opaque)
+{
+    SpiceCharDeviceState *dev = opaque;
+
+    core->timer_cancel(dev->write_to_dev_timer);
+    spice_char_device_write_to_device(dev);
+}
+
+SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
+                                                               RedClient *client, int size)
+{
+    RingItem *item;
+    SpiceCharDeviceWriteBuffer *ret;
+
+    if (!client && !dev->num_self_tokens) {
+        spice_printerr("internal buf is not available");
+        return NULL;
+    }
+
+    if ((item = ring_get_tail(&dev->write_bufs_pool))) {
+        ret = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        ring_remove(item);
+    } else {
+        ret = spice_new0(SpiceCharDeviceWriteBuffer, 1);
+    }
+
+    spice_assert(!ret->buf_used);
+
+    if (ret->buf_size < size) {
+        ret->buf = spice_realloc(ret->buf, size);
+        ret->buf_size = size;
+    }
+
+    if (client) {
+       SpiceCharDeviceClientState *dev_client = spice_char_device_client_find(dev, client);
+       if (dev_client) {
+            if (dev_client->do_flow_control && !dev_client->num_client_tokens) {
+                spice_printerr("token violation: dev %p client %p", dev, client);
+                spice_char_device_handle_client_overflow(dev_client);
+                goto error;
+            }
+            ret->origin = WRITE_BUFFER_ORIGIN_CLIENT;
+            ret->client = client;
+            if (dev_client->do_flow_control) {
+                dev_client->num_client_tokens--;
+            }
+        } else {
+            /* it is possible that the client was removed due to send tokens underflow, but
+             * the caller still receive messages from the client */
+            spice_printerr("client not found: dev %p client %p", dev, client);
+            goto error;
+        }
+    } else {
+        ret->origin = WRITE_BUFFER_ORIGIN_SERVER;
+        dev->num_self_tokens--;
+    }
+
+    return ret;
+error:
+    ring_add(&dev->write_bufs_pool, &ret->link);
+    return NULL;
+}
+
+void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
+                                        SpiceCharDeviceWriteBuffer *write_buf)
+{
+    spice_assert(dev);
+    /* caller shouldn't add buffers for client that was removed */
+    if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
+        !spice_char_device_client_find(dev, write_buf->client)) {
+        spice_printerr("client not found: dev %p client %p", dev, write_buf->client);
+        spice_char_device_write_buffer_pool_add(dev, write_buf);
+        return;
+    }
+
+    ring_add(&dev->write_queue, &write_buf->link);
+    spice_char_device_write_to_device(dev);
+}
+
+void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
+                                            SpiceCharDeviceWriteBuffer *write_buf)
+{
+    int buf_origin = write_buf->origin;
+    RedClient *client = write_buf->client;
+
+    spice_assert(!ring_item_is_linked(&write_buf->link));
+    if (!dev) {
+        spice_printerr("no device. write buffer is freed");
+        free(write_buf->buf);
+        free(write_buf);
+        return;
+    }
+
+    spice_assert(dev->cur_write_buf != write_buf);
+
+    spice_char_device_write_buffer_pool_add(dev, write_buf);
+    if (buf_origin == WRITE_BUFFER_ORIGIN_CLIENT) {
+        SpiceCharDeviceClientState *dev_client;
+
+        spice_assert(client);
+        dev_client = spice_char_device_client_find(dev, client);
+        /* when a client is removed, we remove all the buffers that are associated with it */
+        spice_assert(dev_client);
+        spice_char_device_client_token_add(dev, dev_client);
+    } else if (buf_origin == WRITE_BUFFER_ORIGIN_SERVER) {
+        dev->num_self_tokens++;
+        if (dev->cbs.on_free_self_token) {
+            dev->cbs.on_free_self_token(dev->opaque);
+        }
+    }
+
+}
+
+
+/********************************
+ * char_device_state management *
+ ********************************/
+
+SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
+                                                     uint32_t client_tokens_interval,
+                                                     uint32_t self_tokens,
+                                                     SpiceCharDeviceCallbacks *cbs,
+                                                     void *opaque)
+{
+    SpiceCharDeviceState *char_dev;
+
+    spice_assert(sin);
+    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
+                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
+                 cbs->send_tokens_to_client && cbs->remove_client);
+
+    char_dev = spice_new0(SpiceCharDeviceState, 1);
+    char_dev->sin = sin;
+    char_dev->cbs = *cbs;
+    char_dev->opaque = opaque;
+    char_dev->client_tokens_interval = client_tokens_interval;
+    char_dev->num_self_tokens = self_tokens;
+
+    ring_init(&char_dev->write_queue);
+    ring_init(&char_dev->write_bufs_pool);
+    ring_init(&char_dev->clients);
+
+    char_dev->write_to_dev_timer = core->timer_add(spice_char_dev_write_retry, char_dev);
+    if (!char_dev->write_to_dev_timer) {
+        spice_error("failed creating char dev write timer");
+    }
+    char_dev->refs = 1;
+    sin->st = char_dev;
+    spice_debug("sin %p dev_state %p", sin, char_dev);
+    return char_dev;
+}
+
+void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *state,
+                                                SpiceCharDeviceInstance *sin)
+{
+    spice_debug("sin %p dev_state %p", sin, state);
+    state->sin = sin;
+    sin->st = state;
+}
+
+void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev)
+{
+    return dev->opaque;
+}
+
+static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev)
+{
+    char_dev->refs++;
+}
+
+static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev)
+{
+    /* The refs field protects the char_dev from being deallocated in
+     * case spice_char_device_state_destroy has been called
+     * during a callabck, and we might still access the char_dev afterwards.
+     * spice_char_device_state_unref is always coupled with a preceding
+     * spice_char_device_state_ref. Here, refs can turn 0
+     * only when spice_char_device_state_destroy is called in between
+     * the calls to spice_char_device_state_ref and spice_char_device_state_unref.*/
+    if (!--char_dev->refs) {
+        free(char_dev);
+    }
+}
+
+void spice_char_device_state_destroy(SpiceCharDeviceState *char_dev)
+{
+    core->timer_remove(char_dev->write_to_dev_timer);
+    write_buffers_queue_free(&char_dev->write_queue);
+    write_buffers_queue_free(&char_dev->write_bufs_pool);
+
+    while (!ring_is_empty(&char_dev->clients)) {
+        RingItem *item = ring_get_tail(&char_dev->clients);
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+        spice_char_device_client_free(char_dev, dev_client);
+    }
+    char_dev->running = FALSE;
+ 
+    spice_char_device_state_unref(char_dev);
+}
+
+void spice_char_device_client_add(SpiceCharDeviceState *dev,
+                                  RedClient *client,
+                                  int do_flow_control,
+                                  uint32_t max_send_queue_size,
+                                  uint32_t num_client_tokens,
+                                  uint32_t num_send_tokens)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    spice_assert(dev);
+    spice_assert(client);
+
+    spice_debug("dev_state %p client %p", dev, client);
+    dev_client = spice_new0(SpiceCharDeviceClientState, 1);
+    dev_client->dev = dev;
+    dev_client->client = client;
+    ring_init(&dev_client->send_queue);
+    dev_client->send_queue_size = 0;
+    dev_client->max_send_queue_size = max_send_queue_size;
+    dev_client->do_flow_control = do_flow_control;
+    if (do_flow_control) {
+        dev_client->wait_for_tokens_timer = core->timer_add(device_client_wait_for_tokens_timeout,
+                                                            dev_client);
+        if (!dev_client->wait_for_tokens_timer) {
+            spice_error("failed to create wait for tokens timer");
+        }
+        dev_client->num_client_tokens = num_client_tokens;
+        dev_client->num_send_tokens = num_send_tokens;
+    } else {
+        dev_client->num_client_tokens = ~0;
+        dev_client->num_send_tokens = ~0;
+    }
+    ring_add(&dev->clients, &dev_client->link);
+    dev->num_clients++;
+    /* Now that we have a client, forward any pending device data */
+    spice_char_device_wakeup(dev);
+}
+
+void spice_char_device_client_remove(SpiceCharDeviceState *dev,
+                                     RedClient *client)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    spice_debug("dev_state %p client %p", dev, client);
+    dev_client = spice_char_device_client_find(dev, client);
+
+    if (!dev_client) {
+        spice_error("client wasn't found");
+        return;
+    }
+
+    spice_char_device_client_free(dev, dev_client);
+}
+
+int spice_char_device_client_exists(SpiceCharDeviceState *dev,
+                                    RedClient *client)
+{
+    return (spice_char_device_client_find(dev, client) != NULL);
+}
+
+void spice_char_device_start(SpiceCharDeviceState *dev)
+{
+    spice_debug("dev_state %p", dev);
+    dev->running = TRUE;
+    spice_char_device_state_ref(dev);
+    while (spice_char_device_write_to_device(dev) ||
+           spice_char_device_read_from_device(dev));
+    spice_char_device_state_unref(dev);
+}
+
+void spice_char_device_stop(SpiceCharDeviceState *dev)
+{
+    spice_debug("dev_state %p", dev);
+    dev->running = FALSE;
+    core->timer_cancel(dev->write_to_dev_timer);
+}
+
+void spice_char_device_reset(SpiceCharDeviceState *dev)
+{
+    RingItem *client_item;
+    spice_char_device_stop(dev);
+
+    spice_debug("dev_state %p", dev);
+    while (!ring_is_empty(&dev->write_queue)) {
+        RingItem *item = ring_get_tail(&dev->write_queue);
+        SpiceCharDeviceWriteBuffer *buf;
+
+        ring_remove(item);
+        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        /* tracking the tokens */
+        spice_char_device_write_buffer_release(dev, buf);
+    }
+    if (dev->cur_write_buf) {
+        SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
+
+        dev->cur_write_buf = NULL;
+        spice_char_device_write_buffer_release(dev, release_buf);
+    }
+
+    RING_FOREACH(client_item, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(client_item, SpiceCharDeviceClientState, link);
+        spice_char_device_client_send_queue_free(dev, dev_client);
+    }
+    dev->sin = NULL;
+}
+
+void spice_char_device_wakeup(SpiceCharDeviceState *dev)
+{
+    spice_char_device_read_from_device(dev);
+}
+
diff --git a/server/char_device.h b/server/char_device.h
index bdb32ae..db27bec 100644
--- a/server/char_device.h
+++ b/server/char_device.h
@@ -2,11 +2,212 @@
 #define __CHAR_DEVICE_H__
 
 #include "spice.h"
+#include "red_channel.h"
+
+/*
+ * Shared code for char devices, mainly for flow control.
+ *
+ * How to use the api:
+ * ==================
+ * device attached: call spice_char_device_state_create
+ * device detached: call spice_char_device_state_destroy/reset
+ *
+ * client connected and assoicated with a device: spice_char_device_client_add
+ * client disconnected: spice_char_device_client_remove
+ *
+ * Writing to the device
+ * ---------------------
+ * Write the data into SpiceCharDeviceWriteBuffer:
+ * call spice_char_device_write_buffer_get in order to get an appropriate buffer.
+ * call spice_char_device_write_buffer_add in order to push the buffer to the write queue.
+ * If you choose not to push the buffer to the device, call
+ * spice_char_device_write_buffer_release
+ *
+ * reading from the device
+ * -----------------------
+ *  The callback read_one_msg_from_device (see below) should be implemented
+ *  (using sif->read).
+ *  When the device is ready, this callback is called, and is expected to
+ *  return one message which is addressed to the client, or NULL if the read
+ *  hasn't completed.
+ *
+ * calls triggered from the device (qemu):
+ * --------------------------------------
+ * spice_char_device_start
+ * spice_char_device_stop
+ * spice_char_device_wakeup (for reading from the device)
+ */
+
+/*
+ * Note about multiple-clients:
+ * Multiclients are currently not supported in any of the character devices:
+ * spicevmc does not allow more than one client (and at least for usb, it should stay this way).
+ * smartcard code is not compatible with more than one reader.
+ * The server and guest agent code doesn't distinguish messages from different clients.
+ * In addition, its current flow control code (e.g., tokens handling) is wrong and doesn't
+ * take into account the different clients.
+ *
+ * Nonetheless, the following code introduces some support for multiple-clients:
+ * We track the number of tokens for all the clients, and we read from the device
+ * if one of the clients have enough tokens. For the clients that don't have tokens,
+ * we queue the messages, till they receive tokens, or till a timeout.
+ *
+ * TODO:
+ * At least for the agent, not all the messages from the device will be directed to all
+ * the clients (e.g., copy from guest to a specific client). Thus, support for
+ * client-specific-messages should be added.
+ * In addition, we should have support for clients that are being connected
+ * in the middle of a message transfer from the agent to the clients.
+ *
+ * */
+
+/* buffer that is used for writing to the device */
+typedef struct SpiceCharDeviceWriteBuffer {
+    RingItem link;
+    int origin;
+    RedClient *client; /* The client that sent the message to the device.
+                          NULL if the server created the message */
+
+    uint8_t *buf;
+    uint32_t buf_size;
+    uint32_t buf_used;
+} SpiceCharDeviceWriteBuffer;
+
+typedef void SpiceCharDeviceMsgToClient;
+
+typedef struct SpiceCharDeviceCallbacks {
+    /*
+     * Messages that are addressed to the client can be queued in case we have
+     * multiple clients and some of them don't have enough tokens.
+     */
+
+    /* reads from the device till reaching a msg that should be sent to the client,
+     * or till the reading fails */
+    SpiceCharDeviceMsgToClient* (*read_one_msg_from_device)(SpiceCharDeviceInstance *sin,
+                                                            void *opaque);
+    SpiceCharDeviceMsgToClient* (*ref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
+                                                     void *opaque);
+    void (*unref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
+                                void *opaque);
+    void (*send_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
+                               RedClient *client,
+                               void *opaque); /* after this call, the message is unreferenced */
+
+    /* The cb is called when a predefined number of write buffers were consumed by the
+     * device */
+    void (*send_tokens_to_client)(RedClient *client, uint32_t tokens, void *opaque);
+
+    /* The cb is called when a server (self) message that was addressed to the device,
+     * has been completely written to it */
+    void (*on_free_self_token)(void *opaque);
+
+    /* This cb is called if it is recommanded that a client will be removed
+     * due to slow flow or due to some other error.
+     * The called instance should disconnect the client, or at least the corresponding channel */
+    void (*remove_client)(RedClient *client, void *opaque);
+} SpiceCharDeviceCallbacks;
+
+typedef struct SpiceCharDeviceState SpiceCharDeviceState;
 
 struct SpiceCharDeviceState {
+    int running;
+    uint32_t refs;
+
+    Ring write_queue;
+    Ring write_bufs_pool;
+    SpiceCharDeviceWriteBuffer *cur_write_buf;
+    uint8_t *cur_write_buf_pos;
+    SpiceTimer *write_to_dev_timer;
+    uint64_t num_self_tokens;
+
+    Ring clients;
+    uint32_t num_clients;
+
+    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
+    SpiceCharDeviceInstance *sin;
+
+    int during_read_from_device;
+
+    SpiceCharDeviceCallbacks cbs;
+    void *opaque;
+    /* tmp till all spice char devices will employ the new SpiceCharDeviceState
+     * implementation. Then, SpiceCharDeviceState will be moved to char_device.c and
+     * this callback will be removed */
     void (*wakeup)(SpiceCharDeviceInstance *sin);
 };
 
+
+SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
+                                                     uint32_t client_tokens_interval,
+                                                     uint32_t self_tokens,
+                                                     SpiceCharDeviceCallbacks *cbs,
+                                                     void *opaque);
+
+void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *dev,
+                                                SpiceCharDeviceInstance *sin);
+void spice_char_device_state_destroy(SpiceCharDeviceState *dev);
+
+void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev);
+
+
+/*
+ * Resets write/read queues, and moves that state to being stopped.
+ * This routine is a workaround for a bad tokens management in the vdagent
+ * protocol:
+ *  The client tokens' are set only once, when the main channel is initialized.
+ *  Instead, it would have been more appropriate to reset them upon AGEN_CONNECT.
+ *  The client tokens are tracked as part of the SpiceCharDeviceClientState. Thus,
+ *  in order to be backwartd compatible with the client, we need to track the tokens
+ *  event when the agent is detached. We don't destroy the the char_device state, and
+ *  instead we just reset it.
+ *  In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it
+ *  overrides the amount of tokens, instead of adding the given amount.
+ *
+ *  todo: change AGENT_CONNECT msg to contain tokens count.
+ */
+void spice_char_device_reset(SpiceCharDeviceState *dev);
+
+/* max_send_queue_size = how many messages we can read from the device and enqueue for this client,
+ * when we have tokens for other clients and no tokens for this one */
+void spice_char_device_client_add(SpiceCharDeviceState *dev,
+                                  RedClient *client,
+                                  int do_flow_control,
+                                  uint32_t max_send_queue_size,
+                                  uint32_t num_client_tokens,
+                                  uint32_t num_send_tokens);
+
+void spice_char_device_client_remove(SpiceCharDeviceState *dev,
+                                     RedClient *client);
+int spice_char_device_client_exists(SpiceCharDeviceState *dev,
+                                    RedClient *client);
+
+void spice_char_device_start(SpiceCharDeviceState *dev);
+void spice_char_device_stop(SpiceCharDeviceState *dev);
+
+/** Read from device **/
+
+void spice_char_device_wakeup(SpiceCharDeviceState *dev);
+
+void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens);
+
+
+void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens);
+/** Write to device **/
+
+SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
+                                                               RedClient *client, int size);
+/* Either add the buffer to the write queue or release it */
+void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
+                                        SpiceCharDeviceWriteBuffer *write_buf);
+void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
+                                            SpiceCharDeviceWriteBuffer *write_buf);
+
+/* api for specific char devices */
+
 void spicevmc_device_connect(SpiceCharDeviceInstance *sin,
                              uint8_t channel_type);
 void spicevmc_device_disconnect(SpiceCharDeviceInstance *char_device);
commit 713505c800a9dad3f320198377651816da75f50c
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Mon Jun 25 09:35:54 2012 +0300

    smartcard: fix an error message

diff --git a/server/smartcard.c b/server/smartcard.c
index cb6b40b..fcf210f 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -535,7 +535,7 @@ static void smartcard_init(void)
                                              &channel_cbs);
 
     if (!g_smartcard_channel) {
-        spice_error("failed to allocate Inputs Channel");
+        spice_error("failed to allocate Smartcard Channel");
     }
 
     client_cbs.connect = smartcard_connect;
commit b468f14e23a54e0e502aba59d6aaf4e797339845
Author: Yonit Halperin <yhalperi at redhat.com>
Date:   Sun Jun 24 20:33:19 2012 +0300

    smartcard: fix calc of remaining data size when reading more than one msg from the device

diff --git a/server/smartcard.c b/server/smartcard.c
index 8ded142..cb6b40b 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -107,7 +107,7 @@ void smartcard_char_device_wakeup(SpiceCharDeviceInstance *sin)
             continue;
         }
         smartcard_char_device_on_message_from_device(state, vheader);
-        remaining = state->buf_used - sizeof(VSCMsgHeader) > actual_length;
+        remaining = state->buf_used - sizeof(VSCMsgHeader) - actual_length;
         if (remaining > 0) {
             memcpy(state->buf, state->buf_pos, remaining);
         }


More information about the Spice-commits mailing list