2012-11-30 19:02:11 +04:00
|
|
|
/*
|
|
|
|
* Linux host USB redirector
|
|
|
|
*
|
|
|
|
* Copyright (c) 2005 Fabrice Bellard
|
|
|
|
*
|
|
|
|
* Copyright (c) 2008 Max Krasnyansky
|
|
|
|
* Support for host device auto connect & disconnect
|
|
|
|
* Major rewrite to support fully async operation
|
|
|
|
*
|
|
|
|
* Copyright 2008 TJ <linux@tjworld.net>
|
|
|
|
* Added flexible support for /dev/bus/usb /sys/bus/usb/devices in addition
|
|
|
|
* to the legacy /proc/bus/usb USB device discovery and handling
|
|
|
|
*
|
|
|
|
* (c) 2012 Gerd Hoffmann <kraxel@redhat.com>
|
|
|
|
* Completely rewritten to use libusb instead of usbfs ioctls.
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
2016-01-26 21:17:12 +03:00
|
|
|
#include "qemu/osdep.h"
|
2020-09-03 23:43:22 +03:00
|
|
|
#include "qom/object.h"
|
2016-03-22 10:13:20 +03:00
|
|
|
#ifndef CONFIG_WIN32
|
2012-11-30 19:02:11 +04:00
|
|
|
#include <poll.h>
|
2016-03-22 10:13:20 +03:00
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
#include <libusb.h>
|
|
|
|
|
2020-08-24 14:00:57 +03:00
|
|
|
#ifdef CONFIG_LINUX
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <linux/usbdevice_fs.h>
|
|
|
|
#endif
|
|
|
|
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 11:01:28 +03:00
|
|
|
#include "qapi/error.h"
|
2019-08-12 08:23:45 +03:00
|
|
|
#include "migration/vmstate.h"
|
2012-11-30 19:02:11 +04:00
|
|
|
#include "monitor/monitor.h"
|
2015-03-17 20:29:20 +03:00
|
|
|
#include "qemu/error-report.h"
|
Include qemu/main-loop.h less
In my "build everything" tree, changing qemu/main-loop.h triggers a
recompile of some 5600 out of 6600 objects (not counting tests and
objects that don't depend on qemu/osdep.h). It includes block/aio.h,
which in turn includes qemu/event_notifier.h, qemu/notify.h,
qemu/processor.h, qemu/qsp.h, qemu/queue.h, qemu/thread-posix.h,
qemu/thread.h, qemu/timer.h, and a few more.
Include qemu/main-loop.h only where it's needed. Touching it now
recompiles only some 1700 objects. For block/aio.h and
qemu/event_notifier.h, these numbers drop from 5600 to 2800. For the
others, they shrink only slightly.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20190812052359.30071-21-armbru@redhat.com>
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
2019-08-12 08:23:50 +03:00
|
|
|
#include "qemu/main-loop.h"
|
2019-05-23 17:35:07 +03:00
|
|
|
#include "qemu/module.h"
|
2019-08-12 08:23:59 +03:00
|
|
|
#include "sysemu/runstate.h"
|
2012-11-30 19:02:11 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
|
|
|
#include "trace.h"
|
|
|
|
|
2019-08-12 08:23:51 +03:00
|
|
|
#include "hw/qdev-properties.h"
|
2012-11-30 19:02:11 +04:00
|
|
|
#include "hw/usb.h"
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
#define TYPE_USB_HOST_DEVICE "usb-host"
|
2020-09-16 21:25:19 +03:00
|
|
|
OBJECT_DECLARE_SIMPLE_TYPE(USBHostDevice, USB_HOST_DEVICE)
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
typedef struct USBHostRequest USBHostRequest;
|
|
|
|
typedef struct USBHostIsoXfer USBHostIsoXfer;
|
|
|
|
typedef struct USBHostIsoRing USBHostIsoRing;
|
|
|
|
|
|
|
|
struct USBAutoFilter {
|
|
|
|
uint32_t bus_num;
|
|
|
|
uint32_t addr;
|
|
|
|
char *port;
|
|
|
|
uint32_t vendor_id;
|
|
|
|
uint32_t product_id;
|
|
|
|
};
|
|
|
|
|
|
|
|
enum USBHostDeviceOptions {
|
|
|
|
USB_HOST_OPT_PIPELINE,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBHostDevice {
|
|
|
|
USBDevice parent_obj;
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
struct USBAutoFilter match;
|
2020-06-05 15:59:52 +03:00
|
|
|
char *hostdevice;
|
2012-11-30 19:02:11 +04:00
|
|
|
int32_t bootindex;
|
|
|
|
uint32_t iso_urb_count;
|
|
|
|
uint32_t iso_urb_frames;
|
|
|
|
uint32_t options;
|
|
|
|
uint32_t loglevel;
|
2016-06-03 12:12:55 +03:00
|
|
|
bool needs_autoscan;
|
2019-10-15 09:44:26 +03:00
|
|
|
bool allow_one_guest_reset;
|
|
|
|
bool allow_all_guest_resets;
|
2020-01-08 12:10:43 +03:00
|
|
|
bool suppress_remote_wake;
|
2019-10-15 09:44:26 +03:00
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
/* state */
|
|
|
|
QTAILQ_ENTRY(USBHostDevice) next;
|
|
|
|
int seen, errcount;
|
|
|
|
int bus_num;
|
|
|
|
int addr;
|
|
|
|
char port[16];
|
|
|
|
|
2020-06-05 15:59:52 +03:00
|
|
|
int hostfd;
|
2012-11-30 19:02:11 +04:00
|
|
|
libusb_device *dev;
|
|
|
|
libusb_device_handle *dh;
|
|
|
|
struct libusb_device_descriptor ddesc;
|
|
|
|
|
|
|
|
struct {
|
|
|
|
bool detached;
|
|
|
|
bool claimed;
|
|
|
|
} ifs[USB_MAX_INTERFACES];
|
|
|
|
|
|
|
|
/* callbacks & friends */
|
2013-04-24 16:29:08 +04:00
|
|
|
QEMUBH *bh_nodev;
|
|
|
|
QEMUBH *bh_postld;
|
2018-05-03 09:29:32 +03:00
|
|
|
bool bh_postld_pending;
|
2012-11-30 19:02:11 +04:00
|
|
|
Notifier exit;
|
|
|
|
|
|
|
|
/* request queues */
|
|
|
|
QTAILQ_HEAD(, USBHostRequest) requests;
|
|
|
|
QTAILQ_HEAD(, USBHostIsoRing) isorings;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBHostRequest {
|
|
|
|
USBHostDevice *host;
|
|
|
|
USBPacket *p;
|
|
|
|
bool in;
|
|
|
|
struct libusb_transfer *xfer;
|
|
|
|
unsigned char *buffer;
|
|
|
|
unsigned char *cbuf;
|
|
|
|
unsigned int clen;
|
2014-05-23 12:27:00 +04:00
|
|
|
bool usb3ep0quirk;
|
2012-11-30 19:02:11 +04:00
|
|
|
QTAILQ_ENTRY(USBHostRequest) next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBHostIsoXfer {
|
|
|
|
USBHostIsoRing *ring;
|
|
|
|
struct libusb_transfer *xfer;
|
|
|
|
bool copy_complete;
|
|
|
|
unsigned int packet;
|
|
|
|
QTAILQ_ENTRY(USBHostIsoXfer) next;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct USBHostIsoRing {
|
|
|
|
USBHostDevice *host;
|
|
|
|
USBEndpoint *ep;
|
|
|
|
QTAILQ_HEAD(, USBHostIsoXfer) unused;
|
|
|
|
QTAILQ_HEAD(, USBHostIsoXfer) inflight;
|
|
|
|
QTAILQ_HEAD(, USBHostIsoXfer) copy;
|
|
|
|
QTAILQ_ENTRY(USBHostIsoRing) next;
|
|
|
|
};
|
|
|
|
|
|
|
|
static QTAILQ_HEAD(, USBHostDevice) hostdevs =
|
|
|
|
QTAILQ_HEAD_INITIALIZER(hostdevs);
|
|
|
|
|
|
|
|
static void usb_host_auto_check(void *unused);
|
|
|
|
static void usb_host_release_interfaces(USBHostDevice *s);
|
|
|
|
static void usb_host_nodev(USBHostDevice *s);
|
2013-10-08 23:58:08 +04:00
|
|
|
static void usb_host_detach_kernel(USBHostDevice *s);
|
2012-11-30 19:02:11 +04:00
|
|
|
static void usb_host_attach_kernel(USBHostDevice *s);
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2014-11-05 11:35:22 +03:00
|
|
|
#ifndef LIBUSB_LOG_LEVEL_WARNING /* older libusb didn't define these */
|
|
|
|
#define LIBUSB_LOG_LEVEL_WARNING 2
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
#define CONTROL_TIMEOUT 10000 /* 10 sec */
|
|
|
|
#define BULK_TIMEOUT 0 /* unlimited */
|
|
|
|
#define INTR_TIMEOUT 0 /* unlimited */
|
|
|
|
|
2017-04-03 13:52:38 +03:00
|
|
|
#ifndef LIBUSB_API_VERSION
|
|
|
|
# define LIBUSB_API_VERSION LIBUSBX_API_VERSION
|
|
|
|
#endif
|
|
|
|
#if LIBUSB_API_VERSION >= 0x01000103
|
2014-05-23 13:26:52 +04:00
|
|
|
# define HAVE_STREAMS 1
|
|
|
|
#endif
|
2021-01-21 18:08:32 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000106
|
|
|
|
# define HAVE_SUPER_PLUS 1
|
|
|
|
#endif
|
2014-05-23 13:26:52 +04:00
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
static const char *speed_name[] = {
|
|
|
|
[LIBUSB_SPEED_UNKNOWN] = "?",
|
|
|
|
[LIBUSB_SPEED_LOW] = "1.5",
|
|
|
|
[LIBUSB_SPEED_FULL] = "12",
|
|
|
|
[LIBUSB_SPEED_HIGH] = "480",
|
|
|
|
[LIBUSB_SPEED_SUPER] = "5000",
|
2021-01-21 18:08:32 +03:00
|
|
|
#ifdef HAVE_SUPER_PLUS
|
|
|
|
[LIBUSB_SPEED_SUPER_PLUS] = "5000+",
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned int speed_map[] = {
|
|
|
|
[LIBUSB_SPEED_LOW] = USB_SPEED_LOW,
|
|
|
|
[LIBUSB_SPEED_FULL] = USB_SPEED_FULL,
|
|
|
|
[LIBUSB_SPEED_HIGH] = USB_SPEED_HIGH,
|
|
|
|
[LIBUSB_SPEED_SUPER] = USB_SPEED_SUPER,
|
2021-01-21 18:08:32 +03:00
|
|
|
#ifdef HAVE_SUPER_PLUS
|
|
|
|
[LIBUSB_SPEED_SUPER_PLUS] = USB_SPEED_SUPER,
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static const unsigned int status_map[] = {
|
|
|
|
[LIBUSB_TRANSFER_COMPLETED] = USB_RET_SUCCESS,
|
|
|
|
[LIBUSB_TRANSFER_ERROR] = USB_RET_IOERROR,
|
|
|
|
[LIBUSB_TRANSFER_TIMED_OUT] = USB_RET_IOERROR,
|
|
|
|
[LIBUSB_TRANSFER_CANCELLED] = USB_RET_IOERROR,
|
|
|
|
[LIBUSB_TRANSFER_STALL] = USB_RET_STALL,
|
|
|
|
[LIBUSB_TRANSFER_NO_DEVICE] = USB_RET_NODEV,
|
|
|
|
[LIBUSB_TRANSFER_OVERFLOW] = USB_RET_BABBLE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const char *err_names[] = {
|
|
|
|
[-LIBUSB_ERROR_IO] = "IO",
|
|
|
|
[-LIBUSB_ERROR_INVALID_PARAM] = "INVALID_PARAM",
|
|
|
|
[-LIBUSB_ERROR_ACCESS] = "ACCESS",
|
|
|
|
[-LIBUSB_ERROR_NO_DEVICE] = "NO_DEVICE",
|
|
|
|
[-LIBUSB_ERROR_NOT_FOUND] = "NOT_FOUND",
|
|
|
|
[-LIBUSB_ERROR_BUSY] = "BUSY",
|
|
|
|
[-LIBUSB_ERROR_TIMEOUT] = "TIMEOUT",
|
|
|
|
[-LIBUSB_ERROR_OVERFLOW] = "OVERFLOW",
|
|
|
|
[-LIBUSB_ERROR_PIPE] = "PIPE",
|
|
|
|
[-LIBUSB_ERROR_INTERRUPTED] = "INTERRUPTED",
|
|
|
|
[-LIBUSB_ERROR_NO_MEM] = "NO_MEM",
|
|
|
|
[-LIBUSB_ERROR_NOT_SUPPORTED] = "NOT_SUPPORTED",
|
|
|
|
[-LIBUSB_ERROR_OTHER] = "OTHER",
|
|
|
|
};
|
|
|
|
|
|
|
|
static libusb_context *ctx;
|
|
|
|
static uint32_t loglevel;
|
|
|
|
|
2016-03-22 10:13:20 +03:00
|
|
|
#ifndef CONFIG_WIN32
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
static void usb_host_handle_fd(void *opaque)
|
|
|
|
{
|
|
|
|
struct timeval tv = { 0, 0 };
|
|
|
|
libusb_handle_events_timeout(ctx, &tv);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_add_fd(int fd, short events, void *user_data)
|
|
|
|
{
|
|
|
|
qemu_set_fd_handler(fd,
|
|
|
|
(events & POLLIN) ? usb_host_handle_fd : NULL,
|
|
|
|
(events & POLLOUT) ? usb_host_handle_fd : NULL,
|
|
|
|
ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_del_fd(int fd, void *user_data)
|
|
|
|
{
|
|
|
|
qemu_set_fd_handler(fd, NULL, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2021-06-23 11:52:48 +03:00
|
|
|
#else
|
|
|
|
|
|
|
|
static QEMUTimer *poll_timer;
|
|
|
|
static uint32_t request_count;
|
|
|
|
|
|
|
|
static void usb_host_timer_kick(void)
|
|
|
|
{
|
|
|
|
int64_t delay_ns;
|
|
|
|
|
|
|
|
delay_ns = request_count
|
|
|
|
? (NANOSECONDS_PER_SECOND / 100) /* 10 ms interval with active req */
|
|
|
|
: (NANOSECONDS_PER_SECOND); /* 1 sec interval otherwise */
|
|
|
|
timer_mod(poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + delay_ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_timer(void *opaque)
|
|
|
|
{
|
|
|
|
struct timeval tv = { 0, 0 };
|
|
|
|
|
|
|
|
libusb_handle_events_timeout(ctx, &tv);
|
|
|
|
usb_host_timer_kick();
|
|
|
|
}
|
|
|
|
|
2016-03-22 10:13:20 +03:00
|
|
|
#endif /* !CONFIG_WIN32 */
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
static int usb_host_init(void)
|
|
|
|
{
|
2016-03-22 10:13:20 +03:00
|
|
|
#ifndef CONFIG_WIN32
|
2012-11-30 19:02:11 +04:00
|
|
|
const struct libusb_pollfd **poll;
|
2016-03-22 10:13:20 +03:00
|
|
|
#endif
|
2016-07-29 09:59:29 +03:00
|
|
|
int rc;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
if (ctx) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rc = libusb_init(&ctx);
|
|
|
|
if (rc != 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
2018-04-05 16:20:46 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000106
|
|
|
|
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, loglevel);
|
|
|
|
#else
|
2012-11-30 19:02:11 +04:00
|
|
|
libusb_set_debug(ctx, loglevel);
|
2018-04-05 16:20:46 +03:00
|
|
|
#endif
|
2016-03-22 10:13:20 +03:00
|
|
|
#ifdef CONFIG_WIN32
|
2021-06-23 11:52:48 +03:00
|
|
|
poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, usb_host_timer, NULL);
|
|
|
|
usb_host_timer_kick();
|
2016-03-22 10:13:20 +03:00
|
|
|
#else
|
2012-11-30 19:02:11 +04:00
|
|
|
libusb_set_pollfd_notifiers(ctx, usb_host_add_fd,
|
|
|
|
usb_host_del_fd,
|
|
|
|
ctx);
|
|
|
|
poll = libusb_get_pollfds(ctx);
|
|
|
|
if (poll) {
|
2016-07-29 09:59:29 +03:00
|
|
|
int i;
|
2012-11-30 19:02:11 +04:00
|
|
|
for (i = 0; poll[i] != NULL; i++) {
|
|
|
|
usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(poll);
|
2016-03-22 10:13:20 +03:00
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_get_port(libusb_device *dev, char *port, size_t len)
|
|
|
|
{
|
|
|
|
uint8_t path[7];
|
|
|
|
size_t off;
|
|
|
|
int rc, i;
|
|
|
|
|
2017-04-03 13:52:38 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000102
|
2013-06-06 18:39:34 +04:00
|
|
|
rc = libusb_get_port_numbers(dev, path, 7);
|
|
|
|
#else
|
2012-11-30 19:02:11 +04:00
|
|
|
rc = libusb_get_port_path(ctx, dev, path, 7);
|
2013-06-06 18:39:34 +04:00
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
if (rc < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
off = snprintf(port, len, "%d", path[0]);
|
|
|
|
for (i = 1; i < rc; i++) {
|
|
|
|
off += snprintf(port+off, len-off, ".%d", path[i]);
|
|
|
|
}
|
|
|
|
return off;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_libusb_error(const char *func, int rc)
|
|
|
|
{
|
|
|
|
const char *errname;
|
|
|
|
|
|
|
|
if (rc >= 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (-rc < ARRAY_SIZE(err_names) && err_names[-rc]) {
|
|
|
|
errname = err_names[-rc];
|
|
|
|
} else {
|
|
|
|
errname = "?";
|
|
|
|
}
|
2014-09-19 10:48:27 +04:00
|
|
|
error_report("%s: %d [%s]", func, rc, errname);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
static bool usb_host_use_combining(USBEndpoint *ep)
|
|
|
|
{
|
|
|
|
int type;
|
|
|
|
|
|
|
|
if (!ep->pipeline) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (ep->pid != USB_TOKEN_IN) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
type = usb_ep_get_type(ep->dev, ep->pid, ep->nr);
|
|
|
|
if (type != USB_ENDPOINT_XFER_BULK) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p,
|
|
|
|
bool in, size_t bufsize)
|
|
|
|
{
|
|
|
|
USBHostRequest *r = g_new0(USBHostRequest, 1);
|
|
|
|
|
|
|
|
r->host = s;
|
|
|
|
r->p = p;
|
|
|
|
r->in = in;
|
|
|
|
r->xfer = libusb_alloc_transfer(0);
|
|
|
|
if (bufsize) {
|
|
|
|
r->buffer = g_malloc(bufsize);
|
|
|
|
}
|
|
|
|
QTAILQ_INSERT_TAIL(&s->requests, r, next);
|
2021-06-23 11:52:48 +03:00
|
|
|
#ifdef CONFIG_WIN32
|
|
|
|
request_count++;
|
|
|
|
usb_host_timer_kick();
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_req_free(USBHostRequest *r)
|
|
|
|
{
|
2021-06-23 11:52:48 +03:00
|
|
|
#ifdef CONFIG_WIN32
|
|
|
|
request_count--;
|
|
|
|
#endif
|
2020-02-03 14:41:08 +03:00
|
|
|
QTAILQ_REMOVE(&r->host->requests, r, next);
|
2012-11-30 19:02:11 +04:00
|
|
|
libusb_free_transfer(r->xfer);
|
|
|
|
g_free(r->buffer);
|
|
|
|
g_free(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p)
|
|
|
|
{
|
|
|
|
USBHostRequest *r;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(r, &s->requests, next) {
|
|
|
|
if (r->p == p) {
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-07-29 09:55:31 +03:00
|
|
|
static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
USBHostRequest *r = xfer->user_data;
|
|
|
|
USBHostDevice *s = r->host;
|
|
|
|
bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
|
|
|
|
|
|
|
|
if (r->p == NULL) {
|
|
|
|
goto out; /* request was canceled */
|
|
|
|
}
|
|
|
|
|
|
|
|
r->p->status = status_map[xfer->status];
|
|
|
|
r->p->actual_length = xfer->actual_length;
|
|
|
|
if (r->in && xfer->actual_length) {
|
2020-01-08 12:10:43 +03:00
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
|
|
|
struct libusb_config_descriptor *conf = (void *)r->cbuf;
|
2012-11-30 19:02:11 +04:00
|
|
|
memcpy(r->cbuf, r->buffer + 8, xfer->actual_length);
|
2014-05-23 12:27:00 +04:00
|
|
|
|
|
|
|
/* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
|
|
|
|
* to work redirected to a not superspeed capable hcd */
|
|
|
|
if (r->usb3ep0quirk && xfer->actual_length >= 18 &&
|
|
|
|
r->cbuf[7] == 9) {
|
|
|
|
r->cbuf[7] = 64;
|
|
|
|
}
|
2020-01-08 12:10:43 +03:00
|
|
|
/*
|
|
|
|
*If this is GET_DESCRIPTOR request for configuration descriptor,
|
|
|
|
* remove 'remote wakeup' flag from it to prevent idle power down
|
|
|
|
* in Windows guest
|
|
|
|
*/
|
|
|
|
if (s->suppress_remote_wake &&
|
|
|
|
udev->setup_buf[0] == USB_DIR_IN &&
|
|
|
|
udev->setup_buf[1] == USB_REQ_GET_DESCRIPTOR &&
|
|
|
|
udev->setup_buf[3] == USB_DT_CONFIG && udev->setup_buf[2] == 0 &&
|
|
|
|
xfer->actual_length >
|
|
|
|
offsetof(struct libusb_config_descriptor, bmAttributes) &&
|
|
|
|
(conf->bmAttributes & USB_CFG_ATT_WAKEUP)) {
|
|
|
|
trace_usb_host_remote_wakeup_removed(s->bus_num, s->addr);
|
|
|
|
conf->bmAttributes &= ~USB_CFG_ATT_WAKEUP;
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
|
|
|
|
r->p->status, r->p->actual_length);
|
|
|
|
usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p);
|
|
|
|
|
|
|
|
out:
|
|
|
|
usb_host_req_free(r);
|
|
|
|
if (disconnect) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-29 09:55:31 +03:00
|
|
|
static void LIBUSB_CALL usb_host_req_complete_data(struct libusb_transfer *xfer)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
USBHostRequest *r = xfer->user_data;
|
|
|
|
USBHostDevice *s = r->host;
|
|
|
|
bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
|
|
|
|
|
|
|
|
if (r->p == NULL) {
|
|
|
|
goto out; /* request was canceled */
|
|
|
|
}
|
|
|
|
|
|
|
|
r->p->status = status_map[xfer->status];
|
|
|
|
if (r->in && xfer->actual_length) {
|
|
|
|
usb_packet_copy(r->p, r->buffer, xfer->actual_length);
|
|
|
|
}
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
|
|
|
|
r->p->status, r->p->actual_length);
|
|
|
|
if (usb_host_use_combining(r->p->ep)) {
|
|
|
|
usb_combined_input_packet_complete(USB_DEVICE(s), r->p);
|
|
|
|
} else {
|
|
|
|
usb_packet_complete(USB_DEVICE(s), r->p);
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
usb_host_req_free(r);
|
|
|
|
if (disconnect) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_req_abort(USBHostRequest *r)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = r->host;
|
2013-05-07 18:06:12 +04:00
|
|
|
bool inflight = (r->p && r->p->state == USB_PACKET_ASYNC);
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
if (inflight) {
|
|
|
|
r->p->status = USB_RET_NODEV;
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, r->p,
|
|
|
|
r->p->status, r->p->actual_length);
|
|
|
|
if (r->p->ep->nr == 0) {
|
|
|
|
usb_generic_async_ctrl_complete(USB_DEVICE(s), r->p);
|
|
|
|
} else {
|
|
|
|
usb_packet_complete(USB_DEVICE(s), r->p);
|
|
|
|
}
|
|
|
|
r->p = NULL;
|
|
|
|
|
|
|
|
libusb_cancel_transfer(r->xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2016-07-29 09:55:31 +03:00
|
|
|
static void LIBUSB_CALL
|
|
|
|
usb_host_req_complete_iso(struct libusb_transfer *transfer)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
USBHostIsoXfer *xfer = transfer->user_data;
|
|
|
|
|
|
|
|
if (!xfer) {
|
|
|
|
/* USBHostIsoXfer released while inflight */
|
|
|
|
g_free(transfer->buffer);
|
|
|
|
libusb_free_transfer(transfer);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTAILQ_REMOVE(&xfer->ring->inflight, xfer, next);
|
|
|
|
if (QTAILQ_EMPTY(&xfer->ring->inflight)) {
|
|
|
|
USBHostDevice *s = xfer->ring->host;
|
|
|
|
trace_usb_host_iso_stop(s->bus_num, s->addr, xfer->ring->ep->nr);
|
|
|
|
}
|
|
|
|
if (xfer->ring->ep->pid == USB_TOKEN_IN) {
|
|
|
|
QTAILQ_INSERT_TAIL(&xfer->ring->copy, xfer, next);
|
2015-07-17 10:12:57 +03:00
|
|
|
usb_wakeup(xfer->ring->ep, 0);
|
2012-11-30 19:02:11 +04:00
|
|
|
} else {
|
|
|
|
QTAILQ_INSERT_TAIL(&xfer->ring->unused, xfer, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep)
|
|
|
|
{
|
|
|
|
USBHostIsoRing *ring = g_new0(USBHostIsoRing, 1);
|
|
|
|
USBHostIsoXfer *xfer;
|
|
|
|
/* FIXME: check interval (for now assume one xfer per frame) */
|
|
|
|
int packets = s->iso_urb_frames;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ring->host = s;
|
|
|
|
ring->ep = ep;
|
|
|
|
QTAILQ_INIT(&ring->unused);
|
|
|
|
QTAILQ_INIT(&ring->inflight);
|
|
|
|
QTAILQ_INIT(&ring->copy);
|
|
|
|
QTAILQ_INSERT_TAIL(&s->isorings, ring, next);
|
|
|
|
|
|
|
|
for (i = 0; i < s->iso_urb_count; i++) {
|
|
|
|
xfer = g_new0(USBHostIsoXfer, 1);
|
|
|
|
xfer->ring = ring;
|
|
|
|
xfer->xfer = libusb_alloc_transfer(packets);
|
|
|
|
xfer->xfer->dev_handle = s->dh;
|
|
|
|
xfer->xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
|
|
|
|
|
|
|
|
xfer->xfer->endpoint = ring->ep->nr;
|
|
|
|
if (ring->ep->pid == USB_TOKEN_IN) {
|
|
|
|
xfer->xfer->endpoint |= USB_DIR_IN;
|
|
|
|
}
|
|
|
|
xfer->xfer->callback = usb_host_req_complete_iso;
|
|
|
|
xfer->xfer->user_data = xfer;
|
|
|
|
|
|
|
|
xfer->xfer->num_iso_packets = packets;
|
|
|
|
xfer->xfer->length = ring->ep->max_packet_size * packets;
|
|
|
|
xfer->xfer->buffer = g_malloc0(xfer->xfer->length);
|
|
|
|
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ring;
|
|
|
|
}
|
|
|
|
|
|
|
|
static USBHostIsoRing *usb_host_iso_find(USBHostDevice *s, USBEndpoint *ep)
|
|
|
|
{
|
|
|
|
USBHostIsoRing *ring;
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(ring, &s->isorings, next) {
|
|
|
|
if (ring->ep == ep) {
|
|
|
|
return ring;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_iso_reset_xfer(USBHostIsoXfer *xfer)
|
|
|
|
{
|
|
|
|
libusb_set_iso_packet_lengths(xfer->xfer,
|
|
|
|
xfer->ring->ep->max_packet_size);
|
|
|
|
xfer->packet = 0;
|
|
|
|
xfer->copy_complete = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_iso_free_xfer(USBHostIsoXfer *xfer, bool inflight)
|
|
|
|
{
|
|
|
|
if (inflight) {
|
|
|
|
xfer->xfer->user_data = NULL;
|
|
|
|
} else {
|
|
|
|
g_free(xfer->xfer->buffer);
|
|
|
|
libusb_free_transfer(xfer->xfer);
|
|
|
|
}
|
|
|
|
g_free(xfer);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_iso_free(USBHostIsoRing *ring)
|
|
|
|
{
|
|
|
|
USBHostIsoXfer *xfer;
|
|
|
|
|
|
|
|
while ((xfer = QTAILQ_FIRST(&ring->inflight)) != NULL) {
|
|
|
|
QTAILQ_REMOVE(&ring->inflight, xfer, next);
|
|
|
|
usb_host_iso_free_xfer(xfer, true);
|
|
|
|
}
|
|
|
|
while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) {
|
|
|
|
QTAILQ_REMOVE(&ring->unused, xfer, next);
|
|
|
|
usb_host_iso_free_xfer(xfer, false);
|
|
|
|
}
|
|
|
|
while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL) {
|
|
|
|
QTAILQ_REMOVE(&ring->copy, xfer, next);
|
|
|
|
usb_host_iso_free_xfer(xfer, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
QTAILQ_REMOVE(&ring->host->isorings, ring, next);
|
|
|
|
g_free(ring);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_iso_free_all(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
USBHostIsoRing *ring;
|
|
|
|
|
|
|
|
while ((ring = QTAILQ_FIRST(&s->isorings)) != NULL) {
|
|
|
|
usb_host_iso_free(ring);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool usb_host_iso_data_copy(USBHostIsoXfer *xfer, USBPacket *p)
|
|
|
|
{
|
|
|
|
unsigned int psize;
|
|
|
|
unsigned char *buf;
|
|
|
|
|
|
|
|
buf = libusb_get_iso_packet_buffer_simple(xfer->xfer, xfer->packet);
|
|
|
|
if (p->pid == USB_TOKEN_OUT) {
|
|
|
|
psize = p->iov.size;
|
|
|
|
if (psize > xfer->ring->ep->max_packet_size) {
|
|
|
|
/* should not happen (guest bug) */
|
|
|
|
psize = xfer->ring->ep->max_packet_size;
|
|
|
|
}
|
|
|
|
xfer->xfer->iso_packet_desc[xfer->packet].length = psize;
|
|
|
|
} else {
|
|
|
|
psize = xfer->xfer->iso_packet_desc[xfer->packet].actual_length;
|
|
|
|
if (psize > p->iov.size) {
|
|
|
|
/* should not happen (guest bug) */
|
|
|
|
psize = p->iov.size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
usb_packet_copy(p, buf, psize);
|
|
|
|
xfer->packet++;
|
|
|
|
xfer->copy_complete = (xfer->packet == xfer->xfer->num_iso_packets);
|
|
|
|
return xfer->copy_complete;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p)
|
|
|
|
{
|
|
|
|
USBHostIsoRing *ring;
|
|
|
|
USBHostIsoXfer *xfer;
|
|
|
|
bool disconnect = false;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
ring = usb_host_iso_find(s, p->ep);
|
|
|
|
if (ring == NULL) {
|
|
|
|
ring = usb_host_iso_alloc(s, p->ep);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy data to guest */
|
|
|
|
xfer = QTAILQ_FIRST(&ring->copy);
|
|
|
|
if (xfer != NULL) {
|
|
|
|
if (usb_host_iso_data_copy(xfer, p)) {
|
|
|
|
QTAILQ_REMOVE(&ring->copy, xfer, next);
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* submit empty bufs to host */
|
|
|
|
while ((xfer = QTAILQ_FIRST(&ring->unused)) != NULL) {
|
|
|
|
QTAILQ_REMOVE(&ring->unused, xfer, next);
|
|
|
|
usb_host_iso_reset_xfer(xfer);
|
|
|
|
rc = libusb_submit_transfer(xfer->xfer);
|
|
|
|
if (rc != 0) {
|
|
|
|
usb_host_libusb_error("libusb_submit_transfer [iso]", rc);
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
disconnect = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (QTAILQ_EMPTY(&ring->inflight)) {
|
|
|
|
trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
|
|
|
|
}
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (disconnect) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
|
|
|
|
{
|
|
|
|
USBHostIsoRing *ring;
|
|
|
|
USBHostIsoXfer *xfer;
|
|
|
|
bool disconnect = false;
|
|
|
|
int rc, filled = 0;
|
|
|
|
|
|
|
|
ring = usb_host_iso_find(s, p->ep);
|
|
|
|
if (ring == NULL) {
|
|
|
|
ring = usb_host_iso_alloc(s, p->ep);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy data from guest */
|
|
|
|
xfer = QTAILQ_FIRST(&ring->copy);
|
|
|
|
while (xfer != NULL && xfer->copy_complete) {
|
|
|
|
filled++;
|
|
|
|
xfer = QTAILQ_NEXT(xfer, next);
|
|
|
|
}
|
|
|
|
if (xfer == NULL) {
|
|
|
|
xfer = QTAILQ_FIRST(&ring->unused);
|
|
|
|
if (xfer == NULL) {
|
|
|
|
trace_usb_host_iso_out_of_bufs(s->bus_num, s->addr, p->ep->nr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
QTAILQ_REMOVE(&ring->unused, xfer, next);
|
|
|
|
usb_host_iso_reset_xfer(xfer);
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->copy, xfer, next);
|
|
|
|
}
|
|
|
|
usb_host_iso_data_copy(xfer, p);
|
|
|
|
|
|
|
|
if (QTAILQ_EMPTY(&ring->inflight)) {
|
|
|
|
/* wait until half of our buffers are filled
|
|
|
|
before kicking the iso out stream */
|
|
|
|
if (filled*2 < s->iso_urb_count) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* submit filled bufs to host */
|
|
|
|
while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL &&
|
|
|
|
xfer->copy_complete) {
|
|
|
|
QTAILQ_REMOVE(&ring->copy, xfer, next);
|
|
|
|
rc = libusb_submit_transfer(xfer->xfer);
|
|
|
|
if (rc != 0) {
|
|
|
|
usb_host_libusb_error("libusb_submit_transfer [iso]", rc);
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->unused, xfer, next);
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
disconnect = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (QTAILQ_EMPTY(&ring->inflight)) {
|
|
|
|
trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
|
|
|
|
}
|
|
|
|
QTAILQ_INSERT_TAIL(&ring->inflight, xfer, next);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (disconnect) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
2014-05-23 12:27:00 +04:00
|
|
|
static void usb_host_speed_compat(USBHostDevice *s)
|
2013-05-06 15:12:16 +04:00
|
|
|
{
|
2014-05-23 12:27:00 +04:00
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
2013-05-06 15:12:16 +04:00
|
|
|
struct libusb_config_descriptor *conf;
|
|
|
|
const struct libusb_interface_descriptor *intf;
|
|
|
|
const struct libusb_endpoint_descriptor *endp;
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2014-05-23 12:27:00 +04:00
|
|
|
struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
|
|
|
|
#endif
|
|
|
|
bool compat_high = true;
|
|
|
|
bool compat_full = true;
|
2013-05-06 15:12:16 +04:00
|
|
|
uint8_t type;
|
|
|
|
int rc, c, i, a, e;
|
|
|
|
|
|
|
|
for (c = 0;; c++) {
|
|
|
|
rc = libusb_get_config_descriptor(s->dev, c, &conf);
|
|
|
|
if (rc != 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (i = 0; i < conf->bNumInterfaces; i++) {
|
|
|
|
for (a = 0; a < conf->interface[i].num_altsetting; a++) {
|
|
|
|
intf = &conf->interface[i].altsetting[a];
|
2021-06-24 13:38:33 +03:00
|
|
|
|
|
|
|
if (intf->bInterfaceClass == LIBUSB_CLASS_MASS_STORAGE &&
|
|
|
|
intf->bInterfaceSubClass == 6) { /* SCSI */
|
|
|
|
udev->flags |= (1 << USB_DEV_FLAG_IS_SCSI_STORAGE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-05-06 15:12:16 +04:00
|
|
|
for (e = 0; e < intf->bNumEndpoints; e++) {
|
|
|
|
endp = &intf->endpoint[e];
|
|
|
|
type = endp->bmAttributes & 0x3;
|
|
|
|
switch (type) {
|
|
|
|
case 0x01: /* ISO */
|
2014-05-23 12:27:00 +04:00
|
|
|
compat_full = false;
|
|
|
|
compat_high = false;
|
|
|
|
break;
|
|
|
|
case 0x02: /* BULK */
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2014-05-23 12:27:00 +04:00
|
|
|
rc = libusb_get_ss_endpoint_companion_descriptor
|
|
|
|
(ctx, endp, &endp_ss_comp);
|
|
|
|
if (rc == LIBUSB_SUCCESS) {
|
2016-09-09 10:41:30 +03:00
|
|
|
int streams = endp_ss_comp->bmAttributes & 0x1f;
|
|
|
|
if (streams) {
|
|
|
|
compat_full = false;
|
|
|
|
compat_high = false;
|
|
|
|
}
|
2014-05-23 12:27:00 +04:00
|
|
|
libusb_free_ss_endpoint_companion_descriptor
|
|
|
|
(endp_ss_comp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
break;
|
2013-05-06 15:12:16 +04:00
|
|
|
case 0x03: /* INTERRUPT */
|
|
|
|
if (endp->wMaxPacketSize > 64) {
|
2014-05-23 12:27:00 +04:00
|
|
|
compat_full = false;
|
|
|
|
}
|
|
|
|
if (endp->wMaxPacketSize > 1024) {
|
|
|
|
compat_high = false;
|
2013-05-06 15:12:16 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
libusb_free_config_descriptor(conf);
|
|
|
|
}
|
2014-05-23 12:27:00 +04:00
|
|
|
|
|
|
|
udev->speedmask = (1 << udev->speed);
|
|
|
|
if (udev->speed == USB_SPEED_SUPER && compat_high) {
|
2014-11-10 14:14:22 +03:00
|
|
|
udev->speedmask |= USB_SPEED_MASK_HIGH;
|
2014-05-23 12:27:00 +04:00
|
|
|
}
|
|
|
|
if (udev->speed == USB_SPEED_SUPER && compat_full) {
|
2014-11-10 14:14:22 +03:00
|
|
|
udev->speedmask |= USB_SPEED_MASK_FULL;
|
2014-05-23 12:27:00 +04:00
|
|
|
}
|
|
|
|
if (udev->speed == USB_SPEED_HIGH && compat_full) {
|
2014-11-10 14:14:22 +03:00
|
|
|
udev->speedmask |= USB_SPEED_MASK_FULL;
|
2014-05-23 12:27:00 +04:00
|
|
|
}
|
2013-05-06 15:12:16 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
static void usb_host_ep_update(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
static const char *tname[] = {
|
|
|
|
[USB_ENDPOINT_XFER_CONTROL] = "control",
|
|
|
|
[USB_ENDPOINT_XFER_ISOC] = "isoc",
|
|
|
|
[USB_ENDPOINT_XFER_BULK] = "bulk",
|
|
|
|
[USB_ENDPOINT_XFER_INT] = "int",
|
|
|
|
};
|
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
|
|
|
struct libusb_config_descriptor *conf;
|
|
|
|
const struct libusb_interface_descriptor *intf;
|
|
|
|
const struct libusb_endpoint_descriptor *endp;
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2013-11-21 20:21:00 +04:00
|
|
|
struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
uint8_t devep, type;
|
usb-host: use correct altsetting in usb_host_ep_update
In order to keep track of the alternate setting that should be used for
a given interface, the USBDevice struct keeps an array of alternate
setting values, which is indexed by the interface number. In
usb_host_set_interface, when this array is updated, usb_host_ep_update
is called as a result. However, when usb_host_ep_update accesses the
active libusb_config_descriptor, it indexes udev->altsetting with the
loop variable, rather than the interface number.
With the simple trace backend enable, this behavior can be seen:
[...]
usb_xhci_xfer_start 0.440 pid=1215 xfer=0x5596a4b85930 slotid=0x1 epid=0x1 streamid=0x0
usb_packet_state_change 1.703 pid=1215 bus=0x1 port=b'1' ep=0x0 p=0x5596a4b85938 o=b'undef' n=b'setup'
usb_host_req_control 2.269 pid=1215 bus=0x1 addr=0x5 p=0x5596a4b85938 req=0x10b value=0x1 index=0xd
usb_host_set_interface 0.449 pid=1215 bus=0x1 addr=0x5 interface=0xd alt=0x1
usb_host_parse_config 2542.648 pid=1215 bus=0x1 addr=0x5 value=0x2 active=0x1
usb_host_parse_interface 1.804 pid=1215 bus=0x1 addr=0x5 num=0xc alt=0x0 active=0x1
usb_host_parse_endpoint 2.012 pid=1215 bus=0x1 addr=0x5 ep=0x2 dir=b'in' type=b'int' active=0x1
usb_host_parse_interface 1.598 pid=1215 bus=0x1 addr=0x5 num=0xd alt=0x0 active=0x1
usb_host_req_emulated 3.593 pid=1215 bus=0x1 addr=0x5 p=0x5596a4b85938 status=0x0
usb_packet_state_change 2.550 pid=1215 bus=0x1 port=b'1' ep=0x0 p=0x5596a4b85938 o=b'setup' n=b'complete'
usb_xhci_xfer_success 4.298 pid=1215 xfer=0x5596a4b85930 bytes=0x0
[...]
In particular, it is seen that although usb_host_set_interface sets the
alternate setting of interface 0xd to 0x1, usb_host_ep_update uses 0x0
as the alternate setting due to using the incorrect index to
udev->altsetting.
Fix this problem by getting the interface number from the active
libusb_config_descriptor, and then using that as the index to
udev->altsetting.
Signed-off-by: Nick Rosbrook <rosbrookn@ainfosec.com>
Message-Id: <20210201213021.500277-1-rosbrookn@ainfosec.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2021-02-02 00:30:21 +03:00
|
|
|
int pid, ep, alt;
|
2012-11-30 19:02:11 +04:00
|
|
|
int rc, i, e;
|
|
|
|
|
|
|
|
usb_ep_reset(udev);
|
|
|
|
rc = libusb_get_active_config_descriptor(s->dev, &conf);
|
|
|
|
if (rc != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
trace_usb_host_parse_config(s->bus_num, s->addr,
|
|
|
|
conf->bConfigurationValue, true);
|
|
|
|
|
|
|
|
for (i = 0; i < conf->bNumInterfaces; i++) {
|
usb-host: use correct altsetting in usb_host_ep_update
In order to keep track of the alternate setting that should be used for
a given interface, the USBDevice struct keeps an array of alternate
setting values, which is indexed by the interface number. In
usb_host_set_interface, when this array is updated, usb_host_ep_update
is called as a result. However, when usb_host_ep_update accesses the
active libusb_config_descriptor, it indexes udev->altsetting with the
loop variable, rather than the interface number.
With the simple trace backend enable, this behavior can be seen:
[...]
usb_xhci_xfer_start 0.440 pid=1215 xfer=0x5596a4b85930 slotid=0x1 epid=0x1 streamid=0x0
usb_packet_state_change 1.703 pid=1215 bus=0x1 port=b'1' ep=0x0 p=0x5596a4b85938 o=b'undef' n=b'setup'
usb_host_req_control 2.269 pid=1215 bus=0x1 addr=0x5 p=0x5596a4b85938 req=0x10b value=0x1 index=0xd
usb_host_set_interface 0.449 pid=1215 bus=0x1 addr=0x5 interface=0xd alt=0x1
usb_host_parse_config 2542.648 pid=1215 bus=0x1 addr=0x5 value=0x2 active=0x1
usb_host_parse_interface 1.804 pid=1215 bus=0x1 addr=0x5 num=0xc alt=0x0 active=0x1
usb_host_parse_endpoint 2.012 pid=1215 bus=0x1 addr=0x5 ep=0x2 dir=b'in' type=b'int' active=0x1
usb_host_parse_interface 1.598 pid=1215 bus=0x1 addr=0x5 num=0xd alt=0x0 active=0x1
usb_host_req_emulated 3.593 pid=1215 bus=0x1 addr=0x5 p=0x5596a4b85938 status=0x0
usb_packet_state_change 2.550 pid=1215 bus=0x1 port=b'1' ep=0x0 p=0x5596a4b85938 o=b'setup' n=b'complete'
usb_xhci_xfer_success 4.298 pid=1215 xfer=0x5596a4b85930 bytes=0x0
[...]
In particular, it is seen that although usb_host_set_interface sets the
alternate setting of interface 0xd to 0x1, usb_host_ep_update uses 0x0
as the alternate setting due to using the incorrect index to
udev->altsetting.
Fix this problem by getting the interface number from the active
libusb_config_descriptor, and then using that as the index to
udev->altsetting.
Signed-off-by: Nick Rosbrook <rosbrookn@ainfosec.com>
Message-Id: <20210201213021.500277-1-rosbrookn@ainfosec.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2021-02-02 00:30:21 +03:00
|
|
|
/*
|
|
|
|
* The udev->altsetting array indexes alternate settings
|
|
|
|
* by the interface number. Get the 0th alternate setting
|
|
|
|
* first so that we can grab the interface number, and
|
|
|
|
* then correct the alternate setting value if necessary.
|
|
|
|
*/
|
|
|
|
intf = &conf->interface[i].altsetting[0];
|
|
|
|
alt = udev->altsetting[intf->bInterfaceNumber];
|
|
|
|
|
|
|
|
if (alt != 0) {
|
|
|
|
assert(alt < conf->interface[i].num_altsetting);
|
|
|
|
intf = &conf->interface[i].altsetting[alt];
|
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
trace_usb_host_parse_interface(s->bus_num, s->addr,
|
|
|
|
intf->bInterfaceNumber,
|
|
|
|
intf->bAlternateSetting, true);
|
|
|
|
for (e = 0; e < intf->bNumEndpoints; e++) {
|
|
|
|
endp = &intf->endpoint[e];
|
|
|
|
|
|
|
|
devep = endp->bEndpointAddress;
|
|
|
|
pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
|
|
|
ep = devep & 0xf;
|
|
|
|
type = endp->bmAttributes & 0x3;
|
|
|
|
|
|
|
|
if (ep == 0) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"invalid endpoint address");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (usb_ep_get_type(udev, pid, ep) != USB_ENDPOINT_XFER_INVALID) {
|
|
|
|
trace_usb_host_parse_error(s->bus_num, s->addr,
|
|
|
|
"duplicate endpoint address");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_usb_host_parse_endpoint(s->bus_num, s->addr, ep,
|
|
|
|
(devep & USB_DIR_IN) ? "in" : "out",
|
|
|
|
tname[type], true);
|
|
|
|
usb_ep_set_max_packet_size(udev, pid, ep,
|
|
|
|
endp->wMaxPacketSize);
|
|
|
|
usb_ep_set_type(udev, pid, ep, type);
|
|
|
|
usb_ep_set_ifnum(udev, pid, ep, i);
|
|
|
|
usb_ep_set_halted(udev, pid, ep, 0);
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2013-11-21 20:21:00 +04:00
|
|
|
if (type == LIBUSB_TRANSFER_TYPE_BULK &&
|
|
|
|
libusb_get_ss_endpoint_companion_descriptor(ctx, endp,
|
|
|
|
&endp_ss_comp) == LIBUSB_SUCCESS) {
|
|
|
|
usb_ep_set_max_streams(udev, pid, ep,
|
|
|
|
endp_ss_comp->bmAttributes);
|
|
|
|
libusb_free_ss_endpoint_companion_descriptor(endp_ss_comp);
|
|
|
|
}
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
libusb_free_config_descriptor(conf);
|
|
|
|
}
|
|
|
|
|
2020-06-05 15:59:52 +03:00
|
|
|
static int usb_host_open(USBHostDevice *s, libusb_device *dev, int hostfd)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
2020-08-24 14:00:57 +03:00
|
|
|
int libusb_speed;
|
2020-06-05 15:59:52 +03:00
|
|
|
int bus_num = 0;
|
|
|
|
int addr = 0;
|
2012-11-30 19:02:11 +04:00
|
|
|
int rc;
|
2014-09-19 10:48:24 +04:00
|
|
|
Error *local_err = NULL;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
2018-05-03 09:29:32 +03:00
|
|
|
if (s->bh_postld_pending) {
|
|
|
|
return -1;
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
if (s->dh != NULL) {
|
|
|
|
goto fail;
|
|
|
|
}
|
2020-06-05 15:59:52 +03:00
|
|
|
|
|
|
|
if (dev) {
|
|
|
|
bus_num = libusb_get_bus_number(dev);
|
|
|
|
addr = libusb_get_device_address(dev);
|
|
|
|
trace_usb_host_open_started(bus_num, addr);
|
|
|
|
|
|
|
|
rc = libusb_open(dev, &s->dh);
|
|
|
|
if (rc != 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
} else {
|
2020-06-24 16:45:10 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000107 && !defined(CONFIG_WIN32)
|
2020-06-05 15:59:52 +03:00
|
|
|
trace_usb_host_open_hostfd(hostfd);
|
|
|
|
|
|
|
|
rc = libusb_wrap_sys_device(ctx, hostfd, &s->dh);
|
|
|
|
if (rc != 0) {
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
s->hostfd = hostfd;
|
|
|
|
dev = libusb_get_device(s->dh);
|
|
|
|
bus_num = libusb_get_bus_number(dev);
|
|
|
|
addr = libusb_get_device_address(dev);
|
|
|
|
#else
|
|
|
|
g_assert_not_reached();
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
s->dev = dev;
|
|
|
|
s->bus_num = bus_num;
|
|
|
|
s->addr = addr;
|
2013-10-08 23:58:08 +04:00
|
|
|
|
|
|
|
usb_host_detach_kernel(s);
|
|
|
|
|
|
|
|
libusb_get_device_descriptor(dev, &s->ddesc);
|
2012-11-30 19:02:11 +04:00
|
|
|
usb_host_get_port(s->dev, s->port, sizeof(s->port));
|
|
|
|
|
|
|
|
usb_ep_init(udev);
|
|
|
|
usb_host_ep_update(s);
|
|
|
|
|
2020-08-24 14:00:57 +03:00
|
|
|
libusb_speed = libusb_get_device_speed(dev);
|
2020-12-14 00:30:16 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000107 && defined(CONFIG_LINUX) && \
|
|
|
|
defined(USBDEVFS_GET_SPEED)
|
2020-08-24 14:00:57 +03:00
|
|
|
if (hostfd && libusb_speed == 0) {
|
|
|
|
/*
|
|
|
|
* Workaround libusb bug: libusb_get_device_speed() does not
|
|
|
|
* work for libusb_wrap_sys_device() devices in v1.0.23.
|
|
|
|
*
|
|
|
|
* Speeds are defined in linux/usb/ch9.h, file not included
|
|
|
|
* due to name conflicts.
|
|
|
|
*/
|
|
|
|
int rc = ioctl(hostfd, USBDEVFS_GET_SPEED, NULL);
|
|
|
|
switch (rc) {
|
|
|
|
case 1: /* low */
|
|
|
|
libusb_speed = LIBUSB_SPEED_LOW;
|
|
|
|
break;
|
|
|
|
case 2: /* full */
|
|
|
|
libusb_speed = LIBUSB_SPEED_FULL;
|
|
|
|
break;
|
|
|
|
case 3: /* high */
|
|
|
|
case 4: /* wireless */
|
|
|
|
libusb_speed = LIBUSB_SPEED_HIGH;
|
|
|
|
break;
|
|
|
|
case 5: /* super */
|
2021-01-21 18:08:32 +03:00
|
|
|
libusb_speed = LIBUSB_SPEED_SUPER;
|
|
|
|
break;
|
2020-08-24 14:00:57 +03:00
|
|
|
case 6: /* super plus */
|
2021-01-21 18:08:32 +03:00
|
|
|
#ifdef HAVE_SUPER_PLUS
|
|
|
|
libusb_speed = LIBUSB_SPEED_SUPER_PLUS;
|
|
|
|
#else
|
2020-08-24 14:00:57 +03:00
|
|
|
libusb_speed = LIBUSB_SPEED_SUPER;
|
2021-01-21 18:08:32 +03:00
|
|
|
#endif
|
2020-08-24 14:00:57 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
udev->speed = speed_map[libusb_speed];
|
2014-05-23 12:27:00 +04:00
|
|
|
usb_host_speed_compat(s);
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
if (s->ddesc.iProduct) {
|
|
|
|
libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
|
|
|
|
(unsigned char *)udev->product_desc,
|
|
|
|
sizeof(udev->product_desc));
|
|
|
|
} else {
|
|
|
|
snprintf(udev->product_desc, sizeof(udev->product_desc),
|
|
|
|
"host:%d.%d", bus_num, addr);
|
|
|
|
}
|
|
|
|
|
2014-09-19 10:48:24 +04:00
|
|
|
usb_device_attach(udev, &local_err);
|
|
|
|
if (local_err) {
|
2015-02-12 15:55:05 +03:00
|
|
|
error_report_err(local_err);
|
2012-11-30 19:02:11 +04:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_usb_host_open_success(bus_num, addr);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
trace_usb_host_open_failure(bus_num, addr);
|
|
|
|
if (s->dh != NULL) {
|
2015-06-24 08:40:11 +03:00
|
|
|
usb_host_release_interfaces(s);
|
|
|
|
libusb_reset_device(s->dh);
|
|
|
|
usb_host_attach_kernel(s);
|
2012-11-30 19:02:11 +04:00
|
|
|
libusb_close(s->dh);
|
|
|
|
s->dh = NULL;
|
|
|
|
s->dev = NULL;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_abort_xfers(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
USBHostRequest *r, *rtmp;
|
2020-05-29 10:22:24 +03:00
|
|
|
int limit = 100;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) {
|
|
|
|
usb_host_req_abort(r);
|
|
|
|
}
|
2020-02-03 14:41:08 +03:00
|
|
|
|
|
|
|
while (QTAILQ_FIRST(&s->requests) != NULL) {
|
|
|
|
struct timeval tv;
|
|
|
|
memset(&tv, 0, sizeof(tv));
|
|
|
|
tv.tv_usec = 2500;
|
|
|
|
libusb_handle_events_timeout(ctx, &tv);
|
2020-05-29 10:22:24 +03:00
|
|
|
if (--limit == 0) {
|
|
|
|
/*
|
|
|
|
* Don't wait forever for libusb calling the complete
|
|
|
|
* callback (which will unlink and free the request).
|
|
|
|
*
|
|
|
|
* Leaking memory here, to make sure libusb will not
|
|
|
|
* access memory which we have released already.
|
|
|
|
*/
|
|
|
|
QTAILQ_FOREACH_SAFE(r, &s->requests, next, rtmp) {
|
|
|
|
QTAILQ_REMOVE(&s->requests, r, next);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2020-02-03 14:41:08 +03:00
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_close(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
|
|
|
|
|
|
|
if (s->dh == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_usb_host_close(s->bus_num, s->addr);
|
|
|
|
|
|
|
|
usb_host_abort_xfers(s);
|
|
|
|
usb_host_iso_free_all(s);
|
|
|
|
|
|
|
|
if (udev->attached) {
|
|
|
|
usb_device_detach(udev);
|
|
|
|
}
|
|
|
|
|
|
|
|
usb_host_release_interfaces(s);
|
|
|
|
libusb_reset_device(s->dh);
|
|
|
|
usb_host_attach_kernel(s);
|
|
|
|
libusb_close(s->dh);
|
|
|
|
s->dh = NULL;
|
|
|
|
s->dev = NULL;
|
|
|
|
|
2020-06-05 15:59:52 +03:00
|
|
|
if (s->hostfd != -1) {
|
|
|
|
close(s->hostfd);
|
|
|
|
s->hostfd = -1;
|
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
usb_host_auto_check(NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_nodev_bh(void *opaque)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = opaque;
|
|
|
|
usb_host_close(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_nodev(USBHostDevice *s)
|
|
|
|
{
|
2013-04-24 16:29:08 +04:00
|
|
|
if (!s->bh_nodev) {
|
|
|
|
s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
2013-04-24 16:29:08 +04:00
|
|
|
qemu_bh_schedule(s->bh_nodev);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_exit_notifier(struct Notifier *n, void *data)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = container_of(n, USBHostDevice, exit);
|
|
|
|
|
|
|
|
if (s->dh) {
|
2020-02-03 14:41:08 +03:00
|
|
|
usb_host_abort_xfers(s);
|
2012-11-30 19:02:11 +04:00
|
|
|
usb_host_release_interfaces(s);
|
2018-11-30 09:47:00 +03:00
|
|
|
libusb_reset_device(s->dh);
|
2012-11-30 19:02:11 +04:00
|
|
|
usb_host_attach_kernel(s);
|
2018-11-30 09:47:00 +03:00
|
|
|
libusb_close(s->dh);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-03 12:12:55 +03:00
|
|
|
static libusb_device *usb_host_find_ref(int bus, int addr)
|
|
|
|
{
|
|
|
|
libusb_device **devs = NULL;
|
|
|
|
libusb_device *ret = NULL;
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
n = libusb_get_device_list(ctx, &devs);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (libusb_get_bus_number(devs[i]) == bus &&
|
|
|
|
libusb_get_device_address(devs[i]) == addr) {
|
|
|
|
ret = libusb_ref_device(devs[i]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
libusb_free_device_list(devs, 1);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-09-19 10:48:26 +04:00
|
|
|
static void usb_host_realize(USBDevice *udev, Error **errp)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
2016-06-03 12:12:55 +03:00
|
|
|
libusb_device *ldev;
|
|
|
|
int rc;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
2020-06-05 15:59:52 +03:00
|
|
|
if (usb_host_init() != 0) {
|
|
|
|
error_setg(errp, "failed to init libusb");
|
|
|
|
return;
|
|
|
|
}
|
2013-12-12 17:47:15 +04:00
|
|
|
if (s->match.vendor_id > 0xffff) {
|
2014-09-19 10:48:26 +04:00
|
|
|
error_setg(errp, "vendorid out of range");
|
|
|
|
return;
|
2013-12-12 17:47:15 +04:00
|
|
|
}
|
|
|
|
if (s->match.product_id > 0xffff) {
|
2014-09-19 10:48:26 +04:00
|
|
|
error_setg(errp, "productid out of range");
|
|
|
|
return;
|
2013-12-12 17:47:15 +04:00
|
|
|
}
|
|
|
|
if (s->match.addr > 127) {
|
2014-09-19 10:48:26 +04:00
|
|
|
error_setg(errp, "hostaddr out of range");
|
|
|
|
return;
|
2013-12-12 17:47:15 +04:00
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
loglevel = s->loglevel;
|
2013-06-12 15:17:02 +04:00
|
|
|
udev->flags |= (1 << USB_DEV_FLAG_IS_HOST);
|
2012-11-30 19:02:11 +04:00
|
|
|
udev->auto_attach = 0;
|
|
|
|
QTAILQ_INIT(&s->requests);
|
|
|
|
QTAILQ_INIT(&s->isorings);
|
2020-06-05 15:59:52 +03:00
|
|
|
s->hostfd = -1;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
2020-06-24 16:45:10 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000107 && !defined(CONFIG_WIN32)
|
2020-06-05 15:59:52 +03:00
|
|
|
if (s->hostdevice) {
|
|
|
|
int fd;
|
|
|
|
s->needs_autoscan = false;
|
2020-07-21 15:25:21 +03:00
|
|
|
fd = qemu_open_old(s->hostdevice, O_RDWR);
|
2020-06-05 15:59:52 +03:00
|
|
|
if (fd < 0) {
|
|
|
|
error_setg_errno(errp, errno, "failed to open %s", s->hostdevice);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rc = usb_host_open(s, NULL, fd);
|
|
|
|
if (rc < 0) {
|
|
|
|
error_setg(errp, "failed to open host usb device %s", s->hostdevice);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
#endif
|
2016-06-03 12:12:55 +03:00
|
|
|
if (s->match.addr && s->match.bus_num &&
|
|
|
|
!s->match.vendor_id &&
|
|
|
|
!s->match.product_id &&
|
|
|
|
!s->match.port) {
|
|
|
|
s->needs_autoscan = false;
|
|
|
|
ldev = usb_host_find_ref(s->match.bus_num,
|
|
|
|
s->match.addr);
|
|
|
|
if (!ldev) {
|
|
|
|
error_setg(errp, "failed to find host usb device %d:%d",
|
|
|
|
s->match.bus_num, s->match.addr);
|
|
|
|
return;
|
|
|
|
}
|
2020-06-05 15:59:52 +03:00
|
|
|
rc = usb_host_open(s, ldev, 0);
|
2016-06-03 12:12:55 +03:00
|
|
|
libusb_unref_device(ldev);
|
|
|
|
if (rc < 0) {
|
|
|
|
error_setg(errp, "failed to open host usb device %d:%d",
|
|
|
|
s->match.bus_num, s->match.addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s->needs_autoscan = true;
|
|
|
|
QTAILQ_INSERT_TAIL(&hostdevs, s, next);
|
|
|
|
usb_host_auto_check(NULL);
|
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
s->exit.notify = usb_host_exit_notifier;
|
|
|
|
qemu_add_exit_notifier(&s->exit);
|
|
|
|
}
|
|
|
|
|
2014-10-07 12:00:23 +04:00
|
|
|
static void usb_host_instance_init(Object *obj)
|
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(obj);
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
|
|
|
|
device_add_bootindex_property(obj, &s->bootindex,
|
|
|
|
"bootindex", NULL,
|
2020-05-05 18:29:23 +03:00
|
|
|
&udev->qdev);
|
2014-10-07 12:00:23 +04:00
|
|
|
}
|
|
|
|
|
qdev: Unrealize must not fail
Devices may have component devices and buses.
Device realization may fail. Realization is recursive: a device's
realize() method realizes its components, and device_set_realized()
realizes its buses (which should in turn realize the devices on that
bus, except bus_set_realized() doesn't implement that, yet).
When realization of a component or bus fails, we need to roll back:
unrealize everything we realized so far. If any of these unrealizes
failed, the device would be left in an inconsistent state. Must not
happen.
device_set_realized() lets it happen: it ignores errors in the roll
back code starting at label child_realize_fail.
Since realization is recursive, unrealization must be recursive, too.
But how could a partly failed unrealize be rolled back? We'd have to
re-realize, which can fail. This design is fundamentally broken.
device_set_realized() does not roll back at all. Instead, it keeps
unrealizing, ignoring further errors.
It can screw up even for a device with no buses: if the lone
dc->unrealize() fails, it still unregisters vmstate, and calls
listeners' unrealize() callback.
bus_set_realized() does not roll back either. Instead, it stops
unrealizing.
Fortunately, no unrealize method can fail, as we'll see below.
To fix the design error, drop parameter @errp from all the unrealize
methods.
Any unrealize method that uses @errp now needs an update. This leads
us to unrealize() methods that can fail. Merely passing it to another
unrealize method cannot cause failure, though. Here are the ones that
do other things with @errp:
* virtio_serial_device_unrealize()
Fails when qbus_set_hotplug_handler() fails, but still does all the
other work. On failure, the device would stay realized with its
resources completely gone. Oops. Can't happen, because
qbus_set_hotplug_handler() can't actually fail here. Pass
&error_abort to qbus_set_hotplug_handler() instead.
* hw/ppc/spapr_drc.c's unrealize()
Fails when object_property_del() fails, but all the other work is
already done. On failure, the device would stay realized with its
vmstate registration gone. Oops. Can't happen, because
object_property_del() can't actually fail here. Pass &error_abort
to object_property_del() instead.
* spapr_phb_unrealize()
Fails and bails out when remove_drcs() fails, but other work is
already done. On failure, the device would stay realized with some
of its resources gone. Oops. remove_drcs() fails only when
chassis_from_bus()'s object_property_get_uint() fails, and it can't
here. Pass &error_abort to remove_drcs() instead.
Therefore, no unrealize method can fail before this patch.
device_set_realized()'s recursive unrealization via bus uses
object_property_set_bool(). Can't drop @errp there, so pass
&error_abort.
We similarly unrealize with object_property_set_bool() elsewhere,
always ignoring errors. Pass &error_abort instead.
Several unrealize methods no longer handle errors from other unrealize
methods: virtio_9p_device_unrealize(),
virtio_input_device_unrealize(), scsi_qdev_unrealize(), ...
Much of the deleted error handling looks wrong anyway.
One unrealize methods no longer ignore such errors:
usb_ehci_pci_exit().
Several realize methods no longer ignore errors when rolling back:
v9fs_device_realize_common(), pci_qdev_unrealize(),
spapr_phb_realize(), usb_qdev_realize(), vfio_ccw_realize(),
virtio_device_realize().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20200505152926.18877-17-armbru@redhat.com>
2020-05-05 18:29:24 +03:00
|
|
|
static void usb_host_unrealize(USBDevice *udev)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
|
|
|
|
qemu_remove_exit_notifier(&s->exit);
|
2016-06-03 12:12:55 +03:00
|
|
|
if (s->needs_autoscan) {
|
|
|
|
QTAILQ_REMOVE(&hostdevs, s, next);
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
usb_host_close(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
USBHostRequest *r;
|
|
|
|
|
|
|
|
if (p->combined) {
|
|
|
|
usb_combined_packet_cancel(udev, p);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_usb_host_req_canceled(s->bus_num, s->addr, p);
|
|
|
|
|
|
|
|
r = usb_host_req_find(s, p);
|
|
|
|
if (r && r->p) {
|
|
|
|
r->p = NULL; /* mark as dead */
|
|
|
|
libusb_cancel_transfer(r->xfer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_detach_kernel(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
struct libusb_config_descriptor *conf;
|
|
|
|
int rc, i;
|
|
|
|
|
|
|
|
rc = libusb_get_active_config_descriptor(s->dev, &conf);
|
|
|
|
if (rc != 0) {
|
|
|
|
return;
|
|
|
|
}
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
for (i = 0; i < USB_MAX_INTERFACES; i++) {
|
2012-11-30 19:02:11 +04:00
|
|
|
rc = libusb_kernel_driver_active(s->dh, i);
|
|
|
|
usb_host_libusb_error("libusb_kernel_driver_active", rc);
|
|
|
|
if (rc != 1) {
|
2018-11-20 11:34:19 +03:00
|
|
|
if (rc == 0) {
|
|
|
|
s->ifs[i].detached = true;
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
trace_usb_host_detach_kernel(s->bus_num, s->addr, i);
|
|
|
|
rc = libusb_detach_kernel_driver(s->dh, i);
|
|
|
|
usb_host_libusb_error("libusb_detach_kernel_driver", rc);
|
|
|
|
s->ifs[i].detached = true;
|
|
|
|
}
|
|
|
|
libusb_free_config_descriptor(conf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_attach_kernel(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
struct libusb_config_descriptor *conf;
|
|
|
|
int rc, i;
|
|
|
|
|
|
|
|
rc = libusb_get_active_config_descriptor(s->dev, &conf);
|
|
|
|
if (rc != 0) {
|
|
|
|
return;
|
|
|
|
}
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
for (i = 0; i < USB_MAX_INTERFACES; i++) {
|
2012-11-30 19:02:11 +04:00
|
|
|
if (!s->ifs[i].detached) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
trace_usb_host_attach_kernel(s->bus_num, s->addr, i);
|
|
|
|
libusb_attach_kernel_driver(s->dh, i);
|
|
|
|
s->ifs[i].detached = false;
|
|
|
|
}
|
|
|
|
libusb_free_config_descriptor(conf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
|
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
|
|
|
struct libusb_config_descriptor *conf;
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
int rc, i, claimed;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
for (i = 0; i < USB_MAX_INTERFACES; i++) {
|
|
|
|
udev->altsetting[i] = 0;
|
|
|
|
}
|
|
|
|
udev->ninterfaces = 0;
|
|
|
|
udev->configuration = 0;
|
|
|
|
|
|
|
|
usb_host_detach_kernel(s);
|
|
|
|
|
|
|
|
rc = libusb_get_active_config_descriptor(s->dev, &conf);
|
|
|
|
if (rc != 0) {
|
2013-10-08 23:58:07 +04:00
|
|
|
if (rc == LIBUSB_ERROR_NOT_FOUND) {
|
|
|
|
/* address state - ignore */
|
|
|
|
return USB_RET_SUCCESS;
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
return USB_RET_STALL;
|
|
|
|
}
|
|
|
|
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
claimed = 0;
|
|
|
|
for (i = 0; i < USB_MAX_INTERFACES; i++) {
|
2012-11-30 19:02:11 +04:00
|
|
|
trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i);
|
|
|
|
rc = libusb_claim_interface(s->dh, i);
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
if (rc == 0) {
|
|
|
|
s->ifs[i].claimed = true;
|
|
|
|
if (++claimed == conf->bNumInterfaces) {
|
|
|
|
break;
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
}
|
|
|
|
if (claimed != conf->bNumInterfaces) {
|
|
|
|
return USB_RET_STALL;
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
udev->ninterfaces = conf->bNumInterfaces;
|
|
|
|
udev->configuration = configuration;
|
|
|
|
|
|
|
|
libusb_free_config_descriptor(conf);
|
|
|
|
return USB_RET_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_release_interfaces(USBHostDevice *s)
|
|
|
|
{
|
|
|
|
int i, rc;
|
|
|
|
|
usb-host: support devices with sparse/non-sequential USB interfaces
Some USB devices have sparse interface numbering which is not able to be
passthroughed.
For example, the Sierra Wireless MC7455/MC7430:
# lsusb -D /dev/bus/usb/003/003 | egrep '1199|9071|bNumInterfaces|bInterfaceNumber'
Device: ID 1199:9071 Sierra Wireless, Inc.
idVendor 0x1199 Sierra Wireless, Inc.
idProduct 0x9071
bNumInterfaces 5
bInterfaceNumber 0
bInterfaceNumber 2
bInterfaceNumber 3
bInterfaceNumber 8
bInterfaceNumber 10
In this case, the interface numbers are 0, 2, 3, 8, 10 and not the
0, 1, 2, 3, 4 that QEMU tries to claim.
This change allows sparse USB interface numbering.
Instead of only claiming the interfaces in the range reported by the USB
device through bNumInterfaces, QEMU attempts to claim all possible
interfaces.
v2 to fix broken v1 patch formatting.
v3 to fix indentation.
Signed-off-by: Samuel Brian <sam.brian@accelerated.com>
Message-id: 20170613234039.27201-1-sam.brian@accelerated.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2017-06-14 02:40:39 +03:00
|
|
|
for (i = 0; i < USB_MAX_INTERFACES; i++) {
|
2012-11-30 19:02:11 +04:00
|
|
|
if (!s->ifs[i].claimed) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
trace_usb_host_release_interface(s->bus_num, s->addr, i);
|
|
|
|
rc = libusb_release_interface(s->dh, i);
|
|
|
|
usb_host_libusb_error("libusb_release_interface", rc);
|
|
|
|
s->ifs[i].claimed = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_set_address(USBHostDevice *s, int addr)
|
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
|
|
|
|
|
|
|
trace_usb_host_set_address(s->bus_num, s->addr, addr);
|
|
|
|
udev->addr = addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
|
|
|
|
{
|
2019-05-22 12:47:02 +03:00
|
|
|
int rc = 0;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
trace_usb_host_set_config(s->bus_num, s->addr, config);
|
|
|
|
|
|
|
|
usb_host_release_interfaces(s);
|
2019-05-22 12:47:02 +03:00
|
|
|
if (s->ddesc.bNumConfigurations != 1) {
|
|
|
|
rc = libusb_set_configuration(s->dh, config);
|
|
|
|
if (rc != 0) {
|
|
|
|
usb_host_libusb_error("libusb_set_configuration", rc);
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
return;
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
p->status = usb_host_claim_interfaces(s, config);
|
|
|
|
if (p->status != USB_RET_SUCCESS) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
usb_host_ep_update(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
|
|
|
|
USBPacket *p)
|
|
|
|
{
|
|
|
|
USBDevice *udev = USB_DEVICE(s);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
trace_usb_host_set_interface(s->bus_num, s->addr, iface, alt);
|
|
|
|
|
|
|
|
usb_host_iso_free_all(s);
|
|
|
|
|
|
|
|
if (iface >= USB_MAX_INTERFACES) {
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = libusb_set_interface_alt_setting(s->dh, iface, alt);
|
|
|
|
if (rc != 0) {
|
|
|
|
usb_host_libusb_error("libusb_set_interface_alt_setting", rc);
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
udev->altsetting[iface] = alt;
|
|
|
|
usb_host_ep_update(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
|
|
|
|
int request, int value, int index,
|
|
|
|
int length, uint8_t *data)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
USBHostRequest *r;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
|
|
|
|
|
|
|
|
if (s->dh == NULL) {
|
|
|
|
p->status = USB_RET_NODEV;
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (request) {
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
|
|
usb_host_set_address(s, value);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
|
|
usb_host_set_config(s, value & 0xff, p);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
|
|
|
usb_host_set_interface(s, index, value, p);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
|
|
if (value == 0) { /* clear halt */
|
|
|
|
int pid = (index & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
|
|
|
|
libusb_clear_halt(s->dh, index);
|
|
|
|
usb_ep_set_halted(udev, pid, index & 0x0f, 0);
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r = usb_host_req_alloc(s, p, (request >> 8) & USB_DIR_IN, length + 8);
|
|
|
|
r->cbuf = data;
|
|
|
|
r->clen = length;
|
|
|
|
memcpy(r->buffer, udev->setup_buf, 8);
|
|
|
|
if (!r->in) {
|
|
|
|
memcpy(r->buffer + 8, r->cbuf, r->clen);
|
|
|
|
}
|
|
|
|
|
2014-05-23 12:27:00 +04:00
|
|
|
/* Fix up USB-3 ep0 maxpacket size to allow superspeed connected devices
|
|
|
|
* to work redirected to a not superspeed capable hcd */
|
2015-10-23 15:27:10 +03:00
|
|
|
if ((udev->speedmask & USB_SPEED_MASK_SUPER) &&
|
2014-11-19 09:57:43 +03:00
|
|
|
!(udev->port->speedmask & USB_SPEED_MASK_SUPER) &&
|
2014-05-23 12:27:00 +04:00
|
|
|
request == 0x8006 && value == 0x100 && index == 0) {
|
|
|
|
r->usb3ep0quirk = true;
|
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
libusb_fill_control_transfer(r->xfer, s->dh, r->buffer,
|
|
|
|
usb_host_req_complete_ctrl, r,
|
|
|
|
CONTROL_TIMEOUT);
|
|
|
|
rc = libusb_submit_transfer(r->xfer);
|
|
|
|
if (rc != 0) {
|
|
|
|
p->status = USB_RET_NODEV;
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
USBHostRequest *r;
|
|
|
|
size_t size;
|
|
|
|
int ep, rc;
|
|
|
|
|
|
|
|
if (usb_host_use_combining(p->ep) && p->state == USB_PACKET_SETUP) {
|
|
|
|
p->status = USB_RET_ADD_TO_QUEUE;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
trace_usb_host_req_data(s->bus_num, s->addr, p,
|
|
|
|
p->pid == USB_TOKEN_IN,
|
|
|
|
p->ep->nr, p->iov.size);
|
|
|
|
|
|
|
|
if (s->dh == NULL) {
|
|
|
|
p->status = USB_RET_NODEV;
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (p->ep->halted) {
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (usb_ep_get_type(udev, p->pid, p->ep->nr)) {
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
size = usb_packet_size(p);
|
|
|
|
r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, size);
|
|
|
|
if (!r->in) {
|
|
|
|
usb_packet_copy(p, r->buffer, size);
|
|
|
|
}
|
|
|
|
ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
|
2013-11-21 20:21:02 +04:00
|
|
|
if (p->stream) {
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2013-11-21 20:21:02 +04:00
|
|
|
libusb_fill_bulk_stream_transfer(r->xfer, s->dh, ep, p->stream,
|
|
|
|
r->buffer, size,
|
|
|
|
usb_host_req_complete_data, r,
|
|
|
|
BULK_TIMEOUT);
|
|
|
|
#else
|
|
|
|
usb_host_req_free(r);
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
|
|
|
|
r->buffer, size,
|
|
|
|
usb_host_req_complete_data, r,
|
|
|
|
BULK_TIMEOUT);
|
|
|
|
}
|
2012-11-30 19:02:11 +04:00
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
r = usb_host_req_alloc(s, p, p->pid == USB_TOKEN_IN, p->iov.size);
|
|
|
|
if (!r->in) {
|
|
|
|
usb_packet_copy(p, r->buffer, p->iov.size);
|
|
|
|
}
|
|
|
|
ep = p->ep->nr | (r->in ? USB_DIR_IN : 0);
|
|
|
|
libusb_fill_interrupt_transfer(r->xfer, s->dh, ep,
|
|
|
|
r->buffer, p->iov.size,
|
|
|
|
usb_host_req_complete_data, r,
|
|
|
|
INTR_TIMEOUT);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
if (p->pid == USB_TOKEN_IN) {
|
|
|
|
usb_host_iso_data_in(s, p);
|
|
|
|
} else {
|
|
|
|
usb_host_iso_data_out(s, p);
|
|
|
|
}
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
p->status = USB_RET_STALL;
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = libusb_submit_transfer(r->xfer);
|
|
|
|
if (rc != 0) {
|
|
|
|
p->status = USB_RET_NODEV;
|
|
|
|
trace_usb_host_req_complete(s->bus_num, s->addr, p,
|
|
|
|
p->status, p->actual_length);
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE) {
|
|
|
|
usb_host_nodev(s);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
p->status = USB_RET_ASYNC;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_flush_ep_queue(USBDevice *dev, USBEndpoint *ep)
|
|
|
|
{
|
|
|
|
if (usb_host_use_combining(ep)) {
|
|
|
|
usb_ep_combine_input_packets(ep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_handle_reset(USBDevice *udev)
|
|
|
|
{
|
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
2013-10-08 23:58:06 +04:00
|
|
|
int rc;
|
2012-11-30 19:02:11 +04:00
|
|
|
|
2019-10-15 09:44:26 +03:00
|
|
|
if (!s->allow_one_guest_reset && !s->allow_all_guest_resets) {
|
Introduce new "no_guest_reset" parameter for usb-host device
With certain USB devices passed through via usb-host, a guest attempting to
reset a usb-host device can trigger a reset loop that renders the USB device
unusable. In my use case, the device was an iPhone XR that was passed through to
a Mac OS X Mojave guest. Upon connecting the device, the following happens:
1) Guest recognizes new device, sends reset to emulated USB host
2) QEMU's USB host sends reset to host kernel
3) Host kernel resets device
4) After reset, host kernel determines that some part of the device descriptor
has changed ("device firmware changed" in dmesg), so host kernel decides to
re-enumerate the device.
5) Re-enumeration causes QEMU to disconnect and reconnect the device in the
guest.
6) goto 1)
Here's from the host kernel (note the "device firmware changed" lines")
[3677704.473050] usb 1-1.3: new high-speed USB device number 53 using ehci-pci
[3677704.555594] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08
[3677704.555599] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3677704.555602] usb 1-1.3: Product: iPhone
[3677704.555605] usb 1-1.3: Manufacturer: Apple Inc.
[3677704.555607] usb 1-1.3: SerialNumber: [[removed]]
[3677709.401040] usb 1-1.3: reset high-speed USB device number 53 using ehci-pci
[3677709.479486] usb 1-1.3: device firmware changed
[3677709.479842] usb 1-1.3: USB disconnect, device number 53
[3677709.546039] usb 1-1.3: new high-speed USB device number 54 using ehci-pci
[3677709.627471] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08
[3677709.627476] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3677709.627479] usb 1-1.3: Product: iPhone
[3677709.627481] usb 1-1.3: Manufacturer: Apple Inc.
[3677709.627483] usb 1-1.3: SerialNumber: [[removed]]
[3677762.320044] usb 1-1.3: reset high-speed USB device number 54 using ehci-pci
[3677762.615630] usb 1-1.3: USB disconnect, device number 54
[3677762.787043] usb 1-1.3: new high-speed USB device number 55 using ehci-pci
[3677762.869016] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08
[3677762.869024] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3677762.869028] usb 1-1.3: Product: iPhone
[3677762.869032] usb 1-1.3: Manufacturer: Apple Inc.
[3677762.869035] usb 1-1.3: SerialNumber: [[removed]]
[3677815.662036] usb 1-1.3: reset high-speed USB device number 55 using ehci-pci
Here's from QEMU:
libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/022: No such file or directory
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [_open_sysfs_attr] open /sys/bus/usb/devices/5-1/bConfigurationValue failed ret=-1 errno=2
libusb: error [_get_usbfs_fd] File doesn't exist, wait 10 ms and try again
libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/024: No such file or directory
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [_open_sysfs_attr] open /sys/bus/usb/devices/5-1/bConfigurationValue failed ret=-1 errno=2
libusb: error [_get_usbfs_fd] File doesn't exist, wait 10 ms and try again
libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/026: No such file or directory
The result of this is that the device remains permanently unusable in the guest.
The same problem has been previously reported for an iPad:
https://stackoverflow.com/questions/52617634/how-do-i-get-qemu-usb-passthrough-to-work-for-ipad-iphone
This problem can be elegantly solved by interrupting step 2) above. Instead of
passing through the reset, QEMU simply ignores it. To allow this to be
configured on a per-device level, a new parameter "no_guest_reset" is
introduced for the usb-host device. I can confirm that the configuration
described above (iPhone XS + Mojave guest) works flawlessly with
no_guest_reset=True specified.
Working command line for my scenario:
device_add usb-host,vendorid=0x05ac,productid=0x12a8,no_guest_reset=True,id=iphone
Best regards
Alexander
Signed-off-by: Alexander Kappner <agk@godking.net>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20190128140027.9448-1-kraxel@redhat.com
[ kraxel: rename parameter to "guest-reset" ]
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2019-01-28 17:00:27 +03:00
|
|
|
return;
|
|
|
|
}
|
2019-10-15 09:44:26 +03:00
|
|
|
if (!s->allow_all_guest_resets && udev->addr == 0) {
|
2019-05-22 12:47:01 +03:00
|
|
|
return;
|
|
|
|
}
|
Introduce new "no_guest_reset" parameter for usb-host device
With certain USB devices passed through via usb-host, a guest attempting to
reset a usb-host device can trigger a reset loop that renders the USB device
unusable. In my use case, the device was an iPhone XR that was passed through to
a Mac OS X Mojave guest. Upon connecting the device, the following happens:
1) Guest recognizes new device, sends reset to emulated USB host
2) QEMU's USB host sends reset to host kernel
3) Host kernel resets device
4) After reset, host kernel determines that some part of the device descriptor
has changed ("device firmware changed" in dmesg), so host kernel decides to
re-enumerate the device.
5) Re-enumeration causes QEMU to disconnect and reconnect the device in the
guest.
6) goto 1)
Here's from the host kernel (note the "device firmware changed" lines")
[3677704.473050] usb 1-1.3: new high-speed USB device number 53 using ehci-pci
[3677704.555594] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08
[3677704.555599] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3677704.555602] usb 1-1.3: Product: iPhone
[3677704.555605] usb 1-1.3: Manufacturer: Apple Inc.
[3677704.555607] usb 1-1.3: SerialNumber: [[removed]]
[3677709.401040] usb 1-1.3: reset high-speed USB device number 53 using ehci-pci
[3677709.479486] usb 1-1.3: device firmware changed
[3677709.479842] usb 1-1.3: USB disconnect, device number 53
[3677709.546039] usb 1-1.3: new high-speed USB device number 54 using ehci-pci
[3677709.627471] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08
[3677709.627476] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3677709.627479] usb 1-1.3: Product: iPhone
[3677709.627481] usb 1-1.3: Manufacturer: Apple Inc.
[3677709.627483] usb 1-1.3: SerialNumber: [[removed]]
[3677762.320044] usb 1-1.3: reset high-speed USB device number 54 using ehci-pci
[3677762.615630] usb 1-1.3: USB disconnect, device number 54
[3677762.787043] usb 1-1.3: new high-speed USB device number 55 using ehci-pci
[3677762.869016] usb 1-1.3: New USB device found, idVendor=05ac, idProduct=12a8, bcdDevice=11.08
[3677762.869024] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[3677762.869028] usb 1-1.3: Product: iPhone
[3677762.869032] usb 1-1.3: Manufacturer: Apple Inc.
[3677762.869035] usb 1-1.3: SerialNumber: [[removed]]
[3677815.662036] usb 1-1.3: reset high-speed USB device number 55 using ehci-pci
Here's from QEMU:
libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/022: No such file or directory
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [_open_sysfs_attr] open /sys/bus/usb/devices/5-1/bConfigurationValue failed ret=-1 errno=2
libusb: error [_get_usbfs_fd] File doesn't exist, wait 10 ms and try again
libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/024: No such file or directory
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [udev_hotplug_event] ignoring udev action bind
libusb: error [_open_sysfs_attr] open /sys/bus/usb/devices/5-1/bConfigurationValue failed ret=-1 errno=2
libusb: error [_get_usbfs_fd] File doesn't exist, wait 10 ms and try again
libusb: error [_get_usbfs_fd] libusb couldn't open USB device /dev/bus/usb/005/026: No such file or directory
The result of this is that the device remains permanently unusable in the guest.
The same problem has been previously reported for an iPad:
https://stackoverflow.com/questions/52617634/how-do-i-get-qemu-usb-passthrough-to-work-for-ipad-iphone
This problem can be elegantly solved by interrupting step 2) above. Instead of
passing through the reset, QEMU simply ignores it. To allow this to be
configured on a per-device level, a new parameter "no_guest_reset" is
introduced for the usb-host device. I can confirm that the configuration
described above (iPhone XS + Mojave guest) works flawlessly with
no_guest_reset=True specified.
Working command line for my scenario:
device_add usb-host,vendorid=0x05ac,productid=0x12a8,no_guest_reset=True,id=iphone
Best regards
Alexander
Signed-off-by: Alexander Kappner <agk@godking.net>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Message-id: 20190128140027.9448-1-kraxel@redhat.com
[ kraxel: rename parameter to "guest-reset" ]
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2019-01-28 17:00:27 +03:00
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
trace_usb_host_reset(s->bus_num, s->addr);
|
|
|
|
|
2013-10-08 23:58:06 +04:00
|
|
|
rc = libusb_reset_device(s->dh);
|
|
|
|
if (rc != 0) {
|
|
|
|
usb_host_nodev(s);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-21 20:21:01 +04:00
|
|
|
static int usb_host_alloc_streams(USBDevice *udev, USBEndpoint **eps,
|
|
|
|
int nr_eps, int streams)
|
|
|
|
{
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2013-11-21 20:21:01 +04:00
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
unsigned char endpoints[30];
|
|
|
|
int i, rc;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_eps; i++) {
|
|
|
|
endpoints[i] = eps[i]->nr;
|
|
|
|
if (eps[i]->pid == USB_TOKEN_IN) {
|
|
|
|
endpoints[i] |= 0x80;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rc = libusb_alloc_streams(s->dh, streams, endpoints, nr_eps);
|
|
|
|
if (rc < 0) {
|
|
|
|
usb_host_libusb_error("libusb_alloc_streams", rc);
|
|
|
|
} else if (rc != streams) {
|
2014-09-19 10:48:27 +04:00
|
|
|
error_report("libusb_alloc_streams: got less streams "
|
|
|
|
"then requested %d < %d", rc, streams);
|
2013-11-21 20:21:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (rc == streams) ? 0 : -1;
|
|
|
|
#else
|
2014-09-19 10:48:27 +04:00
|
|
|
error_report("libusb_alloc_streams: error not implemented");
|
2013-11-21 20:21:01 +04:00
|
|
|
return -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_free_streams(USBDevice *udev, USBEndpoint **eps,
|
|
|
|
int nr_eps)
|
|
|
|
{
|
2014-05-23 13:26:52 +04:00
|
|
|
#ifdef HAVE_STREAMS
|
2013-11-21 20:21:01 +04:00
|
|
|
USBHostDevice *s = USB_HOST_DEVICE(udev);
|
|
|
|
unsigned char endpoints[30];
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < nr_eps; i++) {
|
|
|
|
endpoints[i] = eps[i]->nr;
|
|
|
|
if (eps[i]->pid == USB_TOKEN_IN) {
|
|
|
|
endpoints[i] |= 0x80;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
libusb_free_streams(s->dh, endpoints, nr_eps);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-04-24 16:29:08 +04:00
|
|
|
/*
|
|
|
|
* This is *NOT* about restoring state. We have absolutely no idea
|
|
|
|
* what state the host device is in at the moment and whenever it is
|
2021-07-30 04:27:20 +03:00
|
|
|
* still present in the first place. Attempting to continue where we
|
2013-04-24 16:29:08 +04:00
|
|
|
* left off is impossible.
|
|
|
|
*
|
2015-08-26 14:17:13 +03:00
|
|
|
* What we are going to do here is emulate a surprise removal of
|
2013-04-24 16:29:08 +04:00
|
|
|
* the usb device passed through, then kick host scan so the device
|
|
|
|
* will get re-attached (and re-initialized by the guest) in case it
|
|
|
|
* is still present.
|
|
|
|
*
|
|
|
|
* As the device removal will change the state of other devices (usb
|
|
|
|
* host controller, most likely interrupt controller too) we have to
|
|
|
|
* wait with it until *all* vmstate is loaded. Thus post_load just
|
|
|
|
* kicks a bottom half which then does the actual work.
|
|
|
|
*/
|
|
|
|
static void usb_host_post_load_bh(void *opaque)
|
|
|
|
{
|
|
|
|
USBHostDevice *dev = opaque;
|
|
|
|
USBDevice *udev = USB_DEVICE(dev);
|
|
|
|
|
|
|
|
if (dev->dh != NULL) {
|
|
|
|
usb_host_close(dev);
|
|
|
|
}
|
|
|
|
if (udev->attached) {
|
|
|
|
usb_device_detach(udev);
|
|
|
|
}
|
2018-05-03 09:29:32 +03:00
|
|
|
dev->bh_postld_pending = false;
|
2013-04-24 16:29:08 +04:00
|
|
|
usb_host_auto_check(NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_host_post_load(void *opaque, int version_id)
|
|
|
|
{
|
|
|
|
USBHostDevice *dev = opaque;
|
|
|
|
|
|
|
|
if (!dev->bh_postld) {
|
|
|
|
dev->bh_postld = qemu_bh_new(usb_host_post_load_bh, dev);
|
|
|
|
}
|
|
|
|
qemu_bh_schedule(dev->bh_postld);
|
2018-05-03 09:29:32 +03:00
|
|
|
dev->bh_postld_pending = true;
|
2013-04-24 16:29:08 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-11-30 19:02:11 +04:00
|
|
|
static const VMStateDescription vmstate_usb_host = {
|
|
|
|
.name = "usb-host",
|
2013-04-24 16:29:08 +04:00
|
|
|
.version_id = 1,
|
|
|
|
.minimum_version_id = 1,
|
|
|
|
.post_load = usb_host_post_load,
|
2012-11-30 19:02:11 +04:00
|
|
|
.fields = (VMStateField[]) {
|
|
|
|
VMSTATE_USB_DEVICE(parent_obj, USBHostDevice),
|
|
|
|
VMSTATE_END_OF_LIST()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static Property usb_host_dev_properties[] = {
|
|
|
|
DEFINE_PROP_UINT32("hostbus", USBHostDevice, match.bus_num, 0),
|
|
|
|
DEFINE_PROP_UINT32("hostaddr", USBHostDevice, match.addr, 0),
|
|
|
|
DEFINE_PROP_STRING("hostport", USBHostDevice, match.port),
|
2014-02-08 14:01:53 +04:00
|
|
|
DEFINE_PROP_UINT32("vendorid", USBHostDevice, match.vendor_id, 0),
|
|
|
|
DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0),
|
2020-06-05 15:59:52 +03:00
|
|
|
#if LIBUSB_API_VERSION >= 0x01000107
|
|
|
|
DEFINE_PROP_STRING("hostdevice", USBHostDevice, hostdevice),
|
|
|
|
#endif
|
2012-11-30 19:02:11 +04:00
|
|
|
DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4),
|
|
|
|
DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32),
|
2019-10-15 09:44:26 +03:00
|
|
|
DEFINE_PROP_BOOL("guest-reset", USBHostDevice,
|
|
|
|
allow_one_guest_reset, true),
|
|
|
|
DEFINE_PROP_BOOL("guest-resets-all", USBHostDevice,
|
|
|
|
allow_all_guest_resets, false),
|
2012-11-30 19:02:11 +04:00
|
|
|
DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel,
|
|
|
|
LIBUSB_LOG_LEVEL_WARNING),
|
|
|
|
DEFINE_PROP_BIT("pipeline", USBHostDevice, options,
|
|
|
|
USB_HOST_OPT_PIPELINE, true),
|
2020-01-08 12:10:43 +03:00
|
|
|
DEFINE_PROP_BOOL("suppress-remote-wake", USBHostDevice,
|
|
|
|
suppress_remote_wake, true),
|
2012-11-30 19:02:11 +04:00
|
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
|
|
};
|
|
|
|
|
|
|
|
static void usb_host_class_initfn(ObjectClass *klass, void *data)
|
|
|
|
{
|
|
|
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
|
|
|
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
|
|
|
|
2014-09-19 10:48:26 +04:00
|
|
|
uc->realize = usb_host_realize;
|
2012-11-30 19:02:11 +04:00
|
|
|
uc->product_desc = "USB Host Device";
|
|
|
|
uc->cancel_packet = usb_host_cancel_packet;
|
|
|
|
uc->handle_data = usb_host_handle_data;
|
|
|
|
uc->handle_control = usb_host_handle_control;
|
|
|
|
uc->handle_reset = usb_host_handle_reset;
|
2017-02-21 17:14:45 +03:00
|
|
|
uc->unrealize = usb_host_unrealize;
|
2012-11-30 19:02:11 +04:00
|
|
|
uc->flush_ep_queue = usb_host_flush_ep_queue;
|
2013-11-21 20:21:01 +04:00
|
|
|
uc->alloc_streams = usb_host_alloc_streams;
|
|
|
|
uc->free_streams = usb_host_free_streams;
|
2012-11-30 19:02:11 +04:00
|
|
|
dc->vmsd = &vmstate_usb_host;
|
2020-01-10 18:30:32 +03:00
|
|
|
device_class_set_props(dc, usb_host_dev_properties);
|
2013-07-29 18:17:45 +04:00
|
|
|
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
2022-01-17 17:58:04 +03:00
|
|
|
static const TypeInfo usb_host_dev_info = {
|
2012-11-30 19:02:11 +04:00
|
|
|
.name = TYPE_USB_HOST_DEVICE,
|
|
|
|
.parent = TYPE_USB_DEVICE,
|
|
|
|
.instance_size = sizeof(USBHostDevice),
|
|
|
|
.class_init = usb_host_class_initfn,
|
2014-10-07 12:00:23 +04:00
|
|
|
.instance_init = usb_host_instance_init,
|
2012-11-30 19:02:11 +04:00
|
|
|
};
|
2021-06-24 13:38:35 +03:00
|
|
|
module_obj(TYPE_USB_HOST_DEVICE);
|
2022-05-28 01:20:23 +03:00
|
|
|
module_kconfig(USB);
|
2012-11-30 19:02:11 +04:00
|
|
|
|
|
|
|
static void usb_host_register_types(void)
|
|
|
|
{
|
|
|
|
type_register_static(&usb_host_dev_info);
|
2021-06-24 13:38:34 +03:00
|
|
|
monitor_register_hmp("usbhost", true, hmp_info_usbhost);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
type_init(usb_host_register_types)
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
static QEMUTimer *usb_auto_timer;
|
|
|
|
static VMChangeStateEntry *usb_vmstate;
|
|
|
|
|
2021-01-11 18:20:20 +03:00
|
|
|
static void usb_host_vm_state(void *unused, bool running, RunState state)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
|
|
|
if (running) {
|
|
|
|
usb_host_auto_check(unused);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void usb_host_auto_check(void *unused)
|
|
|
|
{
|
|
|
|
struct USBHostDevice *s;
|
|
|
|
struct USBAutoFilter *f;
|
2014-06-20 10:12:52 +04:00
|
|
|
libusb_device **devs = NULL;
|
2012-11-30 19:02:11 +04:00
|
|
|
struct libusb_device_descriptor ddesc;
|
|
|
|
int unconnected = 0;
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
if (usb_host_init() != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (runstate_is_running()) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
QTAILQ_FOREACH(s, &hostdevs, next) {
|
|
|
|
f = &s->match;
|
|
|
|
if (f->bus_num > 0 &&
|
|
|
|
f->bus_num != libusb_get_bus_number(devs[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (f->addr > 0 &&
|
|
|
|
f->addr != libusb_get_device_address(devs[i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (f->port != NULL) {
|
|
|
|
char port[16] = "-";
|
|
|
|
usb_host_get_port(devs[i], port, sizeof(port));
|
|
|
|
if (strcmp(f->port, port) != 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (f->vendor_id > 0 &&
|
|
|
|
f->vendor_id != ddesc.idVendor) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (f->product_id > 0 &&
|
|
|
|
f->product_id != ddesc.idProduct) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We got a match */
|
|
|
|
s->seen++;
|
|
|
|
if (s->errcount >= 3) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (s->dh != NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-05 15:59:52 +03:00
|
|
|
if (usb_host_open(s, devs[i], 0) < 0) {
|
2012-11-30 19:02:11 +04:00
|
|
|
s->errcount++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
libusb_free_device_list(devs, 1);
|
|
|
|
|
|
|
|
QTAILQ_FOREACH(s, &hostdevs, next) {
|
|
|
|
if (s->dh == NULL) {
|
|
|
|
unconnected++;
|
|
|
|
}
|
|
|
|
if (s->seen == 0) {
|
|
|
|
if (s->dh) {
|
|
|
|
usb_host_close(s);
|
|
|
|
}
|
|
|
|
s->errcount = 0;
|
|
|
|
}
|
|
|
|
s->seen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (unconnected == 0) {
|
|
|
|
/* nothing to watch */
|
|
|
|
if (usb_auto_timer) {
|
2013-08-21 19:03:08 +04:00
|
|
|
timer_del(usb_auto_timer);
|
2012-11-30 19:02:11 +04:00
|
|
|
trace_usb_host_auto_scan_disabled();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!usb_vmstate) {
|
|
|
|
usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL);
|
|
|
|
}
|
|
|
|
if (!usb_auto_timer) {
|
2013-08-21 19:03:08 +04:00
|
|
|
usb_auto_timer = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL);
|
2012-11-30 19:02:11 +04:00
|
|
|
if (!usb_auto_timer) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
trace_usb_host_auto_scan_enabled();
|
|
|
|
}
|
2013-08-21 19:03:08 +04:00
|
|
|
timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
|
2012-11-30 19:02:11 +04:00
|
|
|
}
|
|
|
|
|
2015-02-06 16:18:24 +03:00
|
|
|
void hmp_info_usbhost(Monitor *mon, const QDict *qdict)
|
2012-11-30 19:02:11 +04:00
|
|
|
{
|
2014-06-20 10:12:52 +04:00
|
|
|
libusb_device **devs = NULL;
|
2012-11-30 19:02:11 +04:00
|
|
|
struct libusb_device_descriptor ddesc;
|
|
|
|
char port[16];
|
|
|
|
int i, n;
|
|
|
|
|
|
|
|
if (usb_host_init() != 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
monitor_printf(mon, " Bus %d, Addr %d, Port %s, Speed %s Mb/s\n",
|
|
|
|
libusb_get_bus_number(devs[i]),
|
|
|
|
libusb_get_device_address(devs[i]),
|
|
|
|
port,
|
|
|
|
speed_name[libusb_get_device_speed(devs[i])]);
|
|
|
|
monitor_printf(mon, " Class %02x:", ddesc.bDeviceClass);
|
|
|
|
monitor_printf(mon, " USB device %04x:%04x",
|
|
|
|
ddesc.idVendor, ddesc.idProduct);
|
|
|
|
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);
|
|
|
|
monitor_printf(mon, ", %s", name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
monitor_printf(mon, "\n");
|
|
|
|
}
|
|
|
|
libusb_free_device_list(devs, 1);
|
|
|
|
}
|