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