前言
本章记录SPICE USBREDIR中关于USBREDIR Channel初始化的知识。
代码仓库取自:
https://github.com/freedesktop/spice-usbredir (tag: usbredir-0.7)
https://github.com/freedesktop/spice-gtk (tag: v0.30)
1. 客户端初始化
1.1 初始化USB Device Manager
Spice通过USB Device Manager来进行USB设备的管理。
客户端(例如VirtViewer)通过调用spice_usb_device_manager_get()来初始化DeviceManager。
该函数实现于spice-session.c:
SpiceUsbDeviceManager *spice_usb_device_manager_get(SpiceSession *session,
GError **err)
{
SpiceUsbDeviceManager *self;
static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
g_return_val_if_fail(SPICE_IS_SESSION(session), NULL);
g_return_val_if_fail(err == NULL || *err == NULL, NULL);
g_static_mutex_lock(&mutex);
self = session->priv->usb_manager;
if (self == NULL) {
self = g_initable_new(SPICE_TYPE_USB_DEVICE_MANAGER, NULL, err,
"session", session, NULL);
session->priv->usb_manager = self;
}
g_static_mutex_unlock(&mutex);
return self;
}
可以看出USB Device Manger被实现为单体模式,每个session有且只有一个Manager对象。
接下来看看USB Device Manager的构造函数 (usb-device-manager.c):
G_DEFINE_TYPE_WITH_CODE(SpiceUsbDeviceManager, spice_usb_device_manager, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, spice_usb_device_manager_initable_iface_init));
...
static void spice_usb_device_manager_initable_iface_init(GInitableIface *iface)
{
iface->init = spice_usb_device_manager_initable_init;
}
在USB Device Manager的构造初始化函数spice_usb_device_manager_initable_init()中,注册USB设备热插拔回调函数,然后开始侦听USB设备热插拔事件。
static gboolean spice_usb_device_manager_initable_init(GInitable *initable,
GCancellable *cancellable,
GError **err)
{
#ifdef USE_USBREDIR
SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(initable);
SpiceUsbDeviceManagerPrivate *priv = self->priv;
GList *list;
GList *it;
int rc;
#ifdef USE_GUDEV
const gchar *const subsystems[] = {
"usb", NULL};
#endif
#ifdef G_OS_WIN32
priv->installer = spice_win_usb_driver_new(err);
if (!priv->installer) {
SPICE_DEBUG("failed to initialize winusb driver");
return FALSE;
}
#endif
/* Initialize libusb */
rc = libusb_init(&priv->context);
if (rc < 0) {
const char *desc = spice_usbutil_libusb_strerror(rc);
g_warning("Error initializing USB support: %s [%i]", desc, rc);
g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
"Error initializing USB support: %s [%i]", desc, rc);
return FALSE;
}
/* Start listening for usb devices plug / unplug */
#ifdef USE_GUDEV
priv->udev = g_udev_client_new(subsystems);
g_signal_connect(G_OBJECT(priv->udev), "uevent",
G_CALLBACK(spice_usb_device_manager_uevent_cb), self);
/* Do coldplug (detection of already connected devices) */
libusb_get_device_list(priv->context, &priv->coldplug_list);
list = g_udev_client_query_by_subsystem(priv->udev, "usb");
for (it = g_list_first(list); it; it = g_list_next(it)) {
spice_usb_device_manager_add_udev(self, it->data);
g_object_unref(it->data);
}
g_list_free(list);
libusb_free_device_list(priv->coldplug_list, 1);
priv->coldplug_list = NULL;
#else
rc = libusb_hotplug_register_callback(priv->context,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,
LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
spice_usb_device_manager_hotplug_cb, self, &priv->hp_handle);
if (rc < 0) {
const char *desc = spice_usbutil_libusb_strerror(rc);
g_warning("Error initializing USB hotplug support: %s [%i]", desc, rc);
g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
"Error initializing USB hotplug support: %s [%i]", desc, rc);
return FALSE;
}
spice_usb_device_manager_start_event_listening(self, NULL);
#endif
/* Start listening for usb channels connect/disconnect */
spice_g_signal_connect_object(priv->session, "channel-new", G_CALLBACK(channel_new), self, G_CONNECT_AFTER);
g_signal_connect(priv->session, "channel-destroy",
G_CALLBACK(channel_destroy), self);
list = spice_