a5bd9f6
From a79437c27655683e0d1bff3224c63839ae00455c Mon Sep 17 00:00:00 2001
a5bd9f6
From: =?UTF-8?q?Ale=C5=A1=20Nesrsta?=  <starous@volny.cz>
a5bd9f6
Date: Fri, 12 Apr 2013 20:42:46 +0200
a5bd9f6
Subject: [PATCH 291/364] 	Fix handling of split transfers.
a5bd9f6
a5bd9f6
---
a5bd9f6
 ChangeLog                  |  4 ++++
a5bd9f6
 grub-core/bus/usb/ehci.c   | 25 ++++++++++-------------
a5bd9f6
 grub-core/bus/usb/usbhub.c | 50 +++++++++++++++++++++++++++++++++++++++-------
a5bd9f6
 include/grub/usb.h         |  4 ++--
a5bd9f6
 4 files changed, 59 insertions(+), 24 deletions(-)
a5bd9f6
a5bd9f6
diff --git a/ChangeLog b/ChangeLog
a5bd9f6
index 79563b8..e8e4569 100644
a5bd9f6
--- a/ChangeLog
a5bd9f6
+++ b/ChangeLog
a5bd9f6
@@ -1,3 +1,7 @@
a5bd9f6
+2013-04-12  AleŇ° Nesrsta  <starous@volny.cz>
a5bd9f6
+
a5bd9f6
+	Fix handling of split transfers.
a5bd9f6
+
a5bd9f6
 2013-04-12  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
 
a5bd9f6
 	* grub-core/net/http.c: Fix bad free.
a5bd9f6
diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c
a5bd9f6
index e902fcd..18b12b2 100644
a5bd9f6
--- a/grub-core/bus/usb/ehci.c
a5bd9f6
+++ b/grub-core/bus/usb/ehci.c
a5bd9f6
@@ -798,7 +798,7 @@ grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
a5bd9f6
   /* Set ownership of root hub ports to EHCI */
a5bd9f6
   grub_ehci_oper_write32 (e, GRUB_EHCI_CONFIG_FLAG, GRUB_EHCI_CF_EHCI_OWNER);
a5bd9f6
 
a5bd9f6
-  /* Enable asynchronous list */
a5bd9f6
+  /* Enable both lists */
a5bd9f6
   grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND,
a5bd9f6
 			  GRUB_EHCI_CMD_AS_ENABL
a5bd9f6
 			  | GRUB_EHCI_CMD_PS_ENABL
a5bd9f6
@@ -942,9 +942,9 @@ grub_ehci_setup_qh (grub_ehci_qh_t qh, grub_usb_transfer_t transfer)
a5bd9f6
    * SplitCompletionMask - AFAIK it is ignored in asynchronous list,
a5bd9f6
    * InterruptScheduleMask - AFAIK it should be zero in async. list */
a5bd9f6
   ep_cap |= GRUB_EHCI_MULT_THREE;
a5bd9f6
-  ep_cap |= (transfer->dev->port << GRUB_EHCI_DEVPORT_OFF)
a5bd9f6
+  ep_cap |= (transfer->dev->split_hubport << GRUB_EHCI_DEVPORT_OFF)
a5bd9f6
     & GRUB_EHCI_DEVPORT_MASK;
a5bd9f6
-  ep_cap |= (transfer->dev->hubaddr << GRUB_EHCI_HUBADDR_OFF)
a5bd9f6
+  ep_cap |= (transfer->dev->split_hubaddr << GRUB_EHCI_HUBADDR_OFF)
a5bd9f6
     & GRUB_EHCI_HUBADDR_MASK;
a5bd9f6
   if (transfer->dev->speed == GRUB_USB_SPEED_LOW
a5bd9f6
       && transfer->type != GRUB_USB_TRANSACTION_TYPE_CONTROL)
