xhci: Support USB hubs on xhci controllers.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
diff --git a/src/hw/usb-xhci.c b/src/hw/usb-xhci.c
index 4529724..b30bc01 100644
--- a/src/hw/usb-xhci.c
+++ b/src/hw/usb-xhci.c
@@ -818,24 +818,65 @@
     }
     memset(in, 0, size);
 
-    u32 route = 0;
-    while (usbdev->hub->usbdev) {
-        route <<= 4;
-        route |= (usbdev->port+1) & 0xf;
-        usbdev = usbdev->hub->usbdev;
-    }
-
     in->add = 0x01;
     struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64];
     slot->ctx[0]    |= (1 << 27); // context entries
     slot->ctx[0]    |= speed_to_xhci[usbdev->speed] << 20;
-    slot->ctx[0]    |= route;
+
+    // Set high-speed hub flags.
+    struct usbdevice_s *hubdev = usbdev->hub->usbdev;
+    if (hubdev) {
+        if (usbdev->speed == USB_LOWSPEED || usbdev->speed == USB_FULLSPEED) {
+            struct xhci_pipe *hpipe = container_of(
+                hubdev->defpipe, struct xhci_pipe, pipe);
+            if (hubdev->speed == USB_HIGHSPEED) {
+                slot->ctx[2] |= hpipe->slotid;
+                slot->ctx[2] |= (usbdev->port+1) << 8;
+            } else {
+                struct xhci_slotctx *hslot = (void*)xhci->devs[hpipe->slotid].ptr_low;
+                slot->ctx[2] = hslot->ctx[2];
+            }
+        }
+        u32 route = 0;
+        while (usbdev->hub->usbdev) {
+            route <<= 4;
+            route |= (usbdev->port+1) & 0xf;
+            usbdev = usbdev->hub->usbdev;
+        }
+        slot->ctx[0]    |= route;
+    }
+
     slot->ctx[1]    |= (usbdev->port+1) << 16;
-    /* TODO ctx0: hub bit */
-    /* TODO ctx1: hub ports */
+
     return in;
 }
 
+static int xhci_config_hub(struct usbhub_s *hub)
+{
+    struct usb_xhci_s *xhci = container_of(
+        hub->cntl, struct usb_xhci_s, usb);
+    struct xhci_pipe *pipe = container_of(
+        hub->usbdev->defpipe, struct xhci_pipe, pipe);
+    struct xhci_slotctx *hdslot = (void*)xhci->devs[pipe->slotid].ptr_low;
+    if ((hdslot->ctx[3] >> 27) == 3)
+        // Already configured
+        return 0;
+    struct xhci_inctx *in = xhci_alloc_inctx(hub->usbdev);
+    if (!in)
+        return -1;
+    struct xhci_slotctx *slot = (void*)&in[1 << xhci->context64];
+    slot->ctx[0] |= 1 << 26;
+    slot->ctx[1] |= hub->portcount << 24;
+
+    int cc = xhci_cmd_configure_endpoint(xhci, pipe->slotid, in);
+    free(in);
+    if (cc != CC_SUCCESS) {
+        dprintf(1, "%s: configure hub: failed (cc %d)\n", __func__, cc);
+        return -1;
+    }
+    return 0;
+}
+
 struct usb_pipe *
 xhci_alloc_pipe(struct usbdevice_s *usbdev
                 , struct usb_endpoint_descriptor *epdesc)
@@ -875,6 +916,12 @@
     dprintf(3, "%s: usbdev %p, ring %p, slotid %d, epid %d\n", __func__,
             usbdev, &pipe->reqs, pipe->slotid, pipe->epid);
     if (pipe->epid == 1) {
+        if (usbdev->hub->usbdev) {
+            // Make sure parent hub is configured.
+            int ret = xhci_config_hub(usbdev->hub);
+            if (ret)
+                goto fail;
+        }
         // Enable slot and send set_address command.
         u32 size = (sizeof(struct xhci_slotctx) * 32) << xhci->context64;
         struct xhci_slotctx *dev = memalign_high(1024 << xhci->context64, size);