- Added win-usb-driver-install.[ch] - Added win-usb-clerk.h Operation (on Windows, spice-gtk point of view): - After some sanity checks, just before redir'ing a USB device a libusb driver needs to be installed (before libusb can open the device) - A connection (NamedPipe) is established with usb-clerk, a libusb driver installation service, and a request for driver installation is sent. - Installation status is asynchronously read from the pipe, and spice_usb_drv_install_finished() is called. - Upon a successful intallation, usbredir continues. Linux operation is not changed. --- gtk/Makefile.am | 11 ++ gtk/usb-device-manager.c | 143 +++++++++++++++- gtk/win-usb-clerk.h | 35 ++++ gtk/win-usb-driver-install.c | 379 ++++++++++++++++++++++++++++++++++++++++++ gtk/win-usb-driver-install.h | 98 +++++++++++ 5 files changed, 660 insertions(+), 6 deletions(-) create mode 100644 gtk/win-usb-clerk.h create mode 100644 gtk/win-usb-driver-install.c create mode 100644 gtk/win-usb-driver-install.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 4df3ee5..646b614 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -313,9 +313,20 @@ libspice_client_glib_2_0_la_LIBADD += $(GTHREAD_LIBS) endif +NAMED_PIPE_FILES = \ + controller/namedpipe.h \ + controller/namedpipe.c \ + controller/namedpipeconnection.c \ + controller/namedpipeconnection.h \ + $(NULL) + WIN_USB_FILES= \ win-usb-dev.h \ win-usb-dev.c \ + win-usb-clerk.h \ + win-usb-driver-install.h \ + win-usb-driver-install.c \ + $(NAMED_PIPE_FILES) \ $(NULL) if OS_WIN32 diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c index e4c8c32..8340335 100644 --- a/gtk/usb-device-manager.c +++ b/gtk/usb-device-manager.c @@ -33,6 +33,7 @@ #include <gudev/gudev.h> #elif defined(G_OS_WIN32) #include "win-usb-dev.h" +#include "win-usb-driver-install.h" #else #warning "Expecting one of G_OS_WIN32 and USE_GUDEV to be defined" #endif @@ -122,6 +123,14 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient *client, gpointer user_data); static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager *self, GUdevDevice *udev); + +static void +_spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self, + SpiceUsbDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static libusb_device *spice_usb_device_find_libusb_device(SpiceUsbDeviceManager *self, SpiceUsbDevice *device); @@ -129,6 +138,12 @@ libusb_device *spice_usb_device_find_libusb_device(SpiceUsbDeviceManager *self, static void spice_usb_device_manager_initable_iface_init(GInitableIface *iface); +#ifdef G_OS_WIN32 +static void spice_usb_device_manager_drv_install_cb(GObject *gobject, + GAsyncResult *res, + gpointer user_data); +#endif + static guint signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_CODE(SpiceUsbDeviceManager, spice_usb_device_manager, G_TYPE_OBJECT, @@ -604,7 +619,7 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager *self, spice_usb_device_manager_connect_device_async(self, device, NULL, spice_usb_device_manager_auto_connect_cb, - g_object_ref(device)); + device); } } @@ -669,6 +684,87 @@ static void spice_usb_device_manager_channel_connect_cb( g_object_unref(result); } +#ifdef G_OS_WIN32 + +typedef struct _UsbInstallCbInfo { + SpiceUsbDeviceManager *manager; + SpiceUsbDevice *device; + SpiceWinUsbDriver *installer; + GCancellable *cancellable; + GAsyncReadyCallback callback; + gpointer user_data; +} UsbInstallCbInfo; + +/** + * spice_usb_device_manager_drv_install_cb: + * @gobject: #SpiceWinUsbDriver in charge of installing the driver + * @res: #GAsyncResult of async win usb driver installation + * @user_data: #SpiceUsbDeviceManager requested the installation + * + * Called when an Windows libusb driver installation completed. + * + * If the driver installation was successful, continue with USB + * device redirection + */ +static void spice_usb_device_manager_drv_install_cb(GObject *gobject, + GAsyncResult *res, + gpointer user_data) +{ + SpiceUsbDeviceManager *self; + SpiceWinUsbDriver *installer; + gint status; + GError *err = NULL; + SpiceUsbDevice *device; + UsbInstallCbInfo *cbinfo; + GCancellable *cancellable; + GAsyncReadyCallback callback; + + SPICE_DEBUG("Win USB driver Installation finished"); + + g_return_if_fail(user_data != NULL); + + cbinfo = user_data; + self = cbinfo->manager; + device = cbinfo->device; + installer = cbinfo->installer; + cancellable = cbinfo->cancellable; + callback = cbinfo->callback; + user_data = cbinfo->user_data; + + g_free(cbinfo); + + g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self)); + g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(installer)); + g_return_if_fail(SPICE_IS_USB_DEVICE(device)); + + status = spice_win_usb_driver_install_finish(installer, res, &err); + + g_object_unref(installer); + + if (err) { + g_warning("win usb driver installation failed -- %s", + err->message); + g_error_free(err); + g_object_unref(device); + return; + } + + if (!status) { + g_warning("failed to install win usb driver (status=0)"); + g_object_unref(device); + return; + } + + /* device is already ref'ed */ + _spice_usb_device_manager_connect_device_async(self, + device, + cancellable, + callback, + user_data); + +} +#endif + /* ------------------------------------------------------------------ */ /* private api */ @@ -841,11 +937,12 @@ gboolean spice_usb_device_manager_is_device_connected(SpiceUsbDeviceManager *sel * @callback: a #GAsyncReadyCallback to call when the request is satisfied * @user_data: data to pass to callback */ -void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self, - SpiceUsbDevice *device, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +static void +_spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self, + SpiceUsbDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *result; @@ -893,6 +990,40 @@ done: g_object_unref(result); } + +void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self, + SpiceUsbDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + + device = g_object_ref(device); + +#ifdef G_OS_WIN32 + SpiceWinUsbDriver *installer; + UsbInstallCbInfo *cbinfo; + + installer = spice_win_usb_driver_new(); + cbinfo = g_new0(UsbInstallCbInfo, 1); + cbinfo->manager = self; + cbinfo->device = device; + cbinfo->installer = installer; + cbinfo->cancellable = cancellable; + cbinfo->callback = callback; + cbinfo->user_data = user_data; + spice_win_usb_driver_install(installer, device, NULL, + spice_usb_device_manager_drv_install_cb, + cbinfo); +#else + _spice_usb_device_manager_connect_device_async(self, + device, + cancellable, + callback, + user_data); +#endif +} + gboolean spice_usb_device_manager_connect_device_finish( SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err) { diff --git a/gtk/win-usb-clerk.h b/gtk/win-usb-clerk.h new file mode 100644 index 0000000..5b1e3cf --- /dev/null +++ b/gtk/win-usb-clerk.h @@ -0,0 +1,35 @@ +#ifndef _H_USBCLERK +#define _H_USBCLERK + +#include <windows.h> + +#define USB_CLERK_PIPE_NAME TEXT("\\\\.\\pipe\\usbclerkpipe") +#define USB_CLERK_MAGIC 0xDADA +#define USB_CLERK_VERSION 0x0002 + +typedef struct USBClerkHeader { + UINT16 magic; + UINT16 version; + UINT16 type; + UINT16 size; +} USBClerkHeader; + +enum { + USB_CLERK_DRIVER_INSTALL = 1, + USB_CLERK_DRIVER_REMOVE, + USB_CLERK_REPLY, + USB_CLERK_END_MESSAGE, +}; + +typedef struct USBClerkDriverOp { + USBClerkHeader hdr; + UINT16 vid; + UINT16 pid; +} USBClerkDriverOp; + +typedef struct USBClerkReply { + USBClerkHeader hdr; + UINT32 status; +} USBClerkReply; + +#endif diff --git a/gtk/win-usb-driver-install.c b/gtk/win-usb-driver-install.c new file mode 100644 index 0000000..9f4fdfc --- /dev/null +++ b/gtk/win-usb-driver-install.c @@ -0,0 +1,379 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2011 Red Hat, Inc. + + Red Hat Authors: + Uri Lublin <uril@xxxxxxxxxx> + + 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 <gio/gio.h> +#include "spice-util.h" +#include "win-usb-clerk.h" +#include "win-usb-driver-install.h" +#include "controller/namedpipe.h" +#include "controller/namedpipeconnection.h" +#include "spice-usb-device.h" +#include "spice-usb-device-priv.h" + + +/* ------------------------------------------------------------------ */ +/* gobject glue */ + +#define SPICE_WIN_USB_DRIVER_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((obj), SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriverPrivate)) + +struct _SpiceWinUsbDriverPrivate { + USBClerkReply reply; + GSimpleAsyncResult *result; + GCancellable *cancellable; + gulong cancellable_id; + GIOStream *gios; + SpiceUsbDevice *device; +}; + + + +G_DEFINE_TYPE(SpiceWinUsbDriver, spice_win_usb_driver, G_TYPE_OBJECT); + +static void spice_win_usb_driver_init(SpiceWinUsbDriver *self) +{ + self->priv = SPICE_WIN_USB_DRIVER_GET_PRIVATE(self); +} + +static void spice_win_usb_driver_close(SpiceWinUsbDriver *self) +{ + SpiceWinUsbDriverPrivate *priv; + GError *err = NULL; + + g_return_if_fail(self != NULL); + priv = self->priv; + g_return_if_fail(priv != NULL); + + if (priv->gios) { + g_io_stream_close(priv->gios, NULL, &err); + if (err) { /* just warn about it */ + g_warning("failed to close namedpipe stream %s", err->message); + } + + g_clear_object(&priv->gios); + } +} + +static void spice_win_usb_driver_cleanup(SpiceWinUsbDriver *self) +{ + SpiceWinUsbDriverPrivate *priv = self->priv; + + spice_win_usb_driver_close(self); + + if (priv->cancellable_id) { /* FIXME: should be in acl_helper too ? */ + g_cancellable_disconnect(priv->cancellable, priv->cancellable_id); + priv->cancellable = NULL; + priv->cancellable_id = 0; + } + + g_clear_object(&priv->result); + +} + +static void spice_win_usb_driver_finalize(GObject *gobject) +{ + spice_win_usb_driver_cleanup(SPICE_WIN_USB_DRIVER(gobject)); +} + +static void spice_win_usb_driver_class_init(SpiceWinUsbDriverClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = spice_win_usb_driver_finalize; + + g_type_class_add_private(klass, sizeof(SpiceWinUsbDriverPrivate)); +} + +/* ------------------------------------------------------------------ */ +/* callbacks */ + +static void win_usb_driver_async_result_set_cancelled(GSimpleAsyncResult *result) +{ + g_simple_async_result_set_error(result, + G_IO_ERROR, G_IO_ERROR_CANCELLED, + "Win USB driver installation cancelled"); +} + +static void win_usb_driver_cancelled_cb(GCancellable *cancellable, gpointer user_data) +{ + SpiceWinUsbDriver *self = SPICE_WIN_USB_DRIVER(user_data); + SpiceWinUsbDriverPrivate *priv = self->priv; + + g_message("IN %s result=%p", __FUNCTION__, priv->result); + if (priv->result) { + win_usb_driver_async_result_set_cancelled(priv->result); + g_simple_async_result_complete_in_idle(priv->result); + } +} + +void win_usb_driver_handle_reply_cb(GObject *gobject, + GAsyncResult *read_res, + gpointer user_data) +{ + SpiceWinUsbDriver *self; + SpiceWinUsbDriverPrivate *priv; + + GInputStream *istream; + GError *err = NULL; + gssize bytes; + + g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(user_data)); + self = SPICE_WIN_USB_DRIVER(user_data); + priv = self->priv; + istream = g_io_stream_get_input_stream(priv->gios); + bytes = g_input_stream_read_finish(istream, read_res, &err); + + SPICE_DEBUG("Finished reading: bytes=%ld err_exist?=%d", (long)bytes, err!=NULL); + + if (err) { + g_warning("failed to read reply from usbclerk (%s)", err->message); + g_simple_async_result_take_error(priv->result, err); + goto failed_reply; + } + + if (bytes == 0) { + g_warning("unexpected EOF from usbclerk"); + g_simple_async_result_set_error(priv->result, + SPICE_WIN_USB_DRIVER_ERROR, + SPICE_WIN_USB_DRIVER_ERROR_FAILED, + "unexpected EOF from usbclerk"); + goto failed_reply; + } + + if (priv->reply.hdr.magic != USB_CLERK_MAGIC) { + g_warning("usbclerk magic mismatch: mine=0x%04x server=0x%04x", + USB_CLERK_MAGIC, priv->reply.hdr.magic); + g_simple_async_result_set_error(priv->result, + SPICE_WIN_USB_DRIVER_ERROR, + SPICE_WIN_USB_DRIVER_ERROR_MESSAGE, + "usbclerk magic mismatch"); + goto failed_reply; + } + + if (priv->reply.hdr.version != USB_CLERK_VERSION) { + g_warning("usbclerk version mismatch: mine=0x%04x server=0x%04x", + USB_CLERK_VERSION, priv->reply.hdr.version); + /* For now just warn, do not fail */ + } + + if (priv->reply.hdr.type != USB_CLERK_REPLY) { + g_warning("usbclerk message with unexpected type %d", + priv->reply.hdr.type); + g_simple_async_result_set_error(priv->result, + SPICE_WIN_USB_DRIVER_ERROR, + SPICE_WIN_USB_DRIVER_ERROR_MESSAGE, + "usbclerk message with unexpected type"); + goto failed_reply; + } + + if (priv->reply.hdr.size != bytes) { + g_warning("usbclerk message size mismatch: read %d bytes hdr.size=%d", + bytes, priv->reply.hdr.size); + g_simple_async_result_set_error(priv->result, + SPICE_WIN_USB_DRIVER_ERROR, + SPICE_WIN_USB_DRIVER_ERROR_MESSAGE, + "usbclerk message with unexpected size"); + goto failed_reply; + } + + failed_reply: + g_simple_async_result_complete_in_idle(priv->result); + g_clear_object(&priv->result); +} + +/* ------------------------------------------------------------------ */ +/* helper functions */ + +static +gboolean spice_win_usb_driver_send_request(SpiceWinUsbDriver *self, guint16 op, + guint16 vid, guint16 pid, GError **err) +{ + USBClerkDriverOp req; + GOutputStream *ostream; + SpiceWinUsbDriverPrivate *priv; + gsize bytes; + gboolean b; + + SPICE_DEBUG("sending a request to usbclerk service (op=%d vid=0x%04x pid=0x%04x", + op, vid, pid); + + g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), FALSE); + priv = self->priv; + + memset(&req, 0, sizeof(req)); + req.hdr.magic = USB_CLERK_MAGIC; + req.hdr.version = USB_CLERK_VERSION; + req.hdr.type = op; + req.hdr.size = sizeof(req); + req.vid = vid; + req.pid = pid; + + ostream = g_io_stream_get_output_stream(priv->gios); + + b = g_output_stream_write_all(ostream, &req, sizeof(req), &bytes, NULL, err); + SPICE_DEBUG("write_all request returned %d written bytes %u expecting %u", + b, bytes, sizeof(req)); + return b; +} + +static +void spice_win_usb_driver_read_reply_async(SpiceWinUsbDriver *self) +{ + SpiceWinUsbDriverPrivate *priv; + GInputStream *istream; + + g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(self)); + priv = self->priv; + + SPICE_DEBUG("waiting for a reply from usbclerk"); + + istream = g_io_stream_get_input_stream(priv->gios); + + g_input_stream_read_async(istream, &priv->reply, sizeof(priv->reply), + G_PRIORITY_DEFAULT, NULL, + win_usb_driver_handle_reply_cb, self); +} + + +/* ------------------------------------------------------------------ */ +/* private api */ + + +G_GNUC_INTERNAL +SpiceWinUsbDriver *spice_win_usb_driver_new(void) +{ + GObject *obj; + + obj = g_object_new(SPICE_TYPE_WIN_USB_DRIVER, NULL); + + return SPICE_WIN_USB_DRIVER(obj); +} + +/** + * spice_win_usb_driver_install: + * Start libusb driver installation for @device + * + * A new NamedPipe is created for each request. + * + * Returns: TRUE if a request was sent to usbclerk + * FALSE upon failure to send a request. + */ +G_GNUC_INTERNAL +void spice_win_usb_driver_install(SpiceWinUsbDriver *self, + SpiceUsbDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + guint16 vid, pid; + SpiceNamedPipe *np; + SpiceNamedPipeConnection *npc; + GError *err = NULL; + GSimpleAsyncResult *result; + SpiceWinUsbDriverPrivate *priv; + + SPICE_DEBUG("Win usb driver installation started"); + + g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(self)); + g_return_if_fail(SPICE_IS_USB_DEVICE(device)); + + priv = self->priv; + + result = g_simple_async_result_new(G_OBJECT(self), callback, user_data, + spice_win_usb_driver_install); + + vid = spice_usb_device_get_vid(device); + pid = spice_usb_device_get_pid(device); + + SPICE_DEBUG("win-usb-driver-install: connecting to usbclerk named pipe"); + np = spice_named_pipe_new(USB_CLERK_PIPE_NAME, FALSE, &err); + if (!np) { + g_warning("failed to create a named pipe to usbclerk %s", err->message); + g_simple_async_result_take_error(result, err); + goto failed_request; + } + + npc = g_object_new(SPICE_TYPE_NAMED_PIPE_CONNECTION, "namedpipe", np, NULL); + priv->gios = G_IO_STREAM(npc); + + if (!spice_win_usb_driver_send_request(self, USB_CLERK_DRIVER_INSTALL, + vid, pid, &err)) { + g_warning("failed to send a request to usbclerk %s", err->message); + g_simple_async_result_take_error(result, err); + goto failed_request; + } + + /* set up for async read */ + priv->result = result; + priv->device = device; + if (cancellable) { + priv->cancellable = cancellable; + priv->cancellable_id = g_cancellable_connect(cancellable, + G_CALLBACK(win_usb_driver_cancelled_cb), + self, NULL); + } + + spice_win_usb_driver_read_reply_async(self); + + return; + + failed_request: + g_simple_async_result_complete_in_idle(result); + g_clear_object(&result); +} + + +/** + * Returns: currently returns 0 (failure) and 1 (success) + * possibly later we'll add error-codes + */ +G_GNUC_INTERNAL +gint spice_win_usb_driver_install_finish(SpiceWinUsbDriver *self, + GAsyncResult *res, GError **err) +{ + GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(res); + + g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), 0); + g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(self), + spice_win_usb_driver_install), + FALSE); + if (g_simple_async_result_propagate_error(result, err)) + return 0; + + return self->priv->reply.status; +} + +G_GNUC_INTERNAL +SpiceUsbDevice *spice_win_usb_driver_get_device(SpiceWinUsbDriver *self) +{ + g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), 0); + + return self->priv->device; +} + +GQuark spice_win_usb_driver_error_quark(void) +{ + return g_quark_from_static_string("spice-win-usb-driver-error-quark"); +} diff --git a/gtk/win-usb-driver-install.h b/gtk/win-usb-driver-install.h new file mode 100644 index 0000000..eb04707 --- /dev/null +++ b/gtk/win-usb-driver-install.h @@ -0,0 +1,98 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2011 Red Hat, Inc. + + Red Hat Authors: + Uri Lublin <uril@xxxxxxxxxx> + + 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/>. +*/ + +#ifndef SPICE_WIN_USB_DRIVER_H +#define SPICE_WIN_USB_DRIVER_H + +G_BEGIN_DECLS + +GQuark win_usb_driver_error_quark(void); + + +#define SPICE_TYPE_WIN_USB_DRIVER (spice_win_usb_driver_get_type ()) +#define SPICE_WIN_USB_DRIVER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriver)) +#define SPICE_IS_WIN_USB_DRIVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + SPICE_TYPE_WIN_USB_DRIVER)) +#define SPICE_WIN_USB_DRIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ + SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriverClass)) +#define SPICE_IS_WIN_USB_DRIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\ + SPICE_TYPE_WIN_USB_DRIVER)) +#define SPICE_WIN_USB_DRIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\ + SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriverClass)) + +typedef struct _SpiceWinUsbDriver SpiceWinUsbDriver; +typedef struct _SpiceWinUsbDriverClass SpiceWinUsbDriverClass; +typedef struct _SpiceWinUsbDriverPrivate SpiceWinUsbDriverPrivate; + +struct _SpiceWinUsbDriver +{ + GObject parent; + + /*< private >*/ + SpiceWinUsbDriverPrivate *priv; + /* Do not add fields to this struct */ +}; + +struct _SpiceWinUsbDriverClass +{ + GObjectClass parent_class; +}; + +GType spice_win_usb_driver_get_type(void); + +SpiceWinUsbDriver *spice_win_usb_driver_new(void); + + +#include "spice-usb-device.h" + +void spice_win_usb_driver_install(SpiceWinUsbDriver *self, + SpiceUsbDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + +gint spice_win_usb_driver_install_finish(SpiceWinUsbDriver *self, + GAsyncResult *res, GError **err); + + +SpiceUsbDevice *spice_win_usb_driver_get_device(SpiceWinUsbDriver *self); + +#define SPICE_WIN_USB_DRIVER_ERROR spice_win_usb_driver_error_quark() + +/** + * SpiceWinUsbDriverError: + * @SPICE_WIN_USB_DRIVER_ERROR_FAILED: generic error code + * @SPICE_WIN_USB_DRIVER_ERROR_MESSAGE: bad message read from clerk + * + * Error codes returned by spice-client API. + */ +typedef enum +{ + SPICE_WIN_USB_DRIVER_ERROR_FAILED, + SPICE_WIN_USB_DRIVER_ERROR_MESSAGE, +} SpiceWinUsbDriverError; + +GQuark spice_win_usb_driver_error_quark(void); + +G_END_DECLS + +#endif /* SPICE_WIN_USB_DRIVER_H */ -- 1.7.7.6 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel