From 127961742cf7992f6989c6e89a18ab6d8f0b297f Mon Sep 17 00:00:00 2001
From: Patrick Rudolph <patrick.rudolph@9elements.com>
Date: Thu, 3 Dec 2020 13:44:55 +0100
Subject: [PATCH 20/22] grub-core/bus/usb/usbhub: Add xHCI non root hub support

Tested on Intel PCH C246, the USB3 hub can be configured by grub.

Issues:
* USB3 devices connected behind that hub are sometimes not detected.

Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
---
 grub-core/bus/usb/usbhub.c | 38 +++++++++++++++++++++++++++++++++-----
 include/grub/usbdesc.h     |  1 +
 include/grub/usbtrans.h    |  4 ++++
 3 files changed, 38 insertions(+), 5 deletions(-)

diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
index b4b3a1a61..e96505aa9 100644
--- a/grub-core/bus/usb/usbhub.c
+++ b/grub-core/bus/usb/usbhub.c
@@ -148,19 +148,32 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
   return dev;
 }
 
-
+static grub_usb_err_t
+grub_usb_set_hub_depth(grub_usb_device_t dev, grub_uint8_t depth)
+{
+  return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
+			            | GRUB_USB_REQTYPE_CLASS
+			            | GRUB_USB_REQTYPE_TARGET_DEV),
+			      GRUB_USB_HUB_REQ_SET_HUB_DEPTH, depth,
+			      0, 0, NULL);
+}
+
 static grub_usb_err_t
 grub_usb_add_hub (grub_usb_device_t dev)
 {
   struct grub_usb_usb_hubdesc hubdesc;
   grub_usb_err_t err;
+  grub_uint16_t req;
   int i;
 
+  req = (dev->speed == GRUB_USB_SPEED_SUPER) ? GRUB_USB_DESCRIPTOR_SS_HUB :
+    GRUB_USB_DESCRIPTOR_HUB;
+
   err = grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_IN
 	  		            | GRUB_USB_REQTYPE_CLASS
 			            | GRUB_USB_REQTYPE_TARGET_DEV),
-                              GRUB_USB_REQ_GET_DESCRIPTOR,
-			      (GRUB_USB_DESCRIPTOR_HUB << 8) | 0,
+			      GRUB_USB_REQ_GET_DESCRIPTOR,
+			      (req << 8) | 0,
 			      0, sizeof (hubdesc), (char *) &hubdesc);
   if (err)
     return err;
@@ -183,6 +196,19 @@ grub_usb_add_hub (grub_usb_device_t dev)
       return GRUB_USB_ERR_INTERNAL;
     }
 
+  if (dev->speed == GRUB_USB_SPEED_SUPER)
+    {
+      grub_uint8_t depth;
+      grub_uint32_t route;
+      /* Depth maximum value is 5, but root hubs doesn't count */
+      for (depth = 0, route = dev->route; (route & 0xf) > 0; route >>= 4)
+	depth++;
+
+      err = grub_usb_set_hub_depth(dev, depth);
+      if (err)
+        return err;
+    }
+
   /* Power on all Hub ports.  */
   for (i = 1; i <= hubdesc.portcnt; i++)
     {
@@ -637,7 +663,9 @@ poll_nonroot_hub (grub_usb_device_t dev)
 	      int split_hubaddr = 0;
 
 	      /* Determine the device speed.  */
-	      if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
+	      if (dev->speed == GRUB_USB_SPEED_SUPER)
+	        speed = GRUB_USB_SPEED_SUPER;
+	      else if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
 		speed = GRUB_USB_SPEED_LOW;
 	      else
 		{
@@ -651,7 +679,7 @@ poll_nonroot_hub (grub_usb_device_t dev)
 	      grub_millisleep (10);
 
               /* Find correct values for SPLIT hubport and hubaddr */
-	      if (speed == GRUB_USB_SPEED_HIGH)
+	      if (speed == GRUB_USB_SPEED_HIGH || speed == GRUB_USB_SPEED_SUPER)
 	        {
 		  /* HIGH speed device needs not transaction translation */
 		  split_hubport = 0;
diff --git a/include/grub/usbdesc.h b/include/grub/usbdesc.h
index bb2ab2e27..1697aa465 100644
--- a/include/grub/usbdesc.h
+++ b/include/grub/usbdesc.h
@@ -30,6 +30,7 @@ typedef enum {
   GRUB_USB_DESCRIPTOR_ENDPOINT,
   GRUB_USB_DESCRIPTOR_DEBUG = 10,
   GRUB_USB_DESCRIPTOR_HUB = 0x29,
+  GRUB_USB_DESCRIPTOR_SS_HUB = 0x2a,
   GRUB_USB_DESCRIPTOR_SS_ENDPOINT_COMPANION = 0x30
 } grub_usb_descriptor_t;
 
diff --git a/include/grub/usbtrans.h b/include/grub/usbtrans.h
index 039ebed65..d6c3f71dc 100644
--- a/include/grub/usbtrans.h
+++ b/include/grub/usbtrans.h
@@ -110,6 +110,10 @@ enum
     GRUB_USB_REQ_SET_INTERFACE = 0x0B,
     GRUB_USB_REQ_SYNC_FRAME = 0x0C
   };
+enum
+  {
+    GRUB_USB_HUB_REQ_SET_HUB_DEPTH = 0x0C,
+  };
 
 #define GRUB_USB_FEATURE_ENDP_HALT	0x00
 #define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
-- 
2.39.2