a5bd9f6
From 880bee5493e9515001f87355f54af34df64c90f4 Mon Sep 17 00:00:00 2001
a5bd9f6
From: Vladimir 'phcoder' Serbinenko <phcoder@gmail.com>
a5bd9f6
Date: Tue, 19 Mar 2013 08:17:51 +0100
a5bd9f6
Subject: [PATCH 210/364] 	Better estimate the maximum USB transfer size.
a5bd9f6
a5bd9f6
---
a5bd9f6
 ChangeLog                    |  4 ++
a5bd9f6
 grub-core/bus/usb/ehci.c     |  4 +-
a5bd9f6
 grub-core/bus/usb/ohci.c     |  4 +-
a5bd9f6
 grub-core/bus/usb/uhci.c     |  4 +-
a5bd9f6
 grub-core/bus/usb/usbtrans.c | 96 +++++++++++++++++++++++++++-----------------
a5bd9f6
 include/grub/usb.h           |  7 ++++
a5bd9f6
 6 files changed, 80 insertions(+), 39 deletions(-)
a5bd9f6
a5bd9f6
diff --git a/ChangeLog b/ChangeLog
a5bd9f6
index ad84d27..d331cb4 100644
a5bd9f6
--- a/ChangeLog
a5bd9f6
+++ b/ChangeLog
a5bd9f6
@@ -1,3 +1,7 @@
a5bd9f6
+2013-03-19  AleŇ° Nesrsta  <starous@volny.cz>
a5bd9f6
+
a5bd9f6
+	Better estimate the maximum USB transfer size.
a5bd9f6
+
a5bd9f6
 2013-03-17  Vladimir Serbinenko  <phcoder@gmail.com>
a5bd9f6
 
a5bd9f6
 	Resend a packet if we got the wrong buffer in status.
a5bd9f6
diff --git a/grub-core/bus/usb/ehci.c b/grub-core/bus/usb/ehci.c
a5bd9f6
index 9215866..c60873d 100644
a5bd9f6
--- a/grub-core/bus/usb/ehci.c
a5bd9f6
+++ b/grub-core/bus/usb/ehci.c
a5bd9f6
@@ -1902,7 +1902,9 @@ static struct grub_usb_controller_dev usb_controller = {
a5bd9f6
   .cancel_transfer = grub_ehci_cancel_transfer,
a5bd9f6
   .hubports = grub_ehci_hubports,
a5bd9f6
   .portstatus = grub_ehci_portstatus,
a5bd9f6
-  .detect_dev = grub_ehci_detect_dev
a5bd9f6
+  .detect_dev = grub_ehci_detect_dev,
a5bd9f6
+  /* estimated max. count of TDs for one bulk transfer */
a5bd9f6
+  .max_bulk_tds = GRUB_EHCI_N_TD * 3 / 4 
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
 GRUB_MOD_INIT (ehci)
a5bd9f6
diff --git a/grub-core/bus/usb/ohci.c b/grub-core/bus/usb/ohci.c
a5bd9f6
index 835bb15..2f3fd91 100644
a5bd9f6
--- a/grub-core/bus/usb/ohci.c
a5bd9f6
+++ b/grub-core/bus/usb/ohci.c
a5bd9f6
@@ -1431,7 +1431,9 @@ static struct grub_usb_controller_dev usb_controller =
a5bd9f6
   .cancel_transfer = grub_ohci_cancel_transfer,
a5bd9f6
   .hubports = grub_ohci_hubports,
a5bd9f6
   .portstatus = grub_ohci_portstatus,
a5bd9f6
-  .detect_dev = grub_ohci_detect_dev
a5bd9f6
+  .detect_dev = grub_ohci_detect_dev,
a5bd9f6
+  /* estimated max. count of TDs for one bulk transfer */
a5bd9f6
+  .max_bulk_tds = GRUB_OHCI_TDS * 3 / 4
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
 static struct grub_preboot *fini_hnd;
a5bd9f6
diff --git a/grub-core/bus/usb/uhci.c b/grub-core/bus/usb/uhci.c
a5bd9f6
index 74de392..3639c42 100644
a5bd9f6
--- a/grub-core/bus/usb/uhci.c
a5bd9f6
+++ b/grub-core/bus/usb/uhci.c
a5bd9f6
@@ -823,7 +823,9 @@ static struct grub_usb_controller_dev usb_controller =
a5bd9f6
   .cancel_transfer = grub_uhci_cancel_transfer,
a5bd9f6
   .hubports = grub_uhci_hubports,
a5bd9f6
   .portstatus = grub_uhci_portstatus,
a5bd9f6
-  .detect_dev = grub_uhci_detect_dev
a5bd9f6
+  .detect_dev = grub_uhci_detect_dev,
a5bd9f6
+  /* estimated max. count of TDs for one bulk transfer */
a5bd9f6
+  .max_bulk_tds = N_TD * 3 / 4
a5bd9f6
 };
