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 <hdego...@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@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/spice-devel _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel