Implement a QMP command similar to the HMP's "info usbhost" command. This allows a QMP client to query which USB devices may be available for redirection. Because the availability of the command needs to depend on the target's (not the build host's) USB configuration, a stub function in host-stub.c is provided for targets without USB support.
v2 of this patch resolves build failure under some configurations without libusb. Signed-off-by: Alexander Kappner <a...@godking.net> --- hw/usb/host-libusb.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/usb/host-stub.c | 9 ++++++++ qapi/misc.json | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index 1b0be07..efdf577 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -40,6 +40,7 @@ #include <libusb.h> #include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" #include "qemu-common.h" #include "monitor/monitor.h" #include "qemu/error-report.h" @@ -1743,6 +1744,69 @@ bool usb_host_dev_is_scsi_storage(USBDevice *ud) return is_scsi_storage; } +UsbDeviceInfoList *qmp_query_usbhost(Error **errp) +{ + UsbDeviceInfoList *usb_list = NULL; + UsbDeviceInfoList *info; + libusb_device **devs = NULL; + struct libusb_device_descriptor ddesc; + char port[16]; + int i, n; + + if (usb_host_init() != 0) { + return NULL; + } + n = libusb_get_device_list(ctx, &devs); + + for (i = 0; i < n; i++) { + if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) { + continue; + } + if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) { + continue; + } + usb_host_get_port(devs[i], port, sizeof(port)); + info = g_new0(UsbDeviceInfoList, 1); + info->value = g_new0(UsbDeviceInfo, 1); + info->value->id_vendor = ddesc.idVendor; + info->value->bus_num = libusb_get_bus_number(devs[i]); + info->value->dev_addr = libusb_get_device_address(devs[i]); + info->value->id_product = ddesc.idProduct; + info->value->b_device_class = ddesc.bDeviceClass; + info->value->speed = libusb_get_device_speed(devs[i]); + info->next = usb_list; + usb_list = info; + + if (ddesc.iProduct) { + libusb_device_handle *handle; + if (libusb_open(devs[i], &handle) == 0) { + unsigned char name[64] = ""; + libusb_get_string_descriptor_ascii(handle, + ddesc.iProduct, + name, sizeof(name)); + libusb_close(handle); + info->value->str_product = g_strdup((gchar *)name); + } + } else + info->value->str_product = NULL; + + if (ddesc.iManufacturer) { + libusb_device_handle *handle; + if (libusb_open(devs[i], &handle) == 0) { + unsigned char name[64] = ""; + libusb_get_string_descriptor_ascii(handle, + ddesc.iManufacturer, + name, sizeof(name)); + libusb_close(handle); + info->value->str_manufacturer = g_strdup((gchar *)name); + } + } else + info->value->str_manufacturer = NULL; + } + libusb_free_device_list(devs, 1); + return usb_list; +} + void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { libusb_device **devs = NULL; diff --git a/hw/usb/host-stub.c b/hw/usb/host-stub.c index 41d93ec..fd227c4 100644 --- a/hw/usb/host-stub.c +++ b/hw/usb/host-stub.c @@ -35,6 +35,9 @@ #include "ui/console.h" #include "hw/usb.h" #include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qerror.h" void hmp_info_usbhost(Monitor *mon, const QDict *qdict) { @@ -45,3 +48,9 @@ bool usb_host_dev_is_scsi_storage(USBDevice *ud) { return false; } + +UsbDeviceInfoList *qmp_query_usbhost(Error **errp) +{ + error_setg(errp, QERR_FEATURE_DISABLED, "usb"); + return NULL; +}; diff --git a/qapi/misc.json b/qapi/misc.json index 5636f4a..19a1453 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -270,6 +270,46 @@ { 'command': 'query-kvm', 'returns': 'KvmInfo' } ## +# @query-usbhost: +# +# Returns information about USB devices available on the host +# +# Returns: a [UsbDeviceInfo]. Returns an error if compiled without +# CONFIG_USB_LIBUSB +# +# Since: TODO (maintainer insert version number if mainlined) +# +# Example: +# +# -> {"execute": "query-usbhost" } +# { +# "return": [ +# { +# "b_device_class": 239, +# "id_product": 793, +# "id_vendor": 3468, +# "speed": 3, +# "str_manufacturer": "Schiit Audio", +# "str_product": "Schiit Modi Uber" +# "bus_num": 5, +# "dev_addr": 21 +# }, +# { +# "b_device_class": 0, +# "id_product": 6944, +# "id_vendor": 6940, +# "speed": 2, +# "str_manufacturer": "Corsair", +# "str_product": "Corsair STRAFE RGB Gaming Keyboard" +# "bus_num": 5, +# "dev_addr": 35 +# } +# ] +# } +## +{ 'command': 'query-usbhost', 'returns': ['UsbDeviceInfo'] } + +## # @UuidInfo: # # Guest UUID information (Universally Unique Identifier). @@ -876,6 +916,28 @@ 'regions': ['PciMemoryRegion']} } ## +# @UsbDeviceInfo: +# +# @speed: the speed +# +# @id_vendor: idVendor field from device descriptor +# +# @id_product: idProduct field from device descriptor +# +# @str_product: string descriptor referenced by iProduct index, if any +# +# @str_manufacturer: string descriptor referenced by iManufacturer index, if any +# +# @dev_addr: address on bus that device is connected to +# +# @bus_num: bus number device is connected to +## +{ 'struct': 'UsbDeviceInfo', + 'data': + {'speed': 'int', 'id_vendor': 'int', 'id_product' : 'int', 'str_product': 'str', + 'b_device_class': 'int', 'str_manufacturer' : 'str', 'dev_addr' : 'int', 'bus_num' : 'int'} } + +## # @PciInfo: # # Information about a PCI bus -- 2.1.4