summaryrefslogtreecommitdiff
path: root/hw/usb
diff options
context:
space:
mode:
authorhyokeun <hyokeun.jeon@samsung.com>2016-09-06 14:09:22 +0900
committerhyokeun <hyokeun.jeon@samsung.com>2016-09-06 14:09:22 +0900
commitbd54c25035217800f3b1d39f6472d599cd602d5a (patch)
tree299417fe96f546225439ff92b27ac3e55909a970 /hw/usb
parent186efde2677c31fb40d154a81a5f3731eab52414 (diff)
downloadqemu-bd54c25035217800f3b1d39f6472d599cd602d5a.tar.gz
qemu-bd54c25035217800f3b1d39f6472d599cd602d5a.tar.bz2
qemu-bd54c25035217800f3b1d39f6472d599cd602d5a.zip
Imported Upstream version 2.7.0upstream/2.7.0
Diffstat (limited to 'hw/usb')
-rw-r--r--hw/usb/Makefile.objs4
-rw-r--r--hw/usb/bus.c58
-rw-r--r--hw/usb/desc.c1
-rw-r--r--hw/usb/dev-mtp.c4
-rw-r--r--hw/usb/dev-network.c67
-rw-r--r--hw/usb/dev-storage.c52
-rw-r--r--hw/usb/dev-uas.c5
-rw-r--r--hw/usb/hcd-ehci.c31
-rw-r--r--hw/usb/hcd-ehci.h5
-rw-r--r--hw/usb/hcd-ohci.c8
-rw-r--r--hw/usb/hcd-xhci.c47
-rw-r--r--hw/usb/host-libusb.c79
-rw-r--r--hw/usb/redirect.c12
-rw-r--r--hw/usb/trace-events268
-rw-r--r--hw/usb/xen-usb.c1107
15 files changed, 1623 insertions, 125 deletions
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 2717027d3..98b5c9d27 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -38,3 +38,7 @@ common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o
# usb pass-through
common-obj-y += $(patsubst %,host-%.o,$(HOST_USB))
+
+ifeq ($(CONFIG_USB_LIBUSB),y)
+common-obj-$(CONFIG_XEN_BACKEND) += xen-usb.o
+endif
diff --git a/hw/usb/bus.c b/hw/usb/bus.c
index 16c3461d9..25913ad48 100644
--- a/hw/usb/bus.c
+++ b/hw/usb/bus.c
@@ -55,9 +55,9 @@ static int usb_device_post_load(void *opaque, int version_id)
USBDevice *dev = opaque;
if (dev->state == USB_STATE_NOTATTACHED) {
- dev->attached = 0;
+ dev->attached = false;
} else {
- dev->attached = 1;
+ dev->attached = true;
}
if (dev->setup_index < 0 ||
dev->setup_len < 0 ||
@@ -279,6 +279,13 @@ static void usb_qdev_realize(DeviceState *qdev, Error **errp)
static void usb_qdev_unrealize(DeviceState *qdev, Error **errp)
{
USBDevice *dev = USB_DEVICE(qdev);
+ USBDescString *s, *next;
+
+ QLIST_FOREACH_SAFE(s, &dev->strings, next, next) {
+ QLIST_REMOVE(s, next);
+ g_free(s->str);
+ g_free(s);
+ }
if (dev->attached) {
usb_device_detach(dev);
@@ -533,7 +540,7 @@ void usb_device_attach(USBDevice *dev, Error **errp)
return;
}
- dev->attached++;
+ dev->attached = true;
usb_attach(port);
}
@@ -547,7 +554,7 @@ int usb_device_detach(USBDevice *dev)
trace_usb_port_detach(bus->busnr, port->path);
usb_detach(port);
- dev->attached--;
+ dev->attached = false;
return 0;
}
@@ -736,6 +743,48 @@ USBDevice *usbdevice_create(const char *cmdline)
return dev;
}
+static bool usb_get_attached(Object *obj, Error **errp)
+{
+ USBDevice *dev = USB_DEVICE(obj);
+
+ return dev->attached;
+}
+
+static void usb_set_attached(Object *obj, bool value, Error **errp)
+{
+ USBDevice *dev = USB_DEVICE(obj);
+ Error *err = NULL;
+
+ if (dev->attached == value) {
+ return;
+ }
+
+ if (value) {
+ usb_device_attach(dev, &err);
+ if (err) {
+ error_propagate(errp, err);
+ }
+ } else {
+ usb_device_detach(dev);
+ }
+}
+
+static void usb_device_instance_init(Object *obj)
+{
+ USBDevice *dev = USB_DEVICE(obj);
+ USBDeviceClass *klass = USB_DEVICE_GET_CLASS(dev);
+
+ if (klass->attached_settable) {
+ object_property_add_bool(obj, "attached",
+ usb_get_attached, usb_set_attached,
+ NULL);
+ } else {
+ object_property_add_bool(obj, "attached",
+ usb_get_attached, NULL,
+ NULL);
+ }
+}
+
static void usb_device_class_init(ObjectClass *klass, void *data)
{
DeviceClass *k = DEVICE_CLASS(klass);
@@ -749,6 +798,7 @@ static const TypeInfo usb_device_type_info = {
.name = TYPE_USB_DEVICE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(USBDevice),
+ .instance_init = usb_device_instance_init,
.abstract = true,
.class_size = sizeof(USBDeviceClass),
.class_init = usb_device_class_init,
diff --git a/hw/usb/desc.c b/hw/usb/desc.c
index adb026e43..5e0e1d157 100644
--- a/hw/usb/desc.c
+++ b/hw/usb/desc.c
@@ -574,6 +574,7 @@ void usb_desc_create_serial(USBDevice *dev)
}
dst += snprintf(serial+dst, sizeof(serial)-dst, "-%s", dev->port->path);
usb_desc_set_string(dev, index, serial);
+ g_free(path);
}
const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c
index bda84a64b..1be85ae75 100644
--- a/hw/usb/dev-mtp.c
+++ b/hw/usb/dev-mtp.c
@@ -788,8 +788,8 @@ static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c)
trace_usb_mtp_op_get_device_info(s->dev.addr);
usb_mtp_add_u16(d, 100);
- usb_mtp_add_u32(d, 0xffffffff);
- usb_mtp_add_u16(d, 0x0101);
+ usb_mtp_add_u32(d, 0x00000006);
+ usb_mtp_add_u16(d, 0x0064);
usb_mtp_add_wstr(d, L"");
usb_mtp_add_u16(d, 0x0000);
diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c
index 74306b58e..c0f1193ba 100644
--- a/hw/usb/dev-network.c
+++ b/hw/usb/dev-network.c
@@ -670,48 +670,49 @@ static int ndis_query(USBNetState *s, uint32_t oid,
/* general oids (table 4-1) */
/* mandatory */
case OID_GEN_SUPPORTED_LIST:
- for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++)
- ((le32 *) outbuf)[i] = cpu_to_le32(oid_supported_list[i]);
+ for (i = 0; i < ARRAY_SIZE(oid_supported_list); i++) {
+ stl_le_p(outbuf + (i * sizeof(le32)), oid_supported_list[i]);
+ }
return sizeof(oid_supported_list);
/* mandatory */
case OID_GEN_HARDWARE_STATUS:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_GEN_MEDIA_SUPPORTED:
- *((le32 *) outbuf) = cpu_to_le32(s->medium);
+ stl_le_p(outbuf, s->medium);
return sizeof(le32);
/* mandatory */
case OID_GEN_MEDIA_IN_USE:
- *((le32 *) outbuf) = cpu_to_le32(s->medium);
+ stl_le_p(outbuf, s->medium);
return sizeof(le32);
/* mandatory */
case OID_GEN_MAXIMUM_FRAME_SIZE:
- *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+ stl_le_p(outbuf, ETH_FRAME_LEN);
return sizeof(le32);
/* mandatory */
case OID_GEN_LINK_SPEED:
- *((le32 *) outbuf) = cpu_to_le32(s->speed);
+ stl_le_p(outbuf, s->speed);
return sizeof(le32);
/* mandatory */
case OID_GEN_TRANSMIT_BLOCK_SIZE:
- *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+ stl_le_p(outbuf, ETH_FRAME_LEN);
return sizeof(le32);
/* mandatory */
case OID_GEN_RECEIVE_BLOCK_SIZE:
- *((le32 *) outbuf) = cpu_to_le32(ETH_FRAME_LEN);
+ stl_le_p(outbuf, ETH_FRAME_LEN);
return sizeof(le32);
/* mandatory */
case OID_GEN_VENDOR_ID:
- *((le32 *) outbuf) = cpu_to_le32(s->vendorid);
+ stl_le_p(outbuf, s->vendorid);
return sizeof(le32);
/* mandatory */
@@ -720,58 +721,57 @@ static int ndis_query(USBNetState *s, uint32_t oid,
return strlen((char *)outbuf) + 1;
case OID_GEN_VENDOR_DRIVER_VERSION:
- *((le32 *) outbuf) = cpu_to_le32(1);
+ stl_le_p(outbuf, 1);
return sizeof(le32);
/* mandatory */
case OID_GEN_CURRENT_PACKET_FILTER:
- *((le32 *) outbuf) = cpu_to_le32(s->filter);
+ stl_le_p(outbuf, s->filter);
return sizeof(le32);
/* mandatory */
case OID_GEN_MAXIMUM_TOTAL_SIZE:
- *((le32 *) outbuf) = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+ stl_le_p(outbuf, RNDIS_MAX_TOTAL_SIZE);
return sizeof(le32);
/* mandatory */
case OID_GEN_MEDIA_CONNECT_STATUS:
- *((le32 *) outbuf) = cpu_to_le32(s->media_state);
+ stl_le_p(outbuf, s->media_state);
return sizeof(le32);
case OID_GEN_PHYSICAL_MEDIUM:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
case OID_GEN_MAC_OPTIONS:
- *((le32 *) outbuf) = cpu_to_le32(
- NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
- NDIS_MAC_OPTION_FULL_DUPLEX);
+ stl_le_p(outbuf, NDIS_MAC_OPTION_RECEIVE_SERIALIZED |
+ NDIS_MAC_OPTION_FULL_DUPLEX);
return sizeof(le32);
/* statistics OIDs (table 4-2) */
/* mandatory */
case OID_GEN_XMIT_OK:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_GEN_RCV_OK:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_GEN_XMIT_ERROR:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_GEN_RCV_ERROR:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_GEN_RCV_NO_BUFFER:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* ieee802.3 OIDs (table 4-3) */
@@ -787,12 +787,12 @@ static int ndis_query(USBNetState *s, uint32_t oid,
/* mandatory */
case OID_802_3_MULTICAST_LIST:
- *((le32 *) outbuf) = cpu_to_le32(0xe0000000);
+ stl_le_p(outbuf, 0xe0000000);
return sizeof(le32);
/* mandatory */
case OID_802_3_MAXIMUM_LIST_SIZE:
- *((le32 *) outbuf) = cpu_to_le32(1);
+ stl_le_p(outbuf, 1);
return sizeof(le32);
case OID_802_3_MAC_OPTIONS:
@@ -801,17 +801,17 @@ static int ndis_query(USBNetState *s, uint32_t oid,
/* ieee802.3 statistics OIDs (table 4-4) */
/* mandatory */
case OID_802_3_RCV_ERROR_ALIGNMENT:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_802_3_XMIT_ONE_COLLISION:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
/* mandatory */
case OID_802_3_XMIT_MORE_COLLISIONS:
- *((le32 *) outbuf) = cpu_to_le32(0);
+ stl_le_p(outbuf, 0);
return sizeof(le32);
default:
@@ -826,7 +826,7 @@ static int ndis_set(USBNetState *s, uint32_t oid,
{
switch (oid) {
case OID_GEN_CURRENT_PACKET_FILTER:
- s->filter = le32_to_cpup((le32 *) inbuf);
+ s->filter = ldl_le_p(inbuf);
if (s->filter) {
s->rndis_state = RNDIS_DATA_INITIALIZED;
} else {
@@ -1026,10 +1026,7 @@ static void usb_net_reset_in_buf(USBNetState *s)
static int rndis_parse(USBNetState *s, uint8_t *data, int length)
{
- uint32_t msg_type;
- le32 *tmp = (le32 *) data;
-
- msg_type = le32_to_cpup(tmp);
+ uint32_t msg_type = ldl_le_p(data);
switch (msg_type) {
case RNDIS_INITIALIZE_MSG:
@@ -1337,7 +1334,7 @@ static void usb_net_handle_destroy(USBDevice *dev)
}
static NetClientInfo net_usbnet_info = {
- .type = NET_CLIENT_OPTIONS_KIND_NIC,
+ .type = NET_CLIENT_DRIVER_NIC,
.size = sizeof(NICState),
.receive = usbnet_receive,
.cleanup = usbnet_cleanup,
@@ -1399,7 +1396,7 @@ static USBDevice *usb_net_init(USBBus *bus, const char *cmdline)
qemu_opt_set(opts, "type", "nic", &error_abort);
qemu_opt_set(opts, "model", "usb", &error_abort);
- idx = net_client_init(opts, 0, &local_err);
+ idx = net_client_init(opts, false, &local_err);
if (local_err) {
error_report_err(local_err);
return NULL;
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
index 248a58045..c607f7606 100644
--- a/hw/usb/dev-storage.c
+++ b/hw/usb/dev-storage.c
@@ -556,21 +556,6 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p)
}
}
-static void usb_msd_password_cb(void *opaque, int err)
-{
- MSDState *s = opaque;
- Error *local_err = NULL;
-
- if (!err) {
- usb_device_attach(&s->dev, &local_err);
- }
-
- if (local_err) {
- error_report_err(local_err);
- qdev_unplug(&s->dev.qdev, NULL);
- }
-}
-
static void *usb_msd_load_request(QEMUFile *f, SCSIRequest *req)
{
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
@@ -616,37 +601,21 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
return;
}
- if (blk_bs(blk)) {
- bdrv_add_key(blk_bs(blk), NULL, &err);
- if (err) {
- if (monitor_cur_is_qmp()) {
- error_propagate(errp, err);
- return;
- }
- error_free(err);
- err = NULL;
- if (cur_mon) {
- monitor_read_bdrv_key_start(cur_mon, blk_bs(blk),
- usb_msd_password_cb, s);
- s->dev.auto_attach = 0;
- } else {
- autostart = 0;
- }
- }
- }
-
blkconf_serial(&s->conf, &dev->serial);
blkconf_blocksizes(&s->conf);
+ blkconf_apply_backend_options(&s->conf);
/*
* Hack alert: this pretends to be a block device, but it's really
* a SCSI bus that can serve only a single device, which it
* creates automatically. But first it needs to detach from its
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
- * attaches again.
+ * attaches again. We also need to take another reference so that
+ * blk_detach_dev() doesn't free blk while we still need it.
*
* The hack is probably a bad idea.
*/
+ blk_ref(blk);
blk_detach_dev(blk, &s->dev.qdev);
s->conf.blk = NULL;
@@ -657,6 +626,7 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
s->conf.bootindex, dev->serial,
&err);
+ blk_unref(blk);
if (!scsi_dev) {
error_propagate(errp, err);
return;
@@ -668,9 +638,14 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
static void usb_msd_realize_bot(USBDevice *dev, Error **errp)
{
MSDState *s = USB_STORAGE_DEV(dev);
+ DeviceState *d = DEVICE(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
+ if (d->hotplugged) {
+ s->dev.auto_attach = 0;
+ }
+
scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(dev),
&usb_msd_scsi_info_bot, NULL);
usb_msd_handle_reset(dev);
@@ -818,9 +793,7 @@ static void usb_msd_set_bootindex(Object *obj, Visitor *v, const char *name,
}
out:
- if (local_err) {
- error_propagate(errp, local_err);
- }
+ error_propagate(errp, local_err);
}
static const TypeInfo usb_storage_dev_type_info = {
@@ -842,10 +815,9 @@ static void usb_msd_instance_init(Object *obj)
static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data)
{
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
- DeviceClass *dc = DEVICE_CLASS(klass);
uc->realize = usb_msd_realize_bot;
- dc->hotpluggable = false;
+ uc->attached_settable = true;
}
static const TypeInfo msd_info = {
diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c
index 0678b1b05..3a8ff18b1 100644
--- a/hw/usb/dev-uas.c
+++ b/hw/usb/dev-uas.c
@@ -900,9 +900,13 @@ static void usb_uas_handle_destroy(USBDevice *dev)
static void usb_uas_realize(USBDevice *dev, Error **errp)
{
UASDevice *uas = USB_UAS(dev);
+ DeviceState *d = DEVICE(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
+ if (d->hotplugged) {
+ uas->dev.auto_attach = 0;
+ }
QTAILQ_INIT(&uas->results);
QTAILQ_INIT(&uas->requests);
@@ -940,6 +944,7 @@ static void usb_uas_class_initfn(ObjectClass *klass, void *data)
uc->handle_control = usb_uas_handle_control;
uc->handle_data = usb_uas_handle_data;
uc->handle_destroy = usb_uas_handle_destroy;
+ uc->attached_settable = true;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
dc->fw_name = "storage";
dc->vmsd = &vmstate_usb_uas;
diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c
index 43a8f7abc..b093db729 100644
--- a/hw/usb/hcd-ehci.c
+++ b/hw/usb/hcd-ehci.c
@@ -2206,29 +2206,28 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
static void ehci_update_frindex(EHCIState *ehci, int uframes)
{
- int i;
-
if (!ehci_enabled(ehci) && ehci->pstate == EST_INACTIVE) {
return;
}
- for (i = 0; i < uframes; i++) {
- ehci->frindex++;
-
- if (ehci->frindex == 0x00002000) {
- ehci_raise_irq(ehci, USBSTS_FLR);
- }
+ /* Generate FLR interrupt if frame index rolls over 0x2000 */
+ if ((ehci->frindex % 0x2000) + uframes >= 0x2000) {
+ ehci_raise_irq(ehci, USBSTS_FLR);
+ }
- if (ehci->frindex == 0x00004000) {
- ehci_raise_irq(ehci, USBSTS_FLR);
- ehci->frindex = 0;
- if (ehci->usbsts_frindex >= 0x00004000) {
- ehci->usbsts_frindex -= 0x00004000;
- } else {
- ehci->usbsts_frindex = 0;
- }
+ /* How many times will frindex roll over 0x4000 with this frame count?
+ * usbsts_frindex is decremented by 0x4000 on rollover until it reaches 0
+ */
+ int rollovers = (ehci->frindex + uframes) / 0x4000;
+ if (rollovers > 0) {
+ if (ehci->usbsts_frindex >= (rollovers * 0x4000)) {
+ ehci->usbsts_frindex -= 0x4000 * rollovers;
+ } else {
+ ehci->usbsts_frindex = 0;
}
}
+
+ ehci->frindex = (ehci->frindex + uframes) % 0x4000;
}
static void ehci_frame_timer(void *opaque)
diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h
index 30218423c..3fd703865 100644
--- a/hw/usb/hcd-ehci.h
+++ b/hw/usb/hcd-ehci.h
@@ -14,8 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#ifndef HW_USB_EHCI_H
-#define HW_USB_EHCI_H 1
+
+#ifndef HW_USB_HCD_EHCI_H
+#define HW_USB_HCD_EHCI_H
#include "hw/hw.h"
#include "qemu/timer.h"
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index ffab561cf..fa5703832 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1474,7 +1474,7 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
if (tks >= usb_frame_time)
return (ohci->frt << 31);
- tks = muldiv64(1, tks, usb_bit_time);
+ tks = tks / usb_bit_time;
fr = (uint16_t)(ohci->fi - tks);
return (ohci->frt << 31) | fr;
@@ -1848,6 +1848,12 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
ohci->as = as;
+ if (num_ports > OHCI_MAX_PORTS) {
+ error_setg(errp, "OHCI num-ports=%d is too big (limit is %d ports)",
+ num_ports, OHCI_MAX_PORTS);
+ return;
+ }
+
if (usb_frame_time == 0) {
#ifdef OHCI_TIME_WARP
usb_frame_time = NANOSECONDS_PER_SECOND;
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
index bcde8a2f4..188f95416 100644
--- a/hw/usb/hcd-xhci.c
+++ b/hw/usb/hcd-xhci.c
@@ -26,6 +26,7 @@
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "trace.h"
+#include "qapi/error.h"
//#define DEBUG_XHCI
//#define DEBUG_DATA
@@ -461,6 +462,8 @@ struct XHCIState {
uint32_t numslots;
uint32_t flags;
uint32_t max_pstreams_mask;
+ OnOffAuto msi;
+ OnOffAuto msix;
/* Operational Registers */
uint32_t usbcmd;
@@ -498,9 +501,7 @@ typedef struct XHCIEvRingSeg {
} XHCIEvRingSeg;
enum xhci_flags {
- XHCI_FLAG_USE_MSI = 1,
- XHCI_FLAG_USE_MSI_X,
- XHCI_FLAG_SS_FIRST,
+ XHCI_FLAG_SS_FIRST = 1,
XHCI_FLAG_FORCE_PCIE_ENDCAP,
XHCI_FLAG_ENABLE_STREAMS,
};
@@ -1531,7 +1532,10 @@ static TRBCCode xhci_disable_ep(XHCIState *xhci, unsigned int slotid,
usb_packet_cleanup(&epctx->transfers[i].packet);
}
- xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
+ /* only touch guest RAM if we're not resetting the HC */
+ if (xhci->dcbaap_low || xhci->dcbaap_high) {
+ xhci_set_ep_state(xhci, epctx, NULL, EP_DISABLED);
+ }
timer_free(epctx->kick_timer);
g_free(epctx);
@@ -2197,7 +2201,9 @@ static void xhci_kick_ep(XHCIState *xhci, unsigned int slotid,
xfer->trb_count = length;
for (i = 0; i < length; i++) {
- assert(xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL));
+ TRBType type;
+ type = xhci_ring_fetch(xhci, ring, &xfer->trbs[i], NULL);
+ assert(type);
}
xfer->streamid = streamid;
@@ -2360,6 +2366,8 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
slot->uport = uport;
slot->ctx = octx;
+ /* Make sure device is in USB_STATE_DEFAULT state */
+ usb_device_reset(dev);
if (bsr) {
slot_ctx[3] = SLOT_DEFAULT << SLOT_STATE_SHIFT;
} else {
@@ -2367,7 +2375,6 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid,
uint8_t buf[1];
slot_ctx[3] = (SLOT_ADDRESSED << SLOT_STATE_SHIFT) | slotid;
- usb_device_reset(dev);
memset(&p, 0, sizeof(p));
usb_packet_addbuf(&p, buf, sizeof(buf));
usb_packet_setup(&p, USB_TOKEN_OUT,
@@ -3578,6 +3585,7 @@ static void usb_xhci_init(XHCIState *xhci)
static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
{
int i, ret;
+ Error *err = NULL;
XHCIState *xhci = XHCI(dev);
@@ -3588,6 +3596,23 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
usb_xhci_init(xhci);
+ if (xhci->msi != ON_OFF_AUTO_OFF) {
+ ret = msi_init(dev, 0x70, xhci->numintrs, true, false, &err);
+ /* Any error other than -ENOTSUP(board's MSI support is broken)
+ * is a programming error */
+ assert(!ret || ret == -ENOTSUP);
+ if (ret && xhci->msi == ON_OFF_AUTO_ON) {
+ /* Can't satisfy user's explicit msi=on request, fail */
+ error_append_hint(&err, "You have to use msi=auto (default) or "
+ "msi=off with this machine type.\n");
+ error_propagate(errp, err);
+ return;
+ }
+ assert(!err || xhci->msi == ON_OFF_AUTO_AUTO);
+ /* With msi=auto, we fall back to MSI off silently */
+ error_free(err);
+ }
+
if (xhci->numintrs > MAXINTRS) {
xhci->numintrs = MAXINTRS;
}
@@ -3645,10 +3670,8 @@ static void usb_xhci_realize(struct PCIDevice *dev, Error **errp)
assert(ret >= 0);
}
- if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI)) {
- msi_init(dev, 0x70, xhci->numintrs, true, false);
- }
- if (xhci_get_flag(xhci, XHCI_FLAG_USE_MSI_X)) {
+ if (xhci->msix != ON_OFF_AUTO_OFF) {
+ /* TODO check for errors */
msix_init(dev, xhci->numintrs,
&xhci->mem, 0, OFF_MSIX_TABLE,
&xhci->mem, 0, OFF_MSIX_PBA,
@@ -3869,8 +3892,8 @@ static const VMStateDescription vmstate_xhci = {
};
static Property xhci_properties[] = {
- DEFINE_PROP_BIT("msi", XHCIState, flags, XHCI_FLAG_USE_MSI, true),
- DEFINE_PROP_BIT("msix", XHCIState, flags, XHCI_FLAG_USE_MSI_X, true),
+ DEFINE_PROP_ON_OFF_AUTO("msi", XHCIState, msi, ON_OFF_AUTO_AUTO),
+ DEFINE_PROP_ON_OFF_AUTO("msix", XHCIState, msix, ON_OFF_AUTO_AUTO),
DEFINE_PROP_BIT("superspeed-ports-first",
XHCIState, flags, XHCI_FLAG_SS_FIRST, true),
DEFINE_PROP_BIT("force-pcie-endcap", XHCIState, flags,
diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c
index 6458a9448..e94672c15 100644
--- a/hw/usb/host-libusb.c
+++ b/hw/usb/host-libusb.c
@@ -34,7 +34,9 @@
*/
#include "qemu/osdep.h"
+#ifndef CONFIG_WIN32
#include <poll.h>
+#endif
#include <libusb.h>
#include "qapi/error.h"
@@ -79,6 +81,7 @@ struct USBHostDevice {
uint32_t iso_urb_frames;
uint32_t options;
uint32_t loglevel;
+ bool needs_autoscan;
/* state */
QTAILQ_ENTRY(USBHostDevice) next;
@@ -204,6 +207,8 @@ static const char *err_names[] = {
static libusb_context *ctx;
static uint32_t loglevel;
+#ifndef CONFIG_WIN32
+
static void usb_host_handle_fd(void *opaque)
{
struct timeval tv = { 0, 0 };
@@ -223,10 +228,14 @@ static void usb_host_del_fd(int fd, void *user_data)
qemu_set_fd_handler(fd, NULL, NULL, NULL);
}
+#endif /* !CONFIG_WIN32 */
+
static int usb_host_init(void)
{
+#ifndef CONFIG_WIN32
const struct libusb_pollfd **poll;
- int i, rc;
+#endif
+ int rc;
if (ctx) {
return 0;
@@ -236,17 +245,21 @@ static int usb_host_init(void)
return -1;
}
libusb_set_debug(ctx, loglevel);
-
+#ifdef CONFIG_WIN32
+ /* FIXME: add support for Windows. */
+#else
libusb_set_pollfd_notifiers(ctx, usb_host_add_fd,
usb_host_del_fd,
ctx);
poll = libusb_get_pollfds(ctx);
if (poll) {
+ int i;
for (i = 0; poll[i] != NULL; i++) {
usb_host_add_fd(poll[i]->fd, poll[i]->events, ctx);
}
}
free(poll);
+#endif
return 0;
}
@@ -346,7 +359,7 @@ static USBHostRequest *usb_host_req_find(USBHostDevice *s, USBPacket *p)
return NULL;
}
-static void usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
+static void LIBUSB_CALL usb_host_req_complete_ctrl(struct libusb_transfer *xfer)
{
USBHostRequest *r = xfer->user_data;
USBHostDevice *s = r->host;
@@ -379,7 +392,7 @@ out:
}
}
-static void usb_host_req_complete_data(struct libusb_transfer *xfer)
+static void LIBUSB_CALL usb_host_req_complete_data(struct libusb_transfer *xfer)
{
USBHostRequest *r = xfer->user_data;
USBHostDevice *s = r->host;
@@ -435,7 +448,8 @@ static void usb_host_req_abort(USBHostRequest *r)
/* ------------------------------------------------------------------------ */
-static void usb_host_req_complete_iso(struct libusb_transfer *transfer)
+static void LIBUSB_CALL
+usb_host_req_complete_iso(struct libusb_transfer *transfer)
{
USBHostIsoXfer *xfer = transfer->user_data;
@@ -963,9 +977,32 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data)
}
}
+static libusb_device *usb_host_find_ref(int bus, int addr)
+{
+ libusb_device **devs = NULL;
+ libusb_device *ret = NULL;
+ int i, n;
+
+ if (usb_host_init() != 0) {
+ return NULL;
+ }
+ n = libusb_get_device_list(ctx, &devs);
+ for (i = 0; i < n; i++) {
+ if (libusb_get_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;
+}
+
static void usb_host_realize(USBDevice *udev, Error **errp)
{
USBHostDevice *s = USB_HOST_DEVICE(udev);
+ libusb_device *ldev;
+ int rc;
if (s->match.vendor_id > 0xffff) {
error_setg(errp, "vendorid out of range");
@@ -986,11 +1023,33 @@ static void usb_host_realize(USBDevice *udev, Error **errp)
QTAILQ_INIT(&s->requests);
QTAILQ_INIT(&s->isorings);
+ 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;
+ }
+ rc = usb_host_open(s, ldev);
+ 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);
+ }
+
s->exit.notify = usb_host_exit_notifier;
qemu_add_exit_notifier(&s->exit);
-
- QTAILQ_INSERT_TAIL(&hostdevs, s, next);
- usb_host_auto_check(NULL);
}
static void usb_host_instance_init(Object *obj)
@@ -1008,7 +1067,9 @@ static void usb_host_handle_destroy(USBDevice *udev)
USBHostDevice *s = USB_HOST_DEVICE(udev);
qemu_remove_exit_notifier(&s->exit);
- QTAILQ_REMOVE(&hostdevs, s, next);
+ if (s->needs_autoscan) {
+ QTAILQ_REMOVE(&hostdevs, s, next);
+ }
usb_host_close(s);
}
diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c
index 8d8054037..444672a00 100644
--- a/hw/usb/redirect.c
+++ b/hw/usb/redirect.c
@@ -109,6 +109,7 @@ struct USBRedirDevice {
uint8_t debug;
char *filter_str;
int32_t bootindex;
+ bool enable_streams;
/* Data passed from chardev the fd_read cb to the usbredirparser read cb */
const uint8_t *read_buf;
int read_buf_size;
@@ -542,9 +543,9 @@ static void usbredir_handle_iso_data(USBRedirDevice *dev, USBPacket *p,
start_iso.pkts_per_urb = 32;
}
- start_iso.no_urbs = (dev->endpoint[EP2I(ep)].bufpq_target_size +
- start_iso.pkts_per_urb - 1) /
- start_iso.pkts_per_urb;
+ start_iso.no_urbs = DIV_ROUND_UP(
+ dev->endpoint[EP2I(ep)].bufpq_target_size,
+ start_iso.pkts_per_urb);
/* Output endpoints pre-fill only 1/2 of the packets, keeping the rest
as overflow buffer. Also see the usbredir protocol documentation */
if (!(ep & USB_DIR_IN)) {
@@ -1229,7 +1230,9 @@ static void usbredir_create_parser(USBRedirDevice *dev)
usbredirparser_caps_set_cap(caps, usb_redir_cap_32bits_bulk_length);
usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_receiving);
#if USBREDIR_VERSION >= 0x000700
- usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+ if (dev->enable_streams) {
+ usbredirparser_caps_set_cap(caps, usb_redir_cap_bulk_streams);
+ }
#endif
if (runstate_check(RUN_STATE_INMIGRATE)) {
@@ -2476,6 +2479,7 @@ static Property usbredir_properties[] = {
DEFINE_PROP_CHR("chardev", USBRedirDevice, cs),
DEFINE_PROP_UINT8("debug", USBRedirDevice, debug, usbredirparser_warning),
DEFINE_PROP_STRING("filter", USBRedirDevice, filter_str),
+ DEFINE_PROP_BOOL("streams", USBRedirDevice, enable_streams, true),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/hw/usb/trace-events b/hw/usb/trace-events
new file mode 100644
index 000000000..2d42fd45d
--- /dev/null
+++ b/hw/usb/trace-events
@@ -0,0 +1,268 @@
+# See docs/tracing.txt for syntax documentation.
+
+# hw/usb/core.c
+usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s"
+usb_packet_state_fault(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s, expected %s"
+
+# hw/usb/bus.c
+usb_port_claim(int bus, const char *port) "bus %d, port %s"
+usb_port_attach(int bus, const char *port, const char *devspeed, const char *portspeed) "bus %d, port %s, devspeed %s, portspeed %s"
+usb_port_detach(int bus, const char *port) "bus %d, port %s"
+usb_port_release(int bus, const char *port) "bus %d, port %s"
+
+# hw/usb/hcd-ohci.c
+usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at %x"
+usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count 0x%.8x relative %d"
+usb_ohci_iso_td_head_offset(uint32_t o0, uint32_t o1, uint32_t o2, uint32_t o3, uint32_t o4, uint32_t o5, uint32_t o6, uint32_t o7) "0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x"
+usb_ohci_iso_td_relative_frame_number_neg(int rel) "ISO_TD R=%d < 0"
+usb_ohci_iso_td_relative_frame_number_big(int rel, int count) "ISO_TD R=%d > FC=%d"
+usb_ohci_iso_td_bad_direction(int dir) "Bad direction %d"
+usb_ohci_iso_td_bad_bp_be(uint32_t bp, uint32_t be) "ISO_TD bp 0x%.8x be 0x%.8x"
+usb_ohci_iso_td_bad_cc_not_accessed(uint32_t start, uint32_t next) "ISO_TD cc != not accessed 0x%.8x 0x%.8x"
+usb_ohci_iso_td_bad_cc_overrun(uint32_t start, uint32_t next) "ISO_TD start_offset=0x%.8x > next_offset=0x%.8x"
+usb_ohci_iso_td_so(uint32_t so, uint32_t eo, uint32_t s, uint32_t e, const char *str, ssize_t len, int ret) "0x%.8x eo 0x%.8x\nsa 0x%.8x ea 0x%.8x\ndir %s len %zu ret %d"
+usb_ohci_iso_td_data_overrun(int ret, ssize_t len) "DataOverrun %d > %zu"
+usb_ohci_iso_td_data_underrun(int ret) "DataUnderrun %d"
+usb_ohci_iso_td_nak(int ret) "got NAK/STALL %d"
+usb_ohci_iso_td_bad_response(int ret) "Bad device response %d"
+usb_ohci_port_attach(int index) "port #%d"
+usb_ohci_port_detach(int index) "port #%d"
+usb_ohci_port_wakeup(int index) "port #%d"
+usb_ohci_port_suspend(int index) "port #%d"
+usb_ohci_port_reset(int index) "port #%d"
+usb_ohci_remote_wakeup(const char *s) "%s: SUSPEND->RESUME"
+usb_ohci_reset(const char *s) "%s"
+usb_ohci_start(const char *s) "%s: USB Operational"
+usb_ohci_resume(const char *s) "%s: USB Resume"
+usb_ohci_stop(const char *s) "%s: USB Suspended"
+usb_ohci_exit(const char *s) "%s"
+usb_ohci_set_ctl(const char *s, uint32_t new_state) "%s: new state 0x%x"
+usb_ohci_td_underrun(void) ""
+usb_ohci_td_dev_error(void) ""
+usb_ohci_td_nak(void) ""
+usb_ohci_td_stall(void) ""
+usb_ohci_td_babble(void) ""
+usb_ohci_td_bad_device_response(int rc) "%d"
+usb_ohci_td_read_error(uint32_t addr) "TD read error at %x"
+usb_ohci_td_bad_direction(int dir) "Bad direction %d"
+usb_ohci_td_skip_async(void) ""
+usb_ohci_td_pkt_hdr(uint32_t addr, int64_t pktlen, int64_t len, const char *s, int flag_r, uint32_t cbp, uint32_t be) " TD @ 0x%.8x %" PRId64 " of %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x"
+usb_ohci_td_pkt_short(const char *dir, const char *buf) "%s data: %s"
+usb_ohci_td_pkt_full(const char *dir, const char *buf) "%s data: %s"
+usb_ohci_td_too_many_pending(void) ""
+usb_ohci_td_packet_status(int status) "status=%d"
+usb_ohci_ed_read_error(uint32_t addr) "ED read error at %x"
+usb_ohci_ed_pkt(uint32_t cur, int h, int c, uint32_t head, uint32_t tail, uint32_t next) "ED @ 0x%.8x h=%u c=%u\n head=0x%.8x tailp=0x%.8x next=0x%.8x"
+usb_ohci_ed_pkt_flags(uint32_t fa, uint32_t en, uint32_t d, int s, int k, int f, uint32_t mps) "fa=%u en=%u d=%u s=%u k=%u f=%u mps=%u"
+usb_ohci_hcca_read_error(uint32_t addr) "HCCA read error at %x"
+usb_ohci_mem_read_unaligned(uint32_t addr) "at %x"
+usb_ohci_mem_read_bad_offset(uint32_t addr) "%x"
+usb_ohci_mem_write_unaligned(uint32_t addr) "at %x"
+usb_ohci_mem_write_bad_offset(uint32_t addr) "%x"
+usb_ohci_process_lists(uint32_t head, uint32_t cur) "head %x, cur %x"
+usb_ohci_bus_eof_timer_failed(const char *name) "%s: timer_new_ns failed"
+usb_ohci_set_frame_interval(const char *name, uint16_t fi_x, uint16_t fi_u) "%s: FrameInterval = 0x%x (%u)"
+usb_ohci_hub_power_up(void) "powered up all ports"
+usb_ohci_hub_power_down(void) "powered down all ports"
+usb_ohci_init_time(int64_t frametime, int64_t bittime) "usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64
+usb_ohci_die(void) ""
+usb_ohci_async_complete(void) ""
+
+# hw/usb/hcd-ehci.c
+usb_ehci_reset(void) "=== RESET ==="
+usb_ehci_unrealize(void) "=== UNREALIZE ==="
+usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio %04x [%s] = %x"
+usb_ehci_opreg_write(uint32_t addr, const char *str, uint32_t val) "wr mmio %04x [%s] = %x"
+usb_ehci_opreg_change(uint32_t addr, const char *str, uint32_t new, uint32_t old) "ch mmio %04x [%s] = %x (old: %x)"
+usb_ehci_portsc_read(uint32_t addr, uint32_t port, uint32_t val) "rd mmio %04x [port %d] = %x"
+usb_ehci_portsc_write(uint32_t addr, uint32_t port, uint32_t val) "wr mmio %04x [port %d] = %x"
+usb_ehci_portsc_change(uint32_t addr, uint32_t port, uint32_t new, uint32_t old) "ch mmio %04x [port %d] = %x (old: %x)"
+usb_ehci_usbsts(const char *sts, int state) "usbsts %s %d"
+usb_ehci_state(const char *schedule, const char *state) "%s schedule %s"
+usb_ehci_qh_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t c_qtd, uint32_t n_qtd, uint32_t a_qtd) "q %p - QH @ %08x: next %08x qtds %08x,%08x,%08x"
+usb_ehci_qh_fields(uint32_t addr, int rl, int mplen, int eps, int ep, int devaddr) "QH @ %08x - rl %d, mplen %d, eps %d, ep %d, dev %d"
+usb_ehci_qh_bits(uint32_t addr, int c, int h, int dtc, int i) "QH @ %08x - c %d, h %d, dtc %d, i %d"
+usb_ehci_qtd_ptrs(void *q, uint32_t addr, uint32_t nxt, uint32_t altnext) "q %p - QTD @ %08x: next %08x altnext %08x"
+usb_ehci_qtd_fields(uint32_t addr, int tbytes, int cpage, int cerr, int pid) "QTD @ %08x - tbytes %d, cpage %d, cerr %d, pid %d"
+usb_ehci_qtd_bits(uint32_t addr, int ioc, int active, int halt, int babble, int xacterr) "QTD @ %08x - ioc %d, active %d, halt %d, babble %d, xacterr %d"
+usb_ehci_itd(uint32_t addr, uint32_t nxt, uint32_t mplen, uint32_t mult, uint32_t ep, uint32_t devaddr) "ITD @ %08x: next %08x - mplen %d, mult %d, ep %d, dev %d"
+usb_ehci_sitd(uint32_t addr, uint32_t nxt, uint32_t active) "ITD @ %08x: next %08x - active %d"
+usb_ehci_port_attach(uint32_t port, const char *owner, const char *device) "attach port #%d, owner %s, device %s"
+usb_ehci_port_detach(uint32_t port, const char *owner) "detach port #%d, owner %s"
+usb_ehci_port_reset(uint32_t port, int enable) "reset port #%d - %d"
+usb_ehci_port_suspend(uint32_t port) "port #%d"
+usb_ehci_port_wakeup(uint32_t port) "port #%d"
+usb_ehci_port_resume(uint32_t port) "port #%d"
+usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
+usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
+usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x"
+usb_ehci_guest_bug(const char *reason) "%s"
+usb_ehci_doorbell_ring(void) ""
+usb_ehci_doorbell_ack(void) ""
+usb_ehci_dma_error(void) ""
+
+# hw/usb/hcd-uhci.c
+usb_uhci_reset(void) "=== RESET ==="
+usb_uhci_exit(void) "=== EXIT ==="
+usb_uhci_schedule_start(void) ""
+usb_uhci_schedule_stop(void) ""
+usb_uhci_frame_start(uint32_t num) "nr %d"
+usb_uhci_frame_stop_bandwidth(void) ""
+usb_uhci_frame_loop_stop_idle(void) ""
+usb_uhci_frame_loop_continue(void) ""
+usb_uhci_mmio_readw(uint32_t addr, uint32_t val) "addr 0x%04x, ret 0x%04x"
+usb_uhci_mmio_writew(uint32_t addr, uint32_t val) "addr 0x%04x, val 0x%04x"
+usb_uhci_queue_add(uint32_t token) "token 0x%x"
+usb_uhci_queue_del(uint32_t token, const char *reason) "token 0x%x: %s"
+usb_uhci_packet_add(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_link_async(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_unlink_async(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_cancel(uint32_t token, uint32_t addr, int done) "token 0x%x, td 0x%x, done %d"
+usb_uhci_packet_complete_success(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_complete_shortxfer(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_complete_stall(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_complete_babble(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_complete_error(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_packet_del(uint32_t token, uint32_t addr) "token 0x%x, td 0x%x"
+usb_uhci_qh_load(uint32_t qh) "qh 0x%x"
+usb_uhci_td_load(uint32_t qh, uint32_t td, uint32_t ctrl, uint32_t token) "qh 0x%x, td 0x%x, ctrl 0x%x, token 0x%x"
+usb_uhci_td_queue(uint32_t td, uint32_t ctrl, uint32_t token) "td 0x%x, ctrl 0x%x, token 0x%x"
+usb_uhci_td_nextqh(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
+usb_uhci_td_async(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
+usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x"
+
+# hw/usb/hcd-xhci.c
+usb_xhci_reset(void) "=== RESET ==="
+usb_xhci_exit(void) "=== EXIT ==="
+usb_xhci_run(void) ""
+usb_xhci_stop(void) ""
+usb_xhci_cap_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
+usb_xhci_oper_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
+usb_xhci_port_read(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, ret 0x%08x"
+usb_xhci_runtime_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
+usb_xhci_doorbell_read(uint32_t off, uint32_t val) "off 0x%04x, ret 0x%08x"
+usb_xhci_oper_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+usb_xhci_port_write(uint32_t port, uint32_t off, uint32_t val) "port %d, off 0x%04x, val 0x%08x"
+usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
+usb_xhci_irq_intx(uint32_t level) "level %d"
+usb_xhci_irq_msi(uint32_t nr) "nr %d"
+usb_xhci_irq_msix(uint32_t nr) "nr %d"
+usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
+usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
+usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
+usb_xhci_port_reset(uint32_t port, bool warm) "port %d, warm %d"
+usb_xhci_port_link(uint32_t port, uint32_t pls) "port %d, pls %d"
+usb_xhci_port_notify(uint32_t port, uint32_t pls) "port %d, bits %x"
+usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
+usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
+usb_xhci_slot_address(uint32_t slotid, const char *port) "slotid %d, port %s"
+usb_xhci_slot_configure(uint32_t slotid) "slotid %d"
+usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
+usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
+usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint32_t streamid, uint64_t param) "slotid %d, epid %d, streamid %d, ptr %016" PRIx64
+usb_xhci_ep_kick(uint32_t slotid, uint32_t epid, uint32_t streamid) "slotid %d, epid %d, streamid %d"
+usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
+usb_xhci_ep_state(uint32_t slotid, uint32_t epid, const char *os, const char *ns) "slotid %d, epid %d, %s -> %s"
+usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t streamid) "%p: slotid %d, epid %d, streamid %d"
+usb_xhci_xfer_async(void *xfer) "%p"
+usb_xhci_xfer_nak(void *xfer) "%p"
+usb_xhci_xfer_retry(void *xfer) "%p"
+usb_xhci_xfer_success(void *xfer, uint32_t bytes) "%p: len %d"
+usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d"
+usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)"
+
+# hw/usb/desc.c
+usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
+usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"
+usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
+usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
+usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d"
+usb_desc_msos(int addr, int index, int len, int ret) "dev %d msos, index 0x%x, len %d, ret %d"
+usb_set_addr(int addr) "dev %d"
+usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
+usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d"
+usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
+usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
+
+# hw/usb/dev-hub.c
+usb_hub_reset(int addr) "dev %d"
+usb_hub_control(int addr, int request, int value, int index, int length) "dev %d, req 0x%x, value %d, index %d, langth %d"
+usb_hub_get_port_status(int addr, int nr, int status, int changed) "dev %d, port %d, status 0x%x, changed 0x%x"
+usb_hub_set_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s"
+usb_hub_clear_port_feature(int addr, int nr, const char *f) "dev %d, port %d, feature %s"
+usb_hub_attach(int addr, int nr) "dev %d, port %d"
+usb_hub_detach(int addr, int nr) "dev %d, port %d"
+usb_hub_status_report(int addr, int status) "dev %d, status 0x%x"
+
+# hw/usb/dev-uas.c
+usb_uas_reset(int addr) "dev %d"
+usb_uas_command(int addr, uint16_t tag, int lun, uint32_t lun64_1, uint32_t lun64_2) "dev %d, tag 0x%x, lun %d, lun64 %08x-%08x"
+usb_uas_response(int addr, uint16_t tag, uint8_t code) "dev %d, tag 0x%x, code 0x%x"
+usb_uas_sense(int addr, uint16_t tag, uint8_t status) "dev %d, tag 0x%x, status 0x%x"
+usb_uas_read_ready(int addr, uint16_t tag) "dev %d, tag 0x%x"
+usb_uas_write_ready(int addr, uint16_t tag) "dev %d, tag 0x%x"
+usb_uas_xfer_data(int addr, uint16_t tag, uint32_t copy, uint32_t uoff, uint32_t usize, uint32_t soff, uint32_t ssize) "dev %d, tag 0x%x, copy %d, usb-pkt %d/%d, scsi-buf %d/%d"
+usb_uas_scsi_data(int addr, uint16_t tag, uint32_t bytes) "dev %d, tag 0x%x, bytes %d"
+usb_uas_scsi_complete(int addr, uint16_t tag, uint32_t status, uint32_t resid) "dev %d, tag 0x%x, status 0x%x, residue %d"
+usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0x%x, task-tag 0x%x"
+usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d"
+usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x"
+
+# hw/usb/dev-mtp.c
+usb_mtp_reset(int addr) "dev %d"
+usb_mtp_command(int dev, uint16_t code, uint32_t trans, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) "dev %d, code 0x%x, trans 0x%x, args 0x%x, 0x%x, 0x%x, 0x%x, 0x%x"
+usb_mtp_success(int dev, uint32_t trans, uint32_t arg0, uint32_t arg1) "dev %d, trans 0x%x, args 0x%x, 0x%x"
+usb_mtp_error(int dev, uint16_t code, uint32_t trans, uint32_t arg0, uint32_t arg1) "dev %d, code 0x%x, trans 0x%x, args 0x%x, 0x%x"
+usb_mtp_data_in(int dev, uint32_t trans, uint32_t len) "dev %d, trans 0x%x, len %d"
+usb_mtp_xfer(int dev, uint32_t ep, uint32_t dlen, uint32_t plen) "dev %d, ep %d, %d/%d"
+usb_mtp_nak(int dev, uint32_t ep) "dev %d, ep %d"
+usb_mtp_stall(int dev, const char *reason) "dev %d, reason: %s"
+usb_mtp_op_get_device_info(int dev) "dev %d"
+usb_mtp_op_open_session(int dev) "dev %d"
+usb_mtp_op_close_session(int dev) "dev %d"
+usb_mtp_op_get_storage_ids(int dev) "dev %d"
+usb_mtp_op_get_storage_info(int dev) "dev %d"
+usb_mtp_op_get_num_objects(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_op_get_object_handles(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_op_get_object_info(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_op_get_object(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_op_get_partial_object(int dev, uint32_t handle, const char *path, uint32_t offset, uint32_t length) "dev %d, handle 0x%x, path %s, off %d, len %d"
+usb_mtp_op_unknown(int dev, uint32_t code) "dev %d, command code 0x%x"
+usb_mtp_object_alloc(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_object_free(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_add_child(int dev, uint32_t handle, const char *path) "dev %d, handle 0x%x, path %s"
+usb_mtp_inotify_event(int dev, const char *path, uint32_t mask, const char *s) "dev %d, path %s mask 0x%x event %s"
+
+# hw/usb/host-libusb.c
+usb_host_open_started(int bus, int addr) "dev %d:%d"
+usb_host_open_success(int bus, int addr) "dev %d:%d"
+usb_host_open_failure(int bus, int addr) "dev %d:%d"
+usb_host_close(int bus, int addr) "dev %d:%d"
+usb_host_attach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d"
+usb_host_detach_kernel(int bus, int addr, int interface) "dev %d:%d, if %d"
+usb_host_set_address(int bus, int addr, int config) "dev %d:%d, address %d"
+usb_host_set_config(int bus, int addr, int config) "dev %d:%d, config %d"
+usb_host_set_interface(int bus, int addr, int interface, int alt) "dev %d:%d, interface %d, alt %d"
+usb_host_claim_interface(int bus, int addr, int config, int interface) "dev %d:%d, config %d, if %d"
+usb_host_release_interface(int bus, int addr, int interface) "dev %d:%d, if %d"
+usb_host_req_control(int bus, int addr, void *p, int req, int value, int index) "dev %d:%d, packet %p, req 0x%x, value %d, index %d"
+usb_host_req_data(int bus, int addr, void *p, int in, int ep, int size) "dev %d:%d, packet %p, in %d, ep %d, size %d"
+usb_host_req_complete(int bus, int addr, void *p, int status, int length) "dev %d:%d, packet %p, status %d, length %d"
+usb_host_req_emulated(int bus, int addr, void *p, int status) "dev %d:%d, packet %p, status %d"
+usb_host_req_canceled(int bus, int addr, void *p) "dev %d:%d, packet %p"
+usb_host_iso_start(int bus, int addr, int ep) "dev %d:%d, ep %d"
+usb_host_iso_stop(int bus, int addr, int ep) "dev %d:%d, ep %d"
+usb_host_iso_out_of_bufs(int bus, int addr, int ep) "dev %d:%d, ep %d"
+usb_host_reset(int bus, int addr) "dev %d:%d"
+usb_host_auto_scan_enabled(void)
+usb_host_auto_scan_disabled(void)
+usb_host_parse_config(int bus, int addr, int value, int active) "dev %d:%d, value %d, active %d"
+usb_host_parse_interface(int bus, int addr, int num, int alt, int active) "dev %d:%d, num %d, alt %d, active %d"
+usb_host_parse_endpoint(int bus, int addr, int ep, const char *dir, const char *type, int active) "dev %d:%d, ep %d, %s, %s, active %d"
+usb_host_parse_error(int bus, int addr, const char *errmsg) "dev %d:%d, msg %s"
diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c
new file mode 100644
index 000000000..174d715e3
--- /dev/null
+++ b/hw/usb/xen-usb.c
@@ -0,0 +1,1107 @@
+/*
+ * xen paravirt usb device backend
+ *
+ * (c) Juergen Gross <jgross@suse.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include <libusb.h>
+#include <sys/user.h>
+
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+#include "hw/sysbus.h"
+#include "hw/usb.h"
+#include "hw/xen/xen_backend.h"
+#include "monitor/qdev.h"
+#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/qint.h"
+#include "qapi/qmp/qstring.h"
+
+#include <xen/io/ring.h>
+#include <xen/io/usbif.h>
+
+/*
+ * Check for required support of usbif.h: USBIF_SHORT_NOT_OK was the last
+ * macro added we rely on.
+ */
+#ifdef USBIF_SHORT_NOT_OK
+
+#define TR(xendev, lvl, fmt, args...) \
+ { \
+ struct timeval tv; \
+ \
+ gettimeofday(&tv, NULL); \
+ xen_be_printf(xendev, lvl, "%8ld.%06ld xen-usb(%s):" fmt, \
+ tv.tv_sec, tv.tv_usec, __func__, ##args); \
+ }
+#define TR_BUS(xendev, fmt, args...) TR(xendev, 2, fmt, ##args)
+#define TR_REQ(xendev, fmt, args...) TR(xendev, 3, fmt, ##args)
+
+#define USBBACK_MAXPORTS USBIF_PIPE_PORT_MASK
+#define USB_DEV_ADDR_SIZE (USBIF_PIPE_DEV_MASK + 1)
+
+/* USB wire protocol: structure describing control request parameter. */
+struct usbif_ctrlrequest {
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+};
+
+struct usbback_info;
+struct usbback_req;
+
+struct usbback_stub {
+ USBDevice *dev;
+ USBPort port;
+ unsigned int speed;
+ bool attached;
+ QTAILQ_HEAD(submit_q_head, usbback_req) submit_q;
+};
+
+struct usbback_req {
+ struct usbback_info *usbif;
+ struct usbback_stub *stub;
+ struct usbif_urb_request req;
+ USBPacket packet;
+
+ unsigned int nr_buffer_segs; /* # of transfer_buffer segments */
+ unsigned int nr_extra_segs; /* # of iso_frame_desc segments */
+
+ QTAILQ_ENTRY(usbback_req) q;
+
+ void *buffer;
+ void *isoc_buffer;
+ struct libusb_transfer *xfer;
+
+ bool cancelled;
+};
+
+struct usbback_hotplug {
+ QSIMPLEQ_ENTRY(usbback_hotplug) q;
+ unsigned port;
+};
+
+struct usbback_info {
+ struct XenDevice xendev; /* must be first */
+ USBBus bus;
+ void *urb_sring;
+ void *conn_sring;
+ struct usbif_urb_back_ring urb_ring;
+ struct usbif_conn_back_ring conn_ring;
+ int num_ports;
+ int usb_ver;
+ bool ring_error;
+ QTAILQ_HEAD(req_free_q_head, usbback_req) req_free_q;
+ QSIMPLEQ_HEAD(hotplug_q_head, usbback_hotplug) hotplug_q;
+ struct usbback_stub ports[USBBACK_MAXPORTS];
+ struct usbback_stub *addr_table[USB_DEV_ADDR_SIZE];
+ QEMUBH *bh;
+};
+
+static struct usbback_req *usbback_get_req(struct usbback_info *usbif)
+{
+ struct usbback_req *usbback_req;
+
+ if (QTAILQ_EMPTY(&usbif->req_free_q)) {
+ usbback_req = g_new0(struct usbback_req, 1);
+ } else {
+ usbback_req = QTAILQ_FIRST(&usbif->req_free_q);
+ QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q);
+ }
+ return usbback_req;
+}
+
+static void usbback_put_req(struct usbback_req *usbback_req)
+{
+ struct usbback_info *usbif;
+
+ usbif = usbback_req->usbif;
+ memset(usbback_req, 0, sizeof(*usbback_req));
+ QTAILQ_INSERT_HEAD(&usbif->req_free_q, usbback_req, q);
+}
+
+static int usbback_gnttab_map(struct usbback_req *usbback_req)
+{
+ unsigned int nr_segs, i, prot;
+ uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST];
+ struct usbback_info *usbif = usbback_req->usbif;
+ struct XenDevice *xendev = &usbif->xendev;
+ struct usbif_request_segment *seg;
+ void *addr;
+
+ nr_segs = usbback_req->nr_buffer_segs + usbback_req->nr_extra_segs;
+ if (!nr_segs) {
+ return 0;
+ }
+
+ if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) {
+ xen_be_printf(xendev, 0, "bad number of segments in request (%d)\n",
+ nr_segs);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ if ((unsigned)usbback_req->req.seg[i].offset +
+ (unsigned)usbback_req->req.seg[i].length > PAGE_SIZE) {
+ xen_be_printf(xendev, 0, "segment crosses page boundary\n");
+ return -EINVAL;
+ }
+ }
+
+ if (usbback_req->nr_buffer_segs) {
+ prot = PROT_READ;
+ if (usbif_pipein(usbback_req->req.pipe)) {
+ prot |= PROT_WRITE;
+ }
+ for (i = 0; i < usbback_req->nr_buffer_segs; i++) {
+ ref[i] = usbback_req->req.seg[i].gref;
+ }
+ usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev,
+ usbback_req->nr_buffer_segs, xendev->dom, ref, prot);
+
+ if (!usbback_req->buffer) {
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < usbback_req->nr_buffer_segs; i++) {
+ seg = usbback_req->req.seg + i;
+ addr = usbback_req->buffer + i * PAGE_SIZE + seg->offset;
+ qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length);
+ }
+ }
+
+ if (!usbif_pipeisoc(usbback_req->req.pipe)) {
+ return 0;
+ }
+
+ /*
+ * Right now isoc requests are not supported.
+ * Prepare supporting those by doing the work needed on the guest
+ * interface side.
+ */
+
+ if (!usbback_req->nr_extra_segs) {
+ xen_be_printf(xendev, 0, "iso request without descriptor segments\n");
+ return -EINVAL;
+ }
+
+ prot = PROT_READ | PROT_WRITE;
+ for (i = 0; i < usbback_req->nr_extra_segs; i++) {
+ ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref;
+ }
+ usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs(
+ xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot);
+
+ if (!usbback_req->isoc_buffer) {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int usbback_init_packet(struct usbback_req *usbback_req)
+{
+ struct XenDevice *xendev = &usbback_req->usbif->xendev;
+ USBPacket *packet = &usbback_req->packet;
+ USBDevice *dev = usbback_req->stub->dev;
+ USBEndpoint *ep;
+ unsigned int pid, ep_nr;
+ bool sok;
+ int ret = 0;
+
+ qemu_iovec_init(&packet->iov, USBIF_MAX_SEGMENTS_PER_REQUEST);
+ pid = usbif_pipein(usbback_req->req.pipe) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+ ep_nr = usbif_pipeendpoint(usbback_req->req.pipe);
+ sok = !!(usbback_req->req.transfer_flags & USBIF_SHORT_NOT_OK);
+ if (usbif_pipectrl(usbback_req->req.pipe)) {
+ ep_nr = 0;
+ sok = false;
+ }
+ ep = usb_ep_get(dev, pid, ep_nr);
+ usb_packet_setup(packet, pid, ep, 0, 1, sok, true);
+
+ switch (usbif_pipetype(usbback_req->req.pipe)) {
+ case USBIF_PIPE_TYPE_ISOC:
+ TR_REQ(xendev, "iso transfer %s: buflen: %x, %d frames\n",
+ (pid == USB_TOKEN_IN) ? "in" : "out",
+ usbback_req->req.buffer_length,
+ usbback_req->req.u.isoc.nr_frame_desc_segs);
+ ret = -EINVAL; /* isoc not implemented yet */
+ break;
+
+ case USBIF_PIPE_TYPE_INT:
+ TR_REQ(xendev, "int transfer %s: buflen: %x\n",
+ (pid == USB_TOKEN_IN) ? "in" : "out",
+ usbback_req->req.buffer_length);
+ break;
+
+ case USBIF_PIPE_TYPE_CTRL:
+ packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl;
+ TR_REQ(xendev, "ctrl parameter: %"PRIx64", buflen: %x\n",
+ packet->parameter,
+ usbback_req->req.buffer_length);
+ break;
+
+ case USBIF_PIPE_TYPE_BULK:
+ TR_REQ(xendev, "bulk transfer %s: buflen: %x\n",
+ (pid == USB_TOKEN_IN) ? "in" : "out",
+ usbback_req->req.buffer_length);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usbback_do_response(struct usbback_req *usbback_req, int32_t status,
+ int32_t actual_length, int32_t error_count)
+{
+ struct usbback_info *usbif;
+ struct usbif_urb_response *res;
+ struct XenDevice *xendev;
+ unsigned int notify;
+
+ usbif = usbback_req->usbif;
+ xendev = &usbif->xendev;
+
+ TR_REQ(xendev, "id %d, status %d, length %d, errcnt %d\n",
+ usbback_req->req.id, status, actual_length, error_count);
+
+ if (usbback_req->packet.iov.iov) {
+ qemu_iovec_destroy(&usbback_req->packet.iov);
+ }
+
+ if (usbback_req->buffer) {
+ xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer,
+ usbback_req->nr_buffer_segs);
+ usbback_req->buffer = NULL;
+ }
+
+ if (usbback_req->isoc_buffer) {
+ xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer,
+ usbback_req->nr_extra_segs);
+ usbback_req->isoc_buffer = NULL;
+ }
+
+ if (usbif->urb_sring) {
+ res = RING_GET_RESPONSE(&usbif->urb_ring, usbif->urb_ring.rsp_prod_pvt);
+ res->id = usbback_req->req.id;
+ res->status = status;
+ res->actual_length = actual_length;
+ res->error_count = error_count;
+ res->start_frame = 0;
+ usbif->urb_ring.rsp_prod_pvt++;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify);
+
+ if (notify) {
+ xen_be_send_notify(xendev);
+ }
+ }
+
+ if (!usbback_req->cancelled)
+ usbback_put_req(usbback_req);
+}
+
+static void usbback_do_response_ret(struct usbback_req *usbback_req,
+ int32_t status)
+{
+ usbback_do_response(usbback_req, status, 0, 0);
+}
+
+static int32_t usbback_xlat_status(int status)
+{
+ switch (status) {
+ case USB_RET_SUCCESS:
+ return 0;
+ case USB_RET_NODEV:
+ return -ENODEV;
+ case USB_RET_STALL:
+ return -EPIPE;
+ case USB_RET_BABBLE:
+ return -EOVERFLOW;
+ case USB_RET_IOERROR:
+ return -EPROTO;
+ }
+
+ return -ESHUTDOWN;
+}
+
+static void usbback_packet_complete(USBPacket *packet)
+{
+ struct usbback_req *usbback_req;
+ int32_t status;
+
+ usbback_req = container_of(packet, struct usbback_req, packet);
+
+ QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q);
+
+ status = usbback_xlat_status(packet->status);
+ usbback_do_response(usbback_req, status, packet->actual_length, 0);
+}
+
+static void usbback_set_address(struct usbback_info *usbif,
+ struct usbback_stub *stub,
+ unsigned int cur_addr, unsigned int new_addr)
+{
+ if (cur_addr) {
+ usbif->addr_table[cur_addr] = NULL;
+ }
+ if (new_addr) {
+ usbif->addr_table[new_addr] = stub;
+ }
+}
+
+static void usbback_cancel_req(struct usbback_req *usbback_req)
+{
+ if (usb_packet_is_inflight(&usbback_req->packet)) {
+ usb_cancel_packet(&usbback_req->packet);
+ QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q);
+ usbback_req->cancelled = true;
+ usbback_do_response_ret(usbback_req, -EPROTO);
+ }
+}
+
+static void usbback_process_unlink_req(struct usbback_req *usbback_req)
+{
+ struct usbback_info *usbif;
+ struct usbback_req *unlink_req;
+ unsigned int id, devnum;
+ int ret;
+
+ usbif = usbback_req->usbif;
+ ret = 0;
+ id = usbback_req->req.u.unlink.unlink_id;
+ TR_REQ(&usbif->xendev, "unlink id %d\n", id);
+ devnum = usbif_pipedevice(usbback_req->req.pipe);
+ if (unlikely(devnum == 0)) {
+ usbback_req->stub = usbif->ports +
+ usbif_pipeportnum(usbback_req->req.pipe) - 1;
+ if (unlikely(!usbback_req->stub)) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ } else {
+ if (unlikely(!usbif->addr_table[devnum])) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ usbback_req->stub = usbif->addr_table[devnum];
+ }
+
+ QTAILQ_FOREACH(unlink_req, &usbback_req->stub->submit_q, q) {
+ if (unlink_req->req.id == id) {
+ usbback_cancel_req(unlink_req);
+ break;
+ }
+ }
+
+fail_response:
+ usbback_do_response_ret(usbback_req, ret);
+}
+
+/*
+ * Checks whether a request can be handled at once or should be forwarded
+ * to the usb framework.
+ * Return value is:
+ * 0 in case of usb framework is needed
+ * 1 in case of local handling (no error)
+ * The request response has been queued already if return value not 0.
+ */
+static int usbback_check_and_submit(struct usbback_req *usbback_req)
+{
+ struct usbback_info *usbif;
+ unsigned int devnum;
+ struct usbback_stub *stub;
+ struct usbif_ctrlrequest *ctrl;
+ int ret;
+ uint16_t wValue;
+
+ usbif = usbback_req->usbif;
+ stub = NULL;
+ devnum = usbif_pipedevice(usbback_req->req.pipe);
+ ctrl = (struct usbif_ctrlrequest *)usbback_req->req.u.ctrl;
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ /*
+ * When the device is first connected or resetted, USB device has no
+ * address. In this initial state, following requests are sent to device
+ * address (#0),
+ *
+ * 1. GET_DESCRIPTOR (with Descriptor Type is "DEVICE") is sent,
+ * and OS knows what device is connected to.
+ *
+ * 2. SET_ADDRESS is sent, and then device has its address.
+ *
+ * In the next step, SET_CONFIGURATION is sent to addressed device, and
+ * then the device is finally ready to use.
+ */
+ if (unlikely(devnum == 0)) {
+ stub = usbif->ports + usbif_pipeportnum(usbback_req->req.pipe) - 1;
+ if (!stub->dev || !stub->attached) {
+ ret = -ENODEV;
+ goto do_response;
+ }
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ /*
+ * GET_DESCRIPTOR request to device #0.
+ * through normal transfer.
+ */
+ TR_REQ(&usbif->xendev, "devnum 0 GET_DESCRIPTOR\n");
+ usbback_req->stub = stub;
+ return 0;
+ case USB_REQ_SET_ADDRESS:
+ /*
+ * SET_ADDRESS request to device #0.
+ * add attached device to addr_table.
+ */
+ TR_REQ(&usbif->xendev, "devnum 0 SET_ADDRESS\n");
+ usbback_set_address(usbif, stub, 0, wValue);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ goto do_response;
+ }
+
+ if (unlikely(!usbif->addr_table[devnum])) {
+ ret = -ENODEV;
+ goto do_response;
+ }
+ usbback_req->stub = usbif->addr_table[devnum];
+
+ /*
+ * Check special request
+ */
+ if (ctrl->bRequest != USB_REQ_SET_ADDRESS) {
+ return 0;
+ }
+
+ /*
+ * SET_ADDRESS request to addressed device.
+ * change addr or remove from addr_table.
+ */
+ usbback_set_address(usbif, usbback_req->stub, devnum, wValue);
+ ret = 0;
+
+do_response:
+ usbback_do_response_ret(usbback_req, ret);
+ return 1;
+}
+
+static void usbback_dispatch(struct usbback_req *usbback_req)
+{
+ int ret;
+ unsigned int devnum;
+ struct usbback_info *usbif;
+
+ usbif = usbback_req->usbif;
+
+ TR_REQ(&usbif->xendev, "start req_id %d pipe %08x\n", usbback_req->req.id,
+ usbback_req->req.pipe);
+
+ /* unlink request */
+ if (unlikely(usbif_pipeunlink(usbback_req->req.pipe))) {
+ usbback_process_unlink_req(usbback_req);
+ return;
+ }
+
+ if (usbif_pipectrl(usbback_req->req.pipe)) {
+ if (usbback_check_and_submit(usbback_req)) {
+ return;
+ }
+ } else {
+ devnum = usbif_pipedevice(usbback_req->req.pipe);
+ usbback_req->stub = usbif->addr_table[devnum];
+
+ if (!usbback_req->stub || !usbback_req->stub->attached) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ }
+
+ QTAILQ_INSERT_TAIL(&usbback_req->stub->submit_q, usbback_req, q);
+
+ usbback_req->nr_buffer_segs = usbback_req->req.nr_buffer_segs;
+ usbback_req->nr_extra_segs = usbif_pipeisoc(usbback_req->req.pipe) ?
+ usbback_req->req.u.isoc.nr_frame_desc_segs : 0;
+
+ ret = usbback_init_packet(usbback_req);
+ if (ret) {
+ xen_be_printf(&usbif->xendev, 0, "invalid request\n");
+ ret = -ESHUTDOWN;
+ goto fail_free_urb;
+ }
+
+ ret = usbback_gnttab_map(usbback_req);
+ if (ret) {
+ xen_be_printf(&usbif->xendev, 0, "invalid buffer, ret=%d\n", ret);
+ ret = -ESHUTDOWN;
+ goto fail_free_urb;
+ }
+
+ usb_handle_packet(usbback_req->stub->dev, &usbback_req->packet);
+ if (usbback_req->packet.status != USB_RET_ASYNC) {
+ usbback_packet_complete(&usbback_req->packet);
+ }
+ return;
+
+fail_free_urb:
+ QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q);
+
+fail_response:
+ usbback_do_response_ret(usbback_req, ret);
+}
+
+static void usbback_hotplug_notify(struct usbback_info *usbif)
+{
+ struct usbif_conn_back_ring *ring = &usbif->conn_ring;
+ struct usbif_conn_request req;
+ struct usbif_conn_response *res;
+ struct usbback_hotplug *usb_hp;
+ unsigned int notify;
+
+ if (!usbif->conn_sring) {
+ return;
+ }
+
+ /* Check for full ring. */
+ if ((RING_SIZE(ring) - ring->rsp_prod_pvt - ring->req_cons) == 0) {
+ xen_be_send_notify(&usbif->xendev);
+ return;
+ }
+
+ usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q);
+ QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q);
+
+ RING_COPY_REQUEST(ring, ring->req_cons, &req);
+ ring->req_cons++;
+ ring->sring->req_event = ring->req_cons + 1;
+
+ res = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt);
+ res->id = req.id;
+ res->portnum = usb_hp->port;
+ res->speed = usbif->ports[usb_hp->port - 1].speed;
+ ring->rsp_prod_pvt++;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify);
+
+ if (notify) {
+ xen_be_send_notify(&usbif->xendev);
+ }
+
+ TR_BUS(&usbif->xendev, "hotplug port %d speed %d\n", usb_hp->port,
+ res->speed);
+
+ g_free(usb_hp);
+
+ if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) {
+ qemu_bh_schedule(usbif->bh);
+ }
+}
+
+static void usbback_bh(void *opaque)
+{
+ struct usbback_info *usbif;
+ struct usbif_urb_back_ring *urb_ring;
+ struct usbback_req *usbback_req;
+ RING_IDX rc, rp;
+ unsigned int more_to_do;
+
+ usbif = opaque;
+ if (usbif->ring_error) {
+ return;
+ }
+
+ if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) {
+ usbback_hotplug_notify(usbif);
+ }
+
+ urb_ring = &usbif->urb_ring;
+ rc = urb_ring->req_cons;
+ rp = urb_ring->sring->req_prod;
+ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ if (RING_REQUEST_PROD_OVERFLOW(urb_ring, rp)) {
+ rc = urb_ring->rsp_prod_pvt;
+ xen_be_printf(&usbif->xendev, 0, "domU provided bogus ring requests "
+ "(%#x - %#x = %u). Halting ring processing.\n",
+ rp, rc, rp - rc);
+ usbif->ring_error = true;
+ return;
+ }
+
+ while (rc != rp) {
+ if (RING_REQUEST_CONS_OVERFLOW(urb_ring, rc)) {
+ break;
+ }
+ usbback_req = usbback_get_req(usbif);
+
+ RING_COPY_REQUEST(urb_ring, rc, &usbback_req->req);
+ usbback_req->usbif = usbif;
+
+ usbback_dispatch(usbback_req);
+
+ urb_ring->req_cons = ++rc;
+ }
+
+ RING_FINAL_CHECK_FOR_REQUESTS(urb_ring, more_to_do);
+ if (more_to_do) {
+ qemu_bh_schedule(usbif->bh);
+ }
+}
+
+static void usbback_hotplug_enq(struct usbback_info *usbif, unsigned port)
+{
+ struct usbback_hotplug *usb_hp;
+
+ usb_hp = g_new0(struct usbback_hotplug, 1);
+ usb_hp->port = port;
+ QSIMPLEQ_INSERT_TAIL(&usbif->hotplug_q, usb_hp, q);
+ usbback_hotplug_notify(usbif);
+}
+
+static void usbback_portid_drain(struct usbback_info *usbif, unsigned port)
+{
+ struct usbback_req *req, *tmp;
+ bool sched = false;
+
+ QTAILQ_FOREACH_SAFE(req, &usbif->ports[port - 1].submit_q, q, tmp) {
+ usbback_cancel_req(req);
+ sched = true;
+ }
+
+ if (sched) {
+ qemu_bh_schedule(usbif->bh);
+ }
+}
+
+static void usbback_portid_detach(struct usbback_info *usbif, unsigned port)
+{
+ if (!usbif->ports[port - 1].attached) {
+ return;
+ }
+
+ usbif->ports[port - 1].speed = USBIF_SPEED_NONE;
+ usbif->ports[port - 1].attached = false;
+ usbback_portid_drain(usbif, port);
+ usbback_hotplug_enq(usbif, port);
+}
+
+static void usbback_portid_remove(struct usbback_info *usbif, unsigned port)
+{
+ USBPort *p;
+
+ if (!usbif->ports[port - 1].dev) {
+ return;
+ }
+
+ p = &(usbif->ports[port - 1].port);
+ snprintf(p->path, sizeof(p->path), "%d", 99);
+
+ object_unparent(OBJECT(usbif->ports[port - 1].dev));
+ usbif->ports[port - 1].dev = NULL;
+ usbback_portid_detach(usbif, port);
+
+ TR_BUS(&usbif->xendev, "port %d removed\n", port);
+}
+
+static void usbback_portid_add(struct usbback_info *usbif, unsigned port,
+ char *busid)
+{
+ unsigned speed;
+ char *portname;
+ USBPort *p;
+ Error *local_err = NULL;
+ QDict *qdict;
+ QemuOpts *opts;
+
+ if (usbif->ports[port - 1].dev) {
+ return;
+ }
+
+ portname = strchr(busid, '-');
+ if (!portname) {
+ xen_be_printf(&usbif->xendev, 0, "device %s illegal specification\n",
+ busid);
+ return;
+ }
+ portname++;
+ p = &(usbif->ports[port - 1].port);
+ snprintf(p->path, sizeof(p->path), "%s", portname);
+
+ qdict = qdict_new();
+ qdict_put(qdict, "driver", qstring_from_str("usb-host"));
+ qdict_put(qdict, "hostbus", qint_from_int(atoi(busid)));
+ qdict_put(qdict, "hostport", qstring_from_str(portname));
+ opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
+ if (local_err) {
+ goto err;
+ }
+ usbif->ports[port - 1].dev = USB_DEVICE(qdev_device_add(opts, &local_err));
+ if (!usbif->ports[port - 1].dev) {
+ goto err;
+ }
+ QDECREF(qdict);
+ snprintf(p->path, sizeof(p->path), "%d", port);
+ speed = usbif->ports[port - 1].dev->speed;
+ switch (speed) {
+ case USB_SPEED_LOW:
+ speed = USBIF_SPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ speed = USBIF_SPEED_FULL;
+ break;
+ case USB_SPEED_HIGH:
+ speed = (usbif->usb_ver < USB_VER_USB20) ?
+ USBIF_SPEED_NONE : USBIF_SPEED_HIGH;
+ break;
+ default:
+ speed = USBIF_SPEED_NONE;
+ break;
+ }
+ if (speed == USBIF_SPEED_NONE) {
+ xen_be_printf(&usbif->xendev, 0, "device %s wrong speed\n", busid);
+ object_unparent(OBJECT(usbif->ports[port - 1].dev));
+ usbif->ports[port - 1].dev = NULL;
+ return;
+ }
+ usb_device_reset(usbif->ports[port - 1].dev);
+ usbif->ports[port - 1].speed = speed;
+ usbif->ports[port - 1].attached = true;
+ QTAILQ_INIT(&usbif->ports[port - 1].submit_q);
+ usbback_hotplug_enq(usbif, port);
+
+ TR_BUS(&usbif->xendev, "port %d attached\n", port);
+ return;
+
+err:
+ QDECREF(qdict);
+ snprintf(p->path, sizeof(p->path), "%d", 99);
+ xen_be_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid);
+}
+
+static void usbback_process_port(struct usbback_info *usbif, unsigned port)
+{
+ char node[8];
+ char *busid;
+
+ snprintf(node, sizeof(node), "port/%d", port);
+ busid = xenstore_read_be_str(&usbif->xendev, node);
+ if (busid == NULL) {
+ xen_be_printf(&usbif->xendev, 0, "xenstore_read %s failed\n", node);
+ return;
+ }
+
+ /* Remove portid, if the port is not connected. */
+ if (strlen(busid) == 0) {
+ usbback_portid_remove(usbif, port);
+ } else {
+ usbback_portid_add(usbif, port, busid);
+ }
+
+ g_free(busid);
+}
+
+static void usbback_disconnect(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ unsigned int i;
+
+ TR_BUS(xendev, "start\n");
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ xen_be_unbind_evtchn(xendev);
+
+ if (usbif->urb_sring) {
+ xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1);
+ usbif->urb_sring = NULL;
+ }
+ if (usbif->conn_sring) {
+ xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1);
+ usbif->conn_sring = NULL;
+ }
+
+ for (i = 0; i < usbif->num_ports; i++) {
+ if (usbif->ports[i].dev) {
+ usbback_portid_drain(usbif, i + 1);
+ }
+ }
+
+ TR_BUS(xendev, "finished\n");
+}
+
+static int usbback_connect(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ struct usbif_urb_sring *urb_sring;
+ struct usbif_conn_sring *conn_sring;
+ int urb_ring_ref;
+ int conn_ring_ref;
+ unsigned int i;
+
+ TR_BUS(xendev, "start\n");
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ if (xenstore_read_fe_int(xendev, "urb-ring-ref", &urb_ring_ref)) {
+ xen_be_printf(xendev, 0, "error reading urb-ring-ref\n");
+ return -1;
+ }
+ if (xenstore_read_fe_int(xendev, "conn-ring-ref", &conn_ring_ref)) {
+ xen_be_printf(xendev, 0, "error reading conn-ring-ref\n");
+ return -1;
+ }
+ if (xenstore_read_fe_int(xendev, "event-channel", &xendev->remote_port)) {
+ xen_be_printf(xendev, 0, "error reading event-channel\n");
+ return -1;
+ }
+
+ usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom,
+ urb_ring_ref,
+ PROT_READ | PROT_WRITE);
+ usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom,
+ conn_ring_ref,
+ PROT_READ | PROT_WRITE);
+ if (!usbif->urb_sring || !usbif->conn_sring) {
+ xen_be_printf(xendev, 0, "error mapping rings\n");
+ usbback_disconnect(xendev);
+ return -1;
+ }
+
+ urb_sring = usbif->urb_sring;
+ conn_sring = usbif->conn_sring;
+ BACK_RING_INIT(&usbif->urb_ring, urb_sring, XC_PAGE_SIZE);
+ BACK_RING_INIT(&usbif->conn_ring, conn_sring, XC_PAGE_SIZE);
+
+ xen_be_bind_evtchn(xendev);
+
+ xen_be_printf(xendev, 1, "urb-ring-ref %d, conn-ring-ref %d, "
+ "remote port %d, local port %d\n", urb_ring_ref,
+ conn_ring_ref, xendev->remote_port, xendev->local_port);
+
+ for (i = 1; i <= usbif->num_ports; i++) {
+ if (usbif->ports[i - 1].dev) {
+ usbback_hotplug_enq(usbif, i);
+ }
+ }
+
+ return 0;
+}
+
+static void usbback_backend_changed(struct XenDevice *xendev, const char *node)
+{
+ struct usbback_info *usbif;
+ unsigned int i;
+
+ TR_BUS(xendev, "path %s\n", node);
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+ for (i = 1; i <= usbif->num_ports; i++) {
+ usbback_process_port(usbif, i);
+ }
+}
+
+static int usbback_init(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+
+ TR_BUS(xendev, "start\n");
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ if (xenstore_read_be_int(xendev, "num-ports", &usbif->num_ports) ||
+ usbif->num_ports < 1 || usbif->num_ports > USBBACK_MAXPORTS) {
+ xen_be_printf(xendev, 0, "num-ports not readable or out of bounds\n");
+ return -1;
+ }
+ if (xenstore_read_be_int(xendev, "usb-ver", &usbif->usb_ver) ||
+ (usbif->usb_ver != USB_VER_USB11 && usbif->usb_ver != USB_VER_USB20)) {
+ xen_be_printf(xendev, 0, "usb-ver not readable or out of bounds\n");
+ return -1;
+ }
+
+ usbback_backend_changed(xendev, "port");
+
+ TR_BUS(xendev, "finished\n");
+
+ return 0;
+}
+
+static void xen_bus_attach(USBPort *port)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_BUS(&usbif->xendev, "\n");
+ usbif->ports[port->index].attached = true;
+ usbback_hotplug_enq(usbif, port->index + 1);
+}
+
+static void xen_bus_detach(USBPort *port)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_BUS(&usbif->xendev, "\n");
+ usbback_portid_detach(usbif, port->index + 1);
+}
+
+static void xen_bus_child_detach(USBPort *port, USBDevice *child)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_BUS(&usbif->xendev, "\n");
+}
+
+static void xen_bus_complete(USBPort *port, USBPacket *packet)
+{
+ struct usbback_req *usbback_req;
+ struct usbback_info *usbif;
+
+ usbback_req = container_of(packet, struct usbback_req, packet);
+ if (usbback_req->cancelled) {
+ g_free(usbback_req);
+ return;
+ }
+
+ usbif = usbback_req->usbif;
+ TR_REQ(&usbif->xendev, "\n");
+ usbback_packet_complete(packet);
+}
+
+static USBPortOps xen_usb_port_ops = {
+ .attach = xen_bus_attach,
+ .detach = xen_bus_detach,
+ .child_detach = xen_bus_child_detach,
+ .complete = xen_bus_complete,
+};
+
+static USBBusOps xen_usb_bus_ops = {
+};
+
+static void usbback_alloc(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ USBPort *p;
+ unsigned int i, max_grants;
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ usb_bus_new(&usbif->bus, sizeof(usbif->bus), &xen_usb_bus_ops, xen_sysdev);
+ for (i = 0; i < USBBACK_MAXPORTS; i++) {
+ p = &(usbif->ports[i].port);
+ usb_register_port(&usbif->bus, p, usbif, i, &xen_usb_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL |
+ USB_SPEED_MASK_HIGH);
+ snprintf(p->path, sizeof(p->path), "%d", 99);
+ }
+
+ QTAILQ_INIT(&usbif->req_free_q);
+ QSIMPLEQ_INIT(&usbif->hotplug_q);
+ usbif->bh = qemu_bh_new(usbback_bh, usbif);
+
+ /* max_grants: for each request and for the rings (request and connect). */
+ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2;
+ if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) {
+ xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n",
+ strerror(errno));
+ }
+}
+
+static int usbback_free(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ struct usbback_req *usbback_req;
+ struct usbback_hotplug *usb_hp;
+ unsigned int i;
+
+ TR_BUS(xendev, "start\n");
+
+ usbback_disconnect(xendev);
+ usbif = container_of(xendev, struct usbback_info, xendev);
+ for (i = 1; i <= usbif->num_ports; i++) {
+ usbback_portid_remove(usbif, i);
+ }
+
+ while (!QTAILQ_EMPTY(&usbif->req_free_q)) {
+ usbback_req = QTAILQ_FIRST(&usbif->req_free_q);
+ QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q);
+ g_free(usbback_req);
+ }
+ while (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) {
+ usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q);
+ QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q);
+ g_free(usb_hp);
+ }
+
+ qemu_bh_delete(usbif->bh);
+
+ for (i = 0; i < USBBACK_MAXPORTS; i++) {
+ usb_unregister_port(&usbif->bus, &(usbif->ports[i].port));
+ }
+
+ usb_bus_release(&usbif->bus);
+ object_unparent(OBJECT(&usbif->bus));
+
+ TR_BUS(xendev, "finished\n");
+
+ return 0;
+}
+
+static void usbback_event(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+ qemu_bh_schedule(usbif->bh);
+}
+
+struct XenDevOps xen_usb_ops = {
+ .size = sizeof(struct usbback_info),
+ .flags = DEVOPS_FLAG_NEED_GNTDEV,
+ .init = usbback_init,
+ .alloc = usbback_alloc,
+ .free = usbback_free,
+ .backend_changed = usbback_backend_changed,
+ .initialise = usbback_connect,
+ .disconnect = usbback_disconnect,
+ .event = usbback_event,
+};
+
+#else /* USBIF_SHORT_NOT_OK */
+
+static int usbback_not_supported(void)
+{
+ return -EINVAL;
+}
+
+struct XenDevOps xen_usb_ops = {
+ .backend_register = usbback_not_supported,
+};
+
+#endif