a5bd9f6
@@ -1261,16 +1261,6 @@ grub_ehci_setup_transfer (grub_usb_controller_t dev,
a5bd9f6
     /* XXX: Fix it: Currently we don't do anything to restart EHCI */
a5bd9f6
     return GRUB_USB_ERR_INTERNAL;
a5bd9f6
 
a5bd9f6
-  /* Check if transfer is not high speed and connected to root hub.
a5bd9f6
-   * It should not happened but... */
a5bd9f6
-  if ((transfer->dev->speed != GRUB_USB_SPEED_HIGH)
a5bd9f6
-      && !transfer->dev->hubaddr)
a5bd9f6
-    {
a5bd9f6
-      grub_error (GRUB_USB_ERR_BADDEVICE,
a5bd9f6
-		  "FULL/LOW speed device on EHCI port!?!");
a5bd9f6
-      return GRUB_USB_ERR_BADDEVICE;
a5bd9f6
-    }
a5bd9f6
-
a5bd9f6
   /* Allocate memory for controller transfer data.  */
a5bd9f6
   cdata = grub_malloc (sizeof (*cdata));
a5bd9f6
   if (!cdata)
a5bd9f6
@@ -1887,13 +1877,18 @@ grub_ehci_fini_hw (int noreturn __attribute__ ((unused)))
a5bd9f6
   /* We should disable all EHCI HW to prevent any DMA access etc. */
a5bd9f6
   for (e = ehci; e; e = e->next)
a5bd9f6
     {
a5bd9f6
+      /* Disable both lists */
a5bd9f6
+      grub_ehci_oper_write32 (e, GRUB_EHCI_COMMAND,
a5bd9f6
+        ~(GRUB_EHCI_CMD_AS_ENABL | GRUB_EHCI_CMD_PS_ENABL)
a5bd9f6
+        & grub_ehci_oper_read32 (e, GRUB_EHCI_COMMAND));
a5bd9f6
+
a5bd9f6
       /* Check if EHCI is halted and halt it if not */
a5bd9f6
       if (grub_ehci_halt (e) != GRUB_USB_ERR_NONE)
a5bd9f6
-	grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI halt timeout");
a5bd9f6
+	grub_error (GRUB_ERR_TIMEOUT, "fini_hw: EHCI halt timeout");
a5bd9f6
 
a5bd9f6
       /* Reset EHCI */
a5bd9f6
       if (grub_ehci_reset (e) != GRUB_USB_ERR_NONE)
a5bd9f6
-	grub_error (GRUB_ERR_TIMEOUT, "restore_hw: EHCI reset timeout");
a5bd9f6
+	grub_error (GRUB_ERR_TIMEOUT, "fini_hw: EHCI reset timeout");
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
   return GRUB_USB_ERR_NONE;
a5bd9f6
diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
a5bd9f6
index 6fc9d02..e3b7d40 100644
a5bd9f6
--- a/grub-core/bus/usb/usbhub.c
a5bd9f6
+++ b/grub-core/bus/usb/usbhub.c
a5bd9f6
@@ -49,7 +49,7 @@ static grub_usb_controller_dev_t grub_usb_list;
a5bd9f6
 static grub_usb_device_t
a5bd9f6
 grub_usb_hub_add_dev (grub_usb_controller_t controller,
a5bd9f6
                       grub_usb_speed_t speed,
a5bd9f6
-                      int port, int hubaddr)
a5bd9f6
+                      int split_hubport, int split_hubaddr)
a5bd9f6
 {
a5bd9f6
   grub_usb_device_t dev;
a5bd9f6
   int i;
a5bd9f6
@@ -63,8 +63,8 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
a5bd9f6
 
a5bd9f6
   dev->controller = *controller;
a5bd9f6
   dev->speed = speed;
a5bd9f6
-  dev->port = port;
a5bd9f6
-  dev->hubaddr = hubaddr;
a5bd9f6
+  dev->split_hubport = split_hubport;
a5bd9f6
+  dev->split_hubaddr = split_hubaddr;
a5bd9f6
 
a5bd9f6
   err = grub_usb_device_initialize (dev);
a5bd9f6
   if (err)
a5bd9f6
@@ -108,8 +108,8 @@ grub_usb_hub_add_dev (grub_usb_controller_t controller,
a5bd9f6
 
a5bd9f6
   grub_dprintf ("usb", "Added new usb device: %p, addr=%d\n",
a5bd9f6
 		dev, i);
a5bd9f6
-  grub_dprintf ("usb", "speed=%d, port=%d, hubaddr=%d\n",
a5bd9f6
-		speed, port, hubaddr);
a5bd9f6
+  grub_dprintf ("usb", "speed=%d, split_hubport=%d, split_hubaddr=%d\n",
a5bd9f6
+		speed, split_hubport, split_hubaddr);
a5bd9f6
 
a5bd9f6
   /* Wait "recovery interval", spec. says 2ms */
a5bd9f6
   grub_millisleep (2);
a5bd9f6
@@ -219,7 +219,12 @@ attach_root_port (struct grub_usb_hub *hub, int portno,
a5bd9f6
   grub_boot_time ("Port enabled");
a5bd9f6
 
a5bd9f6
   /* Enable the port and create a device.  */
a5bd9f6
-  dev = grub_usb_hub_add_dev (hub->controller, speed, portno, 0);
a5bd9f6
+  /* High speed device needs not transaction translation
a5bd9f6
+     and full/low speed device cannot be connected to EHCI root hub
a5bd9f6
+     and full/low speed device connected to OHCI/UHCI needs not
a5bd9f6
+     transaction translation - e.g. hubport and hubaddr should be
a5bd9f6
+     always none (zero) for any device connected to any root hub. */
a5bd9f6
+  dev = grub_usb_hub_add_dev (hub->controller, speed, 0, 0);
a5bd9f6
   hub->controller->dev->pending_reset = 0;
a5bd9f6
   npending--;
a5bd9f6
   if (! dev)
a5bd9f6
@@ -593,6 +598,8 @@ poll_nonroot_hub (grub_usb_device_t dev)
a5bd9f6
 	    {
a5bd9f6
 	      grub_usb_speed_t speed;
a5bd9f6
 	      grub_usb_device_t next_dev;
a5bd9f6
+	      int split_hubport = 0;
a5bd9f6
+	      int split_hubaddr = 0;
a5bd9f6
 
a5bd9f6
 	      /* Determine the device speed.  */
a5bd9f6
 	      if (status & GRUB_USB_HUB_STATUS_PORT_LOWSPEED)
a5bd9f6
@@ -608,8 +615,37 @@ poll_nonroot_hub (grub_usb_device_t dev)
a5bd9f6
 	      /* Wait a recovery time after reset, spec. says 10ms */
a5bd9f6
 	      grub_millisleep (10);
a5bd9f6
 
a5bd9f6
+              /* Find correct values for SPLIT hubport and hubaddr */
a5bd9f6
+	      if (speed == GRUB_USB_SPEED_HIGH)
a5bd9f6
+	        {
a5bd9f6
+		  /* HIGH speed device needs not transaction translation */
a5bd9f6
+		  split_hubport = 0;
a5bd9f6
+		  split_hubaddr = 0;
a5bd9f6
+		}
a5bd9f6
+	      else
a5bd9f6
+	        /* FULL/LOW device needs hub port and hub address
a5bd9f6
+		   for transaction translation (if connected to EHCI) */
a5bd9f6
+	        if (dev->speed == GRUB_USB_SPEED_HIGH)
a5bd9f6
+	          {
a5bd9f6
+		    /* This port is the first FULL/LOW speed port
a5bd9f6
+		       in the chain from root hub. Attached device
a5bd9f6
+		       should use its port number and hub address */
a5bd9f6
+		    split_hubport = i;
a5bd9f6
+		    split_hubaddr = dev->addr;
a5bd9f6
+		  }
a5bd9f6
+	        else
a5bd9f6
+	          {
a5bd9f6
+		    /* This port is NOT the first FULL/LOW speed port
a5bd9f6
+		       in the chain from root hub. Attached device
a5bd9f6
+		       should use values inherited from some parent
a5bd9f6
+		       HIGH speed hub - if any. */
a5bd9f6
+		    split_hubport = dev->split_hubport;
a5bd9f6
+		    split_hubaddr = dev->split_hubaddr;
a5bd9f6
+		  }
a5bd9f6
+		
a5bd9f6
 	      /* Add the device and assign a device address to it.  */
a5bd9f6
-	      next_dev = grub_usb_hub_add_dev (&dev->controller, speed, i, dev->addr);
a5bd9f6
+	      next_dev = grub_usb_hub_add_dev (&dev->controller, speed,
a5bd9f6
+					       split_hubport, split_hubaddr);
a5bd9f6
 	      if (dev->controller.dev->pending_reset)
a5bd9f6
 		{
a5bd9f6
 		  dev->controller.dev->pending_reset = 0;
a5bd9f6
diff --git a/include/grub/usb.h b/include/grub/usb.h
a5bd9f6
index 9e2c221..1cc9942 100644
a5bd9f6
--- a/include/grub/usb.h
a5bd9f6
+++ b/include/grub/usb.h
a5bd9f6
@@ -225,9 +225,9 @@ struct grub_usb_device
a5bd9f6
   struct grub_usb_desc_endp *hub_endpoint;
a5bd9f6
 
a5bd9f6
   /* EHCI Split Transfer information */
a5bd9f6
-  int port;
a5bd9f6
+  int split_hubport;
a5bd9f6
 
a5bd9f6
-  int hubaddr;
a5bd9f6
+  int split_hubaddr;
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
 
a5bd9f6
-- 
a5bd9f6
1.8.1.4
a5bd9f6