[Spice-commits] server/Makefile.am server/char_device.h server/reds.c server/usbredir.c spice.proto

Hans de Goede jwrdegoede at kemper.freedesktop.org
Fri Aug 12 23:53:04 PDT 2011


 server/Makefile.am   |    1 
 server/char_device.h |    5 
 server/reds.c        |   10 +
 server/usbredir.c    |  272 +++++++++++++++++++++++++++++++++++++++++++++++++++
 spice.proto          |    8 +
 5 files changed, 295 insertions(+), 1 deletion(-)

New commits:
commit c5a291f454a552604807e8dc995c060bfa41a8b9
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Mon Jul 25 11:09:12 2011 +0200

    server: Add a usbredir channel

diff --git a/server/Makefile.am b/server/Makefile.am
index 7af59a6..b9be242 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -95,6 +95,7 @@ libspice_server_la_SOURCES =			\
 	spice-experimental.h			\
 	spice.h					\
 	stat.h					\
+	usbredir.c				\
 	zlib_encoder.c				\
 	zlib_encoder.h				\
 	$(NULL)
diff --git a/server/char_device.h b/server/char_device.h
index 486df6f..4b55869 100644
--- a/server/char_device.h
+++ b/server/char_device.h
@@ -1,11 +1,14 @@
 #ifndef __CHAR_DEVICE_H__
 #define __CHAR_DEVICE_H__
 
-#include "server/spice-experimental.h"
+#include "spice.h"
 
 struct SpiceCharDeviceState {
     void (*wakeup)(SpiceCharDeviceInstance *sin);
 };
 
+int usbredir_device_connect(SpiceCharDeviceInstance *char_device);
+void usbredir_device_disconnect(SpiceCharDeviceInstance *char_device);
+
 #endif // __CHAR_DEVICE_H__
 
diff --git a/server/reds.c b/server/reds.c
index 7f45e46..4490675 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -3178,12 +3178,14 @@ SPICE_GNUC_VISIBLE void spice_server_char_device_wakeup(SpiceCharDeviceInstance*
 
 #define SUBTYPE_VDAGENT "vdagent"
 #define SUBTYPE_SMARTCARD "smartcard"
+#define SUBTYPE_USBREDIR "usbredir"
 
 const char *spice_server_char_device_recognized_subtypes_list[] = {
     SUBTYPE_VDAGENT,
 #ifdef USE_SMARTCARD
     SUBTYPE_SMARTCARD,
 #endif
+    SUBTYPE_USBREDIR,
     NULL,
 };
 
@@ -3214,6 +3216,11 @@ static int spice_server_char_device_add_interface(SpiceServer *s,
         }
     }
 #endif
+    else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
+        if (usbredir_device_connect(char_device) == -1) {
+            return -1;
+        }
+    }
     return 0;
 }
 
@@ -3233,6 +3240,9 @@ static void spice_server_char_device_remove_interface(SpiceBaseInstance *sin)
         smartcard_device_disconnect(char_device);
     }
 #endif
+    else if (strcmp(char_device->subtype, SUBTYPE_USBREDIR) == 0) {
+        usbredir_device_disconnect(char_device);
+    }
 }
 
 SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *s,
