[Spice-devel] [PATCH v8 07/20] usb-backend: include usbredirparser
Victor Toso
victortoso at redhat.com
Thu Sep 19 14:11:20 UTC 2019
From: Yuri Benditovich <yuri.benditovich at daynix.com>
This patch introduces the usage of usbredirparser in
SpiceUsbBackendChannel.
The focus of this patch is to the code path of real devices. As we
don't know beforehand if a SpiceUsbBackendChannel will be used by real
or emulated devices, an instance of usbredirparser must be initialized
with the usbredirhost's first hello message.
This is a must to the current design on supporting emulated devices.
This will be extended in the next patch to match remote's usbredirhost
capabilities and providing support to emulated devices.
Signed-off-by: Yuri Benditovich <yuri.benditovich at daynix.com>
Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
Signed-off-by: Victor Toso <victortoso at redhat.com>
---
src/usb-backend.c | 223 ++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 218 insertions(+), 5 deletions(-)
diff --git a/src/usb-backend.c b/src/usb-backend.c
index c4d8e20..169f1b0 100644
--- a/src/usb-backend.c
+++ b/src/usb-backend.c
@@ -45,6 +45,7 @@
#include "usbutil.h"
#define LOUD_DEBUG(x, ...)
+#define USBREDIR_CALLBACK_NOT_IMPLEMENTED() spice_debug("%s not implemented - FIXME", __func__)
struct _SpiceUsbDevice
{
@@ -82,6 +83,7 @@ struct _SpiceUsbBackend
struct _SpiceUsbBackendChannel
{
struct usbredirhost *usbredirhost;
+ struct usbredirparser *parser;
uint8_t *read_buf;
int read_buf_size;
struct usbredirfilter_rule *rules;
@@ -631,11 +633,48 @@ static void usbredir_log(void *user_data, int level, const char *msg)
}
}
+static struct usbredirparser *create_parser(SpiceUsbBackendChannel *ch);
+
static int usbredir_write_callback(void *user_data, uint8_t *data, int count)
{
SpiceUsbBackendChannel *ch = user_data;
int res;
SPICE_DEBUG("%s ch %p, %d bytes", __FUNCTION__, ch, count);
+
+ // handle first packet (HELLO) creating parser from capabilities
+ if (SPICE_UNLIKELY(ch->parser == NULL)) {
+ // Here we return 0 from this function to keep the packet in
+ // the queue. The packet will then be sent to the guest.
+ // We are initializing SpiceUsbBackendChannel, the
+ // SpiceUsbredirChannel is not ready to receive packets.
+
+ // we are still initializing the host
+ if (ch->usbredirhost == NULL) {
+ return 0;
+ }
+
+ ch->parser = create_parser(ch);
+ if (!ch->parser) {
+ return 0;
+ }
+
+ /* hello is short header (12) + hello struct (64) */
+ const int hello_size = 12 + sizeof(struct usb_redir_hello_header);
+ g_assert(count >= hello_size + 4);
+ g_assert(SPICE_ALIGNED_CAST(struct usb_redir_header *, data)->type == usb_redir_hello);
+
+ const uint32_t flags =
+ usbredirparser_fl_write_cb_owns_buffer | usbredirparser_fl_usb_host |
+ usbredirparser_fl_no_hello;
+
+ usbredirparser_init(ch->parser,
+ PACKAGE_STRING,
+ SPICE_ALIGNED_CAST(uint32_t *, data + hello_size),
+ (count - hello_size) / sizeof(uint32_t),
+ flags);
+
+ return 0;
+ }
res = spice_usbredir_write(ch->usbredir_channel, data, count);
return res;
}
@@ -702,6 +741,165 @@ GError *spice_usb_backend_get_error_details(int error_code, gchar *desc)
return err;
}
+static void
+usbredir_control_packet(void *priv, uint64_t id, struct usb_redir_control_packet_header *h,
+ uint8_t *data, int data_len)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_bulk_packet(void *priv, uint64_t id, struct usb_redir_bulk_packet_header *h,
+ uint8_t *data, int data_len)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void usbredir_device_reset(void *priv)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_interface_info(void *priv, struct usb_redir_interface_info_header *interface_info)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_interface_ep_info(void *priv, struct usb_redir_ep_info_header *ep_info)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_set_configuration(void *priv, uint64_t id,
+ struct usb_redir_set_configuration_header *set_configuration)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void usbredir_get_configuration(void *priv, uint64_t id)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_set_alt_setting(void *priv, uint64_t id, struct usb_redir_set_alt_setting_header *s)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_get_alt_setting(void *priv, uint64_t id, struct usb_redir_get_alt_setting_header *s)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void usbredir_cancel_data(void *priv, uint64_t id)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void usbredir_filter_reject(void *priv)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+/* Note that the ownership of the rules array is passed on to the callback. */
+static void
+usbredir_filter_filter(void *priv, struct usbredirfilter_rule *r, int count)
+{
+ SpiceUsbBackendChannel *ch = priv;
+ SPICE_DEBUG("%s ch %p %d filters", __FUNCTION__, ch, count);
+
+ free(ch->rules);
+ ch->rules = r;
+ ch->rules_count = count;
+ if (count) {
+ int i;
+ for (i = 0; i < count; i++) {
+ SPICE_DEBUG("%s class %d, %X:%X",
+ r[i].allow ? "allowed" : "denied", r[i].device_class,
+ (uint32_t)r[i].vendor_id, (uint32_t)r[i].product_id);
+ }
+ }
+}
+
+static void usbredir_device_disconnect_ack(void *priv)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void
+usbredir_hello(void *priv, struct usb_redir_hello_header *hello)
+{
+ USBREDIR_CALLBACK_NOT_IMPLEMENTED();
+}
+
+static void initialize_parser(SpiceUsbBackendChannel *ch)
+{
+ uint32_t flags, caps[USB_REDIR_CAPS_SIZE] = { 0 };
+
+ g_assert(ch->usbredirhost == NULL);
+
+ flags = usbredirparser_fl_write_cb_owns_buffer | usbredirparser_fl_usb_host;
+
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_connect_device_version);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_filter);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_device_disconnect_ack);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_ep_info_max_packet_size);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_64bits_ids);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+
+ usbredirparser_init(ch->parser, PACKAGE_STRING, caps, USB_REDIR_CAPS_SIZE, flags);
+}
+
+/*
+ We can initialize the usbredirparser with HELLO enabled only in case
+ the libusb is not active and the usbredirhost does not function.
+ Then the parser sends session HELLO and receives server's response.
+ Otherwise (usbredirparser initialized with HELLO disabled):
+ - the usbredirhost sends session HELLO
+ - we look into it to know set of capabilities we shall initialize
+ the parser with
+ - when we receive server's response to HELLO we provide it also to
+ parser to let it synchronize with server's capabilities
+*/
+static struct usbredirparser *create_parser(SpiceUsbBackendChannel *ch)
+{
+ struct usbredirparser *parser = usbredirparser_create();
+
+ g_return_val_if_fail(parser != NULL, NULL);
+
+ parser->priv = ch;
+ parser->log_func = usbredir_log;
+ parser->read_func = usbredir_read_callback;
+ parser->write_func = usbredir_write_callback;
+ parser->reset_func = usbredir_device_reset;
+ parser->set_configuration_func = usbredir_set_configuration;
+ parser->get_configuration_func = usbredir_get_configuration;
+ parser->set_alt_setting_func = usbredir_set_alt_setting;
+ parser->get_alt_setting_func = usbredir_get_alt_setting;
+ parser->cancel_data_packet_func = usbredir_cancel_data;
+ parser->control_packet_func = usbredir_control_packet;
+ parser->bulk_packet_func = usbredir_bulk_packet;
+ parser->alloc_lock_func = usbredir_alloc_lock;
+ parser->lock_func = usbredir_lock_lock;
+ parser->unlock_func = usbredir_unlock_lock;
+ parser->free_lock_func = usbredir_free_lock;
+ parser->hello_func = usbredir_hello;
+ parser->filter_reject_func = usbredir_filter_reject;
+ parser->device_disconnect_ack_func = usbredir_device_disconnect_ack;
+ parser->interface_info_func = usbredir_interface_info;
+ parser->ep_info_func = usbredir_interface_ep_info;
+ parser->filter_filter_func = usbredir_filter_filter;
+
+ return parser;
+}
+
void spice_usb_backend_return_write_data(SpiceUsbBackendChannel *ch, void *data)
{
if (ch->usbredirhost) {
@@ -800,11 +998,22 @@ spice_usb_backend_channel_new(SpiceUsbBackend *be,
spice_util_get_debug() ? usbredirparser_debug : usbredirparser_warning,
usbredirhost_fl_write_cb_owns_buffer);
g_warn_if_fail(ch->usbredirhost != NULL);
- }
- if (ch->usbredirhost) {
- usbredirhost_set_buffered_output_size_cb(ch->usbredirhost, usbredir_buffered_output_size_callback);
+ if (ch->usbredirhost) {
+ usbredirhost_set_buffered_output_size_cb(ch->usbredirhost, usbredir_buffered_output_size_callback);
+ // force flush of HELLO packet and creation of parser
+ usbredirhost_write_guest_data(ch->usbredirhost);
+ }
} else {
- g_free(ch);
+ // no physical device support, only emulated, create the
+ // parser
+ ch->parser = create_parser(ch);
+ if (ch->parser != NULL) {
+ initialize_parser(ch);
+ }
+ }
+
+ if (!ch->parser) {
+ spice_usb_backend_channel_delete(ch);
ch = NULL;
}
@@ -829,9 +1038,13 @@ void spice_usb_backend_channel_delete(SpiceUsbBackendChannel *ch)
if (ch->usbredirhost) {
usbredirhost_close(ch->usbredirhost);
}
+ if (ch->parser) {
+ usbredirparser_destroy(ch->parser);
+ }
if (ch->rules) {
- g_free(ch->rules);
+ /* rules were allocated by usbredirparser */
+ free(ch->rules);
}
g_free(ch);
--
2.21.0
More information about the Spice-devel
mailing list