@@ -601,10 +601,10 @@ void usb_packet_copy(USBPacket *p, void *ptr, size_t bytes)
switch (p->pid) {
case USB_TOKEN_SETUP:
case USB_TOKEN_OUT:
- iov_to_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
+ iov_to_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
break;
case USB_TOKEN_IN:
- iov_from_buf(iov->iov, iov->niov, p->actual_length, ptr, bytes);
+ iov_from_buf(p->iov.iov, p->iov.niov, p->actual_length, ptr, bytes);
break;
default:
fprintf(stderr, "%s: invalid pid: %x\n", __func__, p->pid);
@@ -620,7 +620,7 @@ void usb_packet_skip(USBPacket *p, size_t bytes)
assert(p->actual_length >= 0);
assert(p->actual_length + bytes <= iov->size);
if (p->pid == USB_TOKEN_IN) {
- iov_memset(iov->iov, iov->niov, p->actual_length, 0, bytes);
+ iov_memset(p->iov.iov, p->iov.niov, p->actual_length, 0, bytes);
}
p->actual_length += bytes;
}
@@ -1224,7 +1224,7 @@ static int usb_uhci_common_initfn(PCIDevice *dev)
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
}
}
- s->bh = qemu_bh_new(uhci_bh, s);
+ s->bh = qemu_bh_new_replay(uhci_bh, s, 0);
s->frame_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, uhci_frame_timer, s);
s->num_ports_vmstate = NB_PORTS;
QTAILQ_INIT(&s->queues);
@@ -40,98 +40,15 @@
#include "monitor/monitor.h"
#include "sysemu/sysemu.h"
#include "trace.h"
+#include "qemu/log.h"
+#include "replay/replay.h"
#include "hw/usb.h"
+#include "hw/host-libusb.h"
/* ------------------------------------------------------------------------ */
-#define TYPE_USB_HOST_DEVICE "usb-host"
-#define USB_HOST_DEVICE(obj) \
- OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE)
-
-typedef struct USBHostDevice USBHostDevice;
-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;
- int32_t bootindex;
- uint32_t iso_urb_count;
- uint32_t iso_urb_frames;
- uint32_t options;
- uint32_t loglevel;
-
- /* state */
- QTAILQ_ENTRY(USBHostDevice) next;
- int seen, errcount;
- int bus_num;
- int addr;
- char port[16];
-
- libusb_device *dev;
- libusb_device_handle *dh;
- struct libusb_device_descriptor ddesc;
-
- struct {
- bool detached;
- bool claimed;
- } ifs[USB_MAX_INTERFACES];
-
- /* callbacks & friends */
- QEMUBH *bh_nodev;
- QEMUBH *bh_postld;
- 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;
bool usb3ep0quirk;
- 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);
@@ -143,6 +60,24 @@ static void usb_host_attach_kernel(USBHostDevice *s);
/* ------------------------------------------------------------------------ */
+typedef struct USBHostTimer {
+ QEMUTimer *timer;
+} USBHostTimer;
+
+USBHostTimer usb_auto_timer;
+static int submitted_xfers = 0;
+
+static const VMStateDescription vmstate_usb_host_timer = {
+ .name = "usb-host-timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_TIMER(timer, USBHostTimer),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+
#define CONTROL_TIMEOUT 10000 /* 10 sec */
#define BULK_TIMEOUT 0 /* unlimited */
#define INTR_TIMEOUT 0 /* unlimited */
@@ -214,6 +149,11 @@ static void usb_host_del_fd(int fd, void *user_data)
qemu_set_fd_handler(fd, NULL, NULL, NULL);
}
+bool usb_host_has_xfers(void)
+{
+ return submitted_xfers != 0;
+}
+
static int usb_host_init(void)
{
const struct libusb_pollfd **poll;
@@ -226,18 +166,34 @@ static int usb_host_init(void)
if (rc != 0) {
return -1;
}
- libusb_set_debug(ctx, loglevel);
-
- libusb_set_pollfd_notifiers(ctx, usb_host_add_fd,
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_set_debug(ctx, loglevel);
+ libusb_set_pollfd_notifiers(ctx, usb_host_add_fd,
usb_host_del_fd,
ctx);
- poll = libusb_get_pollfds(ctx);
- if (poll) {
- for (i = 0; poll[i] != NULL; i++) {
- usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx);
+
+ poll = libusb_get_pollfds(ctx);
+ if (poll) {
+ for (i = 0; poll[i] != NULL; i++) {
+ usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx);
+ }
}
+ free(poll);
}
- free(poll);
+
+ /* replay: changed timer for working with VM clock instead of RT */
+ if (!usb_auto_timer.timer) {
+ usb_auto_timer.timer = qemu_new_timer_ms(vm_clock, usb_host_auto_check, NULL);
+ if (!usb_auto_timer.timer) {
+ return -1;
+ }
+ trace_usb_host_auto_scan_enabled();
+
+ if (replay_mode != REPLAY_NONE) {
+ vmstate_register(NULL, 0, &vmstate_usb_host_timer, &usb_auto_timer);
+ }
+ }
+
return 0;
}
@@ -252,6 +208,7 @@ static int usb_host_get_port(libusb_device *dev, char *port, size_t len)
#else
rc = libusb_get_port_path(ctx, dev, path, 7);
#endif
+
if (rc < 0) {
return 0;
}
@@ -307,6 +264,7 @@ static USBHostRequest *usb_host_req_alloc(USBHostDevice *s, USBPacket *p,
r->host = s;
r->p = p;
r->in = in;
+ // allocate xfer's in REPLAY_PLAY too
r->xfer = libusb_alloc_transfer(0);
if (bufsize) {
r->buffer = g_malloc(bufsize);
@@ -320,6 +278,7 @@ static void usb_host_req_free(USBHostRequest *r)
if (r->host) {
QTAILQ_REMOVE(&r->host->requests, r, next);
}
+ // free xfer's in REPLAY_PLAY too
libusb_free_transfer(r->xfer);
g_free(r->buffer);
g_free(r);
@@ -337,12 +296,13 @@ static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p)
return NULL;
}
-static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
+void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
{
USBHostRequest *r = xfer->user_data;
USBHostDevice *s = r->host;
bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
+ --submitted_xfers;
if (r->p == NULL) {
goto out; /* request was canceled */
}
@@ -370,12 +330,13 @@ out:
}
}
-static void usb_host_req_complete_data(struct libusb_transfer *xfer)
+void usb_host_req_complete_data(struct libusb_transfer *xfer)
{
USBHostRequest *r = xfer->user_data;
USBHostDevice *s = r->host;
bool disconnect = (xfer->status == LIBUSB_TRANSFER_NO_DEVICE);
+ --submitted_xfers;
if (r->p == NULL) {
goto out; /* request was canceled */
}
@@ -419,17 +380,17 @@ static void usb_host_req_abort(USBHostRequest *r)
QTAILQ_REMOVE(&r->host->requests, r, next);
r->host = NULL;
- if (inflight) {
+ if (inflight && replay_mode != REPLAY_PLAY) {
libusb_cancel_transfer(r->xfer);
}
}
/* ------------------------------------------------------------------------ */
-static void usb_host_req_complete_iso(struct libusb_transfer *transfer)
+void usb_host_req_complete_iso(struct libusb_transfer *transfer)
{
USBHostIsoXfer *xfer = transfer->user_data;
-
+ --submitted_xfers;
if (!xfer) {
/* USBHostIsoXfer released while inflight */
g_free(transfer->buffer);
@@ -467,6 +428,7 @@ static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep)
for (i = 0; i < s->iso_urb_count; i++) {
xfer = g_new0(USBHostIsoXfer, 1);
xfer->ring = ring;
+ // OK for replay too
xfer->xfer = libusb_alloc_transfer(packets);
xfer->xfer->dev_handle = s->dh;
xfer->xfer->type = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS;
@@ -475,7 +437,7 @@ static USBHostIsoRing *usb_host_iso_alloc(USBHostDevice *s, USBEndpoint *ep)
if (ring->ep->pid == USB_TOKEN_IN) {
xfer->xfer->endpoint |= USB_DIR_IN;
}
- xfer->xfer->callback = usb_host_req_complete_iso;
+ xfer->xfer->callback = replay_mode == REPLAY_NONE ? usb_host_req_complete_iso :
replay_req_complete_iso;
xfer->xfer->user_data = xfer;
xfer->xfer->num_iso_packets = packets;
@@ -514,6 +476,7 @@ static void usb_host_iso_free_xfer(USBHostIsoXfer *xfer, bool inflight)
xfer->xfer->user_data = NULL;
} else {
g_free(xfer->xfer->buffer);
+ // REPLAY_PLAY also allocates xfer
libusb_free_transfer(xfer->xfer);
}
g_free(xfer);
@@ -549,12 +512,19 @@ static void usb_host_iso_free_all(USBHostDevice *s)
}
}
+
+unsigned char *usb_host_get_iso_packet_buffer(USBHostIsoXfer *xfer, int packet)
+{
+ // replay OK
+ return libusb_get_iso_packet_buffer_simple(xfer->xfer, packet);
+}
+
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);
+ buf = usb_host_get_iso_packet_buffer(xfer, xfer->packet);
if (p->pid == USB_TOKEN_OUT) {
psize = p->iov.size;
if (psize > xfer->ring->ep->max_packet_size) {
@@ -600,7 +570,7 @@ static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p)
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);
+ REPLAY_DATA_INT(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);
@@ -608,6 +578,9 @@ static void usb_host_iso_data_in(USBHostDevice *s, USBPacket *p)
disconnect = true;
}
break;
+ } else {
+ replay_req_register_iso(xfer->xfer);
+ ++submitted_xfers;
}
if (QTAILQ_EMPTY(&ring->inflight)) {
trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
@@ -662,7 +635,7 @@ static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
while ((xfer = QTAILQ_FIRST(&ring->copy)) != NULL &&
xfer->copy_complete) {
QTAILQ_REMOVE(&ring->copy, xfer, next);
- rc = libusb_submit_transfer(xfer->xfer);
+ REPLAY_DATA_INT(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);
@@ -670,6 +643,9 @@ static void usb_host_iso_data_out(USBHostDevice *s, USBPacket *p)
disconnect = true;
}
break;
+ } else {
+ replay_req_register_iso(xfer->xfer);
+ ++submitted_xfers;
}
if (QTAILQ_EMPTY(&ring->inflight)) {
trace_usb_host_iso_start(s->bus_num, s->addr, p->ep->nr);
@@ -697,6 +673,11 @@ static void usb_host_speed_compat(USBHostDevice *s)
bool compat_full = true;
uint8_t type;
int rc, c, i, a, e;
+ bool ok = false;
+ if (replay_mode == REPLAY_PLAY) {
+ REPLAY_DATA_VAR(ok);
+ return ok;
+ }
for (c = 0;; c++) {
rc = libusb_get_config_descriptor(s->dev, c, &conf);
@@ -762,9 +743,9 @@ static void usb_host_ep_update(USBHostDevice *s)
[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;
+ struct libusb_config_descriptor dummy_conf;
+ struct libusb_interface_descriptor dummy_intf;
+ struct libusb_endpoint_descriptor dummy_endp;
#ifdef HAVE_STREAMS
struct libusb_ss_endpoint_companion_descriptor *endp_ss_comp;
#endif
@@ -773,21 +754,36 @@ static void usb_host_ep_update(USBHostDevice *s)
int rc, i, e;
usb_ep_reset(udev);
- rc = libusb_get_active_config_descriptor(s->dev, &conf);
+ REPLAY_DATA_INT(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);
+ REPLAY_DATA_VAR(conf->bNumInterfaces);
for (i = 0; i < conf->bNumInterfaces; i++) {
- assert(udev->altsetting[i] < conf->interface[i].num_altsetting);
- intf = &conf->interface[i].altsetting[udev->altsetting[i]];
+ if (replay_mode != REPLAY_PLAY) {
+ assert(udev->altsetting[i] < conf->interface[i].num_altsetting);
+ intf = (struct libusb_interface_descriptor
*)&conf->interface[i].altsetting[udev->altsetting[i]];
+ } else {
+ intf = &dummy_intf;
+ }
+ REPLAY_DATA_VAR(intf->bInterfaceNumber);
+ REPLAY_DATA_VAR(intf->bAlternateSetting);
+ REPLAY_DATA_VAR(intf->bNumEndpoints);
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];
+ if (replay_mode != REPLAY_PLAY) {
+ endp = (struct libusb_endpoint_descriptor *)&intf->endpoint[e];
+ } else {
+ endp = &dummy_endp;
+ }
+ REPLAY_DATA_VAR(endp->bEndpointAddress);
+ REPLAY_DATA_VAR(endp->bmAttributes);
+ REPLAY_DATA_VAR(endp->wMaxPacketSize);
devep = endp->bEndpointAddress;
pid = (devep & USB_DIR_IN) ? USB_TOKEN_IN : USB_TOKEN_OUT;
@@ -825,45 +821,76 @@ static void usb_host_ep_update(USBHostDevice *s)
}
}
- libusb_free_config_descriptor(conf);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_free_config_descriptor(conf);
+ }
}
static int usb_host_open(USBHostDevice *s, libusb_device *dev)
{
USBDevice *udev = USB_DEVICE(s);
- int bus_num = libusb_get_bus_number(dev);
- int addr = libusb_get_device_address(dev);
+ int bus_num;
+ REPLAY_DATA_INT(bus_num, libusb_get_bus_number(dev));
+ int addr;
+ REPLAY_DATA_INT(addr, libusb_get_device_address(dev));
int rc;
+ int speed;
trace_usb_host_open_started(bus_num, addr);
- if (s->dh != NULL) {
+ if (s->is_open) {
goto fail;
}
- rc = libusb_open(dev, &s->dh);
+ REPLAY_DATA_INT(rc, libusb_open(dev, &s->dh));
if (rc != 0) {
goto fail;
}
+ if (replay_mode == REPLAY_PLAY) {
+ // invalid non-NULL pointer for checking conditions
+ s->dh = (void*)0xbad;
+ }
+ s->is_open = true;
+ usb_host_detach_kernel(s);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_get_device_descriptor(dev, &s->ddesc);
+ }
+ // TODO: all fields are read and written as int
+ REPLAY_DATA_VAR(s->ddesc.bLength);
+ REPLAY_DATA_VAR(s->ddesc.bDescriptorType);
+ REPLAY_DATA_VAR(s->ddesc.bcdUSB);
+ REPLAY_DATA_VAR(s->ddesc.bDeviceClass);
+ REPLAY_DATA_VAR(s->ddesc.bDeviceSubClass);
+ REPLAY_DATA_VAR(s->ddesc.bDeviceProtocol);
+ REPLAY_DATA_VAR(s->ddesc.bMaxPacketSize0);
+ REPLAY_DATA_VAR(s->ddesc.idVendor);
+ REPLAY_DATA_VAR(s->ddesc.idProduct);
+ REPLAY_DATA_VAR(s->ddesc.bcdDevice);
+ REPLAY_DATA_VAR(s->ddesc.iManufacturer);
+ REPLAY_DATA_VAR(s->ddesc.iProduct);
+ REPLAY_DATA_VAR(s->ddesc.iSerialNumber);
+ REPLAY_DATA_VAR(s->ddesc.bNumConfigurations);
s->dev = dev;
s->bus_num = bus_num;
s->addr = addr;
-
- usb_host_detach_kernel(s);
-
- libusb_get_device_descriptor(dev, &s->ddesc);
- usb_host_get_port(s->dev, s->port, sizeof(s->port));
+ if (replay_mode != REPLAY_PLAY) {
+ usb_host_get_port(s->dev, s->port, sizeof(s->port));
+ }
+ replay_data_buffer((unsigned char *)s->port, sizeof(s->port));
usb_ep_init(udev);
usb_host_ep_update(s);
- udev->speed = speed_map[libusb_get_device_speed(dev)];
+ udev->speed = speed_map[REPLAY_DATA_INT(speed, libusb_get_device_speed(dev))];
usb_host_speed_compat(s);
if (s->ddesc.iProduct) {
- libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_get_string_descriptor_ascii(s->dh, s->ddesc.iProduct,
(unsigned char *)udev->product_desc,
sizeof(udev->product_desc));
+ }
+ replay_data_buffer((unsigned char*)udev->product_desc, sizeof(udev->product_desc));
} else {
snprintf(udev->product_desc, sizeof(udev->product_desc),
"host:%d.%d", bus_num, addr);
@@ -879,10 +906,13 @@ static int usb_host_open(USBHostDevice *s, libusb_device *dev)
fail:
trace_usb_host_open_failure(bus_num, addr);
- if (s->dh != NULL) {
- libusb_close(s->dh);
+ if (s->is_open) {
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_close(s->dh);
+ }
s->dh = NULL;
s->dev = NULL;
+ s->is_open = false;
}
return -1;
}
@@ -896,11 +926,20 @@ static void usb_host_abort_xfers(USBHostDevice *s)
}
}
+static void usb_host_free_xfers(USBHostDevice *s)
+{
+ USBHostRequest *r;
+
+ while ((r = QTAILQ_FIRST(&s->requests)) != NULL) {
+ usb_host_req_free(r);
+ }
+}
+
static int usb_host_close(USBHostDevice *s)
{
USBDevice *udev = USB_DEVICE(s);
- if (s->dh == NULL) {
+ if (!s->is_open) {
return -1;
}
@@ -914,11 +953,20 @@ static int usb_host_close(USBHostDevice *s)
}
usb_host_release_interfaces(s);
- libusb_reset_device(s->dh);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_reset_device(s->dh);
+ }
+
usb_host_attach_kernel(s);
- libusb_close(s->dh);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_close(s->dh);
+ }
+
+ //replay_unregister_usb_device(udev);
+
s->dh = NULL;
s->dev = NULL;
+ s->is_open = false;
usb_host_auto_check(NULL);
return 0;
@@ -933,7 +981,7 @@ static void usb_host_nodev_bh(void *opaque)
static void usb_host_nodev(USBHostDevice *s)
{
if (!s->bh_nodev) {
- s->bh_nodev = qemu_bh_new(usb_host_nodev_bh, s);
+ s->bh_nodev = qemu_bh_new_replay(usb_host_nodev_bh, s, s->bus_num);
}
qemu_bh_schedule(s->bh_nodev);
}
@@ -942,7 +990,7 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data)
{
USBHostDevice *s = container_of(n, USBHostDevice, exit);
- if (s->dh) {
+ if (s->is_open) {
usb_host_release_interfaces(s);
usb_host_attach_kernel(s);
}
@@ -1004,57 +1052,70 @@ static void usb_host_cancel_packet(USBDevice *udev, USBPacket *p)
r = usb_host_req_find(s, p);
if (r && r->p) {
r->p = NULL; /* mark as dead */
- libusb_cancel_transfer(r->xfer);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_cancel_transfer(r->xfer);
+ }
}
}
static void usb_host_detach_kernel(USBHostDevice *s)
{
- struct libusb_config_descriptor *conf;
+ struct libusb_config_descriptor dummy_conf;
+ struct libusb_config_descriptor *conf = &dummy_conf;
int rc, i;
- rc = libusb_get_active_config_descriptor(s->dev, &conf);
+ REPLAY_DATA_INT(rc, libusb_get_active_config_descriptor(s->dev, &conf));
if (rc != 0) {
return;
}
+ REPLAY_DATA_VAR(conf->bNumInterfaces);
for (i = 0; i < conf->bNumInterfaces; i++) {
- rc = libusb_kernel_driver_active(s->dh, i);
+ REPLAY_DATA_INT(rc, libusb_kernel_driver_active(s->dh, i));
usb_host_libusb_error("libusb_kernel_driver_active", rc);
if (rc != 1) {
continue;
}
trace_usb_host_detach_kernel(s->bus_num, s->addr, i);
- rc = libusb_detach_kernel_driver(s->dh, i);
+ REPLAY_DATA_INT(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);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_free_config_descriptor(conf);
+ }
}
static void usb_host_attach_kernel(USBHostDevice *s)
{
- struct libusb_config_descriptor *conf;
+ struct libusb_config_descriptor dummy_conf;
+ struct libusb_config_descriptor *conf = &dummy_conf;
int rc, i;
- rc = libusb_get_active_config_descriptor(s->dev, &conf);
+ REPLAY_DATA_INT(rc, libusb_get_active_config_descriptor(s->dev, &conf));
if (rc != 0) {
return;
}
- for (i = 0; i < conf->bNumInterfaces; i++) {
+ REPLAY_DATA_VAR(conf->bNumInterfaces);
+ for (i = 0; i < conf->bNumInterfaces ; i++) {
if (!s->ifs[i].detached) {
continue;
}
trace_usb_host_attach_kernel(s->bus_num, s->addr, i);
- libusb_attach_kernel_driver(s->dh, i);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_attach_kernel_driver(s->dh, i);
+ }
s->ifs[i].detached = false;
}
- libusb_free_config_descriptor(conf);
+ if (replay_mode != REPLAY_PLAY) {
+ 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;
+ struct libusb_config_descriptor dummy_conf;
+ struct libusb_config_descriptor *conf = &dummy_conf;
int rc, i;
for (i = 0; i < USB_MAX_INTERFACES; i++) {
@@ -1065,7 +1126,7 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
usb_host_detach_kernel(s);
- rc = libusb_get_active_config_descriptor(s->dev, &conf);
+ REPLAY_DATA_INT(rc, libusb_get_active_config_descriptor(s->dev, &conf));
if (rc != 0) {
if (rc == LIBUSB_ERROR_NOT_FOUND) {
/* address state - ignore */
@@ -1073,10 +1134,11 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
}
return USB_RET_STALL;
}
+ REPLAY_DATA_VAR(conf->bNumInterfaces);
for (i = 0; i < conf->bNumInterfaces; i++) {
trace_usb_host_claim_interface(s->bus_num, s->addr, configuration, i);
- rc = libusb_claim_interface(s->dh, i);
+ REPLAY_DATA_INT(rc, libusb_claim_interface(s->dh, i));
usb_host_libusb_error("libusb_claim_interface", rc);
if (rc != 0) {
return USB_RET_STALL;
@@ -1087,7 +1149,9 @@ static int usb_host_claim_interfaces(USBHostDevice *s, int configuration)
udev->ninterfaces = conf->bNumInterfaces;
udev->configuration = configuration;
- libusb_free_config_descriptor(conf);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_free_config_descriptor(conf);
+ }
return USB_RET_SUCCESS;
}
@@ -1101,7 +1165,7 @@ static void usb_host_release_interfaces(USBHostDevice *s)
continue;
}
trace_usb_host_release_interface(s->bus_num, s->addr, i);
- rc = libusb_release_interface(s->dh, i);
+ REPLAY_DATA_INT(rc, libusb_release_interface(s->dh, i));
usb_host_libusb_error("libusb_release_interface", rc);
s->ifs[i].claimed = false;
}
@@ -1122,7 +1186,7 @@ static void usb_host_set_config(USBHostDevice *s, int config, USBPacket *p)
trace_usb_host_set_config(s->bus_num, s->addr, config);
usb_host_release_interfaces(s);
- rc = libusb_set_configuration(s->dh, config);
+ REPLAY_DATA_INT(rc, libusb_set_configuration(s->dh, config));
if (rc != 0) {
usb_host_libusb_error("libusb_set_configuration", rc);
p->status = USB_RET_STALL;
@@ -1153,7 +1217,7 @@ static void usb_host_set_interface(USBHostDevice *s, int iface, int alt,
return;
}
- rc = libusb_set_interface_alt_setting(s->dh, iface, alt);
+ REPLAY_DATA_INT(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;
@@ -1177,7 +1241,7 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
trace_usb_host_req_control(s->bus_num, s->addr, p, request, value, index);
- if (s->dh == NULL) {
+ if (!s->is_open) {
p->status = USB_RET_NODEV;
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
return;
@@ -1202,7 +1266,9 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
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);
+ if (replay_mode != REPLAY_PLAY) {
+ 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;
@@ -1225,10 +1291,12 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
r->usb3ep0quirk = true;
}
+ // call in REPLAY_PLAY too - this function just copies data
libusb_fill_control_transfer(r->xfer, s->dh, r->buffer,
- usb_host_req_complete_ctrl, r,
- CONTROL_TIMEOUT);
- rc = libusb_submit_transfer(r->xfer);
+ replay_mode == REPLAY_NONE ? usb_host_req_complete_ctrl :
replay_req_complete_ctrl,
+ r, CONTROL_TIMEOUT);
+
+ REPLAY_DATA_INT(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,
@@ -1237,6 +1305,9 @@ static void usb_host_handle_control(USBDevice *udev, USBPacket *p,
usb_host_nodev(s);
}
return;
+ } else {
+ replay_req_register_ctrl(r->xfer);
+ ++submitted_xfers;
}
p->status = USB_RET_ASYNC;
@@ -1258,7 +1329,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
p->pid == USB_TOKEN_IN,
p->ep->nr, p->iov.size);
- if (s->dh == NULL) {
+ if (!s->is_open) {
p->status = USB_RET_NODEV;
trace_usb_host_req_emulated(s->bus_num, s->addr, p, p->status);
return;
@@ -1291,7 +1362,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
} else {
libusb_fill_bulk_transfer(r->xfer, s->dh, ep,
r->buffer, size,
- usb_host_req_complete_data, r,
+ replay_mode == REPLAY_NONE ? usb_host_req_complete_data :
replay_req_complete_data, r,
BULK_TIMEOUT);
}
break;
@@ -1303,7 +1374,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
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,
+ replay_mode == REPLAY_NONE ? usb_host_req_complete_data :
replay_req_complete_data, r,
INTR_TIMEOUT);
break;
case USB_ENDPOINT_XFER_ISOC:
@@ -1322,7 +1393,7 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
return;
}
- rc = libusb_submit_transfer(r->xfer);
+ REPLAY_DATA_INT(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,
@@ -1331,6 +1402,9 @@ static void usb_host_handle_data(USBDevice *udev, USBPacket *p)
usb_host_nodev(s);
}
return;
+ } else {
+ replay_req_register_data(r->xfer);
+ ++submitted_xfers;
}
p->status = USB_RET_ASYNC;
@@ -1350,7 +1424,7 @@ static void usb_host_handle_reset(USBDevice *udev)
trace_usb_host_reset(s->bus_num, s->addr);
- rc = libusb_reset_device(s->dh);
+ REPLAY_DATA_INT(rc, libusb_reset_device(s->dh));
if (rc != 0) {
usb_host_nodev(s);
}
@@ -1425,7 +1499,7 @@ static void usb_host_post_load_bh(void *opaque)
USBHostDevice *dev = opaque;
USBDevice *udev = USB_DEVICE(dev);
- if (dev->dh != NULL) {
+ if (dev->is_open) {
usb_host_close(dev);
}
if (udev->attached) {
@@ -1445,6 +1519,37 @@ static int usb_host_post_load(void *opaque, int version_id)
return 0;
}
+static int usb_host_post_load_replay(void *opaque, int version_id)
+{
+ USBHostDevice *dev = opaque;
+
+ usb_host_free_xfers(dev);
+ usb_host_iso_free_all(dev);
+ submitted_xfers = 0;
+ return 0;
+}
+
+static void usb_host_pre_save_replay(void *opaque)
+{
+ USBHostDevice *dev = opaque;
+
+ if (!QTAILQ_EMPTY(&dev->requests)) {
+ fprintf(stderr, "Replay: Saving VM state with non-empty USB requests queue\n");
+ exit(1);
+ }
+
+ if (!QTAILQ_EMPTY(&dev->isorings)) {
+ fprintf(stderr, "Replay: Saving VM state with non-empty USB iso rings queue\n");
+ exit(1);
+ }
+
+ // paranoidal checking
+ if (submitted_xfers != 0) {
+ fprintf(stderr, "Replay: Saving VM state with non-empty USB iso rings queue\n");
+ exit(1);
+ }
+}
+
static const VMStateDescription vmstate_usb_host = {
.name = "usb-host",
.version_id = 1,
@@ -1456,6 +1561,51 @@ static const VMStateDescription vmstate_usb_host = {
}
};
+static const VMStateDescription vmstate_usb_host_interface = {
+ .name = "usb-host-interface",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_BOOL(detached, USBHostInterface),
+ VMSTATE_BOOL(claimed, USBHostInterface),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static const VMStateDescription vmstate_usb_host_replay = {
+ .name = "usb-host",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = usb_host_post_load_replay,
+ .pre_save = usb_host_pre_save_replay,
+ .fields = (VMStateField[]) {
+ VMSTATE_USB_DEVICE(parent_obj, USBHostDevice),
+ VMSTATE_INT32(seen, USBHostDevice),
+ VMSTATE_INT32(errcount, USBHostDevice),
+ VMSTATE_INT32(bus_num, USBHostDevice),
+ VMSTATE_INT32(addr, USBHostDevice),
+ VMSTATE_INT32(bus_num, USBHostDevice),
+ VMSTATE_BOOL(is_open, USBHostDevice),
+ VMSTATE_CHAR_ARRAY(port, USBHostDevice, 16),
+ VMSTATE_UINT8(ddesc.bLength, USBHostDevice),
+ VMSTATE_UINT8(ddesc.bDescriptorType, USBHostDevice),
+ VMSTATE_UINT16(ddesc.bcdUSB, USBHostDevice),
+ VMSTATE_UINT8(ddesc.bDeviceClass, USBHostDevice),
+ VMSTATE_UINT8(ddesc.bDeviceSubClass, USBHostDevice),
+ VMSTATE_UINT8(ddesc.bDeviceProtocol, USBHostDevice),
+ VMSTATE_UINT8(ddesc.bMaxPacketSize0, USBHostDevice),
+ VMSTATE_UINT16(ddesc.idVendor, USBHostDevice),
+ VMSTATE_UINT16(ddesc.idProduct, USBHostDevice),
+ VMSTATE_UINT16(ddesc.bcdDevice, USBHostDevice),
+ VMSTATE_UINT8(ddesc.iManufacturer, USBHostDevice),
+ VMSTATE_UINT8(ddesc.iProduct, USBHostDevice),
+ VMSTATE_UINT8(ddesc.iSerialNumber, USBHostDevice),
+ VMSTATE_UINT8(ddesc.bNumConfigurations, USBHostDevice),
+ VMSTATE_STRUCT_ARRAY(ifs, USBHostDevice, USB_MAX_INTERFACES, 1, vmstate_usb_host_interface,
USBHostInterface),
+ 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),
@@ -1487,7 +1637,10 @@ static void usb_host_class_initfn(ObjectClass *klass, void *data)
uc->flush_ep_queue = usb_host_flush_ep_queue;
uc->alloc_streams = usb_host_alloc_streams;
uc->free_streams = usb_host_free_streams;
- dc->vmsd = &vmstate_usb_host;
+ if (replay_mode == REPLAY_NONE)
+ dc->vmsd = &vmstate_usb_host;
+ else
+ dc->vmsd = &vmstate_usb_host_replay;
dc->props = usb_host_dev_properties;
set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories);
}
@@ -1508,7 +1661,6 @@ type_init(usb_host_register_types)
/* ------------------------------------------------------------------------ */
-static QEMUTimer *usb_auto_timer;
static VMChangeStateEntry *usb_vmstate;
static void usb_host_vm_state(void *unused, int running, RunState state)
@@ -1525,70 +1677,74 @@ static void usb_host_auto_check(void *unused)
libusb_device **devs;
struct libusb_device_descriptor ddesc;
int unconnected = 0;
- int i, n;
+ int i, n, tmp;
if (usb_host_init() != 0) {
return;
}
if (runstate_is_running()) {
- n = libusb_get_device_list(ctx, &devs);
+ REPLAY_DATA_INT(n, libusb_get_device_list(ctx, &devs));
for (i = 0; i < n; i++) {
- if (libusb_get_device_descriptor(devs[i], &ddesc) != 0) {
+ if (REPLAY_DATA_INT(tmp, libusb_get_device_descriptor(devs[i], &ddesc)) != 0) {
continue;
}
- if (ddesc.bDeviceClass == LIBUSB_CLASS_HUB) {
+ if (REPLAY_DATA_INT(tmp, 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])) {
+ f->bus_num != REPLAY_DATA_INT(tmp, libusb_get_bus_number(devs[i]))) {
continue;
}
if (f->addr > 0 &&
- f->addr != libusb_get_device_address(devs[i])) {
+ f->addr != REPLAY_DATA_INT(tmp, libusb_get_device_address(devs[i]))) {
continue;
}
if (f->port != NULL) {
char port[16] = "-";
- usb_host_get_port(devs[i], port, sizeof(port));
+ if (replay_mode != REPLAY_PLAY) {
+ usb_host_get_port(devs[i], port, sizeof(port));
+ }
+ replay_data_buffer((unsigned char *)port, sizeof(port));
if (strcmp(f->port, port) != 0) {
continue;
}
}
if (f->vendor_id > 0 &&
- f->vendor_id != ddesc.idVendor) {
+ f->vendor_id != REPLAY_DATA_INT(tmp, ddesc.idVendor)) {
continue;
}
if (f->product_id > 0 &&
- f->product_id != ddesc.idProduct) {
+ f->product_id != REPLAY_DATA_INT(tmp, ddesc.idProduct)) {
continue;
}
-
/* We got a match */
s->seen++;
if (s->errcount >= 3) {
continue;
}
- if (s->dh != NULL) {
+ if (s->is_open) {
continue;
}
- if (usb_host_open(s, devs[i]) < 0) {
+ if (usb_host_open(s, replay_mode == REPLAY_PLAY ? (libusb_device*)0xbad : devs[i])
< 0) {
s->errcount++;
continue;
}
break;
}
}
- libusb_free_device_list(devs, 1);
+ if (replay_mode != REPLAY_PLAY) {
+ libusb_free_device_list(devs, 1);
+ }
QTAILQ_FOREACH(s, &hostdevs, next) {
- if (s->dh == NULL) {
+ if (!s->is_open) {
unconnected++;
}
if (s->seen == 0) {
- if (s->dh) {
+ if (s->is_open) {
usb_host_close(s);
}
s->errcount = 0;
@@ -1608,17 +1764,19 @@ static void usb_host_auto_check(void *unused)
#endif
}
- if (!usb_vmstate) {
+ if (!usb_vmstate && replay_mode == REPLAY_NONE) {
usb_vmstate = qemu_add_vm_change_state_handler(usb_host_vm_state, NULL);
}
- if (!usb_auto_timer) {
- usb_auto_timer = timer_new_ms(QEMU_CLOCK_REALTIME, usb_host_auto_check, NULL);
+
+
+ /*if (!usb_auto_timer) {
+ usb_auto_timer.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, usb_host_auto_check, NULL);
if (!usb_auto_timer) {
return;
}
trace_usb_host_auto_scan_enabled();
- }
- timer_mod(usb_auto_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 2000);
+ }*/
+ timer_mod(usb_auto_timer.timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 2000);
}
void usb_host_info(Monitor *mon, const QDict *qdict)
new file mode 100644
@@ -0,0 +1,103 @@
+#ifndef HOST_LIBUSB_H
+#define HOST_LIBUSB_H
+
+#include <libusb.h>
+
+#include "qemu/notify.h"
+
+#define TYPE_USB_HOST_DEVICE "usb-host"
+#define USB_HOST_DEVICE(obj) \
+ OBJECT_CHECK(USBHostDevice, (obj), TYPE_USB_HOST_DEVICE)
+
+struct USBAutoFilter {
+ uint32_t bus_num;
+ uint32_t addr;
+ char *port;
+ uint32_t vendor_id;
+ uint32_t product_id;
+};
+
+typedef struct USBHostDevice USBHostDevice;
+typedef struct USBHostRequest USBHostRequest;
+typedef struct USBHostIsoXfer USBHostIsoXfer;
+typedef struct USBHostIsoRing USBHostIsoRing;
+typedef struct USBHostInterface USBHostInterface;
+
+enum USBHostDeviceOptions {
+ USB_HOST_OPT_PIPELINE,
+};
+
+struct USBHostInterface {
+ bool detached;
+ bool claimed;
+};
+
+struct USBHostDevice {
+ USBDevice parent_obj;
+
+ /* properties */
+ struct USBAutoFilter match;
+ int32_t bootindex;
+ uint32_t iso_urb_count;
+ uint32_t iso_urb_frames;
+ uint32_t options;
+ uint32_t loglevel;
+
+ /* state */
+ QTAILQ_ENTRY(USBHostDevice) next;
+ int seen, errcount;
+ int bus_num;
+ int addr;
+ char port[16];
+ bool is_open;
+
+ libusb_device *dev;
+ libusb_device_handle *dh;
+ struct libusb_device_descriptor ddesc;
+
+ USBHostInterface ifs[USB_MAX_INTERFACES];
+
+ /* callbacks & friends */
+ QEMUBH *bh_nodev;
+ QEMUBH *bh_postld;
+ 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;
+ 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;
+};
+
+void usb_host_req_complete_ctrl(struct libusb_transfer *xfer);
+void usb_host_req_complete_data(struct libusb_transfer *xfer);
+void usb_host_req_complete_iso(struct libusb_transfer *xfer);
+unsigned char *usb_host_get_iso_packet_buffer(USBHostIsoXfer *xfer, int packet);
+
+#endif
@@ -471,6 +471,9 @@ int set_usb_string(uint8_t *buf, const char *str);
USBDevice *usb_host_device_open(USBBus *bus, const char *devname);
void usb_host_info(Monitor *mon, const QDict *qdict);
+/* host-libusb.c */
+bool usb_host_has_xfers(void);
+
/* usb ports of the VM */
#define VM_USB_HUB_SIZE 8
USB passthrough replay. It writes all external data, returned by libusb, to the log. This data is read in replay mode instead of calling libusb functions. Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com> ---