[Spice-devel] [PATCH spice] server: Add a usbredir channel

Alon Levy alevy at redhat.com
Wed Aug 10 23:33:08 PDT 2011


On Wed, Aug 10, 2011 at 10:46:00PM +0200, Hans de Goede wrote:
> ---
>  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 deletions(-)
>  create mode 100644 server/usbredir.c
> 
> 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) {

Can this happen? i.e. usb data from qemu without a channel? shouldn't this at
least print a warning? (but it may be too frequent if it actually happens - don't
know, didn't test, and didn't read the qemu bits).

> +        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;
>  };
> -- 
> 1.7.5.1
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel


More information about the Spice-devel mailing list