|
|
958e1b |
From da332a238d28a29821dc57437f43feed54a3f418 Mon Sep 17 00:00:00 2001
|
|
|
958e1b |
From: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
958e1b |
Date: Fri, 11 Jul 2014 14:20:55 +0200
|
|
|
958e1b |
Subject: [PATCH 21/43] xhci: Call usb_device_alloc/free_streams
|
|
|
958e1b |
|
|
|
958e1b |
Message-id: <1405088470-24115-23-git-send-email-kraxel@redhat.com>
|
|
|
958e1b |
Patchwork-id: 59834
|
|
|
958e1b |
O-Subject: [RHEL-7.1 qemu-kvm PATCH 22/37] xhci: Call usb_device_alloc/free_streams
|
|
|
958e1b |
Bugzilla: 980833
|
|
|
958e1b |
RH-Acked-by: Dr. David Alan Gilbert (git) <dgilbert@redhat.com>
|
|
|
958e1b |
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
958e1b |
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
|
|
|
958e1b |
|
|
|
958e1b |
From: Hans de Goede <hdegoede@redhat.com>
|
|
|
958e1b |
|
|
|
958e1b |
Note this code is not as KISS as I would like, the reason for this is that
|
|
|
958e1b |
the Linux kernel interface wants streams on eps belonging to one interface
|
|
|
958e1b |
to be allocated in one call. Things will also work if we do this one ep at a
|
|
|
958e1b |
time (as long as all eps support the same amount of streams), but lets stick
|
|
|
958e1b |
to the kernel API.
|
|
|
958e1b |
|
|
|
958e1b |
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
|
|
|
958e1b |
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
|
|
958e1b |
(cherry picked from commit 72391da50621c9f11bb8c57193ab2d1ad8bc5ad8)
|
|
|
958e1b |
---
|
|
|
958e1b |
hw/usb/hcd-xhci.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
958e1b |
1 file changed, 117 insertions(+)
|
|
|
958e1b |
|
|
|
958e1b |
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
|
|
|
958e1b |
---
|
|
|
958e1b |
hw/usb/hcd-xhci.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
958e1b |
1 file changed, 117 insertions(+)
|
|
|
958e1b |
|
|
|
958e1b |
diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c
|
|
|
958e1b |
index 944b255..5f0840b 100644
|
|
|
958e1b |
--- a/hw/usb/hcd-xhci.c
|
|
|
958e1b |
+++ b/hw/usb/hcd-xhci.c
|
|
|
958e1b |
@@ -1153,6 +1153,111 @@ static void xhci_free_streams(XHCIEPContext *epctx)
|
|
|
958e1b |
epctx->nr_pstreams = 0;
|
|
|
958e1b |
}
|
|
|
958e1b |
|
|
|
958e1b |
+static int xhci_epmask_to_eps_with_streams(XHCIState *xhci,
|
|
|
958e1b |
+ unsigned int slotid,
|
|
|
958e1b |
+ uint32_t epmask,
|
|
|
958e1b |
+ XHCIEPContext **epctxs,
|
|
|
958e1b |
+ USBEndpoint **eps)
|
|
|
958e1b |
+{
|
|
|
958e1b |
+ XHCISlot *slot;
|
|
|
958e1b |
+ XHCIEPContext *epctx;
|
|
|
958e1b |
+ USBEndpoint *ep;
|
|
|
958e1b |
+ int i, j;
|
|
|
958e1b |
+
|
|
|
958e1b |
+ assert(slotid >= 1 && slotid <= xhci->numslots);
|
|
|
958e1b |
+
|
|
|
958e1b |
+ slot = &xhci->slots[slotid - 1];
|
|
|
958e1b |
+
|
|
|
958e1b |
+ for (i = 2, j = 0; i <= 31; i++) {
|
|
|
958e1b |
+ if (!(epmask & (1 << i))) {
|
|
|
958e1b |
+ continue;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
+ epctx = slot->eps[i - 1];
|
|
|
958e1b |
+ ep = xhci_epid_to_usbep(xhci, slotid, i);
|
|
|
958e1b |
+ if (!epctx || !epctx->nr_pstreams || !ep) {
|
|
|
958e1b |
+ continue;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
+ if (epctxs) {
|
|
|
958e1b |
+ epctxs[j] = epctx;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+ eps[j++] = ep;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+ return j;
|
|
|
958e1b |
+}
|
|
|
958e1b |
+
|
|
|
958e1b |
+static void xhci_free_device_streams(XHCIState *xhci, unsigned int slotid,
|
|
|
958e1b |
+ uint32_t epmask)
|
|
|
958e1b |
+{
|
|
|
958e1b |
+ USBEndpoint *eps[30];
|
|
|
958e1b |
+ int nr_eps;
|
|
|
958e1b |
+
|
|
|
958e1b |
+ nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, NULL, eps);
|
|
|
958e1b |
+ if (nr_eps) {
|
|
|
958e1b |
+ usb_device_free_streams(eps[0]->dev, eps, nr_eps);
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+}
|
|
|
958e1b |
+
|
|
|
958e1b |
+static TRBCCode xhci_alloc_device_streams(XHCIState *xhci, unsigned int slotid,
|
|
|
958e1b |
+ uint32_t epmask)
|
|
|
958e1b |
+{
|
|
|
958e1b |
+ XHCIEPContext *epctxs[30];
|
|
|
958e1b |
+ USBEndpoint *eps[30];
|
|
|
958e1b |
+ int i, r, nr_eps, req_nr_streams, dev_max_streams;
|
|
|
958e1b |
+
|
|
|
958e1b |
+ nr_eps = xhci_epmask_to_eps_with_streams(xhci, slotid, epmask, epctxs,
|
|
|
958e1b |
+ eps);
|
|
|
958e1b |
+ if (nr_eps == 0) {
|
|
|
958e1b |
+ return CC_SUCCESS;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
+ req_nr_streams = epctxs[0]->nr_pstreams;
|
|
|
958e1b |
+ dev_max_streams = eps[0]->max_streams;
|
|
|
958e1b |
+
|
|
|
958e1b |
+ for (i = 1; i < nr_eps; i++) {
|
|
|
958e1b |
+ /*
|
|
|
958e1b |
+ * HdG: I don't expect these to ever trigger, but if they do we need
|
|
|
958e1b |
+ * to come up with another solution, ie group identical endpoints
|
|
|
958e1b |
+ * together and make an usb_device_alloc_streams call per group.
|
|
|
958e1b |
+ */
|
|
|
958e1b |
+ if (epctxs[i]->nr_pstreams != req_nr_streams) {
|
|
|
958e1b |
+ FIXME("guest streams config not identical for all eps");
|
|
|
958e1b |
+ return CC_RESOURCE_ERROR;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+ if (eps[i]->max_streams != dev_max_streams) {
|
|
|
958e1b |
+ FIXME("device streams config not identical for all eps");
|
|
|
958e1b |
+ return CC_RESOURCE_ERROR;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
+ /*
|
|
|
958e1b |
+ * max-streams in both the device descriptor and in the controller is a
|
|
|
958e1b |
+ * power of 2. But stream id 0 is reserved, so if a device can do up to 4
|
|
|
958e1b |
+ * streams the guest will ask for 5 rounded up to the next power of 2 which
|
|
|
958e1b |
+ * becomes 8. For emulated devices usb_device_alloc_streams is a nop.
|
|
|
958e1b |
+ *
|
|
|
958e1b |
+ * For redirected devices however this is an issue, as there we must ask
|
|
|
958e1b |
+ * the real xhci controller to alloc streams, and the host driver for the
|
|
|
958e1b |
+ * real xhci controller will likely disallow allocating more streams then
|
|
|
958e1b |
+ * the device can handle.
|
|
|
958e1b |
+ *
|
|
|
958e1b |
+ * So we limit the requested nr_streams to the maximum number the device
|
|
|
958e1b |
+ * can handle.
|
|
|
958e1b |
+ */
|
|
|
958e1b |
+ if (req_nr_streams > dev_max_streams) {
|
|
|
958e1b |
+ req_nr_streams = dev_max_streams;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
+ r = usb_device_alloc_streams(eps[0]->dev, eps, nr_eps, req_nr_streams);
|
|
|
958e1b |
+ if (r != 0) {
|
|
|
958e1b |
+ fprintf(stderr, "xhci: alloc streams failed\n");
|
|
|
958e1b |
+ return CC_RESOURCE_ERROR;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
+ return CC_SUCCESS;
|
|
|
958e1b |
+}
|
|
|
958e1b |
+
|
|
|
958e1b |
static XHCIStreamContext *xhci_find_stream(XHCIEPContext *epctx,
|
|
|
958e1b |
unsigned int streamid,
|
|
|
958e1b |
uint32_t *cc_error)
|
|
|
958e1b |
@@ -2325,6 +2430,8 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
|
|
|
958e1b |
return CC_CONTEXT_STATE_ERROR;
|
|
|
958e1b |
}
|
|
|
958e1b |
|
|
|
958e1b |
+ xhci_free_device_streams(xhci, slotid, ictl_ctx[0] | ictl_ctx[1]);
|
|
|
958e1b |
+
|
|
|
958e1b |
for (i = 2; i <= 31; i++) {
|
|
|
958e1b |
if (ictl_ctx[0] & (1<
|
|
|
958e1b |
xhci_disable_ep(xhci, slotid, i);
|
|
|
958e1b |
@@ -2346,6 +2453,16 @@ static TRBCCode xhci_configure_slot(XHCIState *xhci, unsigned int slotid,
|
|
|
958e1b |
}
|
|
|
958e1b |
}
|
|
|
958e1b |
|
|
|
958e1b |
+ res = xhci_alloc_device_streams(xhci, slotid, ictl_ctx[1]);
|
|
|
958e1b |
+ if (res != CC_SUCCESS) {
|
|
|
958e1b |
+ for (i = 2; i <= 31; i++) {
|
|
|
958e1b |
+ if (ictl_ctx[1] & (1 << i)) {
|
|
|
958e1b |
+ xhci_disable_ep(xhci, slotid, i);
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+ return res;
|
|
|
958e1b |
+ }
|
|
|
958e1b |
+
|
|
|
958e1b |
slot_ctx[3] &= ~(SLOT_STATE_MASK << SLOT_STATE_SHIFT);
|
|
|
958e1b |
slot_ctx[3] |= SLOT_CONFIGURED << SLOT_STATE_SHIFT;
|
|
|
958e1b |
slot_ctx[0] &= ~(SLOT_CONTEXT_ENTRIES_MASK << SLOT_CONTEXT_ENTRIES_SHIFT);
|
|
|
958e1b |
--
|
|
|
958e1b |
1.8.3.1
|
|
|
958e1b |
|