diff --git a/server/usbredir.c b/server/usbredir.c
new file mode 100644
index 0000000..096a381
--- /dev/null
+++ b/server/usbredir.c
@@ -0,0 +1,272 @@
+/* spice-server usbredir code
+
+   Copyright (C) 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede 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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "server/char_device.h"
+#include "server/red_channel.h"
+
+/* 64K should be enough for all but the largest bulk xfers + 32 bytes hdr */
+#define BUF_SIZE (64 * 1024 + 32)
+
+typedef struct UsbRedirPipeItem {
+    PipeItem base;
+    /* packets which don't fit this will get split, this is not a problem */
+    uint8_t buf[BUF_SIZE];
+    uint32_t buf_used;
+} UsbRedirPipeItem;
+
+typedef struct UsbRedirState {
+    Channel channel;
+    RedChannel *red_channel;
+    SpiceCharDeviceState chardev_st;
+    SpiceCharDeviceInstance *chardev_sin;
+    UsbRedirPipeItem *pipe_item;
+    uint8_t *rcv_buf;
+    uint32_t rcv_buf_size;
+} UsbRedirState;
+
+typedef struct UsbRedirChannel {
+    RedChannel base;
+    UsbRedirState *state;
+} UsbRedirChannel;
+
+static void usbredir_chardev_wakeup(SpiceCharDeviceInstance *sin)
+{
+    UsbRedirState *state;
+    SpiceCharDeviceInterface *sif;
+    int n;
+
+    state = SPICE_CONTAINEROF(sin->st, UsbRedirState, chardev_st);
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+
+    if (!state->red_channel) {
+        return;
+    }
+
+    do {
+        if (!state->pipe_item) {
+            state->pipe_item = spice_malloc(sizeof(UsbRedirPipeItem));
+            red_channel_pipe_item_init(state->red_channel,
+                                       &state->pipe_item->base, 0);
+        }
+
+        n = sif->read(sin, state->pipe_item->buf,
+                      sizeof(state->pipe_item->buf));
+        if (n > 0) {
+            state->pipe_item->buf_used = n;
+            red_channel_pipe_add_push(state->red_channel,
+                                      &state->pipe_item->base);
+            state->pipe_item = NULL;
+        }
+    } while (n > 0);
+}
+
+static int usbredir_red_channel_config_socket(RedChannel *red_channel)
+{
+    return TRUE;
+}
+
+static void usbredir_red_channel_disconnect(RedChannel *red_channel)
+{
+    UsbRedirState *state;
+    SpiceCharDeviceInstance *sin;
+    SpiceCharDeviceInterface *sif;
+
+    if (!red_channel) {
+        return;
+    }
+
+    state = SPICE_CONTAINEROF(red_channel, UsbRedirChannel, base)->state;
+    sin = state->chardev_sin;
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+
+    red_channel_destroy(red_channel);
+    state->red_channel = NULL;
+    if (sif->state) {
+        sif->state(sin, 0);
+    }
+}
+
+static int usbredir_red_channel_handle_message(RedChannel *red_channel,
+    SpiceDataHeader *header, uint8_t *msg)
+{
+    UsbRedirState *state;
+    SpiceCharDeviceInstance *sin;
+    SpiceCharDeviceInterface *sif;
+
+    state = SPICE_CONTAINEROF(red_channel, UsbRedirChannel, base)->state;
+    sin = state->chardev_sin;
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+
+    if (header->type != SPICE_MSGC_USBREDIR_DATA) {
+        return red_channel_handle_message(red_channel, header->size,
+                                          header->type, msg);
+    }
+
+    /*
+     * qemu usbredir will consume everything we give it, no need for
+     * flow control checks (or to use a pipe).
+     */
+    sif->write(sin, msg, header->size);
+
+    return TRUE;
+}
+
+static uint8_t *usbredir_red_channel_alloc_msg_rcv_buf(RedChannel *red_channel,
+    SpiceDataHeader *msg_header)
+{
+    UsbRedirState *state;
+
+    state = SPICE_CONTAINEROF(red_channel, UsbRedirChannel, base)->state;
+
+    if (msg_header->size > state->rcv_buf_size) {
+        state->rcv_buf = spice_realloc(state->rcv_buf, msg_header->size);
+        state->rcv_buf_size = msg_header->size;
+    }
+
+    return state->rcv_buf;
+}
+
+static void usbredir_red_channel_release_msg_rcv_buf(RedChannel *red_channel,
+    SpiceDataHeader *msg_header, uint8_t *msg)
+{
+    /* NOOP, we re-use the buffer every time and only free it on destruction */
+}
+
+static void usbredir_red_channel_hold_pipe_item(RedChannel *red_channel,
+    PipeItem *item)
+{
+    /* NOOP */
+}
+
+static void usbredir_red_channel_send_item(RedChannel *red_channel,
+    PipeItem *item)
+{
+    UsbRedirPipeItem *i = SPICE_CONTAINEROF(item, UsbRedirPipeItem, base);
+    SpiceMarshaller *m = red_channel_get_marshaller(red_channel);
+
+    red_channel_init_send_data(red_channel, SPICE_MSG_USBREDIR_DATA, item);
+    spice_marshaller_add_ref(m, i->buf, i->buf_used);
+    red_channel_begin_send_message(red_channel);
+}
+
+static void usbredir_red_channel_release_pipe_item(RedChannel *red_channel,
+    PipeItem *item, int item_pushed)
+{
+    free(item);
+}
+
+static void usbredir_link(Channel *channel, RedsStream *stream, int migration,
+    int num_common_caps, uint32_t *common_caps, int num_caps, uint32_t *caps)
+{
+    UsbRedirState *state;
+    UsbRedirChannel *redir_chan;
+    SpiceCharDeviceInstance *sin;
+    SpiceCharDeviceInterface *sif;
+
+    state = SPICE_CONTAINEROF(channel, UsbRedirState, channel);
+    sin = state->chardev_sin;
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceCharDeviceInterface, base);
+
+    if (state->red_channel) {
+        WARN("channel %d:%d already connected, refusing second connection\n",
+             channel->type, channel->id);
+        reds_stream_free(stream);
+        return;
+    }
+
+    state->red_channel = red_channel_create(sizeof(UsbRedirChannel),
+                                    stream, core,
+                                    migration, FALSE /* handle_acks */,
+                                    usbredir_red_channel_config_socket,
+                                    usbredir_red_channel_disconnect,
+                                    usbredir_red_channel_handle_message,
+                                    usbredir_red_channel_alloc_msg_rcv_buf,
+                                    usbredir_red_channel_release_msg_rcv_buf,
+                                    usbredir_red_channel_hold_pipe_item,
+                                    usbredir_red_channel_send_item,
+                                    usbredir_red_channel_release_pipe_item,
+                                    NULL,
+                                    NULL,
+                                    NULL);
+    if (!state->red_channel) {
+        return;
+    }
+    red_channel_init_outgoing_messages_window(state->red_channel);
+    redir_chan = SPICE_CONTAINEROF(state->red_channel, UsbRedirChannel, base);
+    redir_chan->state = state;
+
+    if (sif->state) {
+        sif->state(sin, 1);
+    }
+}
+
+static void usbredir_shutdown(Channel *channel)
+{
+    UsbRedirState *state = SPICE_CONTAINEROF(channel, UsbRedirState, channel);
+
+    usbredir_red_channel_disconnect(state->red_channel);
+}
+
+static void usbredir_migrate(Channel *channel)
+{
+    /* NOOP */
+}
+
+int usbredir_device_connect(SpiceCharDeviceInstance *sin)
+{
+    UsbRedirState *state;
+    static int id = 0;
+
+    state = spice_new0(UsbRedirState, 1);
+    state->channel.type = SPICE_CHANNEL_USBREDIR;
+    state->channel.id = id++;
+    state->channel.link = usbredir_link;
+    state->channel.shutdown = usbredir_shutdown;
+    state->channel.migrate = usbredir_migrate;
+    state->chardev_st.wakeup = usbredir_chardev_wakeup;
+    state->chardev_sin = sin;
+    state->rcv_buf = spice_malloc(BUF_SIZE);
+    state->rcv_buf_size = BUF_SIZE;
+
+    sin->st = &state->chardev_st;
+
+    reds_register_channel(&state->channel);
+
+    return 0;
+}
+
+void usbredir_device_disconnect(SpiceCharDeviceInstance *sin)
+{
+    UsbRedirState *state;
+
+    state = SPICE_CONTAINEROF(sin->st, UsbRedirState, chardev_st);
+
+    reds_unregister_channel(&state->channel);
+
+    usbredir_red_channel_disconnect(state->red_channel);
+
+    free(state->pipe_item);
+    free(state->rcv_buf);
+    free(state);
+}
diff --git a/spice.proto b/spice.proto
index 80c40d4..99302ff 100644
--- a/spice.proto
+++ b/spice.proto
@@ -1104,6 +1104,13 @@ client:
     Data data = 101;
 };
 
+channel UsbredirChannel : BaseChannel {
+server:
+    Data data = 101;
+client:
+    Data data = 101;
+};
+
 protocol Spice {
     MainChannel main = 1;
     DisplayChannel display;
@@ -1113,4 +1120,5 @@ protocol Spice {
     RecordChannel record;
     TunnelChannel tunnel;
     SmartcardChannel smartcard;
+    UsbredirChannel usbredir;
 };


More information about the Spice-commits mailing list