a5bd9f6
 
a5bd9f6
 GRUB_MOD_INIT(uhci)
a5bd9f6
diff --git a/grub-core/bus/usb/usbtrans.c b/grub-core/bus/usb/usbtrans.c
a5bd9f6
index 154c72d..4c4d8b4 100644
a5bd9f6
--- a/grub-core/bus/usb/usbtrans.c
a5bd9f6
+++ b/grub-core/bus/usb/usbtrans.c
a5bd9f6
@@ -25,6 +25,26 @@
a5bd9f6
 #include <grub/usbtrans.h>
a5bd9f6
 #include <grub/time.h>
a5bd9f6
 
a5bd9f6
+
a5bd9f6
+static inline unsigned int
a5bd9f6
+grub_usb_bulk_maxpacket (grub_usb_device_t dev, int endpoint)
a5bd9f6
+{
a5bd9f6
+  unsigned int max = 64;
a5bd9f6
+
a5bd9f6
+  /* Use the maximum packet size given in the endpoint descriptor.  */
a5bd9f6
+  if (dev->initialized)
a5bd9f6
+    {
a5bd9f6
+      struct grub_usb_desc_endp *endpdesc;
a5bd9f6
+      endpdesc = grub_usb_get_endpdescriptor (dev, endpoint);
a5bd9f6
+
a5bd9f6
+      if (endpdesc)
a5bd9f6
+	max = endpdesc->maxpacket;
a5bd9f6
+    }
a5bd9f6
+
a5bd9f6
+  return max;
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+
a5bd9f6
 static grub_usb_err_t
a5bd9f6
 grub_usb_execute_and_wait_transfer (grub_usb_device_t dev, 
a5bd9f6
 				    grub_usb_transfer_t transfer,
a5bd9f6
@@ -224,20 +244,6 @@ grub_usb_bulk_setup_readwrite (grub_usb_device_t dev,
a5bd9f6
   if (type == GRUB_USB_TRANSFER_TYPE_OUT)
a5bd9f6
     grub_memcpy ((char *) data, data_in, size);
a5bd9f6
 
a5bd9f6
-  /* Use the maximum packet size given in the endpoint descriptor.  */
a5bd9f6
-  if (dev->initialized)
a5bd9f6
-    {
a5bd9f6
-      struct grub_usb_desc_endp *endpdesc;
a5bd9f6
-      endpdesc = grub_usb_get_endpdescriptor (dev, endpoint);
a5bd9f6
-
a5bd9f6
-      if (endpdesc)
a5bd9f6
-	max = endpdesc->maxpacket;
a5bd9f6
-      else
a5bd9f6
-	max = 64;
a5bd9f6
-    }
a5bd9f6
-  else
a5bd9f6
-    max = 64;
a5bd9f6
-
a5bd9f6
   /* Create a transfer.  */
a5bd9f6
   transfer = grub_malloc (sizeof (struct grub_usb_transfer));
a5bd9f6
   if (! transfer)
a5bd9f6
@@ -246,6 +252,8 @@ grub_usb_bulk_setup_readwrite (grub_usb_device_t dev,
a5bd9f6
       return NULL;
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
+  max = grub_usb_bulk_maxpacket (dev, endpoint);
a5bd9f6
+
a5bd9f6
   datablocks = ((size + max - 1) / max);
a5bd9f6
   transfer->transcnt = datablocks;
a5bd9f6
   transfer->size = size - 1;
a5bd9f6
@@ -333,38 +341,36 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
a5bd9f6
   return err;
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
-grub_usb_err_t
a5bd9f6
-grub_usb_bulk_write (grub_usb_device_t dev,
a5bd9f6
-		     int endpoint, grub_size_t size, char *data)
a5bd9f6
-{
a5bd9f6
-  grub_size_t actual;
a5bd9f6
-  grub_usb_err_t err;
a5bd9f6
-
a5bd9f6
-  err = grub_usb_bulk_readwrite (dev, endpoint, size, data,
a5bd9f6
-				 GRUB_USB_TRANSFER_TYPE_OUT, 1000, &actual);
a5bd9f6
-  if (!err && actual != size)
a5bd9f6
-    err = GRUB_USB_ERR_DATA;
a5bd9f6
-  return err;
a5bd9f6
-}
a5bd9f6
-
a5bd9f6
-grub_usb_err_t
a5bd9f6
-grub_usb_bulk_read (grub_usb_device_t dev,
a5bd9f6
-		    int endpoint, grub_size_t size, char *data)
a5bd9f6
+static grub_usb_err_t
a5bd9f6
+grub_usb_bulk_readwrite_packetize (grub_usb_device_t dev,
a5bd9f6
+				   int endpoint,
a5bd9f6
+				   grub_transfer_type_t type,
a5bd9f6
+				   grub_size_t size, char *data)
a5bd9f6
 {
a5bd9f6
   grub_size_t actual, transferred;
a5bd9f6
   grub_usb_err_t err;
a5bd9f6
   grub_size_t current_size, position;
a5bd9f6
+  grub_size_t max_bulk_transfer_len = MAX_USB_TRANSFER_LEN;
a5bd9f6
+  grub_size_t max;
a5bd9f6
+
a5bd9f6
+  if (dev->controller.dev->max_bulk_tds)
a5bd9f6
+    {
a5bd9f6
+      max = grub_usb_bulk_maxpacket (dev, endpoint);
a5bd9f6
+
a5bd9f6
+      /* Calculate max. possible length of bulk transfer */
a5bd9f6
+      max_bulk_transfer_len = dev->controller.dev->max_bulk_tds * max;
a5bd9f6
+    }
a5bd9f6
 
a5bd9f6
   for (position = 0, transferred = 0;
a5bd9f6
-       position < size; position += MAX_USB_TRANSFER_LEN)
a5bd9f6
+       position < size; position += max_bulk_transfer_len)
a5bd9f6
     {
a5bd9f6
       current_size = size - position;
a5bd9f6
-      if (current_size >= MAX_USB_TRANSFER_LEN)
a5bd9f6
-	current_size = MAX_USB_TRANSFER_LEN;
a5bd9f6
+      if (current_size >= max_bulk_transfer_len)
a5bd9f6
+	current_size = max_bulk_transfer_len;
a5bd9f6
       err = grub_usb_bulk_readwrite (dev, endpoint, current_size,
a5bd9f6
-              &data[position], GRUB_USB_TRANSFER_TYPE_IN, 1000, &actual);
a5bd9f6
+              &data[position], type, 1000, &actual);
a5bd9f6
       transferred += actual;
a5bd9f6
-      if (err || (current_size != actual) ) break;
a5bd9f6
+      if (err || (current_size != actual)) break;
a5bd9f6
     }
a5bd9f6
 
a5bd9f6
   if (!err && transferred != size)
a5bd9f6
@@ -373,6 +379,24 @@ grub_usb_bulk_read (grub_usb_device_t dev,
a5bd9f6
 }
a5bd9f6
 
a5bd9f6
 grub_usb_err_t
a5bd9f6
+grub_usb_bulk_write (grub_usb_device_t dev,
a5bd9f6
+		     int endpoint, grub_size_t size, char *data)
a5bd9f6
+{
a5bd9f6
+  return grub_usb_bulk_readwrite_packetize (dev, endpoint,
a5bd9f6
+					    GRUB_USB_TRANSFER_TYPE_OUT,
a5bd9f6
+					    size, data);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+grub_usb_err_t
a5bd9f6
+grub_usb_bulk_read (grub_usb_device_t dev,
a5bd9f6
+		    int endpoint, grub_size_t size, char *data)
a5bd9f6
+{
a5bd9f6
+  return grub_usb_bulk_readwrite_packetize (dev, endpoint,
a5bd9f6
+					    GRUB_USB_TRANSFER_TYPE_IN,
a5bd9f6
+					    size, data);
a5bd9f6
+}
a5bd9f6
+
a5bd9f6
+grub_usb_err_t
a5bd9f6
 grub_usb_check_transfer (grub_usb_transfer_t transfer, grub_size_t *actual)
a5bd9f6
 {
a5bd9f6
   grub_usb_err_t err;
a5bd9f6
diff --git a/include/grub/usb.h b/include/grub/usb.h
a5bd9f6
index cefa8b6..55f65f7 100644
a5bd9f6
--- a/include/grub/usb.h
a5bd9f6
+++ b/include/grub/usb.h
a5bd9f6
@@ -124,6 +124,13 @@ struct grub_usb_controller_dev
a5bd9f6
 
a5bd9f6
   /* Per controller flag - port reset pending, don't do another reset */
a5bd9f6
   grub_uint64_t pending_reset;
a5bd9f6
+
a5bd9f6
+  /* Max. number of transfer descriptors used per one bulk transfer */
a5bd9f6
+  /* The reason is to prevent "exhausting" of TD by large bulk */
a5bd9f6
+  /* transfer - number of TD is limited in USB host driver */
a5bd9f6
+  /* Value is calculated/estimated in driver - some TDs should be */
a5bd9f6
+  /* reserved for posible concurrent control or "interrupt" transfers */
a5bd9f6
+  grub_size_t max_bulk_tds;
a5bd9f6
   
a5bd9f6
   /* The next host controller.  */
a5bd9f6
   struct grub_usb_controller_dev *next;
a5bd9f6
-- 
a5bd9f6
1.8.1.4
a5bd9f6