All of lore.kernel.org
 help / color / mirror / Atom feed
* [Patch] [bug #26237] multiple problems with usb devices
@ 2010-03-14 22:23 Aleš Nesrsta
  2010-04-02 20:46 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-03-14 22:23 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 1963 bytes --]

Hello,

according to e-mail from Vladimir Serbinenko I am sending patch related
to (mostly) OHCI problem (bug #26237).
Workaround related to grub_memalign problem is removed from patch
because Vladimir Serbinenko wrotes that it will be corrected.

Patch was done by:

diff -urB  grub2-1.98~experimental.20100120 grub2-1.98~my_patched.20100312 > grub2-1.98~my_patched.20100312.patch

As I shortly read the patch file, there sometimes remains some few
things which can be optimized - sorry, it is mostly the rest from
debugging experiments (even I make cleaning of code today)...

Ales

> Od: Vladimir Serbinenko <INVALID.NOREPLY@gnu.org>
> Komu: Oliver Henshaw <yoho_ahoy@hotmail.com>, Vladimir Serbinenko
> <phcoder@gmail.com>, Ales Nesrsta <starous@volny.cz>, bug-grub@gnu.org
> Předmět: [bug #26237] multiple problems with usb devices
> Datum: Sat, 13 Mar 2010 22:08:44 +0000
> 
> Update of bug #26237 (project grub):
> 
>                  Release:                    None => Bazaar - trunk         
> 
>     _______________________________________________________
> 
> Follow-up Comment #6:
> 
> Hello. I found out the problem with grub_memalign. I'll apply the patch
> tomorrow. Some of the fixes can go directly in while other require
> investigation and perhaps even copyright assignment. Can you remove all
> gratuituous changes (like commenting out dprintfs, adding commented out code
> and change register names) and send patch in unified format (-u option in
> diff) to grub-devel@gnu.org? Porting code from linux is appropriate only if
> license is compatible and porting was approved by maintainer. Are perhaps
> interested in coding EHCI?
> 
>     _______________________________________________________
> 
> Reply to this item at:
> 
>   <http://savannah.gnu.org/bugs/?26237>
> 
> _______________________________________________
>   Message sent via/by Savannah
>   http://savannah.gnu.org/
> 
>

[-- Attachment #2: grub2-1.98~my_patched.20100312.patch --]
[-- Type: text/x-patch, Size: 32273 bytes --]

diff -urB grub2-1.98~experimental.20100120/bus/usb/ohci.c grub2-1.98~my_patched.20100312/bus/usb/ohci.c
--- grub2-1.98~experimental.20100120/bus/usb/ohci.c	2010-01-20 23:42:30.000000000 +0100
+++ grub2-1.98~my_patched.20100312/bus/usb/ohci.c	2010-03-14 20:34:55.000000000 +0100
@@ -84,15 +84,35 @@
   GRUB_OHCI_REG_INTENA,
   GRUB_OHCI_REG_INTDIS,
   GRUB_OHCI_REG_HCCA,
-  GRUB_OHCI_REG_PERIODIC,
+  GRUB_OHCI_REG_PERIODICCURR,
   GRUB_OHCI_REG_CONTROLHEAD,
   GRUB_OHCI_REG_CONTROLCURR,
   GRUB_OHCI_REG_BULKHEAD,
   GRUB_OHCI_REG_BULKCURR,
   GRUB_OHCI_REG_DONEHEAD,
   GRUB_OHCI_REG_FRAME_INTERVAL,
-  GRUB_OHCI_REG_RHUBA = 18,
-  GRUB_OHCI_REG_RHUBPORT = 21
+  GRUB_OHCI_REG_FRAME_REMAINING,
+  GRUB_OHCI_REG_FRAME_NUMBER,
+  GRUB_OHCI_REG_PERIODIC_START,
+  GRUB_OHCI_REG_LS_TRESHOLD,
+  GRUB_OHCI_REG_RHUB_DESC_A,
+  GRUB_OHCI_REG_RHUB_DESC_B,
+  GRUB_OHCI_REG_RHUB_STATUS,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_1,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_2,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_3,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_4,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_5,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_6,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_7,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_8,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_9,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_10,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_11,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_12,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_13,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_14,
+  GRUB_OHCI_REG_RHUB_PORT_STAT_15
 } grub_ohci_reg_t;
 
 static grub_uint32_t
@@ -254,20 +277,34 @@
       break;
     }
 
-  /* Generate no interrupts.  */
+  /* Generate no interrupts. Except last TD in ED but it is handled in
+   *  grub_ohci_transfer */
   token |= 7 << 21;
 
   /* Set the token.  */
   token |= toggle << 24;
   token |= 1 << 25;
 
-  buffer = (grub_uint32_t) data;
-  buffer_end = buffer + size - 1;
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+  
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size) 
+     {
+	 buffer = (grub_uint32_t) data;
+	 buffer_end = buffer + size - 1;
+         td->buffer = grub_cpu_to_le32 (buffer);
+         td->buffer_end = grub_cpu_to_le32 (buffer_end);
+     }
+  else 
+     {
+	td->buffer = 0;
+	td->buffer_end = 0;
+     }
 
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -276,13 +313,15 @@
 {
   struct grub_ohci *o = (struct grub_ohci *) dev->data;
   grub_ohci_ed_t ed;
-  grub_ohci_td_t td_list;
+  grub_ohci_td_t td_list ;
   grub_uint32_t target;
   grub_uint32_t td_tail;
   grub_uint32_t td_head;
   grub_uint32_t status;
   grub_uint32_t control;
   grub_usb_err_t err;
+  grub_uint8_t errcode = 0;
+  grub_ohci_td_t tderr = NULL;
   int i;
 
   /* Allocate an Endpoint Descriptor.  */
@@ -297,7 +336,8 @@
       return GRUB_USB_ERR_INTERNAL;
     }
 
-  grub_dprintf ("ohci", "alloc=%p\n", td_list);
+  grub_dprintf ("ohci", "alloc=%p, transcnt=0x%02x\n",
+                td_list, transfer->transcnt);
 
   /* Setup all Transfer Descriptors.  */
   for (i = 0; i < transfer->transcnt; i++)
@@ -310,6 +350,18 @@
       td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]);
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+  /* For safety's sake we partialy initialize last TD... */
+  td_list[transfer->transcnt].token = 0;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td =
+    (grub_uint32_t) &td_list[transfer->transcnt];
+  
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
@@ -336,30 +388,48 @@
   grub_dprintf ("ohci", "program OHCI\n");
 
   /* Program the OHCI to actually transfer.  */
+
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   switch (transfer->type)
     {
     case GRUB_USB_TRANSACTION_TYPE_BULK:
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -369,21 +439,12 @@
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
 	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD,
 			      (grub_uint32_t) ed);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      (grub_uint32_t) ed);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR,
+			      0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -397,38 +459,59 @@
     }
 
   grub_dprintf ("ohci", "wait for completion\n");
-  grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n",
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
-      grub_cpu_idle ();
-
-      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
-	break;
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (&ed->td_head) & 1)
+        break;
+  
+  /* This should be according to OHCI specification:
+   * TD is finished and ED is updated when TD is retired and HcDoneHead
+   * register updated and, if allowed by WDH bit, written into HccaDoneHead.
+   * So we should:
+   * - allow WritebackDoneHead interrupt (WDH) by proper setting of last TD
+   *   token - it is done above in transaction settings
+   * - detect setting of WDH bit in HcInterruptStatus register
+   * - compare HccaDoneHead value with address of last-1 TD. If it is not
+   *   equal, check ED for halt and if not so, reset WDH bit and wait again
+   *   - but it should not happen - debug it!
+   */
+      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+        {
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+               == (grub_uint32_t) &td_list[transfer->transcnt - 1])
+            break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (&ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          continue;
+        }
     }
+  while (1);
 
-  grub_dprintf ("ohci", "complete\n");
-
-/*   if (ed->td_head & 1) */
-/*     err = GRUB_USB_ERR_STALL; */
-/*   else if (ed->td */
-
-
-  if (ed->td_head & 1)
+  grub_dprintf ("ohci", "completed\n");
+  
+  if (grub_le_to_cpu32 (ed->td_head) & 1)
     {
-      grub_uint8_t errcode;
-      grub_ohci_td_t tderr;
-
-      tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o,
-						    GRUB_OHCI_REG_DONEHEAD);
-      errcode = tderr->token >> 28;
+      tderr = (grub_ohci_td_t)
+                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+      if (tderr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always, but... */
+        tderr = (grub_ohci_td_t) (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
 
+      errcode = grub_le_to_cpu32 (tderr->token) >> 28;
+      
       switch (errcode)
 	{
 	case 0:
@@ -515,9 +598,31 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+  
+  /* Set ED to be skipped. */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  /* Auxiliary cleaning. */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err);
+  
   /* XXX */
   grub_free (td_list);
   grub_free (ed);
@@ -533,24 +638,24 @@
    grub_uint32_t status;
 
    /* Reset the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port);
    status |= (1 << 4); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port, status);
    grub_millisleep (100);
 
    /* End the reset signaling.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port);
    status |= (1 << 20); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port, status);
    grub_millisleep (10);
 
    /* Enable the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port);
    status |= (enable << 1); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port, status);
 
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   grub_dprintf ("ohci", "portstatus=0x%02x\n", status);
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port);
+   grub_dprintf ("ohci", "port=0x%02x, portstatus=0x%02x\n", port, status);
 
    return GRUB_ERR_NONE;
 }
@@ -561,9 +666,9 @@
    struct grub_ohci *o = (struct grub_ohci *) dev->data;
    grub_uint32_t status;
 
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUB_PORT_STAT_1 + port);
 
-   grub_dprintf ("ohci", "detect_dev status=0x%02x\n", status);
+   grub_dprintf ("ohci", "detect_dev port=0x%02x, status=0x%02x\n", port, status);
 
    if (! (status & 1))
      return GRUB_USB_SPEED_NONE;
@@ -579,7 +684,7 @@
   struct grub_ohci *o = (struct grub_ohci *) dev->data;
   grub_uint32_t portinfo;
 
-  portinfo = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA);
+  portinfo = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUB_DESC_A);
 
   grub_dprintf ("ohci", "root hub ports=%d\n", portinfo & 0xFF);
 
diff -urB grub2-1.98~experimental.20100120/bus/usb/usbtrans.c grub2-1.98~my_patched.20100312/bus/usb/usbtrans.c
--- grub2-1.98~experimental.20100120/bus/usb/usbtrans.c	2010-01-20 23:42:30.000000000 +0100
+++ grub2-1.98~my_patched.20100312/bus/usb/usbtrans.c	2010-03-14 20:41:33.000000000 +0100
@@ -54,6 +54,12 @@
     max = 64;
 
   datablocks = (size + max - 1) / max;
+  /* Note: This, together with "simple and effective" toggling of toggle bit,
+   * is not safe, it will work only in the case if
+   * OHCI will realy send one TD as one packet - I think it is not guaranted!
+   * Fortunately, control messages are very short, so prabably we do never
+   * need to split it. But another case are bulk transfers - see note about
+   * toggle bit and data blocks in bulk transfer function.	*/
 
   /* XXX: Discriminate between different types of control
      messages.  */
@@ -103,7 +109,7 @@
       size -= max;
     }
 
-  /* End with an empty OUT transaction.  */
+  /* End with an empty OUT/IN transaction (reverse direction than data).  */
   transfer->transactions[datablocks + 1].size = 0;
   transfer->transactions[datablocks + 1].data = NULL;
   if (reqtype & 128)
@@ -133,6 +138,10 @@
   grub_usb_err_t err;
   int toggle = dev->toggle[endpoint];
 
+  grub_dprintf ("usb",
+		"bulk: endpoint=0x%02x type=0x%02x size=%d\n",
+		endpoint, type, size);
+
   /* Use the maximum packet size given in the endpoint descriptor.  */
   if (dev->initialized)
     {
@@ -178,6 +187,22 @@
       tr->size = (size > max) ? max : size;
       /* XXX: Use the right most bit as the data toggle.  Simple and
 	 effective.  */
+      /* Addendum to XXX: This is simple and effective but it is really not
+       * good idea! Why:
+       * 1. It is a wasting of memory - for each 64 (or less!) bytes we need
+       * one TD = 16 bytes! TD can transfer up to 8k if buffer is 4k page
+       * aligned, or 4k if buffer is not aligned (because there can be only one
+       * 4k page crossing.)			 	     
+       * 2. I think there is not guaranted that OHCI will send each TD as one
+       * packet even if it has buffer size up to max packet size. So we can
+       * possibly lost toggle bit synchronization!
+       * Proposal enhancement - to do...:
+       * A. Change toggle bit control in the way that it is controled by OHCI
+       *    itself during TD transfers and final toggle value is read from
+       *    OHCI. This is MUST if we want to do next point B. 			 	 			 			 			 
+       * B. Change max. buffer size in one TD to 4k, i.e. split data to 4k
+       *    blocks instead of "max" (<=64).
+       */
       tr->toggle = toggle;
       toggle = toggle ? 0 : 1;
       tr->pid = type;
diff -urB grub2-1.98~experimental.20100120/disk/scsi.c grub2-1.98~my_patched.20100312/disk/scsi.c
--- grub2-1.98~experimental.20100120/disk/scsi.c	2010-01-20 23:42:31.000000000 +0100
+++ grub2-1.98~my_patched.20100312/disk/scsi.c	2010-03-14 20:50:57.000000000 +0100
@@ -50,6 +50,57 @@
 }
 
 \f
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
 /* Determine the the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
@@ -58,15 +109,23 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +142,24 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
 
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +177,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +190,14 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +208,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +220,14 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +239,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +252,14 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -173,6 +270,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -183,7 +282,14 @@
   wr.reserved = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command probably should be followed by Request Sense.
+     If not so, some devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -290,6 +396,16 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests, issue Test Unit Ready until OK */
+      /* XXX: there should be some timeout... */
+      do
+        {
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -300,12 +416,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
-
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
+
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }
diff -urB grub2-1.98~experimental.20100120/disk/usbms.c grub2-1.98~my_patched.20100312/disk/usbms.c
--- grub2-1.98~experimental.20100120/disk/usbms.c	2010-01-20 23:42:31.000000000 +0100
+++ grub2-1.98~my_patched.20100312/disk/usbms.c	2010-03-14 21:10:57.000000000 +0100
@@ -125,14 +125,12 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,6 +141,9 @@
 	      return 0;
 	    }
 
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
@@ -151,8 +152,8 @@
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
 
 	      /* Just set the amount of LUNs to one.  */
@@ -162,11 +163,6 @@
 	  else
 	    usbms->luns = luns;
 
-	  /* XXX: Check the magic values, does this really make
-	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
-
 	  /* XXX: To make Qemu work?  */
 	  if (usbms->luns == 0)
 	    usbms->luns = 1;
@@ -174,12 +170,12 @@
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 
 	  return 0;
 	}
@@ -240,70 +236,56 @@
   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
-
-  /* Write the request.  */
+  
+  /* Write the request.  XXX: Error recovery should be corrected! */
   err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
       err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      if (err) goto CheckCSW;
     }
 
-  /* Read the status.  */
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
   err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
 			    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
diff -urB grub2-1.98~experimental.20100120/include/grub/scsicmd.h grub2-1.98~my_patched.20100312/include/grub/scsicmd.h
--- grub2-1.98~experimental.20100120/include/grub/scsicmd.h	2010-01-20 23:42:31.000000000 +0100
+++ grub2-1.98~my_patched.20100312/include/grub/scsicmd.h	2010-03-09 18:19:07.000000000 +0100
@@ -25,14 +25,24 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
-struct grub_scsi_inquiry
+struct grub_scsi_test_unit_ready
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
   grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+} __attribute__((packed));
+
+struct grub_scsi_inquiry
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +57,40 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +144,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum
diff -urB grub2-1.98~experimental.20100120/include/grub/usbtrans.h grub2-1.98~my_patched.20100312/include/grub/usbtrans.h
--- grub2-1.98~experimental.20100120/include/grub/usbtrans.h	2010-01-20 23:42:31.000000000 +0100
+++ grub2-1.98~my_patched.20100312/include/grub/usbtrans.h	2010-03-06 13:04:12.000000000 +0100
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-03-14 22:23 [Patch] [bug #26237] multiple problems with usb devices Aleš Nesrsta
@ 2010-04-02 20:46 ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-04-07 19:49   ` Aleš Nesrsta
  0 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-04-02 20:46 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 4355 bytes --]

Aleš Nesrsta wrote:
> Hello,
>
> according to e-mail from Vladimir Serbinenko I am sending patch related
> to (mostly) OHCI problem (bug #26237).
> Workaround related to grub_memalign problem is removed from patch
> because Vladimir Serbinenko wrotes that it will be corrected.
>   
First of all big thanks for debugging this important aspect. And sorry
for being busy lately.

-  GRUB_OHCI_REG_RHUBA = 18,
-  GRUB_OHCI_REG_RHUBPORT = 21
+  GRUB_OHCI_REG_FRAME_REMAINING,
+  GRUB_OHCI_REG_FRAME_NUMBER,
+  GRUB_OHCI_REG_PERIODIC_START,
+  GRUB_OHCI_REG_LS_TRESHOLD,
+  GRUB_OHCI_REG_RHUB_DESC_A,

Please avoid register renaming unless original ones are misnomers. If
they are I would prefer renaming to be a separate patch.

-  grub_ohci_td_t td_list;
+  grub_ohci_td_t td_list ;
This change is spurious

+   * So we should:
+   * - allow WritebackDoneHead interrupt (WDH) by proper setting of last TD
+   *   token - it is done above in transaction settings
+   * - detect setting of WDH bit in HcInterruptStatus register
+   * - compare HccaDoneHead value with address of last-1 TD. If it is not
+   *   equal, check ED for halt and if not so, reset WDH bit and wait again
+   *   - but it should not happen - debug it!
Are the comments from you or any part copied from spec. We need no copy from spec as spec is available anyway and sentences copied from it may cause copyright problems
+/* Self commenting... */
Such comments should be removed, but an empty line at this point would be nice
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
When you ignore an error you need to clean grub_errno.
Sth like
if (!err)
  grub_errno = GRUB_ERR_NONE;
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
Please keep this list sorted.
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
Why do you need to change these constants? Were they wrong?
Tomorrow I'll test your patch on both UHCI and OHCI.

+#define GRUB_USB_FEATURE_TEST_MODE	0x02
     grub_scsi_cmd_read_capacity = 0x25,
+

> Patch was done by:
>
> diff -urB  grub2-1.98~experimental.20100120 grub2-1.98~my_patched.20100312 > grub2-1.98~my_patched.20100312.patch
>
> As I shortly read the patch file, there sometimes remains some few
> things which can be optimized - sorry, it is mostly the rest from
> debugging experiments (even I make cleaning of code today)...
>
> Ales
>
>   
>> Od: Vladimir Serbinenko <INVALID.NOREPLY@gnu.org>
>> Komu: Oliver Henshaw <yoho_ahoy@hotmail.com>, Vladimir Serbinenko
>> <phcoder@gmail.com>, Ales Nesrsta <starous@volny.cz>, bug-grub@gnu.org
>> Předmět: [bug #26237] multiple problems with usb devices
>> Datum: Sat, 13 Mar 2010 22:08:44 +0000
>>
>> Update of bug #26237 (project grub):
>>
>>                  Release:                    None => Bazaar - trunk         
>>
>>     _______________________________________________________
>>
>> Follow-up Comment #6:
>>
>> Hello. I found out the problem with grub_memalign. I'll apply the patch
>> tomorrow. Some of the fixes can go directly in while other require
>> investigation and perhaps even copyright assignment. Can you remove all
>> gratuituous changes (like commenting out dprintfs, adding commented out code
>> and change register names) and send patch in unified format (-u option in
>> diff) to grub-devel@gnu.org? Porting code from linux is appropriate only if
>> license is compatible and porting was approved by maintainer. Are perhaps
>> interested in coding EHCI?
>>
>>     _______________________________________________________
>>
>> Reply to this item at:
>>
>>   <http://savannah.gnu.org/bugs/?26237>
>>
>> _______________________________________________
>>   Message sent via/by Savannah
>>   http://savannah.gnu.org/
>>
>>
>>     
>> ------------------------------------------------------------------------
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> http://lists.gnu.org/mailman/listinfo/grub-devel


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-02 20:46 ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-04-07 19:49   ` Aleš Nesrsta
  2010-04-07 20:39     ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-04-07 19:49 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> Aleš Nesrsta wrote:
> > Hello,
> >
> > according to e-mail from Vladimir Serbinenko I am sending patch related
> > to (mostly) OHCI problem (bug #26237).
> > Workaround related to grub_memalign problem is removed from patch
> > because Vladimir Serbinenko wrotes that it will be corrected.
> >   
> First of all big thanks for debugging this important aspect. And sorry
> for being busy lately.
> 
> -  GRUB_OHCI_REG_RHUBA = 18,
> -  GRUB_OHCI_REG_RHUBPORT = 21
> +  GRUB_OHCI_REG_FRAME_REMAINING,
> +  GRUB_OHCI_REG_FRAME_NUMBER,
> +  GRUB_OHCI_REG_PERIODIC_START,
> +  GRUB_OHCI_REG_LS_TRESHOLD,
> +  GRUB_OHCI_REG_RHUB_DESC_A,
> 
> Please avoid register renaming unless original ones are misnomers. If
> they are I would prefer renaming to be a separate patch.

No, problem. It is the rest of lot of my experiments. I have added names
for some previously missing registers and I also renamed something to be
more closed to OHCI specification and understand it, but later I decided
that I don't need additional registers and I did not cleanup.

> 
> -  grub_ohci_td_t td_list;
> +  grub_ohci_td_t td_list ;
> This change is spurious

Hmmm, it is also rest of some experiments which I was not properly
cleaned up. I made more changes on this place but I deleted them
later...
As I wrote in previous e-mail, there can be more such curious changes...


> 
> +   * So we should:
> +   * - allow WritebackDoneHead interrupt (WDH) by proper setting of last TD
> +   *   token - it is done above in transaction settings
> +   * - detect setting of WDH bit in HcInterruptStatus register
> +   * - compare HccaDoneHead value with address of last-1 TD. If it is not
> +   *   equal, check ED for halt and if not so, reset WDH bit and wait again
> +   *   - but it should not happen - debug it!
> Are the comments from you or any part copied from spec. We need no copy from spec as spec is available anyway and sentences copied from it may cause copyright problems

It is not copy from specification, it is only inspired by OHCI
specification. I wrote it mainly for myself (and possibly others) to
explain what is the main change against previous algorithm, which is not
safe (from my point of view). It can be removed if it is not
interesting.

"Copied" are "official" names of OHCI registers, bits etc., i.e.
HccaDoneHead, WDH bit, HcInterruptStatus etc. - but from my point of
view it could be no problem - it is "keyword" which is necessary to
"speak with the same language". And it should be aim of such
specifications, specially of such "free" specifications as USB
specifications are. Or not?

I think it is far of any OHCI specification texts - but I have no
experiences with code licensing...


> +/* Self commenting... */
> Such comments should be removed, but an empty line at this point would be nice

OK

> +  err_sense = grub_scsi_request_sense (scsi);
> +  /* err_sense is ignored for now and Request Sense Data also... */
> When you ignore an error you need to clean grub_errno.
> Sth like
> if (!err)
>   grub_errno = GRUB_ERR_NONE;
>      grub_scsi_cmd_inquiry = 0x12,
> +    grub_scsi_cmd_test_unit_ready = 0x00,
> 

I will check it.


> Please keep this list sorted.
> -#define GRUB_USB_FEATURE_ENDP_HALT	0x01
> -#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
> -#define GRUB_USB_FEATURE_TEST_MODE	0x04
> +#define GRUB_USB_FEATURE_ENDP_HALT	0x00
> +#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
> Why do you need to change these constants? Were they wrong?
> Tomorrow I'll test your patch on both UHCI and OHCI.

Yes, I found they are wrong. But I cannot remember the source of such
information (maybe it was USB Mass Storage Class Compliance Test
Specification but I am not sure).


> 
> +#define GRUB_USB_FEATURE_TEST_MODE	0x02
>      grub_scsi_cmd_read_capacity = 0x25,
> +
> 
> > Patch was done by:
> >
> > diff -urB  grub2-1.98~experimental.20100120 grub2-1.98~my_patched.20100312 > grub2-1.98~my_patched.20100312.patch
> >
> > As I shortly read the patch file, there sometimes remains some few
> > things which can be optimized - sorry, it is mostly the rest from
> > debugging experiments (even I make cleaning of code today)...
> >
> > Ales
> >
> >   
> >> Od: Vladimir Serbinenko <INVALID.NOREPLY@gnu.org>
> >> Komu: Oliver Henshaw <yoho_ahoy@hotmail.com>, Vladimir Serbinenko
> >> <phcoder@gmail.com>, Ales Nesrsta <starous@volny.cz>, bug-grub@gnu.org
> >> Předmět: [bug #26237] multiple problems with usb devices
> >> Datum: Sat, 13 Mar 2010 22:08:44 +0000
> >>
> >> Update of bug #26237 (project grub):
> >>
> >>                  Release:                    None => Bazaar - trunk         
> >>
> >>     _______________________________________________________
> >>
> >> Follow-up Comment #6:
> >>
> >> Hello. I found out the problem with grub_memalign. I'll apply the patch
> >> tomorrow. Some of the fixes can go directly in while other require
> >> investigation and perhaps even copyright assignment. Can you remove all
> >> gratuituous changes (like commenting out dprintfs, adding commented out code
> >> and change register names) and send patch in unified format (-u option in
> >> diff) to grub-devel@gnu.org? Porting code from linux is appropriate only if
> >> license is compatible and porting was approved by maintainer. Are perhaps
> >> interested in coding EHCI?
> >>
> >>     _______________________________________________________
> >>
> >> Reply to this item at:
> >>
> >>   <http://savannah.gnu.org/bugs/?26237>
> >>
> >> _______________________________________________
> >>   Message sent via/by Savannah
> >>   http://savannah.gnu.org/
> >>
> >>
> >>     
> >> ------------------------------------------------------------------------
> >>
> >> _______________________________________________
> >> Grub-devel mailing list
> >> Grub-devel@gnu.org
> >> http://lists.gnu.org/mailman/listinfo/grub-devel
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel

Some questions and others:

I am not so far oriented in such kind of development, so I am asking:
Will You correct my patch yourself (as maintainer of code) or should I
prepare new corrected code and related patch? And against which version
- I noted that in meantime new official version was released...

Did You tests on OHCI/UHCI and what was the result?
What HW are You using for testing - some real PC or qemu ?

It was little bit surprise for me that OHCI and UHCI drivers are not
using IRQ (and maybe also some other drivers). Is it intention of GRUB?
Nothing against such philosophy, some things are easier in this way.

I shortly read the UHCI specification and I was thought about my
comments of TD buffer size optimalization included in patch - such
optimization cannot be done simply because TD preparation code is common
for OHCI and UHCI and:
- UHCI needs (as far as I understand) TD buffer size length up to packet
size only (<=64 bytes).
- In contradiction, OHCI allows up to 8Kbytes TD buffer size (if buffer
is aligned to page).
So there is the question if it make sense to do any optimization for
OHCI - probably not. In fact slow data transfer and wasting of memory
caused by not optimized TD buffer is probably not so big problem.

I read also part of EHCI specification. There is question if GRUB 2
needs support of EHCI because:
1. According to EHCI specification, each EHCI interface should normaly
have also OHCI (or UHCI ?) interface. So, any USB 2.0 interface should
work (slowly) via GRUB2 UHCI/OHCI support.
2. EHCI is relative new HW - so it was usually used on PCs with BIOS
which supports booting from USB - so, probably, there is not necessary
to prepare special support in GRUB for such HW. But maybe I am wrong.

Best regards
Ales




^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-07 19:49   ` Aleš Nesrsta
@ 2010-04-07 20:39     ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-04-08 21:27       ` Aleš Nesrsta
  0 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-04-07 20:39 UTC (permalink / raw)
  To: The development of GNU GRUB


[-- Attachment #1.1: Type: text/plain, Size: 4869 bytes --]

Aleš Nesrsta wrote:
>   
>   
>> +   * So we should:
>> +   * - allow WritebackDoneHead interrupt (WDH) by proper setting of last TD
>> +   *   token - it is done above in transaction settings
>> +   * - detect setting of WDH bit in HcInterruptStatus register
>> +   * - compare HccaDoneHead value with address of last-1 TD. If it is not
>> +   *   equal, check ED for halt and if not so, reset WDH bit and wait again
>> +   *   - but it should not happen - debug it!
>> Are the comments from you or any part copied from spec. We need no copy from spec as spec is available anyway and sentences copied from it may cause copyright problems
>>     
>
> It is not copy from specification, it is only inspired by OHCI
> specification. I wrote it mainly for myself (and possibly others) to
> explain what is the main change against previous algorithm, which is not
> safe (from my point of view). It can be removed if it is not
> interesting.
>   
Ok then.
> "Copied" are "official" names of OHCI registers, bits etc., i.e.
> HccaDoneHead, WDH bit, HcInterruptStatus etc. - but from my point of
> view it could be no problem - it is "keyword" which is necessary to
> "speak with the same language". 
Ok, then.
> And it should be aim of such
> specifications, specially of such "free" specifications as USB
> specifications are. Or not?
>
>   
Normally yes, but the details of license may be a problem. Many
specifications has a clause which forbids modification. GPL on the other
hand forbids forbiding modifications. So they are incompatible.
>
> Some questions and others:
>   

> I am not so far oriented in such kind of development, so I am asking:
> Will You correct my patch yourself (as maintainer of code) or should I
> prepare new corrected code and related patch? And against which version
> - I noted that in meantime new official version was released...
>   
Normally we ask contributors to clean their patches themselves. But
given the great importance of your changes I'll cleaned them up myself.
You can find the partially cleaned version in attachment.
I recommend using bzr trunk but USB code hasn't changed since few months
due to lack of dev time
> Did You tests on OHCI/UHCI and what was the result?
> What HW are You using for testing - some real PC or qemu ?
>
>   
I have a computer with integrated UHCI and OHCI+EHCI PCI card.
Testing on it showed that your patch fixed UHCI but had no effect on
OHCI. with OHCI grub_ohci_detect_dev always sees status = 0. I see the
similar behaviour when testing usb on grub-yeeloong-firmware (but not
when chinloaded by pmon). I guess some register initialisation is missing.
> It was little bit surprise for me that OHCI and UHCI drivers are not
> using IRQ (and maybe also some other drivers). Is it intention of GRUB?
> Nothing against such philosophy, some things are easier in this way.
>
>   
GRUB has chosen to be single thread for reducing complexity. But we have
plans to add IRQs nevertheless because it will easier porting drivers
and allow parallel uncompress and read.
> I shortly read the UHCI specification and I was thought about my
> comments of TD buffer size optimalization included in patch - such
> optimization cannot be done simply because TD preparation code is common
> for OHCI and UHCI and:
> - UHCI needs (as far as I understand) TD buffer size length up to packet
> size only (<=64 bytes).
> - In contradiction, OHCI allows up to 8Kbytes TD buffer size (if buffer
> is aligned to page).
> So there is the question if it make sense to do any optimization for
> OHCI - probably not. In fact slow data transfer and wasting of memory
> caused by not optimized TD buffer is probably not so big problem.
>
>   
Keep it simple in this case.
> I read also part of EHCI specification. There is question if GRUB 2
> needs support of EHCI because:
> 1. According to EHCI specification, each EHCI interface should normaly
> have also OHCI (or UHCI ?) interface. So, any USB 2.0 interface should
> work (slowly) via GRUB2 UHCI/OHCI support.
> 2. EHCI is relative new HW - so it was usually used on PCs with BIOS
> which supports booting from USB - so, probably, there is not necessary
> to prepare special support in GRUB for such HW. But maybe I am wrong.
>
>   
Unfortunately BIOS is far from being bugless especially in USB. We have
seen e.g. 128GiB limits. On Yeeloong grub is the firmware. And this
reasoning doesn't apply to PCI expansion cards.
And on my laptop a bug in legacy support prevents UHCI controller from
working properly w/o EHCI.
> Best regards
> Ales
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: usb.diff --]
[-- Type: text/x-diff; name="usb.diff", Size: 27977 bytes --]

=== modified file 'bus/usb/ohci.c'
--- bus/usb/ohci.c	2010-01-20 19:40:30 +0000
+++ bus/usb/ohci.c	2010-03-13 08:08:37 +0000
@@ -254,20 +274,33 @@
       break;
     }
 
-  /* Generate no interrupts.  */
+  /* Generate no interrupts.  */
   token |= 7 << 21;
 
   /* Set the token.  */
   token |= toggle << 24;
   token |= 1 << 25;
 
-  buffer = (grub_uint32_t) data;
-  buffer_end = buffer + size - 1;
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+  
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size) 
+     {
+	 buffer = (grub_uint32_t) data;
+	 buffer_end = buffer + size - 1;
+         td->buffer = grub_cpu_to_le32 (buffer);
+         td->buffer_end = grub_cpu_to_le32 (buffer_end);
+     }
+  else 
+     {
+	td->buffer = 0;
+	td->buffer_end = 0;
+     }
 
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -283,6 +316,9 @@
   grub_uint32_t status;
   grub_uint32_t control;
   grub_usb_err_t err;
+
+  grub_uint8_t errcode = 0;
+  grub_ohci_td_t tderr = NULL;
   int i;
 
   /* Allocate an Endpoint Descriptor.  */
@@ -310,6 +346,18 @@
       td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]);
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+
+  td_list[transfer->transcnt].token = 0;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td =
+    (grub_uint32_t) &td_list[transfer->transcnt];
+  
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
@@ -336,30 +384,48 @@
   grub_dprintf ("ohci", "program OHCI\n");
 
   /* Program the OHCI to actually transfer.  */
+
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   switch (transfer->type)
     {
     case GRUB_USB_TRANSACTION_TYPE_BULK:
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -368,22 +434,11 @@
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD,
 			      (grub_uint32_t) ed);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      (grub_uint32_t) ed);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR,
+			      0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -402,33 +457,72 @@
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
       grub_cpu_idle ();
 
-      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
-	break;
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (&ed->td_head) & 1)
+        break;
+  
+  /* The original style of detection of finished transaction looks hazardeous.
+  while ((grub_le_to_cpu32 (grub_ohci_read_mem32 (&ed->td_head)) & ~0xf)
+         != (grub_le_to_cpu32 (grub_ohci_read_mem32 (&ed->td_tail)) & ~0xf));
+  */
+  /* This should be according to OHCI specification:
+   * TD is finished and ED is updated when TD is retired and HcDoneHead
+   * register updated and, if allowed by WDH bit, written into HccaDoneHead.
+   * So we should:
+   * - allow WritebackDoneHead interrupt (WDH) by proper setting of last TD
+   *   token - it is done above in transaction settings
+   * - detect setting of WDH bit in HcInterruptStatus register
+   * - compare HccaDoneHead value with address of last-1 TD. If it is not
+   *   equal, check ED for halt and if not so, reset WDH bit and wait again
+   *   - but it should not happen - debug it!
+   */
+      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+        {
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+               == (grub_uint32_t) &td_list[transfer->transcnt - 1])
+            break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (&ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          continue;
+        }
     }
-
-  grub_dprintf ("ohci", "complete\n");
-
-/*   if (ed->td_head & 1) */
-/*     err = GRUB_USB_ERR_STALL; */
-/*   else if (ed->td */
-
-
-  if (ed->td_head & 1)
+  while (1);
+
+  /*
+  grub_dprintf ("ohci", "completed\n");
+  */
+  
+  /* Additional debug - to be removed. */
+  /*
+  grub_dprintf ("ohci", "DoneHead_reg=0x%08x DoneHead_HCCA=0x%08x\n",
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD),
+    grub_le_to_cpu32 (o->hcca->donehead));
+  */
+  
+  if (grub_le_to_cpu32 (ed->td_head) & 1)
     {
-      grub_uint8_t errcode;
-      grub_ohci_td_t tderr;
-
-      tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o,
-						    GRUB_OHCI_REG_DONEHEAD);
-      errcode = tderr->token >> 28;
-
+      tderr = (grub_ohci_td_t)
+                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+      if (tderr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always now! */
+        tderr = (grub_ohci_td_t) (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
+
+      errcode = grub_le_to_cpu32 (tderr->token) >> 28;
+      
       switch (errcode)
 	{
 	case 0:
@@ -515,9 +639,38 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+  
+  /* Set ED to be skipped - for safety */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  /* Additional debug print. to be removed.
+  grub_dprintf ("ohci", "Before reset of WDH: INTSTATUS=0x%08x\n",
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+    */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  /* Additional debug print. to be removed.
+  grub_dprintf ("ohci", "After reset of WDH: INTSTATUS=0x%08x\n",
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+    */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err);
+  
   /* XXX */
   grub_free (td_list);
   grub_free (ed);
=== modified file 'disk/scsi.c'
--- disk/scsi.c	2010-03-05 14:29:28 +0000
+++ disk/scsi.c	2010-03-13 07:59:11 +0000
@@ -50,6 +50,57 @@
 }
 
 \f
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
 /* Determine the the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
@@ -58,15 +109,23 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +142,24 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
 
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +177,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +190,14 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +208,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +220,14 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +239,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +252,14 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -173,6 +270,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -183,7 +282,14 @@
   wr.reserved = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -292,6 +398,16 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests, issue Test Unit Ready until OK */
+      /* XXX: there should be some timeout... */
+      do
+        {
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -302,12 +418,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
 
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }

=== modified file 'disk/usbms.c'
--- disk/usbms.c	2010-01-20 08:12:47 +0000
+++ disk/usbms.c	2010-03-13 07:59:11 +0000
@@ -125,14 +125,16 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,6 +145,9 @@
 	      return 0;
 	    }
 
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
@@ -151,8 +156,8 @@
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
 
 	      /* Just set the amount of LUNs to one.  */
@@ -164,8 +169,9 @@
 
 	  /* XXX: Check the magic values, does this really make
 	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
+	  /* What is it? Removed. */
+	  /* grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
+				0, i, 0, 0); */
 
 	  /* XXX: To make Qemu work?  */
 	  if (usbms->luns == 0)
@@ -174,12 +180,12 @@
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 
 	  return 0;
 	}
@@ -225,6 +231,7 @@
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
+  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -240,70 +247,86 @@
   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
+  
+  /* Debug print of CBW content. */
+  grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n",
+  	cbw.signature, cbw.tag, cbw.transfer_length);
+  grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n",
+  	cbw.flags, cbw.lun, cbw.length);
+  grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3],
+  	cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7],
+  	cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11],
+  	cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]);
 
-  /* Write the request.  */
+  /* Write the request.  XXX: Error recovery should be corrected! */
   err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
       err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
+      /* Debug print of received data. */
+      grub_dprintf ("usb", "buf:\n");
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "buf:\n");
+      /* Debug print of sent data. */
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
+      if (err) goto CheckCSW;
     }
 
-  /* Read the status.  */
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
   err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
 			    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* Debug print of CSW content. */
+  grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n",
+  	status.signature, status.tag, status.residue);
+  grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
+  
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);

=== modified file 'include/grub/scsicmd.h'
--- include/grub/scsicmd.h	2008-08-27 15:05:00 +0000
+++ include/grub/scsicmd.h	2010-03-13 07:59:12 +0000
@@ -25,14 +25,24 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
+struct grub_scsi_test_unit_ready
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+} __attribute__((packed));
+
 struct grub_scsi_inquiry
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
-  grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +57,40 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +144,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum

=== modified file 'include/grub/usbtrans.h'
--- include/grub/usbtrans.h	2009-02-08 17:58:32 +0000
+++ include/grub/usbtrans.h	2010-03-13 07:59:12 +0000
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-07 20:39     ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-04-08 21:27       ` Aleš Nesrsta
  2010-04-16 16:39         ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-04-08 21:27 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> > I am not so far oriented in such kind of development, so I am asking:
> > Will You correct my patch yourself (as maintainer of code) or should I
> > prepare new corrected code and related patch? And against which version
> > - I noted that in meantime new official version was released...
> >   
> Normally we ask contributors to clean their patches themselves. But
> given the great importance of your changes I'll cleaned them up myself.
> You can find the partially cleaned version in attachment.
> I recommend using bzr trunk but USB code hasn't changed since few months
> due to lack of dev time

Thanks.

> > Did You tests on OHCI/UHCI and what was the result?
> > What HW are You using for testing - some real PC or qemu ?
> >   
> I have a computer with integrated UHCI and OHCI+EHCI PCI card.
> Testing on it showed that your patch fixed UHCI but had no effect on
> OHCI. with OHCI grub_ohci_detect_dev always sees status = 0. I see the
> similar behaviour when testing usb on grub-yeeloong-firmware (but not
> when chinloaded by pmon). I guess some register initialisation is missing.

I will test the usb.diff soon and than I try to more study EHCI
specification. On my PC is OHCI detected properly but I have very old
machine Toshiba Satellite 2140CDS (i586 at 450MHz) with one OHCI only
interface. I.e. it looks like problem appears only on combined OHCI-EHCI
interfaces. I have also plan to debug GRUB2 USB support on other, newer
machine with OHCI-EHCI interfaces.
I will inform You there if I will discover something interesting...

Best regards
Ales




^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-08 21:27       ` Aleš Nesrsta
@ 2010-04-16 16:39         ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-04-20 20:14           ` Aleš Nesrsta
  0 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-04-16 16:39 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 2571 bytes --]

Aleš Nesrsta wrote:
> I will test the usb.diff soon and than I try to more study EHCI
> specification. On my PC is OHCI detected properly but I have very old
> machine Toshiba Satellite 2140CDS (i586 at 450MHz) with one OHCI only
> interface. I.e. it looks like problem appears only on combined OHCI-EHCI
> interfaces. I have also plan to debug GRUB2 USB support on other, newer
> machine with OHCI-EHCI interfaces.
> I will inform You there if I will discover something interesting...
>
>   
I discovered something. It looks like on Yeeloong the problem is caused
by the skipped step of initialising of timings and power management.
Quick and dirty fix:
=== modified file 'bus/usb/ohci.c'
--- bus/usb/ohci.c      2010-01-20 19:40:30 +0000
+++ bus/usb/ohci.c      2010-04-16 16:32:38 +0000
@@ -91,10 +93,15 @@
   GRUB_OHCI_REG_BULKCURR,
   GRUB_OHCI_REG_DONEHEAD,
   GRUB_OHCI_REG_FRAME_INTERVAL,
+  GRUB_OHCI_REG_PERIODIC_START = 16,
   GRUB_OHCI_REG_RHUBA = 18,
   GRUB_OHCI_REG_RHUBPORT = 21
 } grub_ohci_reg_t;
 
+#define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
+#define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200
+
+
 static grub_uint32_t
 grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
 {
@@ -166,6 +215,11 @@
   if ((revision & 0xFF) != 0x10)
     goto fail;
 
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
+                       (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
+                        & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
+                       | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+
   /* Backup the frame interval register.  */
   frame_interval = grub_ohci_readreg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL);
@@ -175,10 +229,14 @@
   grub_dprintf ("ohci", "OHCI reset\n");
 
   /* Restore the frame interval register.  */
+  /* FIXME: is this choice of timings sane?  */
+  frame_interval = (frame_interval & 0xffff) | 0x27780000;
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval);
 
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START, 0x257f);
+
   /* Setup the HCCA.  */
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca);
   grub_dprintf ("ohci", "OHCI HCCA\n");
 
   /* Enable the OHCI.  */
 

> Best regards
> Ales
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-16 16:39         ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-04-20 20:14           ` Aleš Nesrsta
  2010-04-24 19:50             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-21 23:46             ` Aleš Nesrsta
  0 siblings, 2 replies; 41+ messages in thread
From: Aleš Nesrsta @ 2010-04-20 20:14 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
...
> I discovered something. It looks like on Yeeloong the problem is caused
> by the skipped step of initialising of timings and power management.
> Quick and dirty fix:
...

Hello,

first of all sorry about delay - I am little bit busy now.


1.
I fully agree with power enable fix (via register ...RHUBA) - it is
simple and probably sufficiently universal.


2.
The second fix, related to FRAME_INTERVAL (FSMPS part) and
PERIODIC_START - where you get the fixed values?
I have little bit another suggestion:

According to PERIODIC_START:
OHCI specification says "The value is calculated roughly as 10% off from
HcFrameInterval. A typical value will be 3E67h."
I think "typical value 3E67h" is some mistake - because it is greater
than default frame interval value (11999=2EDFh) ! - and it should be
probably 2A2Fh=2EDF*9/10.
So I suggest calculation of PERIODIC_START like this: ((frame_interval &
0x3fff) * 9) / 10.

According to FSMPS part of FRAME_INTERVAL (high part of register):
OHCI specification says this: "...FMPS is calculated by subtracting from
FrameInterval the maximum number of bit times needed for EOF processing,
then multiplying the result by 6/7..."
In example in specification is "maximum number of bit times needed for
EOF processing" set to 210 - I don't know why, probably it is related to
USB1.1 specification.
So, the FSMPS default value should be (11999-210) *6 / 7 = 10104 =2778h,
ie. it is the value in your patch - but only in case if low part of
frame interval register really contains default value 11999=2EDFh. I
think better will be to calculate it because frame interval can have
another default value.

There is also question, why actually to change FSMPS ?
I think, if we take into frame_interval previous valid value from
FRAME_INTERVAL register, this part should be correct.
Maybe it is not always true - specification says that this value should
be calculated by driver. So, if Yeeloong has no BIOS, probably nobody
calculate and set this field and it is not set by OHCI HW itself. On
another machines it is probably set by BIOS - at least on my machine
with OHCI only :-) - but maybe it is done only in case if "Legacy USB
support" is selected in BIOS (or similar USB setting).

Additionally, specification says that bit 31 (FIT) should be set to one
if any change of FRAME_INTERVAL occurs - but I think it is necessary
only if OHCI is in operational state - which is not in our case.

Finally it can look like this (not optimal code :-), not tested yet):
>    grub_dprintf ("ohci", "OHCI reset\n");
>  
>    /* Restore the frame interval register.  */
> +  frame_interval = (frame_interval & 0x3fff) | (((((frame_interval & 0x3fff) - 210) * 6) / 7) << 16);
>    grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval);
>  
> +  /* Set periodic start reg. - it is necessary only after HW reset, but it is probably our case */
> +  grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START, ((frame_interval & 0x3fff) * 9) / 10);
> +
>    /* Setup the HCCA.  */
> -  grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca);
> +  grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca);
>    grub_dprintf ("ohci", "OHCI HCCA\n");
>  
>    /* Enable the OHCI.  */


3.
I tested your patch usb.diff from previous e-mail - it works fine.


4.
I cannot test OHCI on my second machine as I promised - the second
machine has no OHCI-EHCI, but UHCI-EHCI, I was wrong...
But I tried to test UHCI on this second machine - and it was not
working.
I had no time to debug why it is not working - I suspect BIOS (which
supports booting from USB) is doing something bad in background, maybe.
I will check the BIOS settings and I will try test it once again. But
first I have to add disk drive with Linux to this machine to be able
make some debugging experiments with GRUB (I have only WinXP there
currently).


Best regards
Ales





^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-20 20:14           ` Aleš Nesrsta
@ 2010-04-24 19:50             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-04-28  8:20               ` Aleš Nesrsta
  2010-05-21 23:46             ` Aleš Nesrsta
  1 sibling, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-04-24 19:50 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 3056 bytes --]

Aleš Nesrsta wrote:
> Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> ...
>   
>> I discovered something. It looks like on Yeeloong the problem is caused
>> by the skipped step of initialising of timings and power management.
>> Quick and dirty fix:
>>     
> ...
>
> Hello,
>
> first of all sorry about delay - I am little bit busy now.
>
>
> 1.
> I fully agree with power enable fix (via register ...RHUBA) - it is
> simple and probably sufficiently universal.
>
>
> 2.
> The second fix, related to FRAME_INTERVAL (FSMPS part) and
> PERIODIC_START - where you get the fixed values?
>   
I've just dumped them from a working controller.
> There is also question, why actually to change FSMPS ?
>   
Problem is that if firmware didn't set this register correctly we'll
have problems with it. Additionaly the advantage of always using the
same values is to avoid having different behaviour on different chipsets
which is a plus from debugging perspective.
> I think, if we take into frame_interval previous valid value from
> FRAME_INTERVAL register, this part should be correct.
> Maybe it is not always true - specification says that this value should
> be calculated by driver. So, if Yeeloong has no BIOS, probably nobody
> calculate and set this field and it is not set by OHCI HW itself. On
> another machines it is probably set by BIOS - at least on my machine
> with OHCI only :-) - but maybe it is done only in case if "Legacy USB
> support" is selected in BIOS (or similar USB setting).
>   
But I doubt it's a case for OHCI expansion card if BIOS fails to execute
option ROM.
> Additionally, specification says that bit 31 (FIT) should be set to one
> if any change of FRAME_INTERVAL occurs - but I think it is necessary
> only if OHCI is in operational state - which is not in our case.
>
>   
It can be done. Thanks.
> 3.
> I tested your patch usb.diff from previous e-mail - it works fine.
>
>   
Thanks. When legal part will be done I'll apply it to trunk.
> 4.
> I cannot test OHCI on my second machine as I promised - the second
> machine has no OHCI-EHCI, but UHCI-EHCI, I was wrong...
> But I tried to test UHCI on this second machine - and it was not
> working.
> I had no time to debug why it is not working - I suspect BIOS (which
> supports booting from USB) is doing something bad in background, maybe.
> I will check the BIOS settings and I will try test it once again. But
> first I have to add disk drive with Linux to this machine to be able
> make some debugging experiments with GRUB (I have only WinXP there
> currently).
>   
I think the real problem are channels. I have suspicion that only first
channel of UHCI works. Another possible problem are the legacy support
bits in EHCI controller.
>
> Best regards
> Ales
>
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-24 19:50             ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-04-28  8:20               ` Aleš Nesrsta
  2010-05-09 13:50                 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-04-28  8:20 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> Problem is that if firmware didn't set this register correctly we'll
> have problems with it. Additionaly the advantage of always using the
> same values is to avoid having different behaviour on different chipsets
> which is a plus from debugging perspective.
> ...
> But I doubt it's a case for OHCI expansion card if BIOS fails to execute
> option ROM.
I agree, it will be better if FSMPS value will be set by GRUB itself because
on some platforms or add-on cards etc. it is high possibility that FSMPS 
will be
bad / not set.

>> Additionally, specification says that bit 31 (FIT) should be set to one
>> if any change of FRAME_INTERVAL occurs - but I think it is necessary
>> only if OHCI is in operational state - which is not in our case.
> It can be done. Thanks.
As I wrote, it is most probably not our case, (GRUB is setting controller
in STOP state and nobody takes care about immediate impact of such setting,
there are delays between set of FRAME_INTERVAL register and real
start of first communication transaction) so don't hurry with such 
change...:-)
(Because if you set FIT bit, you should also wait for FRT bit in register
FRAME_REMAINING to be set by OHCI controller as the reaction.
But it should happen in next start of frame, i.e. max. after 1ms.)

>> I tested your patch usb.diff from previous e-mail - it works fine.
> Thanks. When legal part will be done I'll apply it to trunk.
>
Mr. Donald R. Robertson wrote:
> Hello,
>
> I'm not sure what that error is. I received your request and am mailing
> your assignment form. If you are employed to do programming, then please
> have your employer fill out that disclaimer form found in the previous
> message I just sent.
So, I am awaiting the mail... Till now it is not there (after approx. one 
week.).
I expect it can be normal post delay (as it is little bit far from USA to 
us...
And, additionally, there was the problem with Eyjafjallajökull volcano...).

>   ...
> I think the real problem are channels. I have suspicion that only first
> channel of UHCI works. Another possible problem are the legacy support
> bits in EHCI controller.
It looks like good idea. I also told myself that I have to first discover 
how are
GRUB modules usb and usbms working with USB ports of USB controller,
how are they looking for connected USB mass storage device.
So I try first to more study actual related source code.

Another source of problem could be not only legacy support itself but
EHCI set up made by BIOS. If I good understood leading part of EHCI
specification, if EHCI is enabled - working, all USB 2.0 capable devices
are not "seen" on UHCI/OHCI but on EHCI only. So, if BIOS enables EHCI
to be working, none USB mass storage device will be reported on UHCI
or OHCI ports. And all USB mass storage that I have are USB 2.0
capable...
If it is in this way, I don't see any simple way how to avoid it (maybe 
except
disabling USB 2.0 support in BIOS if it is possible).
But maybe it is my misunderstanding only.

(If you will be find something sooner than me, inform me, please,
thanks in advance.)

Best regards
Ales




^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-28  8:20               ` Aleš Nesrsta
@ 2010-05-09 13:50                 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 0 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-09 13:50 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 3124 bytes --]

Aleš Nesrsta wrote:
>>> Additionally, specification says that bit 31 (FIT) should be set to one
>>> if any change of FRAME_INTERVAL occurs - but I think it is necessary
>>> only if OHCI is in operational state - which is not in our case.
>> It can be done. Thanks.
> As I wrote, it is most probably not our case, (GRUB is setting controller
> in STOP state and nobody takes care about immediate impact of such
> setting,
> there are delays between set of FRAME_INTERVAL register and real
> start of first communication transaction) so don't hurry with such
> change...:-)
> (Because if you set FIT bit, you should also wait for FRT bit in register
> FRAME_REMAINING to be set by OHCI controller as the reaction.
> But it should happen in next start of frame, i.e. max. after 1ms.)
>
Ok, let's just keep it in mind but since it doesn't create any real
problems keep it the simple way.
>>   ...
>> I think the real problem are channels. I have suspicion that only first
>> channel of UHCI works. Another possible problem are the legacy support
>> bits in EHCI controller.
> It looks like good idea. I also told myself that I have to first
> discover how are
> GRUB modules usb and usbms working with USB ports of USB controller,
> how are they looking for connected USB mass storage device.
> So I try first to more study actual related source code.
>
> Another source of problem could be not only legacy support itself but
> EHCI set up made by BIOS. If I good understood leading part of EHCI
> specification, if EHCI is enabled - working, all USB 2.0 capable devices
> are not "seen" on UHCI/OHCI but on EHCI only. So, if BIOS enables EHCI
> to be working, none USB mass storage device will be reported on UHCI
> or OHCI ports. And all USB mass storage that I have are USB 2.0
> capable...
> If it is in this way, I don't see any simple way how to avoid it
> (maybe except
> disabling USB 2.0 support in BIOS if it is possible).
> But maybe it is my misunderstanding only.
Perhaps we can shutdown EHCI somehow? Do you know of reliable way to
completely reset all USB controllers?
Currently on Yeeloong ohci partially works but is able to retrieve
string only from webcam. I suppose that webcam wasn't used by pmon so
grub was able to assign an address to it itself. But maybe webcam is
simply on another controller (one of controller is Geode which differs a
bit from normal OHCI).
When I run grub without pmon detect_dev is correct but it's not possible
to send or receive anything and frame number stays 0xffff. I guess I
need to launch OHCI somehow.
>
> (If you will be find something sooner than me, inform me, please,
> thanks in advance.)
I also had to add old way of end detection because otherwise it hanged
sometimes.
Also in few places you had:
grub_le_to_cpu32 (&var)
instead of
grub_le_to_cpu32 (var)
>
> Best regards
> Ales
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-04-20 20:14           ` Aleš Nesrsta
  2010-04-24 19:50             ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-21 23:46             ` Aleš Nesrsta
  2010-05-22  1:20               ` Grégoire Sutre
                                 ` (2 more replies)
  1 sibling, 3 replies; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-21 23:46 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi Vladimir,

I have some news - I made extensive testing and researching and I
discovered some things related to OHCI and generally to USB MS or USB
itself.

I tried to fix them and it looks like I was probably successful in some
cases:

- multiple LUNs related bug (meaning of value from device & coding into
CBW)
- bulk transport toggling related bug - on devices with endpoints with
the same "low" nuber (e.g. 01 & 81)
- bug related to devices with max. packet size for control pipe 0 lower
than 64
- problem with one kind of OHCI BIOS legacy support maybe solved
- another bug - some devices hangs because of Bulk-only Mass Storage
Reset used during opening when it is not expected by device
- my OHCI done-head loop should not hang anymore - timeout was added
- "ls" should not hang anymore on USB MS device with not ready unit -
timeout was added in SCSI open unit ready loop
- experimentally added support for another subclasses of USB MS devices
(as all are SCSI based - it possibly should work, at least it is working
with one my device with subclass SFF-8070i - but I don't have another
devices to test)

In fact, all my USB MS devices are working now with grub2 with exception
of one camera which have not Bulk-Only but CBI protocol.

What I was not solved till now:
- Some devices are not working on UHCI
- If I connect some device and load uhci module, I cannot connect
another device without rebooting. On OHCI it is possible without
rebooting in this sequence:
	rmmod usbms
	rmmod ohci
	(unplug old device and plug new one)
	insmod ohci
	insmod usbms
- It looks like my OHCI is not properly reporting STALL state of pipe -
it is probably reason why done-head loop hanged if some problem happened
in communication.

I want to prepare patch and send it here but I cannot get actual bzr
code. I would want have it at least because grub_memalign problem is not
solved in last grub version wich I have (1.98) and I have to solve it by
some workarounds (maybe it is also reason why UHCI does not work
properly on my computer, I did not any workaround in its code yet).

I cannot get actual bzr code because of this error:
$ bzr get http://bzr.savannah.gnu.org/r/grub
bzr: ERROR: Not a branch:
"http://bzr.savannah.gnu.org/r/grub/.bzr/branch/".

I tried to found some advice in man bzr and forums but I was not
successful. It looks like problem is that there is really not existing
folder ".../r/.bzr/branch" (I found "branch-lock" and "repository" only)
- should I use some another address or some option ?
Do You have some experiences with such problem ?
Could You put me in right direction...?

(I tried to download CVS source code - it works - but it looks outdated.
I also tried register ssh public code and made access via ssh to
sftp://starous@bzr.savannah.gnu.org/srv/bzr/grub - but I cannot login,
there is ssh debug message that it is not possible to find
authentication method...)

Best regards
Ales



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-21 23:46             ` Aleš Nesrsta
@ 2010-05-22  1:20               ` Grégoire Sutre
  2010-05-22  9:35               ` Colin Watson
  2010-05-22 23:13               ` Vladimir 'φ-coder/phcoder' Serbinenko
  2 siblings, 0 replies; 41+ messages in thread
From: Grégoire Sutre @ 2010-05-22  1:20 UTC (permalink / raw)
  To: The development of GNU GRUB

On 05/22/2010 01:46 AM, Aleš Nesrsta wrote:

> I cannot get actual bzr code because of this error:
> $ bzr get http://bzr.savannah.gnu.org/r/grub
> bzr: ERROR: Not a branch:
> "http://bzr.savannah.gnu.org/r/grub/.bzr/branch/".

For trunk, use:

bzr branch http://bzr.savannah.gnu.org/r/grub/trunk/grub

Grégoire


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-21 23:46             ` Aleš Nesrsta
  2010-05-22  1:20               ` Grégoire Sutre
@ 2010-05-22  9:35               ` Colin Watson
  2010-05-22 23:13               ` Vladimir 'φ-coder/phcoder' Serbinenko
  2 siblings, 0 replies; 41+ messages in thread
From: Colin Watson @ 2010-05-22  9:35 UTC (permalink / raw)
  To: The development of GNU GRUB

On Sat, May 22, 2010 at 01:46:57AM +0200, Aleš Nesrsta wrote:
> I cannot get actual bzr code because of this error:
> $ bzr get http://bzr.savannah.gnu.org/r/grub
> bzr: ERROR: Not a branch:
> "http://bzr.savannah.gnu.org/r/grub/.bzr/branch/".

  bzr get http://bzr.savannah.gnu.org/r/grub/trunk/grub

Unfortunately there's no way for us to change the incorrect instructions
on Savannah. :-(

-- 
Colin Watson                                       [cjwatson@ubuntu.com]


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-21 23:46             ` Aleš Nesrsta
  2010-05-22  1:20               ` Grégoire Sutre
  2010-05-22  9:35               ` Colin Watson
@ 2010-05-22 23:13               ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 10:27                 ` Aleš Nesrsta
  2 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-22 23:13 UTC (permalink / raw)
  To: The development of GNU GRUB


[-- Attachment #1.1: Type: text/plain, Size: 3852 bytes --]

Aleš Nesrsta wrote:
> Hi Vladimir,
>
> I have some news - I made extensive testing and researching and I
> discovered some things related to OHCI and generally to USB MS or USB
> itself.
>
> I tried to fix them and it looks like I was probably successful in some
> cases:
>
> - multiple LUNs related bug (meaning of value from device & coding into
> CBW)
> - bulk transport toggling related bug - on devices with endpoints with
> the same "low" nuber (e.g. 01 & 81)
> - bug related to devices with max. packet size for control pipe 0 lower
> than 64
> - problem with one kind of OHCI BIOS legacy support maybe solved
> - another bug - some devices hangs because of Bulk-only Mass Storage
> Reset used during opening when it is not expected by device
> - my OHCI done-head loop should not hang anymore - timeout was added
> - "ls" should not hang anymore on USB MS device with not ready unit -
> timeout was added in SCSI open unit ready loop
>   
I hope in future we'll be able to do useful operations during wait.
> - experimentally added support for another subclasses of USB MS devices
> (as all are SCSI based - it possibly should work, at least it is working
> with one my device with subclass SFF-8070i - but I don't have another
> devices to test)
>
> In fact, all my USB MS devices are working now with grub2 with exception
> of one camera which have not Bulk-Only but CBI protocol.
>
> What I was not solved till now:
> - Some devices are not working on UHCI
> - If I connect some device and load uhci module, I cannot connect
> another device without rebooting. On OHCI it is possible without
> rebooting in this sequence:
> 	rmmod usbms
> 	rmmod ohci
> 	(unplug old device and plug new one)
> 	insmod ohci
> 	insmod usbms
>   
In my yeeloongfw branch I have hotplug support for ATA. I'm planning on
adding usbms hotplugging support but first I want to make Geode USB work.
> - It looks like my OHCI is not properly reporting STALL state of pipe -
> it is probably reason why done-head loop hanged if some problem happened
> in communication.
>   
In general there should be timeout in any loop which may hang if device
doesn't behave the way it's supposed to.

The fixes I've come up with are available in yeeloongfw branch. I've
adjusted your previous patch to work on top of yeeloongfw branch.
> I want to prepare patch and send it here but I cannot get actual bzr
> code. I would want have it at least because grub_memalign problem is not
> solved in last grub version wich I have (1.98) and I have to solve it by
> some workarounds (maybe it is also reason why UHCI does not work
> properly on my computer, I did not any workaround in its code yet).
>
> I cannot get actual bzr code because of this error:
> $ bzr get http://bzr.savannah.gnu.org/r/grub
> bzr: ERROR: Not a branch:
> "http://bzr.savannah.gnu.org/r/grub/.bzr/branch/".
>
> I tried to found some advice in man bzr and forums but I was not
> successful. It looks like problem is that there is really not existing
> folder ".../r/.bzr/branch" (I found "branch-lock" and "repository" only)
> - should I use some another address or some option ?
> Do You have some experiences with such problem ?
> Could You put me in right direction...?
>
> (I tried to download CVS source code - it works - but it looks outdated.
> I also tried register ssh public code and made access via ssh to
> sftp://starous@bzr.savannah.gnu.org/srv/bzr/grub - but I cannot login,
> there is ssh debug message that it is not possible to find
> authentication method...)
>
> Best regards
> Ales
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: yeeloong_usb_f.diff --]
[-- Type: text/x-diff; name="yeeloong_usb_f.diff", Size: 31358 bytes --]

=== modified file 'bus/usb/ohci.c'
--- bus/usb/ohci.c	2010-05-22 22:17:51 +0000
+++ bus/usb/ohci.c	2010-05-22 22:17:58 +0000
@@ -317,13 +317,29 @@
   token |= toggle << 24;
   token |= 1 << 25;
 
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+
   buffer = data;
   buffer_end = buffer + size - 1;
 
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size) 
+    {
+      buffer = data;
+      buffer_end = buffer + size - 1;
+      td->buffer = grub_cpu_to_le32 (buffer);
+      td->buffer_end = grub_cpu_to_le32 (buffer_end);
+    }
+  else 
+    {
+      td->buffer = 0;
+      td->buffer_end = 0;
+    }
+ 
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -375,6 +391,24 @@
 					     + (i + 1) * sizeof (td_list[0]));
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+  
+  td_list[transfer->transcnt].token = 15 << 28;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td = grub_cpu_to_le32 (td_list_addr
+							  + transfer->transcnt
+  							  * sizeof (*td_list));
+
+  for (i = 0; i <= transfer->transcnt; i++)
+    grub_dprintf ("ohci", "transaction %d: %x, %x, %x, %x\n", i,
+		  td_list[i].token, td_list[i].buffer,
+		  td_list[i].buffer_end, td_list[i].next_td);
+
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
@@ -401,30 +435,48 @@
   grub_dprintf ("ohci", "program OHCI\n");
 
   /* Program the OHCI to actually transfer.  */
+
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   switch (transfer->type)
     {
     case GRUB_USB_TRANSACTION_TYPE_BULK:
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -433,21 +485,9 @@
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -466,16 +506,61 @@
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
       grub_cpu_idle ();
 
       grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
-	break;
+      grub_dprintf ("ohci", "framenumber=0x%02x\n", o->hcca->framenumber);
+
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (ed->td_head) & 1)
+        break;
+
+      //      if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
+      //break;
+  
+      /* The original style of detection of finished transaction looks hazardeous.
+	 while ((grub_le_to_cpu32 (grub_ohci_read_mem32 (&ed->td_head)) & ~0xf)
+         != (grub_le_to_cpu32 (grub_ohci_read_mem32 (&ed->td_tail)) & ~0xf));
+      */
+      /* This should be according to OHCI specification:
+       * TD is finished and ED is updated when TD is retired and HcDoneHead
+       * register updated and, if allowed by WDH bit, written into HccaDoneHead.
+       * So we should:
+       * - allow WritebackDoneHead interrupt (WDH) by proper setting of last TD
+       *   token - it is done above in transaction settings
+       * - detect setting of WDH bit in HcInterruptStatus register
+       * - compare HccaDoneHead value with address of last-1 TD. If it is not
+       *   equal, check ED for halt and if not so, reset WDH bit and wait again
+       *   - but it should not happen - debug it!
+       */
+      grub_uint32_t intstatus = grub_ohci_readreg32 (o,
+						     GRUB_OHCI_REG_INTSTATUS);
+      grub_dprintf ("ohci", "intstatus = %x\n", intstatus);
+      if ((intstatus & 0x2) != 0)
+        {
+	  grub_dprintf ("ohci", "donehead = %x\n",
+			grub_le_to_cpu32 (o->hcca->donehead));
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+	      == td_list_addr + (transfer->transcnt - 1) * sizeof (*td_list))
+            break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+	  //          continue;
+	  break;
+        }
     }
+  while (1);
 
   grub_dprintf ("ohci", "complete\n");
 
@@ -484,23 +569,34 @@
 /*   else if (ed->td */
 
 
-  if (ed->td_head & 1)
+  if (grub_le_to_cpu32 (ed->td_head) & 1)
     {
-      grub_uint8_t errcode;
+      grub_uint8_t errcode = 0;
       grub_ohci_td_t tderr;
       grub_uint32_t td_err_addr;
 
-      td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD);
-
-      tderr = (grub_ohci_td_t) ((char *) td_list
-				+ (td_err_addr - td_list_addr));
-      errcode = tderr->token >> 28;
+      td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf;
+      grub_dprintf ("ohci", "td_err_addr =0x%x\n", td_err_addr);
+      if (td_err_addr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always now! */
+        td_err_addr = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
+
+      grub_dprintf ("ohci", "td_err_addr =0x%x\n", td_err_addr);
+
+      if (td_err_addr != 0)
+	{
+	  tderr = (grub_ohci_td_t) ((char *) td_list
+				    + (td_err_addr - td_list_addr));
+	  errcode = grub_le_to_cpu32 (tderr->token) >> 28;
+	}
+      grub_dprintf ("ohci", "errcode = %d\n", errcode);
 
       switch (errcode)
 	{
 	case 0:
 	  /* XXX: Should not happen!  */
-	  grub_error (GRUB_ERR_IO, "OHCI without reporting the reason");
+	  grub_error (GRUB_ERR_IO, "OHCI failed without reporting the reason");
 	  err = GRUB_USB_ERR_INTERNAL;
 	  break;
 
@@ -582,12 +678,41 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+  
+  /* Set ED to be skipped - for safety */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  /* Additional debug print. to be removed.
+  grub_dprintf ("ohci", "Before reset of WDH: INTSTATUS=0x%08x\n",
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+    */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  /* Additional debug print. to be removed.
+  grub_dprintf ("ohci", "After reset of WDH: INTSTATUS=0x%08x\n",
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+    */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err);
+  
   /* XXX */
-  grub_free (td_list);
-  grub_free (ed);
+  grub_dma_free (td_list_chunk);
+  grub_dma_free (ed_chunk);
 
   return err;
 }
@@ -668,8 +793,11 @@
 
 GRUB_MOD_INIT(ohci)
 {
+  grub_dprintf ("ohci", "init1\n");
   grub_ohci_inithw ();
+  grub_dprintf ("ohci", "init2\n");
   grub_usb_controller_dev_register (&usb_controller);
+  grub_dprintf ("ohci", "init3\n");
 }
 
 GRUB_MOD_FINI(ohci)

=== modified file 'bus/usb/usbhub.c'
--- bus/usb/usbhub.c	2009-12-24 22:53:05 +0000
+++ bus/usb/usbhub.c	2010-05-22 22:17:58 +0000
@@ -65,6 +65,9 @@
   dev->initialized = 1;
   grub_usb_devs[i] = dev;
 
+  grub_dprintf ("usb", "dev=%p, speed = %d\n", grub_usb_devs[i],
+		grub_usb_devs[i]->speed);
+
   return dev;
 }
 
@@ -155,11 +158,15 @@
 	{
 	  grub_usb_device_t dev;
 
+	  grub_dprintf ("usb", "calling portstatus\n");
+
 	  /* Enable the port.  */
 	  err = controller->dev->portstatus (controller, i, 1);
 	  if (err)
 	    continue;
 
+	  grub_dprintf ("usb", "calling add_dev\n");
+
 	  /* Enable the port and create a device.  */
 	  dev = grub_usb_hub_add_dev (controller, speed);
 	  if (! dev)
@@ -183,6 +190,8 @@
     {
       if (grub_usb_devs[i])
 	{
+	  grub_dprintf ("usb", "dev=%p, speed = %d\n", grub_usb_devs[i],
+			grub_usb_devs[i]->speed);
 	  if (hook (grub_usb_devs[i]))
 	      return 1;
 	}

=== modified file 'commands/usbtest.c'
--- commands/usbtest.c	2010-01-03 18:24:22 +0000
+++ commands/usbtest.c	2010-05-22 22:17:58 +0000
@@ -83,15 +83,14 @@
 			      0x06, (3 << 8) | index,
 			      langid, descstr.length, (char *) descstrp);
 
-  *string = grub_malloc (descstr.length / 2);
+  *string = grub_malloc (descstr.length * 2);
   if (! *string)
     {
       grub_free (descstrp);
       return GRUB_USB_ERR_INTERNAL;
     }
 
-  grub_utf16_to_utf8 ((grub_uint8_t *) *string, descstrp->str, descstrp->length / 2 - 1);
-  (*string)[descstr.length / 2 - 1] = '\0';
+  *grub_utf16_to_utf8 ((grub_uint8_t *) *string, descstrp->str, descstrp->length / 2 - 1) = 0;
   grub_free (descstrp);
 
   return GRUB_USB_ERR_NONE;
@@ -124,6 +123,11 @@
   int i;
 
   descdev = &dev->descdev;
+  grub_dprintf ("usb", "dev=%p, descdev=%p\n", dev, descdev);
+
+  grub_dprintf ("usb", "strprod=%d\n", descdev->strprod);
+  grub_dprintf ("usb", "strvendor=%d\n", descdev->strvendor);
+  grub_dprintf ("usb", "strserial=%d\n", descdev->strserial);
 
   usb_print_str ("Product", dev, descdev->strprod);
   usb_print_str ("Vendor", dev, descdev->strvendor);
@@ -139,6 +143,11 @@
 
   grub_printf ("%s speed device\n", usb_devspeed[dev->speed]);
 
+  grub_dprintf ("usb", "configcnt=%d\n", descdev->configcnt);
+  grub_dprintf ("usb", "config=%p\n", dev->config);
+
+  return 0;
+
   for (i = 0; i < descdev->configcnt; i++)
     {
       struct grub_usb_desc_config *config;

=== modified file 'disk/scsi.c'
--- disk/scsi.c	2010-03-05 14:29:28 +0000
+++ disk/scsi.c	2010-05-22 22:17:58 +0000
@@ -50,6 +50,57 @@
 }
 
 \f
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
 /* Determine the the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
@@ -58,15 +109,23 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +142,24 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
 
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +177,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +190,14 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +208,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +220,14 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +239,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +252,14 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -173,6 +270,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -183,7 +282,14 @@
   wr.reserved = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -292,6 +398,16 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests, issue Test Unit Ready until OK */
+      /* XXX: there should be some timeout... */
+      do
+        {
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -302,12 +418,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
 
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }

=== modified file 'disk/usbms.c'
--- disk/usbms.c	2010-01-20 08:12:47 +0000
+++ disk/usbms.c	2010-05-22 22:17:58 +0000
@@ -125,14 +125,16 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,6 +145,9 @@
 	      return 0;
 	    }
 
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
@@ -151,8 +156,8 @@
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
 
 	      /* Just set the amount of LUNs to one.  */
@@ -164,8 +169,9 @@
 
 	  /* XXX: Check the magic values, does this really make
 	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
+	  /* What is it? Removed. */
+	  /* grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
+				0, i, 0, 0); */
 
 	  /* XXX: To make Qemu work?  */
 	  if (usbms->luns == 0)
@@ -174,12 +180,12 @@
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 
 	  return 0;
 	}
@@ -225,6 +231,7 @@
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
+  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -240,70 +247,86 @@
   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
+  
+  /* Debug print of CBW content. */
+  grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n",
+  	cbw.signature, cbw.tag, cbw.transfer_length);
+  grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n",
+  	cbw.flags, cbw.lun, cbw.length);
+  grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3],
+  	cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7],
+  	cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11],
+  	cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]);
 
-  /* Write the request.  */
+  /* Write the request.  XXX: Error recovery should be corrected! */
   err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
       err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
+      /* Debug print of received data. */
+      grub_dprintf ("usb", "buf:\n");
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "buf:\n");
+      /* Debug print of sent data. */
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
+      if (err) goto CheckCSW;
     }
 
-  /* Read the status.  */
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
   err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
 			    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* Debug print of CSW content. */
+  grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n",
+  	status.signature, status.tag, status.residue);
+  grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
+  
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);

=== modified file 'include/grub/scsicmd.h'
--- include/grub/scsicmd.h	2008-08-27 15:05:00 +0000
+++ include/grub/scsicmd.h	2010-05-22 22:17:58 +0000
@@ -25,14 +25,24 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
+struct grub_scsi_test_unit_ready
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+} __attribute__((packed));
+
 struct grub_scsi_inquiry
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
-  grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +57,40 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +144,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum

=== modified file 'include/grub/usbtrans.h'
--- include/grub/usbtrans.h	2010-05-05 08:40:48 +0000
+++ include/grub/usbtrans.h	2010-05-22 22:17:58 +0000
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-22 23:13               ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-23 10:27                 ` Aleš Nesrsta
  2010-05-23 14:41                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 14:44                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 2 replies; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-23 10:27 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 10591 bytes --]

Hi Vladimir and others,

first of all thanks for immediate advice about Bazaar.

Attached is the usb patch related to OHCI and USB MS (SCSI).
(There is also very small patch for uhci.c - it signals in debug print
failed transaction even if it finished correctly.)
Patch is done against Bazaar source revision 2391 but.
I checked if files changed by me were changed by somebody other between
version 1.98 and current Bazaar revision - it looks like none of files
was changed in the meantime. So, in fact the patch should work also
against 1.98 release.

I partially cleaned some parts of code before making diff, I hope I did
not any mistake - I very shortly tested this patch and it looks OK.

There is lot of changes in many files - unfortunately some general
things was necessary to change.
(Some solutions could be made better maybe - I made the first simple
solutions, first ideas, to make things go on...)

Changes are (sometimes...) commented to clarify why it was done (and
what...). There are (I hope) no exact citations from specifications but
some keywords in comments are the same as in OHCI (or another)
specification - it is done only to "speak by the same language" and it
could be removed.

In patch remains some added or extended debug prints - You can delete
them.
I have them still in my source because I will probably sooner or later
continue in "researching" mainly in these directions:
- Why STALL (or maybe NAK?) is not properly indicated on OHCI (at least
on my computer) and instead of it timeout occurs (or hang in old source,
before timeout was added).
- OHCI can switch itself into Unrecoverable Error in some cases - it
should be detected and recovered by some reset
- Some devices are not working on UHCI
- UHCI is not working properly when module is removed and inserted again
- Maybe in future I change EDs into more "static" allocation - because
there is too big performance penalty with safe EDs and TDs allocation
and setup in OHCI registers and then reseting in OHCI registers and
deallocation of them - we should wait twice for SOF, i.e we lost approx.
2ms per each USB access. In suggested example of OHCI driver in OHCI
specification are EDs allocated permanently (whole time of driver
activity, not only on communication time) - it should improve speed of
OHCI because we will not need to change ED addresses in registers on
OHCI and we will not have to wait for SOF more.

Some members in SCSI command structures I changed and named according to
their role - only to have better overview of functions and values set in
related SCSI functions, I don't insist on usage of these names...

Maybe it is not fully true the comment "Each SCSI command should be
followed by Request Sense. If not so, many devices STALLs or definitely
freezes.". I discovered it in time when also some others bugs were not
solved and maybe it is not necessary in many cases, who knows...? But I
dont have sufficient patience to do such deep testing (every command on
every device...).

I found at home one device which has another USB MS subclass -
SFF-8070i. I tried to look into specifications of other subclasses than
SCSI and I discover that all of them are SCSI based and with exception
of QIC-157 (which is tape oriented) all should have implemented basic
SCSI command which are used in GRUB. There is only one thing which can
cause incompatibility - ATAPI SCSI commands must have size of command
block length set to 12 bytes (instead of variable length 6,8,10,...
defined in "normal" SCSI). It could make troubles on SCSI subclass
devices but I tried all my devices with SCSI subclass with
ATAPI-compatible length of command and all devices are working normally
- so I think it should be safe to use ATAPI format of commands
universally for any subclass and I tried to "add support" for most of
USB MS subclasses - in fact I only extended one condition in usbms.c...
I did not testing devices subclasses RBC, UFI and MMC-2 - I don't have
such devices - can do somebody such tests ?

Tests and "researching" were made on 10 devices, all of them are working
now (3 USB flash disks, 3 cameras - problem with control packet max.
length and also another subclass support, 1 USB external disk, 1
universal card reader "21 in one" - problem with more LUNs, 1 SDHC card
reader, 1 mobile phone - problem with "the same" enpoint numbers 01 & 81
and toggling of bulk transfer).

There is also potential problem with powering and current GRUB USB
"philosophy" - unfortunately, USB bus is enumerated in time when OHCI
module is loaded and activated.
It can make trouble on PC where it is necessary to do "SMM ownership
change" - which results (on my computer) in power-off and power-on of
USB bus - or on computers which have not powered USB bus before OHCI
module is loaded. Many devices has very long time between powering of
then and proper function on USB bus - it could be also problem of
Yeeloong.
But currently I have no idea how to solve it in GRUB. In "normal" OS the
registering of new USB device (enumerating) is done probably via
interrupt from hub port status change when newly attached USB device
becomes really ready. But in GRUB we cannot do such solution. What we
can do now is:
- set longer waiting period after powering of USB bus
- "rmmod usbms" (if loaded), "rmmod ohci", connect device and wait for
when becomes ready, "insmod ohci", "insmod usbms" (this sequence does
not work on UHCI, I currently don't know why).

Best regards
Ales


P.S. I received e-mails from Vladimir today, there are my quick
reactions:

Vladimir 'φ-coder/phcoder' Serbinenko wrote:

> > What I was not solved till now:
> > - Some devices are not working on UHCI
> > - If I connect some device and load uhci module, I cannot connect
> > another device without rebooting. On OHCI it is possible without
> > rebooting in this sequence:
> > 	rmmod usbms
> > 	rmmod ohci
> > 	(unplug old device and plug new one)
> > 	insmod ohci
> > 	insmod usbms
> >   
> In my yeeloongfw branch I have hotplug support for ATA. I'm planning on
> adding usbms hotplugging support but first I want to make Geode USB work.

I will look into yeeloongfw branch, I got Bazaar working yesterday
evening only... I agree with You, first should be solved known problems
on simple base.
Unfortunately, I probably cannot help You as I don't have GEODE or
Yeeloong... I can hope only that my corrections would help You or
inspire You in some other ways.
For me were very "disgusting", unexpected and hard to find problems with
short max. packet size on control pipe and problem with toggling bulk
transfers when EP have numbers 0x01 and 0x81.

> > - It looks like my OHCI is not properly reporting STALL state of pipe -
> > it is probably reason why done-head loop hanged if some problem happened
> > in communication.
> >   
> In general there should be timeout in any loop which may hang if device
> doesn't behave the way it's supposed to.

> The fixes I've come up with are available in yeeloongfw branch. I've
> adjusted your previous patch to work on top of yeeloongfw branch.

I implemented also such timeouts independently, You can use finally the
better solution...
But the timeout probably should never happen if all is programmed well,
so probably there is still something bad somewhere...


Vladimir 'φ-coder/phcoder' Serbinenko wrote: 

> > I also find that my concept is sometimes not working well, so there is
> > probably some bug (at least the one what it is mentioned in previous
> > sentence). But still I think the my way could be better and more closed
> > to specification and suggested OHCI control then original algorithm
> > which was probably taken from (or inspired by) UHCI - for example OHCI
> > is more "intelligent" than UHCI, because it takes done TDs and puts them
> > into "done queue" - and because it is working asynchronously it can be
> > done later then in time of transaction end detection in the way by
> > comparison head TD and last TD - it can be source of possible hazard.
> >   
> AFAIR According to spec if  these values are the same queue is empty.
> 
Yes, but there is question WHEN it is done. OHCI HC is doing things
sequentially (and in parallel with CPU via DMA). OHCI HC is doing so
called "retiring" of TDs AFTER values in ED are the same. I.e., when You
detect the same value in ED and make deallocation of ED and TDs, OHCI
can do something in TDs memory location at this time. "Retiring finish"
signaled by interrupt from DoneHead should be the LAST thing which OHCI
HC is doing, so it is more safe to use it - and such interrupt is used
by driver in Linux and Windows also (as it is in example in OHCI
specification).

But if You have better results with the old style of transfer end
detection, it is signal that I have still something wrong in my code -
maybe it is related to problem with mising STALL or NAK indication from
OHCI (or possibly also Unrecoverable error state).
Or it can be also some HW bug - when You look into Linux USB drivers
source code, You will find some workarounds related to HW "specialites"
of some OHCI chips...

> > Also I tested influences of BIOS settings - USB 2.0 can be enabled in
> > BIOS but "Legacy support" makes mostly troubles (but it depends on used
> > computer, there are probably many variations... - for example on my
> > computer with UHCI "Legacy Support" means also "USB booting support" but
> > on second computer with OHCI "Legacy Support" means keyboard&mouse
> > support only and unfortunately it is done in another way than it is
> > written in OHCI Legacy Support specification...).
> > Question - how can BIOS influence GRUB2 ? Are somewhere in GRUB2
> > callings of BIOS functions or something like that ? Can BIOS do
> > something via its previously claimed interrupts (which are all disabled
> > in GRUB2 - if I good understand) ?
> >   
> This legacy support is real trouble because it uses SMI which are colled
> e.g. on register writes. In specifications you can find the expected
> handoff BIOS->OS protocol but not all BIOSes implement it correctly. You
> can have a look at my mail a while ago entitled " [PATCH] reset USB
> controller".
I did not noticed this mail, I will look into it - I made also some
simple workaround in OHCI initialization function but I don't know if it
is correct. Another bad thing is also re-powering of USB bus which I
mentioned above.

Best regards
Ales


[-- Attachment #2: usb_patch_100523_0 --]
[-- Type: text/x-patch, Size: 46106 bytes --]

diff -urB ./grub/bus/usb/ohci.c ./grub-patched/bus/usb/ohci.c
--- ./grub/bus/usb/ohci.c	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/bus/usb/ohci.c	2010-05-22 23:36:59.000000000 +0200
@@ -91,10 +91,18 @@
   GRUB_OHCI_REG_BULKCURR,
   GRUB_OHCI_REG_DONEHEAD,
   GRUB_OHCI_REG_FRAME_INTERVAL,
+  GRUB_OHCI_REG_PERIODIC_START = 16,
   GRUB_OHCI_REG_RHUBA = 18,
-  GRUB_OHCI_REG_RHUBPORT = 21
+  GRUB_OHCI_REG_RHUBPORT = 21,
+  GRUB_OHCI_REG_LEGACY_CONTROL = 0x100,
+  GRUB_OHCI_REG_LEGACY_INPUT = 0x104,
+  GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108,
+  GRUB_OHCI_REG_LEGACY_STATUS = 0x10c
 } grub_ohci_reg_t;
 
+#define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
+#define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200
+
 static grub_uint32_t
 grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
 {
@@ -125,7 +133,9 @@
   struct grub_ohci *o;
   grub_uint32_t revision;
   grub_uint32_t frame_interval;
-
+  grub_uint32_t control;
+  grub_uint32_t i;
+  
   addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
   class_code = grub_pci_read (addr) >> 8;
 
@@ -157,17 +167,58 @@
   /* Reserve memory for the HCCA.  */
   o->hcca = (struct grub_ohci_hcca *) grub_memalign (256, 256);
 
-  grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x base=%p\n",
- 		class, subclass, interf, o->iobase);
+  grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x base=%p hcca=%p\n",
+ 		class, subclass, interf, o->iobase, o->hcca);
 
   /* Check if the OHCI revision is actually 1.0 as supported.  */
   revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION);
-  grub_dprintf ("ohci", "OHCI revision=0x%02x\n", revision & 0xFF);
+  grub_dprintf ("ohci", "OHCI revision=0x%02x, rev. register=0x%04x\n",
+                revision & 0xFF, revision);
   if ((revision & 0xFF) != 0x10)
     goto fail;
 
+  /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  if ((control & 0x100) != 0)
+    {
+      grub_dprintf("ohci", "OHCI is owned by SMM\n");
+      /* Do change of ownership */
+      /* Ownership change request */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic.  */
+      /* Waiting for SMM deactivation */
+      for (i=0; i < 10; i++)
+        {
+          if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0)
+            {
+              grub_dprintf("ohci", "Ownership changed normally.\n");
+              break;
+            }
+          grub_millisleep (100);
+          }
+      if (i >= 10)
+        {
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+          grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100);
+          grub_dprintf("ohci", "Ownership changing timeout, change forced !\n");
+        }
+    }
+  else if (((control & 0x100) == 0) && 
+           ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */
+    {
+      grub_dprintf("ohci", "OHCI is owned by BIOS\n");
+      /* Do change of ownership - not implemented yet... */
+      /* In fact we probably need to do nothing ...? */
+    }
+  else
+    {
+      grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n");
+      /* We can setup OHCI. */
+    }
+  
   /* Backup the frame interval register.  */
   frame_interval = grub_ohci_readreg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL);
+  if (frame_interval == 0)
+  	frame_interval = 0x27782EDF;
 
   /* Suspend the OHCI by issuing a reset.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
@@ -175,18 +226,64 @@
   grub_dprintf ("ohci", "OHCI reset\n");
 
   /* Restore the frame interval register.  */
+  frame_interval = (frame_interval & 0x3fff) | (((((frame_interval & 0x3fff) - 210) * 6) / 7) << 16);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL, frame_interval);
 
+  /* Set periodic start reg. - it is necessary only after HW reset, but it is probably our case */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START, ((frame_interval & 0x3fff) * 9) / 10);
+
+  /* Misc. pre-sets. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
   /* Setup the HCCA.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, (grub_uint32_t) o->hcca);
   grub_dprintf ("ohci", "OHCI HCCA\n");
 
+  /* Check OHCI Legacy Support */
+  if ((revision & 0x100) != 0)
+  	{
+  	grub_dprintf ("ohci", "Legacy Support registers detected\n");
+  	grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n",
+  		grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL));
+  	grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL,
+  	                      (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1);
+  	grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
+	}
+
   /* Enable the OHCI.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
 			(2 << 6));
   grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
 		(grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
 
+  /* Power on all ports */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
+                       (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
+                        & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
+                       | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+  /* Wait for stable power (100ms) and stable attachment (100ms) */
+  /* I.e. minimum wait time should be probably 200ms. */
+  /* We assume that device is attached when ohci is loaded. */
+  /* Some devices take long time to power-on or indicate attach. */
+  /* Here is some experimental value which should probably mostly work. */
+  /* Cameras with manual USB mode selection and maybe some other similar
+  * devices will not work in some cases - they are repowered during
+  * ownership change and then they are starting slowly and mostly they
+  * are wanting select proper mode again...
+  * The same situation can be on computers where BIOS not set-up OHCI
+  * to be at least powered USB bus (maybe it is Yeelong case...?)
+  * Possible workaround could be for example some prompt
+  * for user with confirmation of proper USB device connection.
+  * Another workaround - "rmmod usbms", "rmmod ohci", proper start
+  * and configuration of USB device and then "insmod ohci"
+  * and "insmod usbms". */
+  grub_millisleep (500);
+	
   /* Link to ohci now that initialisation is successful.  */
   o->next = ohci;
   ohci = o;
@@ -261,13 +358,26 @@
   token |= toggle << 24;
   token |= 1 << 25;
 
-  buffer = (grub_uint32_t) data;
-  buffer_end = buffer + size - 1;
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+  
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size) 
+    {
+      buffer = (grub_uint32_t) data;
+      buffer_end = buffer + size - 1;
+      td->buffer = grub_cpu_to_le32 (buffer);
+      td->buffer_end = grub_cpu_to_le32 (buffer_end);
+    }
+  else 
+    {
+      td->buffer = 0;
+      td->buffer_end = 0;
+    }
 
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -283,7 +393,11 @@
   grub_uint32_t status;
   grub_uint32_t control;
   grub_usb_err_t err;
-  int i;
+  grub_uint8_t errcode = 0;
+  grub_ohci_td_t tderr = NULL;
+  int i, j;
+  grub_uint64_t maxtime;
+  int err_timeout = 0;
 
   /* Allocate an Endpoint Descriptor.  */
   ed = grub_memalign (16, sizeof (*ed));
@@ -310,13 +424,25 @@
       td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]);
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * normal end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+
+  td_list[transfer->transcnt].token = 0;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td =
+    (grub_uint32_t) &td_list[transfer->transcnt];
+  
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
   target = transfer->devaddr;
 
-  /* Set the endpoint.  */
-  target |= transfer->endpoint << 7;
+  /* Set the endpoint. It should be masked, we need 4 bits only. */
+  target |= (transfer->endpoint & 15) << 7;
 
   /* Set the device speed.  */
   target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
@@ -336,30 +462,48 @@
   grub_dprintf ("ohci", "program OHCI\n");
 
   /* Program the OHCI to actually transfer.  */
+
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   switch (transfer->type)
     {
     case GRUB_USB_TRANSACTION_TYPE_BULK:
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, (grub_uint32_t) ed);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -368,22 +512,11 @@
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD,
 			      (grub_uint32_t) ed);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      (grub_uint32_t) ed);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR,
+			      0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -398,37 +531,76 @@
 
   grub_dprintf ("ohci", "wait for completion\n");
   grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n",
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
+  /* Starting time for timeout - feel free to change the value,
+   * I have no idea about correct value ...
+   * It is workaround only because it looks like my OHCI does not
+   * inicate properly STALL or NAK (or both...) - or there is
+   * some mistake somewhere... */
+  maxtime = grub_get_time_ms () + 1000;
+	
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
       grub_cpu_idle ();
 
-      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
-	break;
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (ed->td_head) & 1)
+        break;
+  
+      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+        {
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+               == (grub_uint32_t) &td_list[transfer->transcnt - 1])
+            break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          continue;
+        }
+      /* Timeout ? */
+      if (grub_get_time_ms () > maxtime)
+      	{
+	  /* Disable the Control and Bulk lists.  */
+  	  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+	  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4));
+      	  err_timeout = 1;
+      	  break;
+      	}
     }
+  while (1);
 
-  grub_dprintf ("ohci", "complete\n");
-
-/*   if (ed->td_head & 1) */
-/*     err = GRUB_USB_ERR_STALL; */
-/*   else if (ed->td */
-
-
-  if (ed->td_head & 1)
+  if (err_timeout)
     {
-      grub_uint8_t errcode;
-      grub_ohci_td_t tderr;
-
-      tderr = (grub_ohci_td_t) grub_ohci_readreg32 (o,
-						    GRUB_OHCI_REG_DONEHEAD);
-      errcode = tderr->token >> 28;
+      err = GRUB_ERR_TIMEOUT;
+      grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+      grub_le_to_cpu32(ed->target),
+      grub_le_to_cpu32(ed->td_head),
+      grub_le_to_cpu32(ed->td_tail),
+      grub_le_to_cpu32(ed->next_ed));
+    }
+  		
+  else if (grub_le_to_cpu32 (ed->td_head) & 1)
+    {
+      tderr = (grub_ohci_td_t)
+                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+      if (tderr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always now! */
+        tderr = (grub_ohci_td_t) (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
 
+      errcode = grub_le_to_cpu32 (tderr->token) >> 28;
+      
       switch (errcode)
 	{
 	case 0:
@@ -473,11 +645,17 @@
 	case 8:
 	  /* XXX: Data overrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 9:
 	  /* XXX: Data underrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
+	                1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer));
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 10:
@@ -515,9 +693,34 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+  
+  /* Set ED to be skipped - for safety */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame.
+   * It is necessary because we will invalidate pointer to ED and it
+   * can be on OHCI active till SOF!
+   * Because we are not using interrupt, we reset SF bit and wait when
+   * it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x (OHCI errcode=0x%02x)\n",
+                err, errcode);
+  
   /* XXX */
   grub_free (td_list);
   grub_free (ed);
@@ -532,11 +735,20 @@
    struct grub_ohci *o = (struct grub_ohci *) dev->data;
    grub_uint32_t status;
 
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
+                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+
+   /* Enable the port.  */
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status |= (enable << 1); /* XXX: Magic.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+
    /* Reset the port.  */
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
    status |= (1 << 4); /* XXX: Magic.  */
    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-   grub_millisleep (100);
+   grub_millisleep (50); /* For root hub should be nominaly 50ms */
 
    /* End the reset signaling.  */
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
@@ -544,13 +756,8 @@
    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
    grub_millisleep (10);
 
-   /* Enable the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (enable << 1); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   grub_dprintf ("ohci", "portstatus=0x%02x\n", status);
+   grub_dprintf ("ohci", "end of portstatus=0x%02x\n", status);
 
    return GRUB_ERR_NONE;
 }
diff -urB ./grub/bus/usb/uhci.c ./grub-patched/bus/usb/uhci.c
--- ./grub/bus/usb/uhci.c	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/bus/usb/uhci.c	2010-05-16 13:15:33.000000000 +0200
@@ -174,14 +174,15 @@
     return 1;
 
   u->iobase = base & GRUB_UHCI_IOMASK;
-  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n",
-		class, subclass, interf, u->iobase);
 
   /* Reserve a page for the frame list.  */
   u->framelist = grub_memalign (4096, 4096);
   if (! u->framelist)
     goto fail;
 
+  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n",
+		class, subclass, interf, u->iobase, u->framelist);
+
   /* The framelist pointer of UHCI is only 32 bits, make sure this
      code works on on 64 bits architectures.  */
 #if GRUB_CPU_SIZEOF_VOID_P == 8
@@ -221,6 +222,9 @@
     }
 #endif
 
+  grub_dprintf ("uhci", "QH=%p, TD=%p\n",
+		u->qh, u->td);
+
   /* Link all Transfer Descriptors in a list of available Transfer
      Descriptors.  */
   for (i = 0; i < 256; i++)
@@ -441,6 +445,8 @@
   if (! qh)
     return grub_errno;
 
+  grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase);
+  
   for (i = 0; i < transfer->transcnt; i++)
     {
       grub_usb_transaction_t tr = &transfer->transactions[i];
@@ -548,7 +554,8 @@
 
  fail:
 
-  grub_dprintf ("uhci", "transaction failed\n");
+  if (err != GRUB_USB_ERR_NONE)
+    grub_dprintf ("uhci", "transaction failed\n");
 
   /* Place the QH back in the free list and deallocate the associated
      TDs.  */
@@ -583,6 +590,8 @@
   unsigned int status;
   grub_uint64_t endtime;
 
+  grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase);
+  
   grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
 
   if (port == 0)
@@ -631,6 +640,8 @@
   int reg;
   unsigned int status;
 
+  grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase);
+  
   if (port == 0)
     reg = GRUB_UHCI_REG_PORTSC1;
   else if (port == 1)
diff -urB ./grub/bus/usb/usb.c ./grub-patched/bus/usb/usb.c
--- ./grub/bus/usb/usb.c	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/bus/usb/usb.c	2010-05-22 23:16:30.000000000 +0200
@@ -107,7 +107,7 @@
 {
   int i;
 
-  for (i = 0; i < 16; i++)
+  for (i = 0; i < 256; i++)
     dev->toggle[i] = 0;
 
   return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
@@ -163,6 +163,16 @@
   grub_usb_err_t err;
   int i;
 
+  /* First we have to read first 8 bytes only and determine
+   * max. size of packet */
+  dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */
+  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
+				 0, 8, (char *) &dev->descdev);
+  if (err)
+    return err;
+
+  /* Now we have valid value in dev->descdev.maxsize0,
+   * so we can read whole device descriptor */
   err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
 				 0, sizeof (struct grub_usb_desc_device),
 				 (char *) &dev->descdev);
diff -urB ./grub/bus/usb/usbtrans.c ./grub-patched/bus/usb/usbtrans.c
--- ./grub/bus/usb/usbtrans.c	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/bus/usb/usbtrans.c	2010-05-22 23:18:05.000000000 +0200
@@ -37,7 +37,7 @@
   struct grub_usb_packet_setup setupdata;
   grub_usb_err_t err;
   unsigned int max;
-
+	
   grub_dprintf ("usb",
 		"control: reqtype=0x%02x req=0x%02x val=0x%02x idx=0x%02x size=%d\n",
 		reqtype, request,  value, index, size);
@@ -48,10 +48,15 @@
     return grub_errno;
 
   /* Determine the maximum packet size.  */
-  if (dev->initialized)
+  if (dev->descdev.maxsize0)
     max = dev->descdev.maxsize0;
   else
     max = 64;
+  /* Note: dev->descdev.maxsize0 should be valid always !
+   * Only one exception - read of first 8 bytes of device descriptor,
+   * but legth of data must be 8 bytes in this case !
+   * (8 bytes is minimal value of max. packet length and some devices
+   * surprisingly use it...) */
 
   datablocks = (size + max - 1) / max;
 
@@ -103,10 +108,11 @@
       size -= max;
     }
 
-  /* End with an empty OUT transaction.  */
+  /* End with an empty transaction in reverse direction.  */
+  /* Or, if no data are transferred, use IN direction */
   transfer->transactions[datablocks + 1].size = 0;
   transfer->transactions[datablocks + 1].data = NULL;
-  if (reqtype & 128)
+  if ((reqtype & 128) && i)
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT;
   else
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN;
@@ -116,6 +122,7 @@
   err = dev->controller.dev->transfer (&dev->controller, transfer);
 
   grub_free (transfer->transactions);
+  
   grub_free (transfer);
 
   return err;
@@ -155,7 +162,7 @@
   datablocks = ((size + max - 1) / max);
   transfer->transcnt = datablocks;
   transfer->size = size - 1;
-  transfer->endpoint = endpoint;
+  transfer->endpoint = endpoint & 15;
   transfer->devaddr = dev->addr;
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
   transfer->max = max;
diff -urB ./grub/disk/scsi.c ./grub-patched/disk/scsi.c
--- ./grub/disk/scsi.c	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/disk/scsi.c	2010-05-22 23:22:31.000000000 +0200
@@ -25,6 +25,7 @@
 #include <grub/types.h>
 #include <grub/scsi.h>
 #include <grub/scsicmd.h>
+#include <grub/time.h>
 
 \f
 static grub_scsi_dev_t grub_scsi_dev_list;
@@ -50,7 +51,62 @@
 }
 
 \f
-/* Determine the the device is removable and the type of the device
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+  grub_memset (rs.pad, 0, sizeof(rs.pad));
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+  grub_memset (tur.pad, 0, sizeof(tur.pad));
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
+/* Determine if the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
 grub_scsi_inquiry (grub_scsi_t scsi)
@@ -58,15 +114,26 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
+  grub_memset (iq.pad, 0, sizeof(iq.pad));
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +150,27 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
-
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
+  rc.pad = 0;
+	
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+/* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +188,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +201,16 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +221,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +233,16 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +254,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +267,16 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -172,7 +286,9 @@
 		   grub_size_t size, char *buf)
 {
   grub_scsi_t scsi;
-  struct grub_scsi_write10 wr;
+  struct grub_scsi_write12 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -181,9 +297,18 @@
   wr.lba = grub_cpu_to_be32 (sector);
   wr.size = grub_cpu_to_be32 (size);
   wr.reserved = 0;
-  wr.pad = 0;
+  wr.control = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -235,6 +360,7 @@
   grub_err_t err;
   int len;
   int lun;
+  grub_uint64_t maxtime;
 
   scsi = grub_malloc (sizeof (*scsi));
   if (! scsi)
@@ -292,6 +418,31 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests specification, issue Test Unit Ready
+       * until OK */
+      maxtime = grub_get_time_ms () + 1000;
+      do
+        {
+	  /* Timeout is necessary - for example in case when we have
+	   * universal card reader with more LUNs and we have only
+	   * one card inserted (or none), so only one LUN (or none)
+	   * will be ready - and we want not to hang... */
+	  if (grub_get_time_ms () > maxtime)
+            {
+              err = GRUB_ERR_READ_ERROR;
+              grub_free (scsi);
+              grub_dprintf ("scsi", "LUN is not ready - timeout\n");
+              return err;
+            }
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+      /* Reset grub_errno !
+       * It is set to some error code in loop before... */
+      grub_errno = GRUB_ERR_NONE;
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -302,12 +453,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
-
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
+
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }
diff -urB ./grub/disk/usbms.c ./grub-patched/disk/usbms.c
--- ./grub/disk/usbms.c	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/disk/usbms.c	2010-05-22 23:51:47.000000000 +0200
@@ -93,14 +93,22 @@
 	  struct grub_usbms_dev *usbms;
 	  struct grub_usb_desc_if *interf;
 	  int j;
-	  grub_uint8_t luns;
+	  grub_uint8_t luns = 0;
 
 	  interf = usbdev->config[0].interf[i].descif;
 
 	  /* If this is not a USB Mass Storage device with a supported
 	     protocol, just skip it.  */
+	  grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n",
+	                i, interf->class, interf->subclass, interf->protocol);
+
 	  if (interf->class != GRUB_USB_CLASS_MASS_STORAGE
-	      || interf->subclass != GRUB_USBMS_SUBCLASS_BULK
+	      || ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK &&
+	    /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */
+	           interf->subclass != GRUB_USBMS_SUBCLASS_RBC &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_UFI &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 )
 	      || interf->protocol != GRUB_USBMS_PROTOCOL_BULK)
 	    {
 	      continue;
@@ -125,14 +133,16 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,18 +153,21 @@
 	      return 0;
 	    }
 
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
+		
 	  if (err)
 	    {
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
-
 	      /* Just set the amount of LUNs to one.  */
 	      grub_errno = GRUB_ERR_NONE;
 	      usbms->luns = 1;
@@ -160,26 +173,27 @@
 	      usbms->luns = 1;
 	    }
 	  else
-	    usbms->luns = luns;
-
-	  /* XXX: Check the magic values, does this really make
-	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
-
-	  /* XXX: To make Qemu work?  */
-	  if (usbms->luns == 0)
-	    usbms->luns = 1;
+            /* luns = 0 means one LUN with ID 0 present ! */
+            /* We get from device not number of LUNs but highest
+             * LUN number. LUNs are numbered from 0, 
+             * i.e. number of LUNs is luns+1 ! */
+	    usbms->luns = luns + 1;
 
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+#if 0 /* All this part should be probably deleted.
+     * This make trouble on some devices if they are not in
+     * Phase Error state - and there they should be not in such state...
+     * Bulk only mass storage reset procedure should be used only
+     * on place and in time when it is really necessary. */
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
+#endif
 
 	  return 0;
 	}
@@ -225,6 +239,7 @@
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
+  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -237,73 +252,89 @@
   cbw.tag = tag++;
   cbw.transfer_length = grub_cpu_to_le32 (size);
   cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT;
-  cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
-
-  /* Write the request.  */
-  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
+  
+  /* Debug print of CBW content. */
+  grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n",
+  	cbw.signature, cbw.tag, cbw.transfer_length);
+  grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n",
+  	cbw.flags, cbw.lun, cbw.length);
+  grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3],
+  	cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7],
+  	cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11],
+  	cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]);
+
+  /* Write the request.
+   * XXX: Error recovery is maybe still not fully correct. */
+  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
-      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf);
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
+      /* Debug print of received data. */
+      grub_dprintf ("usb", "buf:\n");
+      if (size <= 64)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "buf:\n");
+      /* Debug print of sent data. */
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
 
-  /* Read the status.  */
-  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
-			    sizeof (status), (char *) &status);
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
+  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+		    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* Debug print of CSW content. */
+  grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n",
+  	status.signature, status.tag, status.residue);
+  grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
+  
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
diff -urB ./grub/include/grub/scsicmd.h ./grub-patched/include/grub/scsicmd.h
--- ./grub/include/grub/scsicmd.h	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/include/grub/scsicmd.h	2010-05-20 23:02:12.000000000 +0200
@@ -25,14 +25,26 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
-struct grub_scsi_inquiry
+struct grub_scsi_test_unit_ready
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
   grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
+struct grub_scsi_inquiry
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +59,42 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
+  grub_uint16_t pad; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +148,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum
diff -urB ./grub/include/grub/usb.h ./grub-patched/include/grub/usb.h
--- ./grub/include/grub/usb.h	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/include/grub/usb.h	2010-05-21 12:30:30.000000000 +0200
@@ -156,7 +156,7 @@
   int initialized;
 
   /* Data toggle values (used for bulk transfers only).  */
-  int toggle[16];
+  int toggle[256];
 
   /* Device-specific data.  */
   void *data;
@@ -184,7 +184,12 @@
 
 typedef enum
   {
-    GRUB_USBMS_SUBCLASS_BULK = 0x06
+    GRUB_USBMS_SUBCLASS_BULK = 0x06,
+  	/* Experimental support for non-pure SCSI devices */
+    GRUB_USBMS_SUBCLASS_RBC = 0x01,
+    GRUB_USBMS_SUBCLASS_MMC2 = 0x02,
+    GRUB_USBMS_SUBCLASS_UFI = 0x04,
+    GRUB_USBMS_SUBCLASS_SFF8070 = 0x05
   } grub_usbms_subclass_t;
 
 typedef enum
diff -urB ./grub/include/grub/usbtrans.h ./grub-patched/include/grub/usbtrans.h
--- ./grub/include/grub/usbtrans.h	2010-05-22 16:44:52.000000000 +0200
+++ ./grub-patched/include/grub/usbtrans.h	2010-04-08 22:25:13.000000000 +0200
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 10:27                 ` Aleš Nesrsta
@ 2010-05-23 14:41                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 17:42                     ` Aleš Nesrsta
  2010-05-23 17:51                     ` [Patch] [bug #26237] " Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 14:44                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  1 sibling, 2 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-23 14:41 UTC (permalink / raw)
  To: The development of GNU GRUB


[-- Attachment #1.1: Type: text/plain, Size: 9036 bytes --]

Aleš Nesrsta wrote:
> Hi Vladimir and others,
>
> first of all thanks for immediate advice about Bazaar.
>
> Attached is the usb patch related to OHCI and USB MS (SCSI).
> (There is also very small patch for uhci.c - it signals in debug print
> failed transaction even if it finished correctly.)
> Patch is done against Bazaar source revision 2391 but.
> I checked if files changed by me were changed by somebody other between
> version 1.98 and current Bazaar revision - it looks like none of files
> was changed in the meantime. So, in fact the patch should work also
> against 1.98 release.
>
>   
I've adjusted it for yeeloongfw branch. Attached. It also fixes 2
further problems: after port reset you have to power it up by writing (1
<< 1) and in usbms code tries to read config[0] even if configcnt = 0.
With this patch I was able to use PCI OHCI controller but not Geode. I
discovered that one of yeeloong external ports (upper right one) it
routed to PCI OHCI and I was able to load kernel from stick inserted
into this port.
> In patch remains some added or extended debug prints - You can delete
> them.
>   
It's better to leave them until some parts stabilise.
> I have them still in my source because I will probably sooner or later
> continue in "researching" mainly in these directions:
> - Why STALL (or maybe NAK?) is not properly indicated on OHCI (at least
> on my computer) and instead of it timeout occurs (or hang in old source,
> before timeout was added).
>   
I think that interrupt you use just notifies that some progress is done.
And old way of detecting was mostly correct. But I may be wrong.
Another possibility is that some devices don't work correctly on the
speed they report.
> - OHCI can switch itself into Unrecoverable Error in some cases - it
> should be detected and recovered by some reset
> - Some devices are not working on UHCI
>   
Speed or power problems perhaps. You may need to configure the amount of
current available to device. Does they work with booted OS?

> - UHCI is not working properly when module is removed and inserted again
> - Maybe in future I change EDs into more "static" allocation - because
> there is too big performance penalty with safe EDs and TDs allocation
> and setup in OHCI registers and then reseting in OHCI registers and
> deallocation of them - we should wait twice for SOF, i.e we lost approx.
> 2ms per each USB access. In suggested example of OHCI driver in OHCI
> specification are EDs allocated permanently (whole time of driver
> activity, not only on communication time) - it should improve speed of
> OHCI because we will not need to change ED addresses in registers on
> OHCI and we will not have to wait for SOF more.
>
>   
It's a good idea.

> Maybe it is not fully true the comment "Each SCSI command should be
> followed by Request Sense. If not so, many devices STALLs or definitely
> freezes.". I discovered it in time when also some others bugs were not
> solved and maybe it is not necessary in many cases, who knows...? But I
> dont have sufficient patience to do such deep testing (every command on
> every device...).
>
>   
Right now I would prefer something working rather than fast but
half-working.
> I found at home one device which has another USB MS subclass -
> SFF-8070i. I tried to look into specifications of other subclasses than
> SCSI and I discover that all of them are SCSI based and with exception
> of QIC-157 (which is tape oriented) all should have implemented basic
> SCSI command which are used in GRUB. There is only one thing which can
> cause incompatibility - ATAPI SCSI commands must have size of command
> block length set to 12 bytes (instead of variable length 6,8,10,...
> defined in "normal" SCSI).
You can add a field for this in scsi structures.
>  It could make troubles on SCSI subclass
> devices but I tried all my devices with SCSI subclass with
> ATAPI-compatible length of command and all devices are working normally
> - so I think it should be safe to use ATAPI format of commands
> universally for any subclass and I tried to "add support" for most of
> USB MS subclasses - in fact I only extended one condition in usbms.c...
> I did not testing devices subclasses RBC, UFI and MMC-2 - I don't have
> such devices - can do somebody such tests ?
>   
Is MMC-2 subclass of cardreaders?
> Tests and "researching" were made on 10 devices, all of them are working
> now (3 USB flash disks, 3 cameras - problem with control packet max.
> length and also another subclass support, 1 USB external disk, 1
> universal card reader "21 in one" - problem with more LUNs, 1 SDHC card
> reader, 1 mobile phone - problem with "the same" enpoint numbers 01 & 81
> and toggling of bulk transfer).
>
> There is also potential problem with powering and current GRUB USB
> "philosophy" - unfortunately, USB bus is enumerated in time when OHCI
> module is loaded and activated.
> It can make trouble on PC where it is necessary to do "SMM ownership
> change" - which results (on my computer) in power-off and power-on of
> USB bus - or on computers which have not powered USB bus before OHCI
> module is loaded. Many devices has very long time between powering of
> then and proper function on USB bus - it could be also problem of
> Yeeloong.
> But currently I have no idea how to solve it in GRUB. In "normal" OS the
> registering of new USB device (enumerating) is done probably via
> interrupt from hub port status change when newly attached USB device
> becomes really ready. 
I plan on adding IRQs. But first things first.
> But in GRUB we cannot do such solution. What we
> can do now is:
> - set longer waiting period after powering of USB bus
>   
I would go for this right now and change to interrupts afterwards.
> I will look into yeeloongfw branch, I got Bazaar working yesterday
> evening only... I agree with You, first should be solved known problems
> on simple base.
> Unfortunately, I probably cannot help You as I don't have GEODE or
> Yeeloong... I can hope only that my corrections would help You or
> inspire You in some other ways.
>   
I don't think it's something deeply specific to Geode. Other than having
Geode MSR instead of PCI configuration space it's normal OHCI. Actually
with your patch when you forgot to power device up it resulted in
similar symptoms on PCI OHCI controller. OHCI specification mentions
different ways to configure power settings. I guess geode defaults to
another mode than most controllers do. Or perhaps I need to somehow
enable ports.
>
>> The fixes I've come up with are available in yeeloongfw branch. I've
>> adjusted your previous patch to work on top of yeeloongfw branch.
>>     
>
>   
> I implemented also such timeouts independently, You can use finally the
> better solution...
> But the timeout probably should never happen if all is programmed well,
> so probably there is still something bad somewhere...
>
>   
Timeout may happen also because of hw problem. They should be kept even
after we chase all the bugs in the code.
>> AFAIR According to spec if  these values are the same queue is empty.
>>
>>     
> Yes, but there is question WHEN it is done. OHCI HC is doing things
> sequentially (and in parallel with CPU via DMA). OHCI HC is doing so
> called "retiring" of TDs AFTER values in ED are the same. I.e., when You
> detect the same value in ED and make deallocation of ED and TDs, OHCI
> can do something in TDs memory location at this time. "Retiring finish"
> signaled by interrupt from DoneHead should be the LAST thing which OHCI
> HC is doing, so it is more safe to use it - and such interrupt is used
> by driver in Linux and Windows also (as it is in example in OHCI
> specification).
>
>   
ok
> But if You have better results with the old style of transfer end
> detection, it is signal that I have still something wrong in my code -
> maybe it is related to problem with mising STALL or NAK indication from
> OHCI (or possibly also Unrecoverable error state).
> Or it can be also some HW bug - when You look into Linux USB drivers
> source code, You will find some workarounds related to HW "specialites"
> of some OHCI chips...
>
>   
Perhaps you need clearing more interrupt status bits?
> I did not noticed this mail, I will look into it - I made also some
> simple workaround in OHCI initialization function but I don't know if it
> is correct. Another bad thing is also re-powering of USB bus which I
> mentioned above.
>   
Taking USB controller by force is of course wrong but with some BIOSes
it's the only choice
> Best regards
> Ales
>
>   
> ------------------------------------------------------------------------
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: yeeloong_usb_h.diff --]
[-- Type: text/x-diff; name="yeeloong_usb_h.diff", Size: 46119 bytes --]

=== modified file 'bus/usb/ohci.c'
--- bus/usb/ohci.c	2010-05-22 22:17:51 +0000
+++ bus/usb/ohci.c	2010-05-23 13:46:08 +0000
@@ -96,7 +96,11 @@
   GRUB_OHCI_REG_FRAME_INTERVAL,
   GRUB_OHCI_REG_PERIODIC_START = 16,
   GRUB_OHCI_REG_RHUBA = 18,
-  GRUB_OHCI_REG_RHUBPORT = 21
+  GRUB_OHCI_REG_RHUBPORT = 21,
+  GRUB_OHCI_REG_LEGACY_CONTROL = 0x100,
+  GRUB_OHCI_REG_LEGACY_INPUT = 0x104,
+  GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108,
+  GRUB_OHCI_REG_LEGACY_STATUS = 0x10c
 } grub_ohci_reg_t;
 
 #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
@@ -212,10 +216,48 @@
   if ((revision & 0xFF) != 0x10)
     goto fail;
 
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
-			(grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
-			 & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
-			| GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+
+  {
+    grub_uint32_t control;
+    /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */
+    control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+    if ((control & 0x100) != 0)
+      {
+	unsigned i;
+	grub_dprintf("ohci", "OHCI is owned by SMM\n");
+	/* Do change of ownership */
+	/* Ownership change request */
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic.  */
+	/* Waiting for SMM deactivation */
+	for (i=0; i < 10; i++)
+	  {
+	    if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0)
+	      {
+		grub_dprintf("ohci", "Ownership changed normally.\n");
+		break;
+	      }
+	    grub_millisleep (100);
+          }
+	if (i >= 10)
+	  {
+	    grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+				  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100);
+	    grub_dprintf("ohci", "Ownership changing timeout, change forced !\n");
+	  }
+      }
+    else if (((control & 0x100) == 0) && 
+	     ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */
+      {
+	grub_dprintf("ohci", "OHCI is owned by BIOS\n");
+	/* Do change of ownership - not implemented yet... */
+	/* In fact we probably need to do nothing ...? */
+      }
+    else
+      {
+	grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n");
+	/* We can setup OHCI. */
+      }  
+  }
 
   /* Suspend the OHCI by issuing a reset.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
@@ -232,15 +274,58 @@
 			GRUB_OHCI_PERIODIC_START);
 
   /* Setup the HCCA.  */
+  o->hcca->donehead = 0;
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
   grub_dprintf ("ohci", "OHCI HCCA\n");
 
+  /* Misc. pre-sets. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  /* Check OHCI Legacy Support */
+  if ((revision & 0x100) != 0)
+    {
+      grub_dprintf ("ohci", "Legacy Support registers detected\n");
+      grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n",
+		    grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL));
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL,
+			    (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1);
+      grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
+    }
+
   /* Enable the OHCI.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
 			(2 << 6));
   grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
 		(grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
 
+  /* Power on all ports */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
+                       (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
+                        & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
+                       | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+  /* Wait for stable power (100ms) and stable attachment (100ms) */
+  /* I.e. minimum wait time should be probably 200ms. */
+  /* We assume that device is attached when ohci is loaded. */
+  /* Some devices take long time to power-on or indicate attach. */
+  /* Here is some experimental value which should probably mostly work. */
+  /* Cameras with manual USB mode selection and maybe some other similar
+   * devices will not work in some cases - they are repowered during
+   * ownership change and then they are starting slowly and mostly they
+   * are wanting select proper mode again...
+   * The same situation can be on computers where BIOS not set-up OHCI
+   * to be at least powered USB bus (maybe it is Yeelong case...?)
+   * Possible workaround could be for example some prompt
+   * for user with confirmation of proper USB device connection.
+   * Another workaround - "rmmod usbms", "rmmod ohci", proper start
+   * and configuration of USB device and then "insmod ohci"
+   * and "insmod usbms". */
+  grub_millisleep (500);	
+
   /* Link to ohci now that initialisation is successful.  */
   o->next = ohci;
   ohci = o;
@@ -317,13 +402,29 @@
   token |= toggle << 24;
   token |= 1 << 25;
 
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+
   buffer = data;
   buffer_end = buffer + size - 1;
 
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size)
+    {
+      buffer = (grub_uint32_t) data;
+      buffer_end = buffer + size - 1;
+      td->buffer = grub_cpu_to_le32 (buffer);
+      td->buffer_end = grub_cpu_to_le32 (buffer_end);
+    }
+  else 
+    {
+      td->buffer = 0;
+      td->buffer_end = 0;
+    }
+
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -342,7 +443,9 @@
   grub_uint32_t status;
   grub_uint32_t control;
   grub_usb_err_t err;
-  int i;
+  int i, j;
+  grub_uint64_t maxtime;
+  int err_timeout = 0;
 
   /* Allocate an Endpoint Descriptor.  */
   ed_chunk = grub_memalign_dma32 (256, sizeof (*ed));
@@ -375,13 +478,25 @@
 					     + (i + 1) * sizeof (td_list[0]));
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * normal end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+
+  td_list[transfer->transcnt].token = 0;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td =
+    (grub_uint32_t) &td_list[transfer->transcnt];
+  
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
   target = transfer->devaddr;
 
-  /* Set the endpoint.  */
-  target |= transfer->endpoint << 7;
+  /* Set the endpoint. It should be masked, we need 4 bits only. */
+  target |= (transfer->endpoint & 15) << 7;
 
   /* Set the device speed.  */
   target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
@@ -400,6 +515,30 @@
 
   grub_dprintf ("ohci", "program OHCI\n");
 
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   /* Program the OHCI to actually transfer.  */
   switch (transfer->type)
     {
@@ -407,24 +546,17 @@
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -433,21 +565,9 @@
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -465,36 +585,77 @@
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
+  /* Safety measure to avoid a hang. */
+  maxtime = grub_get_time_ms () + 1000;
+	
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
       grub_cpu_idle ();
 
-      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (ed->td_head) & 1)
+        break;
+  
+      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+        {
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+	      == td_list_addr + (transfer->transcnt - 1) * sizeof (td_list[0]))
+	    break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          continue;
+        }
+      /* Timeout ? */
+      if (grub_get_time_ms () > maxtime)
+      	{
+	  /* Disable the Control and Bulk lists.  */
+  	  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+	  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4));
+      	  err_timeout = 1;
+      	  break;
+      	}
+
+      if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
 	break;
     }
-
-  grub_dprintf ("ohci", "complete\n");
-
-/*   if (ed->td_head & 1) */
-/*     err = GRUB_USB_ERR_STALL; */
-/*   else if (ed->td */
-
-
-  if (ed->td_head & 1)
-    {
+  while (1);
+
+  if (err_timeout)
+    {
+      err = GRUB_ERR_TIMEOUT;
+      grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+      grub_le_to_cpu32(ed->target),
+      grub_le_to_cpu32(ed->td_head),
+      grub_le_to_cpu32(ed->td_tail),
+      grub_le_to_cpu32(ed->next_ed));
+    }
+  else if (grub_le_to_cpu32 (ed->td_head) & 1)
+    {
+      grub_uint32_t td_err_addr;
       grub_uint8_t errcode;
-      grub_ohci_td_t tderr;
-      grub_uint32_t td_err_addr;
+      grub_ohci_td_t tderr = NULL;
 
-      td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD);
+      td_err_addr = (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+      if (td_err_addr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always now! */
+        td_err_addr = (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
 
       tderr = (grub_ohci_td_t) ((char *) td_list
 				+ (td_err_addr - td_list_addr));
-      errcode = tderr->token >> 28;
+ 
+      errcode = grub_le_to_cpu32 (tderr->token) >> 28;      
+      grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode);
 
       switch (errcode)
 	{
@@ -540,11 +701,17 @@
 	case 8:
 	  /* XXX: Data overrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 9:
 	  /* XXX: Data underrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
+	                1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer));
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 10:
@@ -582,12 +749,37 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+  
+  /* Set ED to be skipped - for safety */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame.
+   * It is necessary because we will invalidate pointer to ED and it
+   * can be on OHCI active till SOF!
+   * Because we are not using interrupt, we reset SF bit and wait when
+   * it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n",
+                err);
+  
   /* XXX */
-  grub_free (td_list);
-  grub_free (ed);
+  grub_dma_free (td_list_chunk);
+  grub_dma_free (ed_chunk);
 
   return err;
 }
@@ -599,11 +791,20 @@
    struct grub_ohci *o = (struct grub_ohci *) dev->data;
    grub_uint32_t status;
 
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
+                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+
+   /* Enable the port.  */
+   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
+   status |= (enable << 1); /* XXX: Magic.  */
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+
    /* Reset the port.  */
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
    status |= (1 << 4); /* XXX: Magic.  */
    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-   grub_millisleep (100);
+   grub_millisleep (50); /* For root hub should be nominaly 50ms */
 
    /* End the reset signaling.  */
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
@@ -611,13 +812,14 @@
    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
    grub_millisleep (10);
 
-   /* Enable the port.  */
+   /* Power-on port.  */
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (enable << 1); /* XXX: Magic.  */
+   status |= (1 << 1); /* XXX: Magic.  */
    grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_millisleep (10);
 
    status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   grub_dprintf ("ohci", "portstatus=0x%02x\n", status);
+   grub_dprintf ("ohci", "end of portstatus=0x%02x\n", status);
 
    return GRUB_ERR_NONE;
 }

=== modified file 'bus/usb/uhci.c'
--- bus/usb/uhci.c	2010-05-05 08:40:48 +0000
+++ bus/usb/uhci.c	2010-05-23 13:08:06 +0000
@@ -174,14 +174,15 @@
     return 1;
 
   u->iobase = base & GRUB_UHCI_IOMASK;
-  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n",
-		class, subclass, interf, u->iobase);
 
   /* Reserve a page for the frame list.  */
   u->framelist = grub_memalign (4096, 4096);
   if (! u->framelist)
     goto fail;
 
+  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n",
+		class, subclass, interf, u->iobase, u->framelist);
+
   /* The framelist pointer of UHCI is only 32 bits, make sure this
      code works on on 64 bits architectures.  */
 #if GRUB_CPU_SIZEOF_VOID_P == 8
@@ -221,6 +222,9 @@
     }
 #endif
 
+  grub_dprintf ("uhci", "QH=%p, TD=%p\n",
+		u->qh, u->td);
+
   /* Link all Transfer Descriptors in a list of available Transfer
      Descriptors.  */
   for (i = 0; i < 256; i++)
@@ -441,6 +445,8 @@
   if (! qh)
     return grub_errno;
 
+  grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase);
+  
   for (i = 0; i < transfer->transcnt; i++)
     {
       grub_usb_transaction_t tr = &transfer->transactions[i];
@@ -548,7 +554,8 @@
 
  fail:
 
-  grub_dprintf ("uhci", "transaction failed\n");
+  if (err != GRUB_USB_ERR_NONE)
+    grub_dprintf ("uhci", "transaction failed\n");
 
   /* Place the QH back in the free list and deallocate the associated
      TDs.  */
@@ -583,6 +590,8 @@
   unsigned int status;
   grub_uint64_t endtime;
 
+  grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase);
+  
   grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
 
   if (port == 0)
@@ -631,6 +640,8 @@
   int reg;
   unsigned int status;
 
+  grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase);
+  
   if (port == 0)
     reg = GRUB_UHCI_REG_PORTSC1;
   else if (port == 1)

=== modified file 'bus/usb/usb.c'
--- bus/usb/usb.c	2009-11-09 17:43:53 +0000
+++ bus/usb/usb.c	2010-05-23 13:08:06 +0000
@@ -107,7 +107,7 @@
 {
   int i;
 
-  for (i = 0; i < 16; i++)
+  for (i = 0; i < 256; i++)
     dev->toggle[i] = 0;
 
   return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
@@ -163,6 +163,16 @@
   grub_usb_err_t err;
   int i;
 
+  /* First we have to read first 8 bytes only and determine
+   * max. size of packet */
+  dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */
+  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
+				 0, 8, (char *) &dev->descdev);
+  if (err)
+    return err;
+
+  /* Now we have valid value in dev->descdev.maxsize0,
+   * so we can read whole device descriptor */
   err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
 				 0, sizeof (struct grub_usb_desc_device),
 				 (char *) &dev->descdev);

=== modified file 'bus/usb/usbtrans.c'
--- bus/usb/usbtrans.c	2010-05-22 22:13:37 +0000
+++ bus/usb/usbtrans.c	2010-05-23 13:10:07 +0000
@@ -138,7 +138,7 @@
   /* End with an empty OUT transaction.  */
   transfer->transactions[datablocks + 1].size = 0;
   transfer->transactions[datablocks + 1].data = 0;
-  if (reqtype & 128)
+  if ((reqtype & 128) && datablocks)
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT;
   else
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN;
@@ -148,6 +148,7 @@
   err = dev->controller.dev->transfer (&dev->controller, transfer);
 
   grub_free (transfer->transactions);
+  
   grub_free (transfer);
   grub_dma_free (data_chunk);
   grub_dma_free (setupdata_chunk);
@@ -207,7 +208,7 @@
   datablocks = ((size + max - 1) / max);
   transfer->transcnt = datablocks;
   transfer->size = size - 1;
-  transfer->endpoint = endpoint;
+  transfer->endpoint = endpoint & 15;
   transfer->devaddr = dev->addr;
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
   transfer->max = max;

=== modified file 'commands/usbtest.c'
--- commands/usbtest.c	2010-05-23 12:37:28 +0000
+++ commands/usbtest.c	2010-05-23 13:41:32 +0000
@@ -148,6 +148,8 @@
 
   grub_printf ("%s speed device\n", usb_devspeed[dev->speed]);
 
+  return 0;
+
   for (i = 0; i < descdev->configcnt; i++)
     {
       struct grub_usb_desc_config *config;

=== modified file 'disk/scsi.c'
--- disk/scsi.c	2010-03-05 14:29:28 +0000
+++ disk/scsi.c	2010-05-23 13:08:06 +0000
@@ -25,6 +25,7 @@
 #include <grub/types.h>
 #include <grub/scsi.h>
 #include <grub/scsicmd.h>
+#include <grub/time.h>
 
 \f
 static grub_scsi_dev_t grub_scsi_dev_list;
@@ -50,7 +51,62 @@
 }
 
 \f
-/* Determine the the device is removable and the type of the device
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+  grub_memset (rs.pad, 0, sizeof(rs.pad));
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+  grub_memset (tur.pad, 0, sizeof(tur.pad));
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
+/* Determine if the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
 grub_scsi_inquiry (grub_scsi_t scsi)
@@ -58,15 +114,26 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
+  grub_memset (iq.pad, 0, sizeof(iq.pad));
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +150,27 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
-
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
+  rc.pad = 0;
+	
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+/* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +188,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +201,16 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +221,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +233,16 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +254,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +267,16 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -172,7 +286,9 @@
 		   grub_size_t size, char *buf)
 {
   grub_scsi_t scsi;
-  struct grub_scsi_write10 wr;
+  struct grub_scsi_write12 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -181,9 +297,18 @@
   wr.lba = grub_cpu_to_be32 (sector);
   wr.size = grub_cpu_to_be32 (size);
   wr.reserved = 0;
-  wr.pad = 0;
-
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  wr.control = 0;
+
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -235,6 +360,7 @@
   grub_err_t err;
   int len;
   int lun;
+  grub_uint64_t maxtime;
 
   scsi = grub_malloc (sizeof (*scsi));
   if (! scsi)
@@ -292,6 +418,31 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests specification, issue Test Unit Ready
+       * until OK */
+      maxtime = grub_get_time_ms () + 1000;
+      do
+        {
+	  /* Timeout is necessary - for example in case when we have
+	   * universal card reader with more LUNs and we have only
+	   * one card inserted (or none), so only one LUN (or none)
+	   * will be ready - and we want not to hang... */
+	  if (grub_get_time_ms () > maxtime)
+            {
+              err = GRUB_ERR_READ_ERROR;
+              grub_free (scsi);
+              grub_dprintf ("scsi", "LUN is not ready - timeout\n");
+              return err;
+            }
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+      /* Reset grub_errno !
+       * It is set to some error code in loop before... */
+      grub_errno = GRUB_ERR_NONE;
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -302,12 +453,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
 
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }

=== modified file 'disk/usbms.c'
--- disk/usbms.c	2010-01-20 08:12:47 +0000
+++ disk/usbms.c	2010-05-23 14:08:52 +0000
@@ -84,7 +84,8 @@
       struct grub_usb_desc_device *descdev = &usbdev->descdev;
       int i;
 
-      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0)
+      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0
+	  || descdev->configcnt == 0)
 	return 0;
 
       /* XXX: Just check configuration 0 for now.  */
@@ -93,19 +94,31 @@
 	  struct grub_usbms_dev *usbms;
 	  struct grub_usb_desc_if *interf;
 	  int j;
-	  grub_uint8_t luns;
+	  grub_uint8_t luns = 0;
+
+	  grub_dprintf ("usbms", "alive\n");
 
 	  interf = usbdev->config[0].interf[i].descif;
 
 	  /* If this is not a USB Mass Storage device with a supported
 	     protocol, just skip it.  */
+	  grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n",
+	                i, interf->class, interf->subclass, interf->protocol);
+
 	  if (interf->class != GRUB_USB_CLASS_MASS_STORAGE
-	      || interf->subclass != GRUB_USBMS_SUBCLASS_BULK
+	      || ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK &&
+	    /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */
+	           interf->subclass != GRUB_USBMS_SUBCLASS_RBC &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_UFI &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 )
 	      || interf->protocol != GRUB_USBMS_PROTOCOL_BULK)
 	    {
 	      continue;
 	    }
 
+	  grub_dprintf ("usbms", "alive\n");
+
 	  devcnt++;
 	  usbms = grub_zalloc (sizeof (struct grub_usbms_dev));
 	  if (! usbms)
@@ -114,6 +127,8 @@
 	  usbms->dev = usbdev;
 	  usbms->interface = i;
 
+	  grub_dprintf ("usbms", "alive\n");
+
 	  /* Iterate over all endpoints of this interface, at least a
 	     IN and OUT bulk endpoint are required.  */
 	  for (j = 0; j < interf->endpointcnt; j++)
@@ -125,14 +140,16 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,51 +160,63 @@
 	      return 0;
 	    }
 
+	  grub_dprintf ("usbms", "alive\n");
+
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
+		
 	  if (err)
 	    {
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
-
 	      /* Just set the amount of LUNs to one.  */
 	      grub_errno = GRUB_ERR_NONE;
 	      usbms->luns = 1;
 	    }
 	  else
-	    usbms->luns = luns;
-
-	  /* XXX: Check the magic values, does this really make
-	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
-
-	  /* XXX: To make Qemu work?  */
-	  if (usbms->luns == 0)
-	    usbms->luns = 1;
+            /* luns = 0 means one LUN with ID 0 present ! */
+            /* We get from device not number of LUNs but highest
+             * LUN number. LUNs are numbered from 0, 
+             * i.e. number of LUNs is luns+1 ! */
+	    usbms->luns = luns + 1;
+
+	  grub_dprintf ("usbms", "alive\n");
 
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+#if 0 /* All this part should be probably deleted.
+     * This make trouble on some devices if they are not in
+     * Phase Error state - and there they should be not in such state...
+     * Bulk only mass storage reset procedure should be used only
+     * on place and in time when it is really necessary. */
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
+#endif
 
 	  return 0;
 	}
 
+      grub_dprintf ("usbms", "alive\n");
       return 0;
     }
+  grub_dprintf ("usbms", "alive\n");
 
   grub_usb_iterate (usb_iterate);
+  grub_dprintf ("usbms", "alive\n");
+
 }
 
 \f
@@ -225,6 +254,7 @@
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
+  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -237,73 +267,89 @@
   cbw.tag = tag++;
   cbw.transfer_length = grub_cpu_to_le32 (size);
   cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT;
-  cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
+  
+  /* Debug print of CBW content. */
+  grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n",
+  	cbw.signature, cbw.tag, cbw.transfer_length);
+  grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n",
+  	cbw.flags, cbw.lun, cbw.length);
+  grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3],
+  	cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7],
+  	cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11],
+  	cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]);
 
-  /* Write the request.  */
-  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
+  /* Write the request.
+   * XXX: Error recovery is maybe still not fully correct. */
+  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
-      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf);
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
+      /* Debug print of received data. */
+      grub_dprintf ("usb", "buf:\n");
+      if (size <= 64)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "buf:\n");
+      /* Debug print of sent data. */
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
 
-  /* Read the status.  */
-  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
-			    sizeof (status), (char *) &status);
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
+  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+		    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* Debug print of CSW content. */
+  grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n",
+  	status.signature, status.tag, status.residue);
+  grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
+  
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -384,8 +430,11 @@
 
 GRUB_MOD_INIT(usbms)
 {
+  grub_dprintf ("usbms", "alive\n");
   grub_usbms_finddevs ();
+  grub_dprintf ("usbms", "alive\n");
   grub_scsi_dev_register (&grub_usbms_dev);
+  grub_dprintf ("usbms", "alive\n");
 }
 
 GRUB_MOD_FINI(usbms)

=== modified file 'include/grub/scsicmd.h'
--- include/grub/scsicmd.h	2008-08-27 15:05:00 +0000
+++ include/grub/scsicmd.h	2010-05-23 13:08:06 +0000
@@ -25,14 +25,26 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
+struct grub_scsi_test_unit_ready
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
 struct grub_scsi_inquiry
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
-  grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +59,42 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
+  grub_uint16_t pad; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +148,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum

=== modified file 'include/grub/usb.h'
--- include/grub/usb.h	2009-11-09 17:43:53 +0000
+++ include/grub/usb.h	2010-05-23 13:08:06 +0000
@@ -156,7 +156,7 @@
   int initialized;
 
   /* Data toggle values (used for bulk transfers only).  */
-  int toggle[16];
+  int toggle[256];
 
   /* Device-specific data.  */
   void *data;
@@ -184,7 +184,12 @@
 
 typedef enum
   {
-    GRUB_USBMS_SUBCLASS_BULK = 0x06
+    GRUB_USBMS_SUBCLASS_BULK = 0x06,
+  	/* Experimental support for non-pure SCSI devices */
+    GRUB_USBMS_SUBCLASS_RBC = 0x01,
+    GRUB_USBMS_SUBCLASS_MMC2 = 0x02,
+    GRUB_USBMS_SUBCLASS_UFI = 0x04,
+    GRUB_USBMS_SUBCLASS_SFF8070 = 0x05
   } grub_usbms_subclass_t;
 
 typedef enum

=== modified file 'include/grub/usbtrans.h'
--- include/grub/usbtrans.h	2010-05-05 08:40:48 +0000
+++ include/grub/usbtrans.h	2010-05-23 13:08:06 +0000
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)

=== modified file 'kern/misc.c'
--- kern/misc.c	2010-05-22 14:58:45 +0000
+++ kern/misc.c	2010-05-23 13:52:25 +0000
@@ -189,7 +189,7 @@
   const char *debug = grub_env_get ("debug");
 
   if (! debug)
-    return;
+    debug = "all";
 
   if (grub_strword (debug, "all") || grub_strword (debug, condition))
     {


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 10:27                 ` Aleš Nesrsta
  2010-05-23 14:41                   ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-23 14:44                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 17:16                     ` Aleš Nesrsta
  1 sibling, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-23 14:44 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 925 bytes --]


Aleš Nesrsta wrote:
> I will look into yeeloongfw branch, I got Bazaar working yesterday
> evening only... I agree with You, first should be solved known problems
> on simple base.
> Unfortunately, I probably cannot help You as I don't have GEODE or
> Yeeloong... I can hope only that my corrections would help You or
> inspire You in some other ways.
> For me were very "disgusting", unexpected and hard to find problems with
> short max. packet size on control pipe and problem with toggling bulk
> transfers when EP have numbers 0x01 and 0x81.
>
>   
Forgot to mention: on Yeeloong the value you write into  registers
expecting the memory address is not the same as the value of the pointer
so simple cast won't work. You need to use dma32_alloc, dma_get_virt and
dma_get_phys functions.
And no need to capitalise "you" when it refers to me.

-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 14:44                   ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-23 17:16                     ` Aleš Nesrsta
  2010-05-23 19:35                       ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-23 17:16 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> Aleš Nesrsta wrote:
> > I will look into yeeloongfw branch, I got Bazaar working yesterday
> > evening only... I agree with You, first should be solved known problems
> > on simple base.
> > Unfortunately, I probably cannot help You as I don't have GEODE or
> > Yeeloong... I can hope only that my corrections would help You or
> > inspire You in some other ways.
> > For me were very "disgusting", unexpected and hard to find problems with
> > short max. packet size on control pipe and problem with toggling bulk
> > transfers when EP have numbers 0x01 and 0x81.
> >
> >   
> Forgot to mention: on Yeeloong the value you write into  registers
> expecting the memory address is not the same as the value of the pointer
> so simple cast won't work. You need to use dma32_alloc, dma_get_virt and
> dma_get_phys functions.

Hi,

it is interesting - why on Yeeloong only ? Why it is not needed on
"normal" GRUB ?

And another idea - could be there some problem with PCI memory mapped
register access caching ? Some Linux drivers uses so called "barrier"
when doing access to PCI memory to be sure that previous access was
really done - could be there such case ?

Best regards
Ales



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 14:41                   ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-23 17:42                     ` Aleš Nesrsta
  2010-05-23 18:40                       ` Thomas Schmitt
  2010-05-23 17:51                     ` [Patch] [bug #26237] " Vladimir 'φ-coder/phcoder' Serbinenko
  1 sibling, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-23 17:42 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' wrote:

> >  It could make troubles on SCSI subclass
> > devices but I tried all my devices with SCSI subclass with
> > ATAPI-compatible length of command and all devices are working normally
> > - so I think it should be safe to use ATAPI format of commands
> > universally for any subclass and I tried to "add support" for most of
> > USB MS subclasses - in fact I only extended one condition in usbms.c...
> > I did not testing devices subclasses RBC, UFI and MMC-2 - I don't have
> > such devices - can do somebody such tests ?
> >   
> Is MMC-2 subclass of cardreaders?

Hi,
I think probably not.
Full description of subcalss is "SFF-8020i, MMC-2 (ATAPI)" and comment
is that such subclass is used mainly by CD/DVD.

For example here http://www.ata-atapi.com/scsi.html is written:
"If you are looking for the SCSI command set used by ATAPI CD-ROM or DVD
devices then you should go to the T10 site and find the MMC or MMC-2
documents. Please note that the old SFF-8020 (INF-8020) document is now
obsolete. SFF-8020 (INF-8020) has been replaced by the T13 ATA/ATAPI-4
standard (for the interface and command protocol descriptions) and by
the T10 MMC or MMC-2 standards (for the SCSI command set).
The SCSI standards are published by ANSI NCITS Technical Committee 10,
or just T10: T10 - Technical Committee 10  "

Unfortunately you should be a member of T10 to get access to many
documents, MMC-2 is one of them...

But there is free access for example to some implementation notes on
T10:
 http://www.t10.org/ftp/t10/document.97/97-118r0.pdf
 http://www.t10.org/ftp/t10/document.97/97-119r0.pdf


> > Unfortunately, I probably cannot help You as I don't have GEODE or
> > Yeeloong... I can hope only that my corrections would help You or
> > inspire You in some other ways.
> >   
> I don't think it's something deeply specific to Geode. Other than having
> Geode MSR instead of PCI configuration space it's normal OHCI. Actually
> with your patch when you forgot to power device up it resulted in
> similar symptoms on PCI OHCI controller. OHCI specification mentions
> different ways to configure power settings. I guess geode defaults to
> another mode than most controllers do. Or perhaps I need to somehow
> enable ports.
> >

OHCI specification is little bit unclear in the case of root hub port
registers - some values are there marked "Implementation Specific" and
there is not clear for me if any OHCI HC must support any combination of
powering or not.

Best regards
Ales



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 14:41                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 17:42                     ` Aleš Nesrsta
@ 2010-05-23 17:51                     ` Vladimir 'φ-coder/phcoder' Serbinenko
  1 sibling, 0 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-23 17:51 UTC (permalink / raw)
  To: The development of GNU GRUB


[-- Attachment #1.1: Type: text/plain, Size: 2471 bytes --]

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
>> I will look into yeeloongfw branch, I got Bazaar working yesterday
>> evening only... I agree with You, first should be solved known problems
>> on simple base.
>> Unfortunately, I probably cannot help You as I don't have GEODE or
>> Yeeloong... I can hope only that my corrections would help You or
>> inspire You in some other ways.
>>   
>>     
> I don't think it's something deeply specific to Geode. Other than having
> Geode MSR instead of PCI configuration space it's normal OHCI. Actually
> with your patch when you forgot to power device up it resulted in
> similar symptoms on PCI OHCI controller. OHCI specification mentions
> different ways to configure power settings. I guess geode defaults to
> another mode than most controllers do. Or perhaps I need to somehow
> enable ports.
>   
I was right this was the problem with grub_ohci_portstatus. RHUBPORT is
of type R/WC and not of type R/W so you just write to it the bits you
want to modify and not (previous OR modification). New
grub_ohci_portstatus looks like (or patch attached):
#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1)
#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0)
#define GRUB_OHCI_SET_PORT_RESET (1 << 4)
#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)

static grub_err_t
grub_ohci_portstatus (grub_usb_controller_t dev,
              unsigned int port, unsigned int enable)
{
   struct grub_ohci *o = (struct grub_ohci *) dev->data;

   grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));

   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
             GRUB_OHCI_SET_PORT_RESET);
   grub_millisleep (50); /* For root hub should be nominaly 50ms */

   /* End the reset signaling.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
             GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
   grub_millisleep (10);

   if (enable)
     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
               GRUB_OHCI_SET_PORT_ENABLE);
   else
     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
               GRUB_OHCI_CLEAR_PORT_ENABLE);
   grub_millisleep (10);

   grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
         grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));

   return GRUB_ERR_NONE;
}




-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: yeeloong_usb_i.diff --]
[-- Type: text/x-diff; name="yeeloong_usb_i.diff", Size: 46834 bytes --]

=== modified file 'bus/usb/ohci.c'
--- bus/usb/ohci.c	2010-05-22 22:17:51 +0000
+++ bus/usb/ohci.c	2010-05-23 17:27:15 +0000
@@ -96,7 +96,11 @@
   GRUB_OHCI_REG_FRAME_INTERVAL,
   GRUB_OHCI_REG_PERIODIC_START = 16,
   GRUB_OHCI_REG_RHUBA = 18,
-  GRUB_OHCI_REG_RHUBPORT = 21
+  GRUB_OHCI_REG_RHUBPORT = 21,
+  GRUB_OHCI_REG_LEGACY_CONTROL = 0x100,
+  GRUB_OHCI_REG_LEGACY_INPUT = 0x104,
+  GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108,
+  GRUB_OHCI_REG_LEGACY_STATUS = 0x10c
 } grub_ohci_reg_t;
 
 #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
@@ -195,7 +199,7 @@
   if (! o)
     return 1;
 
-  o->iobase = grub_pci_device_map_range (dev, base, 0x100);
+  o->iobase = grub_pci_device_map_range (dev, base, 0x800);
 
   grub_dprintf ("ohci", "base=%p\n", o->iobase);
 
@@ -212,10 +216,48 @@
   if ((revision & 0xFF) != 0x10)
     goto fail;
 
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
-			(grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
-			 & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
-			| GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+
+  {
+    grub_uint32_t control;
+    /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */
+    control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+    if ((control & 0x100) != 0)
+      {
+	unsigned i;
+	grub_dprintf("ohci", "OHCI is owned by SMM\n");
+	/* Do change of ownership */
+	/* Ownership change request */
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic.  */
+	/* Waiting for SMM deactivation */
+	for (i=0; i < 10; i++)
+	  {
+	    if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0)
+	      {
+		grub_dprintf("ohci", "Ownership changed normally.\n");
+		break;
+	      }
+	    grub_millisleep (100);
+          }
+	if (i >= 10)
+	  {
+	    grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+				  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100);
+	    grub_dprintf("ohci", "Ownership changing timeout, change forced !\n");
+	  }
+      }
+    else if (((control & 0x100) == 0) && 
+	     ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */
+      {
+	grub_dprintf("ohci", "OHCI is owned by BIOS\n");
+	/* Do change of ownership - not implemented yet... */
+	/* In fact we probably need to do nothing ...? */
+      }
+    else
+      {
+	grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n");
+	/* We can setup OHCI. */
+      }  
+  }
 
   /* Suspend the OHCI by issuing a reset.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
@@ -232,15 +274,58 @@
 			GRUB_OHCI_PERIODIC_START);
 
   /* Setup the HCCA.  */
+  o->hcca->donehead = 0;
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
   grub_dprintf ("ohci", "OHCI HCCA\n");
 
+  /* Misc. pre-sets. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  /* Check OHCI Legacy Support */
+  if ((revision & 0x100) != 0)
+    {
+      grub_dprintf ("ohci", "Legacy Support registers detected\n");
+      grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n",
+		    grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL));
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL,
+			    (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1);
+      grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
+    }
+
   /* Enable the OHCI.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
 			(2 << 6));
   grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
 		(grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
 
+  /* Power on all ports */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
+                       (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
+                        & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
+                       | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+  /* Wait for stable power (100ms) and stable attachment (100ms) */
+  /* I.e. minimum wait time should be probably 200ms. */
+  /* We assume that device is attached when ohci is loaded. */
+  /* Some devices take long time to power-on or indicate attach. */
+  /* Here is some experimental value which should probably mostly work. */
+  /* Cameras with manual USB mode selection and maybe some other similar
+   * devices will not work in some cases - they are repowered during
+   * ownership change and then they are starting slowly and mostly they
+   * are wanting select proper mode again...
+   * The same situation can be on computers where BIOS not set-up OHCI
+   * to be at least powered USB bus (maybe it is Yeelong case...?)
+   * Possible workaround could be for example some prompt
+   * for user with confirmation of proper USB device connection.
+   * Another workaround - "rmmod usbms", "rmmod ohci", proper start
+   * and configuration of USB device and then "insmod ohci"
+   * and "insmod usbms". */
+  grub_millisleep (500);	
+
   /* Link to ohci now that initialisation is successful.  */
   o->next = ohci;
   ohci = o;
@@ -317,13 +402,29 @@
   token |= toggle << 24;
   token |= 1 << 25;
 
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+
   buffer = data;
   buffer_end = buffer + size - 1;
 
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size)
+    {
+      buffer = (grub_uint32_t) data;
+      buffer_end = buffer + size - 1;
+      td->buffer = grub_cpu_to_le32 (buffer);
+      td->buffer_end = grub_cpu_to_le32 (buffer_end);
+    }
+  else 
+    {
+      td->buffer = 0;
+      td->buffer_end = 0;
+    }
+
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -342,7 +443,9 @@
   grub_uint32_t status;
   grub_uint32_t control;
   grub_usb_err_t err;
-  int i;
+  int i, j;
+  grub_uint64_t maxtime;
+  int err_timeout = 0;
 
   /* Allocate an Endpoint Descriptor.  */
   ed_chunk = grub_memalign_dma32 (256, sizeof (*ed));
@@ -375,13 +478,25 @@
 					     + (i + 1) * sizeof (td_list[0]));
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * normal end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+
+  td_list[transfer->transcnt].token = 0;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td =
+    (grub_uint32_t) &td_list[transfer->transcnt];
+  
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
   target = transfer->devaddr;
 
-  /* Set the endpoint.  */
-  target |= transfer->endpoint << 7;
+  /* Set the endpoint. It should be masked, we need 4 bits only. */
+  target |= (transfer->endpoint & 15) << 7;
 
   /* Set the device speed.  */
   target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
@@ -400,6 +515,30 @@
 
   grub_dprintf ("ohci", "program OHCI\n");
 
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   /* Program the OHCI to actually transfer.  */
   switch (transfer->type)
     {
@@ -407,24 +546,17 @@
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -433,21 +565,9 @@
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -465,36 +585,77 @@
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
+  /* Safety measure to avoid a hang. */
+  maxtime = grub_get_time_ms () + 1000;
+	
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
       grub_cpu_idle ();
 
-      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (ed->td_head) & 1)
+        break;
+  
+      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+        {
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+	      == td_list_addr + (transfer->transcnt - 1) * sizeof (td_list[0]))
+	    break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          continue;
+        }
+      /* Timeout ? */
+      if (grub_get_time_ms () > maxtime)
+      	{
+	  /* Disable the Control and Bulk lists.  */
+  	  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+	  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4));
+      	  err_timeout = 1;
+      	  break;
+      	}
+
+      if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
 	break;
     }
-
-  grub_dprintf ("ohci", "complete\n");
-
-/*   if (ed->td_head & 1) */
-/*     err = GRUB_USB_ERR_STALL; */
-/*   else if (ed->td */
-
-
-  if (ed->td_head & 1)
-    {
+  while (1);
+
+  if (err_timeout)
+    {
+      err = GRUB_ERR_TIMEOUT;
+      grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+      grub_le_to_cpu32(ed->target),
+      grub_le_to_cpu32(ed->td_head),
+      grub_le_to_cpu32(ed->td_tail),
+      grub_le_to_cpu32(ed->next_ed));
+    }
+  else if (grub_le_to_cpu32 (ed->td_head) & 1)
+    {
+      grub_uint32_t td_err_addr;
       grub_uint8_t errcode;
-      grub_ohci_td_t tderr;
-      grub_uint32_t td_err_addr;
+      grub_ohci_td_t tderr = NULL;
 
-      td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD);
+      td_err_addr = (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+      if (td_err_addr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always now! */
+        td_err_addr = (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
 
       tderr = (grub_ohci_td_t) ((char *) td_list
 				+ (td_err_addr - td_list_addr));
-      errcode = tderr->token >> 28;
+ 
+      errcode = grub_le_to_cpu32 (tderr->token) >> 28;      
+      grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode);
 
       switch (errcode)
 	{
@@ -540,11 +701,17 @@
 	case 8:
 	  /* XXX: Data overrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 9:
 	  /* XXX: Data underrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
+	                1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer));
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 10:
@@ -582,42 +749,74 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+  
+  /* Set ED to be skipped - for safety */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame.
+   * It is necessary because we will invalidate pointer to ED and it
+   * can be on OHCI active till SOF!
+   * Because we are not using interrupt, we reset SF bit and wait when
+   * it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n",
+                err);
+  
   /* XXX */
-  grub_free (td_list);
-  grub_free (ed);
+  grub_dma_free (td_list_chunk);
+  grub_dma_free (ed_chunk);
 
   return err;
 }
 
+#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1)
+#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0)
+#define GRUB_OHCI_SET_PORT_RESET (1 << 4)
+#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)
+
 static grub_err_t
 grub_ohci_portstatus (grub_usb_controller_t dev,
 		      unsigned int port, unsigned int enable)
 {
    struct grub_ohci *o = (struct grub_ohci *) dev->data;
-   grub_uint32_t status;
-
-   /* Reset the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (1 << 4); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-   grub_millisleep (100);
+
+   grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
+                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			 GRUB_OHCI_SET_PORT_RESET);
+   grub_millisleep (50); /* For root hub should be nominaly 50ms */
 
    /* End the reset signaling.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (1 << 20); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-   grub_millisleep (10);
-
-   /* Enable the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (enable << 1); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   grub_dprintf ("ohci", "portstatus=0x%02x\n", status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			 GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
+   grub_millisleep (10);
+
+   if (enable)
+     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			   GRUB_OHCI_SET_PORT_ENABLE);
+   else
+     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			   GRUB_OHCI_CLEAR_PORT_ENABLE);
+   grub_millisleep (10);
+
+   grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
+		 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
 
    return GRUB_ERR_NONE;
 }

=== modified file 'bus/usb/uhci.c'
--- bus/usb/uhci.c	2010-05-05 08:40:48 +0000
+++ bus/usb/uhci.c	2010-05-23 13:08:06 +0000
@@ -174,14 +174,15 @@
     return 1;
 
   u->iobase = base & GRUB_UHCI_IOMASK;
-  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n",
-		class, subclass, interf, u->iobase);
 
   /* Reserve a page for the frame list.  */
   u->framelist = grub_memalign (4096, 4096);
   if (! u->framelist)
     goto fail;
 
+  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n",
+		class, subclass, interf, u->iobase, u->framelist);
+
   /* The framelist pointer of UHCI is only 32 bits, make sure this
      code works on on 64 bits architectures.  */
 #if GRUB_CPU_SIZEOF_VOID_P == 8
@@ -221,6 +222,9 @@
     }
 #endif
 
+  grub_dprintf ("uhci", "QH=%p, TD=%p\n",
+		u->qh, u->td);
+
   /* Link all Transfer Descriptors in a list of available Transfer
      Descriptors.  */
   for (i = 0; i < 256; i++)
@@ -441,6 +445,8 @@
   if (! qh)
     return grub_errno;
 
+  grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase);
+  
   for (i = 0; i < transfer->transcnt; i++)
     {
       grub_usb_transaction_t tr = &transfer->transactions[i];
@@ -548,7 +554,8 @@
 
  fail:
 
-  grub_dprintf ("uhci", "transaction failed\n");
+  if (err != GRUB_USB_ERR_NONE)
+    grub_dprintf ("uhci", "transaction failed\n");
 
   /* Place the QH back in the free list and deallocate the associated
      TDs.  */
@@ -583,6 +590,8 @@
   unsigned int status;
   grub_uint64_t endtime;
 
+  grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase);
+  
   grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
 
   if (port == 0)
@@ -631,6 +640,8 @@
   int reg;
   unsigned int status;
 
+  grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase);
+  
   if (port == 0)
     reg = GRUB_UHCI_REG_PORTSC1;
   else if (port == 1)

=== modified file 'bus/usb/usb.c'
--- bus/usb/usb.c	2009-11-09 17:43:53 +0000
+++ bus/usb/usb.c	2010-05-23 13:08:06 +0000
@@ -107,7 +107,7 @@
 {
   int i;
 
-  for (i = 0; i < 16; i++)
+  for (i = 0; i < 256; i++)
     dev->toggle[i] = 0;
 
   return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
@@ -163,6 +163,16 @@
   grub_usb_err_t err;
   int i;
 
+  /* First we have to read first 8 bytes only and determine
+   * max. size of packet */
+  dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */
+  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
+				 0, 8, (char *) &dev->descdev);
+  if (err)
+    return err;
+
+  /* Now we have valid value in dev->descdev.maxsize0,
+   * so we can read whole device descriptor */
   err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
 				 0, sizeof (struct grub_usb_desc_device),
 				 (char *) &dev->descdev);

=== modified file 'bus/usb/usbtrans.c'
--- bus/usb/usbtrans.c	2010-05-22 22:13:37 +0000
+++ bus/usb/usbtrans.c	2010-05-23 13:10:07 +0000
@@ -138,7 +138,7 @@
   /* End with an empty OUT transaction.  */
   transfer->transactions[datablocks + 1].size = 0;
   transfer->transactions[datablocks + 1].data = 0;
-  if (reqtype & 128)
+  if ((reqtype & 128) && datablocks)
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT;
   else
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN;
@@ -148,6 +148,7 @@
   err = dev->controller.dev->transfer (&dev->controller, transfer);
 
   grub_free (transfer->transactions);
+  
   grub_free (transfer);
   grub_dma_free (data_chunk);
   grub_dma_free (setupdata_chunk);
@@ -207,7 +208,7 @@
   datablocks = ((size + max - 1) / max);
   transfer->transcnt = datablocks;
   transfer->size = size - 1;
-  transfer->endpoint = endpoint;
+  transfer->endpoint = endpoint & 15;
   transfer->devaddr = dev->addr;
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
   transfer->max = max;

=== modified file 'commands/usbtest.c'
--- commands/usbtest.c	2010-05-23 12:37:28 +0000
+++ commands/usbtest.c	2010-05-23 13:41:32 +0000
@@ -148,6 +148,8 @@
 
   grub_printf ("%s speed device\n", usb_devspeed[dev->speed]);
 
+  return 0;
+
   for (i = 0; i < descdev->configcnt; i++)
     {
       struct grub_usb_desc_config *config;

=== modified file 'disk/scsi.c'
--- disk/scsi.c	2010-03-05 14:29:28 +0000
+++ disk/scsi.c	2010-05-23 13:08:06 +0000
@@ -25,6 +25,7 @@
 #include <grub/types.h>
 #include <grub/scsi.h>
 #include <grub/scsicmd.h>
+#include <grub/time.h>
 
 \f
 static grub_scsi_dev_t grub_scsi_dev_list;
@@ -50,7 +51,62 @@
 }
 
 \f
-/* Determine the the device is removable and the type of the device
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+  grub_memset (rs.pad, 0, sizeof(rs.pad));
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+  grub_memset (tur.pad, 0, sizeof(tur.pad));
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
+/* Determine if the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
 grub_scsi_inquiry (grub_scsi_t scsi)
@@ -58,15 +114,26 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
+  grub_memset (iq.pad, 0, sizeof(iq.pad));
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +150,27 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
-
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
+  rc.pad = 0;
+	
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+/* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +188,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +201,16 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +221,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +233,16 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +254,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +267,16 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -172,7 +286,9 @@
 		   grub_size_t size, char *buf)
 {
   grub_scsi_t scsi;
-  struct grub_scsi_write10 wr;
+  struct grub_scsi_write12 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -181,9 +297,18 @@
   wr.lba = grub_cpu_to_be32 (sector);
   wr.size = grub_cpu_to_be32 (size);
   wr.reserved = 0;
-  wr.pad = 0;
-
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  wr.control = 0;
+
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -235,6 +360,7 @@
   grub_err_t err;
   int len;
   int lun;
+  grub_uint64_t maxtime;
 
   scsi = grub_malloc (sizeof (*scsi));
   if (! scsi)
@@ -292,6 +418,31 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests specification, issue Test Unit Ready
+       * until OK */
+      maxtime = grub_get_time_ms () + 1000;
+      do
+        {
+	  /* Timeout is necessary - for example in case when we have
+	   * universal card reader with more LUNs and we have only
+	   * one card inserted (or none), so only one LUN (or none)
+	   * will be ready - and we want not to hang... */
+	  if (grub_get_time_ms () > maxtime)
+            {
+              err = GRUB_ERR_READ_ERROR;
+              grub_free (scsi);
+              grub_dprintf ("scsi", "LUN is not ready - timeout\n");
+              return err;
+            }
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+      /* Reset grub_errno !
+       * It is set to some error code in loop before... */
+      grub_errno = GRUB_ERR_NONE;
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -302,12 +453,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
 
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }

=== modified file 'disk/usbms.c'
--- disk/usbms.c	2010-01-20 08:12:47 +0000
+++ disk/usbms.c	2010-05-23 14:08:52 +0000
@@ -84,7 +84,8 @@
       struct grub_usb_desc_device *descdev = &usbdev->descdev;
       int i;
 
-      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0)
+      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0
+	  || descdev->configcnt == 0)
 	return 0;
 
       /* XXX: Just check configuration 0 for now.  */
@@ -93,19 +94,31 @@
 	  struct grub_usbms_dev *usbms;
 	  struct grub_usb_desc_if *interf;
 	  int j;
-	  grub_uint8_t luns;
+	  grub_uint8_t luns = 0;
+
+	  grub_dprintf ("usbms", "alive\n");
 
 	  interf = usbdev->config[0].interf[i].descif;
 
 	  /* If this is not a USB Mass Storage device with a supported
 	     protocol, just skip it.  */
+	  grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n",
+	                i, interf->class, interf->subclass, interf->protocol);
+
 	  if (interf->class != GRUB_USB_CLASS_MASS_STORAGE
-	      || interf->subclass != GRUB_USBMS_SUBCLASS_BULK
+	      || ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK &&
+	    /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */
+	           interf->subclass != GRUB_USBMS_SUBCLASS_RBC &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_UFI &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 )
 	      || interf->protocol != GRUB_USBMS_PROTOCOL_BULK)
 	    {
 	      continue;
 	    }
 
+	  grub_dprintf ("usbms", "alive\n");
+
 	  devcnt++;
 	  usbms = grub_zalloc (sizeof (struct grub_usbms_dev));
 	  if (! usbms)
@@ -114,6 +127,8 @@
 	  usbms->dev = usbdev;
 	  usbms->interface = i;
 
+	  grub_dprintf ("usbms", "alive\n");
+
 	  /* Iterate over all endpoints of this interface, at least a
 	     IN and OUT bulk endpoint are required.  */
 	  for (j = 0; j < interf->endpointcnt; j++)
@@ -125,14 +140,16 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,51 +160,63 @@
 	      return 0;
 	    }
 
+	  grub_dprintf ("usbms", "alive\n");
+
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
+		
 	  if (err)
 	    {
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
-
 	      /* Just set the amount of LUNs to one.  */
 	      grub_errno = GRUB_ERR_NONE;
 	      usbms->luns = 1;
 	    }
 	  else
-	    usbms->luns = luns;
-
-	  /* XXX: Check the magic values, does this really make
-	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
-
-	  /* XXX: To make Qemu work?  */
-	  if (usbms->luns == 0)
-	    usbms->luns = 1;
+            /* luns = 0 means one LUN with ID 0 present ! */
+            /* We get from device not number of LUNs but highest
+             * LUN number. LUNs are numbered from 0, 
+             * i.e. number of LUNs is luns+1 ! */
+	    usbms->luns = luns + 1;
+
+	  grub_dprintf ("usbms", "alive\n");
 
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+#if 0 /* All this part should be probably deleted.
+     * This make trouble on some devices if they are not in
+     * Phase Error state - and there they should be not in such state...
+     * Bulk only mass storage reset procedure should be used only
+     * on place and in time when it is really necessary. */
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
+#endif
 
 	  return 0;
 	}
 
+      grub_dprintf ("usbms", "alive\n");
       return 0;
     }
+  grub_dprintf ("usbms", "alive\n");
 
   grub_usb_iterate (usb_iterate);
+  grub_dprintf ("usbms", "alive\n");
+
 }
 
 \f
@@ -225,6 +254,7 @@
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
+  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -237,73 +267,89 @@
   cbw.tag = tag++;
   cbw.transfer_length = grub_cpu_to_le32 (size);
   cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT;
-  cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
+  
+  /* Debug print of CBW content. */
+  grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n",
+  	cbw.signature, cbw.tag, cbw.transfer_length);
+  grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n",
+  	cbw.flags, cbw.lun, cbw.length);
+  grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3],
+  	cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7],
+  	cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11],
+  	cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]);
 
-  /* Write the request.  */
-  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
+  /* Write the request.
+   * XXX: Error recovery is maybe still not fully correct. */
+  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
-      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf);
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
+      /* Debug print of received data. */
+      grub_dprintf ("usb", "buf:\n");
+      if (size <= 64)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "buf:\n");
+      /* Debug print of sent data. */
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
 
-  /* Read the status.  */
-  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
-			    sizeof (status), (char *) &status);
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
+  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+		    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* Debug print of CSW content. */
+  grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n",
+  	status.signature, status.tag, status.residue);
+  grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
+  
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -384,8 +430,11 @@
 
 GRUB_MOD_INIT(usbms)
 {
+  grub_dprintf ("usbms", "alive\n");
   grub_usbms_finddevs ();
+  grub_dprintf ("usbms", "alive\n");
   grub_scsi_dev_register (&grub_usbms_dev);
+  grub_dprintf ("usbms", "alive\n");
 }
 
 GRUB_MOD_FINI(usbms)

=== modified file 'include/grub/scsicmd.h'
--- include/grub/scsicmd.h	2008-08-27 15:05:00 +0000
+++ include/grub/scsicmd.h	2010-05-23 13:08:06 +0000
@@ -25,14 +25,26 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
+struct grub_scsi_test_unit_ready
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
 struct grub_scsi_inquiry
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
-  grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +59,42 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
+  grub_uint16_t pad; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +148,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum

=== modified file 'include/grub/usb.h'
--- include/grub/usb.h	2009-11-09 17:43:53 +0000
+++ include/grub/usb.h	2010-05-23 13:08:06 +0000
@@ -156,7 +156,7 @@
   int initialized;
 
   /* Data toggle values (used for bulk transfers only).  */
-  int toggle[16];
+  int toggle[256];
 
   /* Device-specific data.  */
   void *data;
@@ -184,7 +184,12 @@
 
 typedef enum
   {
-    GRUB_USBMS_SUBCLASS_BULK = 0x06
+    GRUB_USBMS_SUBCLASS_BULK = 0x06,
+  	/* Experimental support for non-pure SCSI devices */
+    GRUB_USBMS_SUBCLASS_RBC = 0x01,
+    GRUB_USBMS_SUBCLASS_MMC2 = 0x02,
+    GRUB_USBMS_SUBCLASS_UFI = 0x04,
+    GRUB_USBMS_SUBCLASS_SFF8070 = 0x05
   } grub_usbms_subclass_t;
 
 typedef enum

=== modified file 'include/grub/usbtrans.h'
--- include/grub/usbtrans.h	2010-05-05 08:40:48 +0000
+++ include/grub/usbtrans.h	2010-05-23 13:08:06 +0000
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)

=== modified file 'kern/misc.c'
--- kern/misc.c	2010-05-22 14:58:45 +0000
+++ kern/misc.c	2010-05-23 17:32:21 +0000
@@ -189,7 +189,7 @@
   const char *debug = grub_env_get ("debug");
 
   if (! debug)
-    return;
+    debug = "all";
 
   if (grub_strword (debug, "all") || grub_strword (debug, condition))
     {


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: multiple problems with usb devices
  2010-05-23 17:42                     ` Aleš Nesrsta
@ 2010-05-23 18:40                       ` Thomas Schmitt
  0 siblings, 0 replies; 41+ messages in thread
From: Thomas Schmitt @ 2010-05-23 18:40 UTC (permalink / raw)
  To: grub-devel

Hi,

> > Is MMC-2 subclass of cardreaders?
> If you are looking for the SCSI command set used by ATAPI CD-ROM or DVD
> devices then you should go to the T10 site and find the MMC or MMC-2

Yes, MMC is the CD/DVD/Blu-ray part of SCSI.
Released is MMC-5 meanwhile. MMC-6 is the current
development version, afaik.
No references to other devices except the general
storage media model for SCSI disks.

The complete command set for CD/DVD/BD is covered
by SPC-3 (for general commands), SBC-2 (for
load/eject) and MMC-5.
They include older specs like MMC-2 but omit
deprecated parts. So for CD it pays off to
also read MMC-1 to MMC-3.

SPC-3 mentions
  SCSI Specification for Optical Card Reader/Writer OCRW [ISO/IEC 14776-381]
but that sounds somewhat exotic. Aren't memory
cards based on electrical memory ?


> Unfortunately you should be a member of T10 to get access to many
> documents, MMC-2 is one of them...

In the past, the development drafts were free
- and mature in their latest versions. The final
standards were published as expensive books
for 200 to 300 dollar per standard.
But now T10 changed policy to 30 dollar per PDF
and no free drafts any more.

On the other hand, there is nothing new on the
market since the last free drafts were published.


Have a nice day :)

Thomas



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 17:16                     ` Aleš Nesrsta
@ 2010-05-23 19:35                       ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 20:41                         ` seth.goldberg
  2010-05-25 18:58                         ` Aleš Nesrsta
  0 siblings, 2 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-23 19:35 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 2330 bytes --]

>  =20
>> Ale=C5=A1 Nesrsta wrote:
>>    =20
>>> I will look into yeeloongfw branch, I got Bazaar working yesterday
>>> evening only... I agree with You, first should be solved known proble=
ms
>>> on simple base.
>>> Unfortunately, I probably cannot help You as I don't have GEODE or
>>> Yeeloong... I can hope only that my corrections would help You or
>>> inspire You in some other ways.
>>> For me were very "disgusting", unexpected and hard to find problems w=
ith
>>> short max. packet size on control pipe and problem with toggling bulk=

>>> transfers when EP have numbers 0x01 and 0x81.
>>>
>>>  =20
>>>      =20
>> Forgot to mention: on Yeeloong the value you write into  registers
>> expecting the memory address is not the same as the value of the point=
er
>> so simple cast won't work. You need to use dma32_alloc, dma_get_virt a=
nd
>> dma_get_phys functions.
>>    =20
>
> Hi,
>
> it is interesting - why on Yeeloong only ? Why it is not needed on
> "normal" GRUB ?
>  =20
There are multiple considerations:
1) PCI BARs on Yeeloong don't map into standard address space but in
separate windows found at 0xb0000000.
2) In kernel mode memory starts from 0x80000000 and not 0 as on x86 in
no-paging mode.
3) But if you want to circumvent cache you need to OR 0x20000000 to your
address
4) PCI DMA space starts from 0x80000000
So if you allocate e.g. 0x81234560 then you have to write 0x81234560 to
registers but use 0xa1234560 in pointer.
On some platforms like sparc and PPC grub has to work in an environment
where mapping is even more difficult.
> And another idea - could be there some problem with PCI memory mapped
> register access caching ? Some Linux drivers uses so called "barrier"
> when doing access to PCI memory to be sure that previous access was
> really done - could be there such case ?
>  =20
I'm not sure. I've looked into intel manual and it looks like it
supposes mobo has some kind of way to inform CPU of memory writes/reads.
This may be wrong impression from very fast look into spec.
Also there are MTRR and cacheability attribute in page table.
> Best regards
> Ales
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>  =20


--=20
Regards
Vladimir '=CF=86-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 19:35                       ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-23 20:41                         ` seth.goldberg
  2010-05-25 18:58                         ` Aleš Nesrsta
  1 sibling, 0 replies; 41+ messages in thread
From: seth.goldberg @ 2010-05-23 20:41 UTC (permalink / raw)
  To: The development of GNU GRUB



On May 23, 2010, at 12:35 PM, Vladimir 'φ-coder/phcoder' Serbinenko <phcoder@gmail.co 
m> wrote:

>
>> Vladimir '=CF=86-coder/phcoder' Serbinenko wrote:
>
>> =20
>>> Ale=C5=A1 Nesrsta wrote:
>>>   =20
>>>> I will look into yeeloongfw branch, I got Bazaar working yesterday
>>>> evening only... I agree with You, first should be solved known  
>>>> proble=
> ms
>>>> on simple base.
>>>> Unfortunately, I probably cannot help You as I don't have GEODE or
>>>> Yeeloong... I can hope only that my corrections would help You or
>>>> inspire You in some other ways.
>>>> For me were very "disgusting", unexpected and hard to find  
>>>> problems w=
> ith
>>>> short max. packet size on control pipe and problem with toggling  
>>>> bulk=
>
>>>> transfers when EP have numbers 0x01 and 0x81.
>>>>
>>>> =20
>>>>     =20
>>> Forgot to mention: on Yeeloong the value you write into  registers
>>> expecting the memory address is not the same as the value of the  
>>> point=
> er
>>> so simple cast won't work. You need to use dma32_alloc,  
>>> dma_get_virt a=
> nd
>>> dma_get_phys functions.
>>>   =20
>>
>> Hi,
>>
>> it is interesting - why on Yeeloong only ? Why it is not needed on
>> "normal" GRUB ?
>> =20
> There are multiple considerations:
> 1) PCI BARs on Yeeloong don't map into standard address space but in
> separate windows found at 0xb0000000.
> 2) In kernel mode memory starts from 0x80000000 and not 0 as on x86 in
> no-paging mode.
> 3) But if you want to circumvent cache you need to OR 0x20000000 to  
> your
> address
> 4) PCI DMA space starts from 0x80000000
> So if you allocate e.g. 0x81234560 then you have to write 0x81234560  
> to
> registers but use 0xa1234560 in pointer.
> On some platforms like sparc and PPC grub has to work in an  
> environment
> where mapping is even more difficult.
>> And another idea - could be there some problem with PCI memory mapped
>> register access caching ? Some Linux drivers uses so called "barrier"
>> when doing access to PCI memory to be sure that previous access was
>> really done - could be there such case ?
>> =20
> I'm not sure. I've looked into intel manual and it looks like it
> supposes mobo has some kind of way to inform CPU of memory writes/ 
> reads.
> This may be wrong impression from very fast look into spec.
> Also there are MTRR and cacheability attribute in page table.

Besides cache attributes, another issue is forcing pci express posted  
writes to hit the device (reading back from that same address causes  
the posted write to hit the device).

   --S




>> Best regards
>> Ales
>>
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> http://lists.gnu.org/mailman/listinfo/grub-devel
>>
>> =20
>
>
> --=20
> Regards
> Vladimir '=CF=86-coder/phcoder' Serbinenko
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-23 19:35                       ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-23 20:41                         ` seth.goldberg
@ 2010-05-25 18:58                         ` Aleš Nesrsta
  2010-05-30 16:51                           ` Aleš Nesrsta
  1 sibling, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-25 18:58 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 1577 bytes --]

Hi,

I discovered something when I was looking for some hangs (timeouts):

1.
There is problem in used toggling system on bulk transfers - in case of
some errors (underrun, overrun etc.) it missed synchronization with
device, because it expects that all TDs will be processed - and in error
case it is usually not true.
I made some simple workaround (only for OHCI yet) which looks working -
see patch (patch contains also some another things, mainly unrecoverable
error detection and related reset of OHCI and some change in error
handling in usbms_transfer).

2.
I discovered some curious bug on one of my devices. It looks to be
little bit similar to this (which is unfortunately not solved, at least
in this mail list...):
http://www.mail-archive.com/linux-usb-users@lists.sourceforge.net/msg16836.html
Symptoms:
Device is not able to send more then 1kb in one transfer. If grub
requests usual 4kb data block, device hangs together with OHCI when
first 1kb data is sent - there is no NAK, no STALL nor another error,
OHCI or device (or both together) simply hangs the bulk IN endpoint -
OHCI is waiting for the end of the IN transfer which never happened...
There is no reaction to CLEAR STALL sent to IN endpoint.
Device and OHCI leave this state when Bulk-only reset device procedure
is used.
It looks like there is no way how to make OHCI&device do longer
transfer.

My first idea for workaround - to do in scsi read/write functions
splitting of large data block into parts of device sector size or simply
of grub sector size.
Any other better idea ?

Best regards
Ales

[-- Attachment #2: usb_toggle_patch_100525_0 --]
[-- Type: text/x-patch, Size: 12455 bytes --]

diff -urB ./grub/bus/usb/ohci.c ./grub_toggle/bus/usb/ohci.c
--- ./grub/bus/usb/ohci.c	2010-05-25 20:06:40.000000000 +0200
+++ ./grub_toggle/bus/usb/ohci.c	2010-05-25 08:02:44.000000000 +0200
@@ -395,9 +400,11 @@
   grub_usb_err_t err;
   grub_uint8_t errcode = 0;
   grub_ohci_td_t tderr = NULL;
-  int i, j;
+  int i;
   grub_uint64_t maxtime;
   int err_timeout = 0;
+  int err_unrec = 0;
+  grub_uint32_t intstatus;
 
   /* Allocate an Endpoint Descriptor.  */
   ed = grub_memalign (16, sizeof (*ed));
@@ -530,27 +537,27 @@
     }
 
   grub_dprintf ("ohci", "wait for completion\n");
-  grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n",
+  grub_dprintf ("ohci", "begin: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n",
   grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
-  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS),
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
 
   /* Starting time for timeout - feel free to change the value,
    * I have no idea about correct value ...
    * It is workaround only because it looks like my OHCI does not
-   * inicate properly STALL or NAK (or both...) - or there is
+   * indicate properly STALL or NAK (or both...) - or there is
    * some mistake somewhere... */
   maxtime = grub_get_time_ms () + 1000;
 	
   /* Wait until the transfer is completed or STALLs.  */
   do
     {
-      grub_cpu_idle ();
-
       /* Detected a HALT.  */
       if (grub_le_to_cpu32 (ed->td_head) & 1)
         break;
   
-      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+      intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+      if ((intstatus & 0x2) != 0)
         {
           if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
                == (grub_uint32_t) &td_list[transfer->transcnt - 1])
@@ -568,6 +575,13 @@
           grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
           continue;
         }
+
+      if ((intstatus & 0x10) != 0)
+        { /* Unrecoverable error - only reset can help...! */
+          err_unrec = 1;
+          break;
+        }
+
       /* Timeout ? */
       if (grub_get_time_ms () > maxtime)
       	{
@@ -577,9 +591,54 @@
       	  err_timeout = 1;
       	  break;
       	}
+
+      grub_cpu_idle ();
     }
   while (1);
+  
+  grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n",
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS),
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+
+  tderr = (grub_ohci_td_t)
+            (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
 
+  if (tderr == 0)
+    {
+      /* If hcca->donehead==0 it means that something wrong happened,
+       * it could be:
+       * - timeout
+       * - some error
+       * - something unexpected... */
+      /* Try look into DONEHEAD reg. */
+      grub_dprintf("ohci", "HCCA DoneHead is zero, something is bad!\n");
+      tderr = (grub_ohci_td_t)
+                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+    }
+  /* Zero can be in case when timeout or unrecoverable error occured */
+  if ((tderr == 0) && (err_timeout || err_unrec))
+    tderr = &td_list[0]; /* No TD was probably executed, take first */
+  
+  /* Remember last processed transaction (TD) - it is necessary for
+   * proper setting of toggle bit in next transaction. */
+  transfer->last_trans = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+  /* The expression above maybe will not work on Yeeloong because
+   * of DMA memory mapping. (?) I.e., maybe we will need to have some
+   * additional informations in TDs. */
+  /* Check correct value in last_trans */
+  if (transfer->last_trans >= transfer->transcnt)
+    {
+      grub_dprintf("ohci", "Bad index of last processed transaction!\n");
+      grub_dprintf("ohci", "Something very very bad happened!\n");
+      grub_dprintf("ohci", "tderr=%p, td_list=%p,\n\t\t last_trans=%d, transcnt=%d\n",
+                   tderr, td_list, transfer->last_trans, transfer->transcnt);
+      /* We should set something valid... */
+      transfer->last_trans = 0; /* Can we do something better ? */
+      tderr = &td_list[0]; 
+    }
+
+  /* In case of timeout do not detect error from TD */    
   if (err_timeout)
     {
       err = GRUB_ERR_TIMEOUT;
@@ -590,15 +649,19 @@
       grub_le_to_cpu32(ed->next_ed));
     }
   		
+  /* In case of unrecoverable error do not detect error from TD */    
+  else if (err_unrec)
+    {
+      err = GRUB_USB_ERR_UNRECOVERABLE;
+      grub_dprintf("ohci", "Unrecoverable error, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+      grub_le_to_cpu32(ed->target),
+      grub_le_to_cpu32(ed->td_head),
+      grub_le_to_cpu32(ed->td_tail),
+      grub_le_to_cpu32(ed->next_ed));
+    }
+  		
   else if (grub_le_to_cpu32 (ed->td_head) & 1)
     {
-      tderr = (grub_ohci_td_t)
-                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
-      if (tderr == 0)
-        /* If DONEHEAD==0 it means that correct address is in HCCA.
-         * It should be always now! */
-        tderr = (grub_ohci_td_t) (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
-
       errcode = grub_le_to_cpu32 (tderr->token) >> 28;
       
       switch (errcode)
@@ -707,7 +770,8 @@
   /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
   /* Wait for new SOF */
-  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  while (((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0)
+         && !err_unrec);
   /* Now it should be safe to change CONTROL and BULK lists. */
   
   /* Important cleaning. */
@@ -718,6 +782,28 @@
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
+  if (err_unrec)
+    {
+      /* Do OHCI reset in case of unrecoverable error - maybe we will need
+       * do more - re-enumerate bus etc. (?) */
+
+      /* Suspend the OHCI by issuing a reset.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
+      grub_millisleep (1);
+      grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
+
+      /* Misc. resets. */
+      o->hcca->donehead = 0;
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+      /* Enable the OHCI.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6));
+    }
+  
   grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x (OHCI errcode=0x%02x)\n",
                 err, errcode);
   
diff -urB ./grub/bus/usb/usbtrans.c ./grub_toggle/bus/usb/usbtrans.c
--- ./grub/bus/usb/usbtrans.c	2010-05-25 20:06:40.000000000 +0200
+++ ./grub_toggle/bus/usb/usbtrans.c	2010-05-24 22:26:54.000000000 +0200
@@ -167,6 +167,7 @@
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
   transfer->max = max;
   transfer->dev = dev;
+  transfer->last_trans = 0; /* Reset index of last processed transaction (TD) */
 
   /* Allocate an array of transfer data structures.  */
   transfer->transactions = grub_malloc (transfer->transcnt
@@ -193,6 +194,10 @@
     }
 
   err = dev->controller.dev->transfer (&dev->controller, transfer);
+  /* We must remember proper toggle value even if some transactions
+   * were not processed - correct value should be inversion of last
+   * processed transaction (TD). */
+  toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1;
   grub_dprintf ("usb", "toggle=%d\n", toggle);
   dev->toggle[endpoint] = toggle;
 
diff -urB ./grub/disk/usbms.c ./grub_toggle/disk/usbms.c
--- ./grub/disk/usbms.c	2010-05-25 20:06:40.000000000 +0200
+++ ./grub_toggle/disk/usbms.c	2010-05-24 20:38:23.000000000 +0200
@@ -238,6 +239,7 @@
   struct grub_usbms_csw status;
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
+  grub_usb_err_t errCSW = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
   grub_size_t i;
 
@@ -275,9 +277,8 @@
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	  goto retry;
+	  goto CheckCSW;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
@@ -287,7 +288,12 @@
     {
       err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf);
       grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
-      if (err) goto CheckCSW;
+      if (err)
+        {
+          if (err == GRUB_USB_ERR_STALL)
+	    grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          goto CheckCSW;
+        }
       /* Debug print of received data. */
       grub_dprintf ("usb", "buf:\n");
       if (size <= 64)
@@ -301,6 +307,12 @@
       err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
       grub_dprintf ("usb", "buf:\n");
+      if (err)
+        {
+          if (err == GRUB_USB_ERR_STALL)
+	    grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+          goto CheckCSW;
+        }
       /* Debug print of sent data. */
       if (size <= 256)
         for (i=0; i<size; i++)
@@ -311,15 +323,16 @@
 
   /* Read the status - (maybe) according to specification.  */
 CheckCSW:
-  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+  errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
 		    sizeof (status), (char *) &status);
-  if (err)
+  if (errCSW)
     {
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+      errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
 			        sizeof (status), (char *) &status);
-      if (err)
+      if (errCSW)
         { /* Bulk-only reset device. */
+          grub_dprintf ("usb", "Bulk-only reset device - errCSW\n");
           grub_usbms_reset (dev->dev, dev->interface);
           grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
           grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -332,9 +345,11 @@
   	status.signature, status.tag, status.residue);
   grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
   
-  /* If phase error, do bulk-only reset device. */
-  if (status.status == 2)
-    {
+  /* If phase error or not valid signature, do bulk-only reset device. */
+  if ((status.status == 2) ||
+      (status.signature != grub_cpu_to_le32(0x53425355)))
+    { /* Bulk-only reset device. */
+      grub_dprintf ("usb", "Bulk-only reset device - bad status\n");
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -342,9 +356,13 @@
       goto retry;
     }
 
-  if (status.status)
+  /* If "command failed" status or data transfer failed -> error */
+  if ((status.status || err) && !read_write)
     return grub_error (GRUB_ERR_READ_ERROR,
 		       "error communication with USB Mass Storage device");
+  else if ((status.status || err) && read_write)
+    return grub_error (GRUB_ERR_WRITE_ERROR,
+		       "error communication with USB Mass Storage device");
 
   return GRUB_ERR_NONE;
 }
diff -urB ./grub/include/grub/usb.h ./grub_toggle/include/grub/usb.h
--- ./grub/include/grub/usb.h	2010-05-25 20:06:40.000000000 +0200
+++ ./grub_toggle/include/grub/usb.h	2010-05-24 19:49:11.000000000 +0200
@@ -35,7 +35,8 @@
     GRUB_USB_ERR_NAK,
     GRUB_USB_ERR_BABBLE,
     GRUB_USB_ERR_TIMEOUT,
-    GRUB_USB_ERR_BITSTUFF
+    GRUB_USB_ERR_BITSTUFF,
+    GRUB_USB_ERR_UNRECOVERABLE
   } grub_usb_err_t;
 
 typedef enum
diff -urB ./grub/include/grub/usbtrans.h ./grub_toggle/include/grub/usbtrans.h
--- ./grub/include/grub/usbtrans.h	2010-05-25 20:06:40.000000000 +0200
+++ ./grub_toggle/include/grub/usbtrans.h	2010-05-24 19:52:48.000000000 +0200
@@ -58,6 +58,9 @@
   struct grub_usb_device *dev;
 
   struct grub_usb_transaction *transactions;
+  
+  int last_trans;
+  /* Index of last processed transaction in OHCI/UHCI driver. */
 };
 typedef struct grub_usb_transfer *grub_usb_transfer_t;
 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-25 18:58                         ` Aleš Nesrsta
@ 2010-05-30 16:51                           ` Aleš Nesrsta
  2010-05-30 22:26                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-30 16:51 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 2899 bytes --]

Hi,

there is some new patch which includes:

- OHCI power and configuration counter check patch sent by Vladimir in
the meantime

- simple handling of unrecoverable OHCI error (not tested yet - I cannot
set OHCI into unrec. error state...)

- (I hope) proper toggle bit handling also in error states - ohci.c,
uhci.c, usbtrans.c, usbtrans.h

- some changes in OHCI DoneHead handling (it is partialy related to
toggle bit handling)

- some changes in bulk transfers error handling in usbms, should be more
close to specification now

- scsi.c simple workaround related to problem with device which cannot
transfer 4KB blocks - but it is disabled. It works but transfer is very
slow when this workaround is active - I don't know why. It is little bit
surprising - device works better without this workaround... So I
disabled it.

Patch is made against files taken from revision 2391 (should be the same
as in 1.98 release) patched by my previous patch usb_patch_100523_0.
Don't use my intermediate "toggle" patch from 25 May 2010.


Remaining problems:

1.
Some devices (at least my BUFFALO USB clip drive flash disk, more
precisely "ID 0ea0:2168 Ours Technology, Inc. Transcend JetFlash 2.0 /
Astone USB Drive") cannot transfer 4KB data blocks independent on
controller OHCI/UHCI - my workaround is not good.
But it is probably problem with low priority - device is working finally
but it is slower than another device.

2.
Some devices (at least my APACER cardreader "ID 058f:6366 Alcor Micro
Corp. Multi Flash Reader") are not working on UHCI. Such device does not
accept any control message and UHCI returns status 0x450007 - it means
STALL during sending SETUP packet.
It looks to be the same problem as described by Vladimir: "I have
somewhat similar issue with Geode OHCI controller right now: devices and
speeds are correctly seen but trying to send a message results in a halt
in first TD and error code 5.".
But I have problem on UHCI, not on OHCI - on computer with OHCI is this
device working well (it is normal USB Mass Storage Bulk-Only device with
SCSI subclass)! Maybe it depends on "combined controllers" UHCI-EHCI,
OHCI-EHCI (?) - device is working on computer with OHCI only computer
and it is not working on computer with UHCI-EHCI controller. But any
other device is working well on both computers... I don't understand, I
currently have no idea what can be wrong. Does anybody know...?

3.
There is not working USB hub support, GRUB does not see device connected
via USB hub - does anybody know some details or have some specification
of USB Hub class ? I cannot find it on USB site (maybe I have not
sufficient patience...).


I will probably focus in OHCI speed-up now, i.e. I try to do some other
handling of ED to prevent changes in OHCI registers which are slowing
down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
this reason).


Best regards
Ales


[-- Attachment #2: usb_patch_100530_0 --]
[-- Type: text/x-patch, Size: 21450 bytes --]

diff -urB ./grub/bus/usb/ohci.c ./grub_patched/bus/usb/ohci.c
--- ./grub/bus/usb/ohci.c	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/bus/usb/ohci.c	2010-05-28 19:02:38.000000000 +0200
@@ -103,6 +103,11 @@
 #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
 #define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200
 
+#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1)
+#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0)
+#define GRUB_OHCI_SET_PORT_RESET (1 << 4)
+#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)
+
 static grub_uint32_t
 grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
 {
@@ -234,7 +239,7 @@
 
   /* Misc. pre-sets. */
   o->hcca->donehead = 0;
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
@@ -351,8 +356,10 @@
       break;
     }
 
+#if 0 /* Always generate interrupt */
   /* Generate no interrupts.  */
   token |= 7 << 21;
+#endif
 
   /* Set the token.  */
   token |= toggle << 24;
@@ -395,9 +402,11 @@
   grub_usb_err_t err;
   grub_uint8_t errcode = 0;
   grub_ohci_td_t tderr = NULL;
-  int i, j;
+  int i;
   grub_uint64_t maxtime;
   int err_timeout = 0;
+  int err_unrec = 0;
+  grub_uint32_t intstatus;
 
   /* Allocate an Endpoint Descriptor.  */
   ed = grub_memalign (16, sizeof (*ed));
@@ -424,11 +433,13 @@
       td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]);
     }
 
+#if 0 /* Better will be enable interrupt on all TDs. */
   /* The last-1 TD token we should change to enable interrupt when TD finishes.
    * As OHCI interrupts are disabled, it does only setting of WDH bit in
    * HcInterruptStatus register - and that is what we want to safely detect
    * normal end of all transactions. */
   td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+#endif
 
   td_list[transfer->transcnt].token = 0;
   td_list[transfer->transcnt].buffer = 0;
@@ -530,44 +541,49 @@
     }
 
   grub_dprintf ("ohci", "wait for completion\n");
-  grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n",
+  grub_dprintf ("ohci", "begin: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n",
   grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
-  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS),
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
 
   /* Starting time for timeout - feel free to change the value,
    * I have no idea about correct value ...
    * It is workaround only because it looks like my OHCI does not
-   * inicate properly STALL or NAK (or both...) - or there is
+   * indicate properly STALL or NAK (or both...) - or there is
    * some mistake somewhere... */
   maxtime = grub_get_time_ms () + 1000;
-	
+
   /* Wait until the transfer is completed or STALLs.  */
   do
     {
-      grub_cpu_idle ();
-
-      /* Detected a HALT.  */
-      if (grub_le_to_cpu32 (ed->td_head) & 1)
-        break;
-  
-      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+      /* Check transfer status */
+      intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+      if ((intstatus & 0x2) != 0)
         {
-          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
-               == (grub_uint32_t) &td_list[transfer->transcnt - 1])
-            break;
-
-          /* Done Head can be updated on some another place if ED is halted. */          
-          if (grub_le_to_cpu32 (ed->td_head) & 1)
-            break;
-
-          /* If there is not HALT in ED, it is not correct, so debug it, reset
-           * donehead and WDH and continue waiting. */
-          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+          grub_dprintf ("ohci", "Current HccaDoneHead=0x%08x\n",
                         o->hcca->donehead);
+          /* Remember last successful TD */
+          tderr = (grub_ohci_td_t)
+            (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
+          /* Reset DoneHead */
           o->hcca->donehead = 0;
           grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          /* if TD is last, finish */
+          if (tderr == (grub_ohci_td_t) &td_list[transfer->transcnt - 1])
+            break;
           continue;
         }
+
+      if ((intstatus & 0x10) != 0)
+        { /* Unrecoverable error - only reset can help...! */
+          err_unrec = 1;
+          break;
+        }
+
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (ed->td_head) & 1)
+        break;
+
       /* Timeout ? */
       if (grub_get_time_ms () > maxtime)
       	{
@@ -577,9 +593,49 @@
       	  err_timeout = 1;
       	  break;
       	}
+
+      grub_cpu_idle ();
     }
   while (1);
+  
+  grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n",
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS),
+    grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+
+  if (!tderr)
+    {
+      /* It means that something wrong happened,
+       * it could be:
+       * - timeout and no TD processed
+       * - some or unrecoverable error and no TD processed
+       * - something unexpected... :-( */
+      /* Try look into DONEHEAD reg., but there should be also zero */
+      grub_dprintf("ohci", "HCCA DoneHead is zero, something is bad!\n");
+      tderr = (grub_ohci_td_t)
+                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+    }
+  
+  /* Remember last processed transaction (TD) - it is necessary for
+   * proper setting of toggle bit in next transaction. */
+  transfer->last_trans = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+  /* The expression above maybe will not work on Yeeloong because
+   * of DMA memory mapping. (?) I.e., maybe we will need to have some
+   * additional informations in TDs. */
+  
+  /* Check correct value in last_trans */
+  /* It could happen if timeout happens and no TD was retired */
+  if (transfer->last_trans >= transfer->transcnt || !tderr)
+    {
+      grub_dprintf("ohci", "tder==0 or out of TDs range!\n");
+      grub_dprintf("ohci", "tderr=%p, td_list=%p,\n\t\t last_trans=%d, transcnt=%d\n",
+                   tderr, td_list, transfer->last_trans, transfer->transcnt);
+      /* We should set something valid... */
+      transfer->last_trans = -1; /* Probably no TD done */
+      tderr = &td_list[0]; 
+    }
 
+  /* In case of timeout do not detect error from TD */    
   if (err_timeout)
     {
       err = GRUB_ERR_TIMEOUT;
@@ -590,15 +646,19 @@
       grub_le_to_cpu32(ed->next_ed));
     }
   		
+  /* In case of unrecoverable error do not detect error from TD */    
+  else if (err_unrec)
+    {
+      err = GRUB_USB_ERR_UNRECOVERABLE;
+      grub_dprintf("ohci", "Unrecoverable error, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+      grub_le_to_cpu32(ed->target),
+      grub_le_to_cpu32(ed->td_head),
+      grub_le_to_cpu32(ed->td_tail),
+      grub_le_to_cpu32(ed->next_ed));
+    }
+  		
   else if (grub_le_to_cpu32 (ed->td_head) & 1)
     {
-      tderr = (grub_ohci_td_t)
-                (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
-      if (tderr == 0)
-        /* If DONEHEAD==0 it means that correct address is in HCCA.
-         * It should be always now! */
-        tderr = (grub_ohci_td_t) (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
-
       errcode = grub_le_to_cpu32 (tderr->token) >> 28;
       
       switch (errcode)
@@ -645,17 +705,17 @@
 	case 8:
 	  /* XXX: Data overrun error.  */
 	  err = GRUB_USB_ERR_DATA;
-	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
-	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j);
+	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n",
+	                tderr, transfer->last_trans);
 	  break;
 
 	case 9:
 	  /* XXX: Data underrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
+	                tderr, transfer->last_trans);
 	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
 	                1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer));
-	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
-	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 10:
@@ -707,7 +767,8 @@
   /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
   /* Wait for new SOF */
-  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  while (((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0)
+         && !err_unrec);
   /* Now it should be safe to change CONTROL and BULK lists. */
   
   /* Important cleaning. */
@@ -718,6 +779,28 @@
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
+  if (err_unrec)
+    {
+      /* Do OHCI reset in case of unrecoverable error - maybe we will need
+       * do more - re-enumerate bus etc. (?) */
+
+      /* Suspend the OHCI by issuing a reset.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
+      grub_millisleep (1);
+      grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
+
+      /* Misc. resets. */
+      o->hcca->donehead = 0;
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+      /* Enable the OHCI.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6));
+    }
+  
   grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x (OHCI errcode=0x%02x)\n",
                 err, errcode);
   
@@ -730,34 +813,32 @@
 
 static grub_err_t
 grub_ohci_portstatus (grub_usb_controller_t dev,
-		      unsigned int port, unsigned int enable)
+              unsigned int port, unsigned int enable)
 {
    struct grub_ohci *o = (struct grub_ohci *) dev->data;
-   grub_uint32_t status;
 
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
    grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
                  grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
 
-   /* Enable the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (enable << 1); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-
-   /* Reset the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (1 << 4); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+             GRUB_OHCI_SET_PORT_RESET);
    grub_millisleep (50); /* For root hub should be nominaly 50ms */
 
    /* End the reset signaling.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (1 << 20); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+             GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
    grub_millisleep (10);
 
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   grub_dprintf ("ohci", "end of portstatus=0x%02x\n", status);
+   if (enable)
+     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+               GRUB_OHCI_SET_PORT_ENABLE);
+   else
+     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+               GRUB_OHCI_CLEAR_PORT_ENABLE);
+   grub_millisleep (10);
+
+   grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
+         grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
 
    return GRUB_ERR_NONE;
 }
diff -urB ./grub/bus/usb/uhci.c ./grub_patched/bus/usb/uhci.c
--- ./grub/bus/usb/uhci.c	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/bus/usb/uhci.c	2010-05-28 21:26:39.000000000 +0200
@@ -332,13 +332,20 @@
 }
 
 static void
-grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td)
+grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td,
+                 grub_usb_transfer_t transfer)
 {
-  /* Free the TDs in this queue.  */
-  while (td)
+  int i; /* Index of TD in transfer */
+  
+  /* Free the TDs in this queue and set last_trans.  */
+  for (i=0; td; i++)
     {
       grub_uhci_td_t tdprev;
 
+      /* Check state of TD and possibly set last_trans */
+      if (transfer && (td->linkptr & 1))
+        transfer->last_trans = i;
+      
       /* Unlink the queue.  */
       tdprev = td;
       td = (grub_uhci_td_t) td->linkptr2;
@@ -461,7 +468,7 @@
 	  td_prev->linkptr = 1;
 
 	  if (td_first)
-	    grub_free_queue (u, td_first);
+	    grub_free_queue (u, td_first, NULL);
 
 	  return GRUB_USB_ERR_INTERNAL;
 	}
@@ -560,7 +567,7 @@
   /* Place the QH back in the free list and deallocate the associated
      TDs.  */
   qh->elinkptr = 1;
-  grub_free_queue (u, td_first);
+  grub_free_queue (u, td_first, transfer);
 
   return err;
 }
@@ -609,7 +616,7 @@
   grub_uhci_writereg16 (u, reg, enable << 9);
 
   /* Wait for the reset to complete.  XXX: How long exactly?  */
-  grub_millisleep (10);
+  grub_millisleep (50); /* For root hub should be nominaly 50ms */
   status = grub_uhci_readreg16 (u, reg);
   grub_uhci_writereg16 (u, reg, status & ~(1 << 9));
   grub_dprintf ("uhci", "reset completed\n");
diff -urB ./grub/bus/usb/usbtrans.c ./grub_patched/bus/usb/usbtrans.c
--- ./grub/bus/usb/usbtrans.c	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/bus/usb/usbtrans.c	2010-05-28 16:05:04.000000000 +0200
@@ -167,6 +167,7 @@
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
   transfer->max = max;
   transfer->dev = dev;
+  transfer->last_trans = -1; /* Reset index of last processed transaction (TD) */
 
   /* Allocate an array of transfer data structures.  */
   transfer->transactions = grub_malloc (transfer->transcnt
@@ -193,6 +194,13 @@
     }
 
   err = dev->controller.dev->transfer (&dev->controller, transfer);
+  /* We must remember proper toggle value even if some transactions
+   * were not processed - correct value should be inversion of last
+   * processed transaction (TD). */
+  if (transfer->last_trans >= 0)
+    toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1;
+  else
+    toggle = dev->toggle[endpoint]; /* Nothing done, take original */
   grub_dprintf ("usb", "toggle=%d\n", toggle);
   dev->toggle[endpoint] = toggle;
 
diff -urB ./grub/disk/scsi.c ./grub_patched/disk/scsi.c
--- ./grub/disk/scsi.c	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/disk/scsi.c	2010-05-30 15:59:32.000000000 +0200
@@ -421,7 +421,7 @@
 
       /* According to USB MS tests specification, issue Test Unit Ready
        * until OK */
-      maxtime = grub_get_time_ms () + 1000;
+      maxtime = grub_get_time_ms () + 5000; /* It is safer value */
       do
         {
 	  /* Timeout is necessary - for example in case when we have
@@ -519,6 +519,37 @@
 
   /* XXX: Never reached.  */
   return GRUB_ERR_NONE;
+
+#if 0 /* Workaround - it works - but very slowly, from some reason
+       * unknown to me (specially on OHCI). Do not use it. */
+  /* Split transfer requests to device sector size because */
+  /* some devices are not able to transfer more than 512-1024 bytes */
+  grub_err_t err = GRUB_ERR_NONE;
+
+  for ( ; size; size--)
+    {
+      /* Depending on the type, select a read function.  */
+      switch (scsi->devtype)
+        {
+          case grub_scsi_devtype_direct:
+            err = grub_scsi_read10 (disk, sector, 1, buf);
+            break;
+
+          case grub_scsi_devtype_cdrom:
+            err = grub_scsi_read12 (disk, sector, 1, buf);
+            break;
+
+          default: /* This should not happen */
+            return GRUB_ERR_READ_ERROR;
+        }
+      if (err)
+        return err;
+      sector++;
+      buf += scsi->blocksize;
+    }
+
+  return err;
+#endif
 }
 
 static grub_err_t
diff -urB ./grub/disk/usbms.c ./grub_patched/disk/usbms.c
--- ./grub/disk/usbms.c	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/disk/usbms.c	2010-05-24 20:38:23.000000000 +0200
@@ -84,9 +84,10 @@
       struct grub_usb_desc_device *descdev = &usbdev->descdev;
       int i;
 
-      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0)
-	return 0;
-
+      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0
+         || descdev->configcnt == 0)
+         return 0;
+         
       /* XXX: Just check configuration 0 for now.  */
       for (i = 0; i < usbdev->config[0].descconf->numif; i++)
 	{
@@ -238,6 +239,7 @@
   struct grub_usbms_csw status;
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
+  grub_usb_err_t errCSW = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
   grub_size_t i;
 
@@ -275,9 +277,8 @@
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	  goto retry;
+	  goto CheckCSW;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
@@ -287,7 +288,12 @@
     {
       err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf);
       grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
-      if (err) goto CheckCSW;
+      if (err)
+        {
+          if (err == GRUB_USB_ERR_STALL)
+	    grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          goto CheckCSW;
+        }
       /* Debug print of received data. */
       grub_dprintf ("usb", "buf:\n");
       if (size <= 64)
@@ -301,6 +307,12 @@
       err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
       grub_dprintf ("usb", "buf:\n");
+      if (err)
+        {
+          if (err == GRUB_USB_ERR_STALL)
+	    grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+          goto CheckCSW;
+        }
       /* Debug print of sent data. */
       if (size <= 256)
         for (i=0; i<size; i++)
@@ -311,15 +323,16 @@
 
   /* Read the status - (maybe) according to specification.  */
 CheckCSW:
-  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+  errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
 		    sizeof (status), (char *) &status);
-  if (err)
+  if (errCSW)
     {
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+      errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
 			        sizeof (status), (char *) &status);
-      if (err)
+      if (errCSW)
         { /* Bulk-only reset device. */
+          grub_dprintf ("usb", "Bulk-only reset device - errCSW\n");
           grub_usbms_reset (dev->dev, dev->interface);
           grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
           grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -332,9 +345,11 @@
   	status.signature, status.tag, status.residue);
   grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
   
-  /* If phase error, do bulk-only reset device. */
-  if (status.status == 2)
-    {
+  /* If phase error or not valid signature, do bulk-only reset device. */
+  if ((status.status == 2) ||
+      (status.signature != grub_cpu_to_le32(0x53425355)))
+    { /* Bulk-only reset device. */
+      grub_dprintf ("usb", "Bulk-only reset device - bad status\n");
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -342,9 +356,13 @@
       goto retry;
     }
 
-  if (status.status)
+  /* If "command failed" status or data transfer failed -> error */
+  if ((status.status || err) && !read_write)
     return grub_error (GRUB_ERR_READ_ERROR,
 		       "error communication with USB Mass Storage device");
+  else if ((status.status || err) && read_write)
+    return grub_error (GRUB_ERR_WRITE_ERROR,
+		       "error communication with USB Mass Storage device");
 
   return GRUB_ERR_NONE;
 }
diff -urB ./grub/include/grub/usb.h ./grub_patched/include/grub/usb.h
--- ./grub/include/grub/usb.h	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/include/grub/usb.h	2010-05-24 19:49:11.000000000 +0200
@@ -35,7 +35,8 @@
     GRUB_USB_ERR_NAK,
     GRUB_USB_ERR_BABBLE,
     GRUB_USB_ERR_TIMEOUT,
-    GRUB_USB_ERR_BITSTUFF
+    GRUB_USB_ERR_BITSTUFF,
+    GRUB_USB_ERR_UNRECOVERABLE
   } grub_usb_err_t;
 
 typedef enum
diff -urB ./grub/include/grub/usbtrans.h ./grub_patched/include/grub/usbtrans.h
--- ./grub/include/grub/usbtrans.h	2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/include/grub/usbtrans.h	2010-05-24 19:52:48.000000000 +0200
@@ -58,6 +58,9 @@
   struct grub_usb_device *dev;
 
   struct grub_usb_transaction *transactions;
+  
+  int last_trans;
+  /* Index of last processed transaction in OHCI/UHCI driver. */
 };
 typedef struct grub_usb_transfer *grub_usb_transfer_t;
 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-30 16:51                           ` Aleš Nesrsta
@ 2010-05-30 22:26                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-31  4:16                               ` Seth Goldberg
  2010-05-31 12:23                               ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 2 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-30 22:26 UTC (permalink / raw)
  To: The development of GNU GRUB


[-- Attachment #1.1: Type: text/plain, Size: 3936 bytes --]

Aleš Nesrsta wrote:
Hello, Aleš. I've seen that your assignment was completed. I added you
to grub contributors. Feel free to create a branch in branches/usb.
Right now I'm busy but next week I should be able to assist. Perhaps
even this week. Right now I send uncommitted changes sitting in my
yeeloongfw branch. It's largely your rebased changes + few other things
like shutting the controller down before booting OS to avoid memory used
by OS to be clobbered by e.g. HCCA.
Branch usb should contain pci improvements + your usb changes. If you
have trouble with bzr or are short on time me (or perhaps someone else)
can give a hand.
This branch can go into mainline. Since mainline usb is in deplorable
state there is no need to pass this changes through experimental at all.
Merging into mainline doesn't mean that no further developpement should
be done on usb, just that this part is already a huge improvement.

>
> Remaining problems:
>
> 1.
> Some devices (at least my BUFFALO USB clip drive flash disk, more
> precisely "ID 0ea0:2168 Ours Technology, Inc. Transcend JetFlash 2.0 /
> Astone USB Drive") cannot transfer 4KB data blocks independent on
> controller OHCI/UHCI - my workaround is not good.
> But it is probably problem with low priority - device is working finally
> but it is slower than another device.
>
>   
Agreed
> 2.
> Some devices (at least my APACER cardreader "ID 058f:6366 Alcor Micro
> Corp. Multi Flash Reader") are not working on UHCI. Such device does not
> accept any control message and UHCI returns status 0x450007 - it means
> STALL during sending SETUP packet.
> It looks to be the same problem as described by Vladimir: "I have
> somewhat similar issue with Geode OHCI controller right now: devices and
> speeds are correctly seen but trying to send a message results in a halt
> in first TD and error code 5.".
>   
The problem was that the port was plainly off due to Geode reaction on
incorrect writes differring from that of other controllers.
> But I have problem on UHCI, not on OHCI - on computer with OHCI is this
> device working well (it is normal USB Mass Storage Bulk-Only device with
> SCSI subclass)! Maybe it depends on "combined controllers" UHCI-EHCI,
> OHCI-EHCI (?) - device is working on computer with OHCI only computer
> and it is not working on computer with UHCI-EHCI controller. But any
> other device is working well on both computers... I don't understand, I
> currently have no idea what can be wrong. Does anybody know...?
>
>   
Does this device work under OS on this computer? Some ports are plainly
underpowered.
> 3.
> There is not working USB hub support, GRUB does not see device connected
> via USB hub - does anybody know some details or have some specification
> of USB Hub class ? I cannot find it on USB site (maybe I have not
> sufficient patience...).
>
>
> I will probably focus in OHCI speed-up now, i.e. I try to do some other
> handling of ED to prevent changes in OHCI registers which are slowing
> down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
> this reason).
>   
How useful would the interrupts be for this matter? If the answer is
"very useful" I can implement them. Also we need some restructuring of
code of both ohci and grub in general to decrease the wait time. I think
specifically all the waits in init sequence. If system has 2 OHCI
controllers currently you need to wait twice as long as with 1
controllers while it's possible to init them in parallel and not wait
longer than in the case of one controller.
>
> Best regards
> Ales
>
>   
> ------------------------------------------------------------------------
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1.2: usb.diff --]
[-- Type: text/x-diff; name="usb.diff", Size: 50714 bytes --]

=== modified file 'boot/mips/yeeloong/fwstart.S'
--- boot/mips/yeeloong/fwstart.S	2010-05-22 14:58:45 +0000
+++ boot/mips/yeeloong/fwstart.S	2010-05-25 01:08:42 +0000
@@ -381,7 +381,6 @@
 no_cs5536:	.asciz "No CS5536 found.\n\r"
 cs5536_found:	.asciz "CS5536 at "
 sm_failed: .asciz "SM transaction failed.\n\r"
-not_implemented: .asciz "Nothing more is implemented. Bye.\n\r"
 unhandled_tlb_refill:	.asciz "Unhandled TLB refill.\n\r"
 unhandled_cache_error:	.asciz "Unhandled cache error.\n\r"
 unhandled_exception:	.asciz "Unhandled exception.\n\r"

=== modified file 'bus/usb/ohci.c'
--- bus/usb/ohci.c	2010-05-22 22:17:51 +0000
+++ bus/usb/ohci.c	2010-05-30 15:29:38 +0000
@@ -27,6 +27,7 @@
 #include <grub/cpu/io.h>
 #include <grub/time.h>
 #include <grub/cs5536.h>
+#include <grub/loader.h>
 
 struct grub_ohci_hcca
 {
@@ -96,7 +97,11 @@
   GRUB_OHCI_REG_FRAME_INTERVAL,
   GRUB_OHCI_REG_PERIODIC_START = 16,
   GRUB_OHCI_REG_RHUBA = 18,
-  GRUB_OHCI_REG_RHUBPORT = 21
+  GRUB_OHCI_REG_RHUBPORT = 21,
+  GRUB_OHCI_REG_LEGACY_CONTROL = 0x100,
+  GRUB_OHCI_REG_LEGACY_INPUT = 0x104,
+  GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108,
+  GRUB_OHCI_REG_LEGACY_STATUS = 0x10c
 } grub_ohci_reg_t;
 
 #define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
@@ -195,7 +200,7 @@
   if (! o)
     return 1;
 
-  o->iobase = grub_pci_device_map_range (dev, base, 0x100);
+  o->iobase = grub_pci_device_map_range (dev, base, 0x800);
 
   grub_dprintf ("ohci", "base=%p\n", o->iobase);
 
@@ -212,10 +217,48 @@
   if ((revision & 0xFF) != 0x10)
     goto fail;
 
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
-			(grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
-			 & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
-			| GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+
+  {
+    grub_uint32_t control;
+    /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */
+    control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+    if ((control & 0x100) != 0)
+      {
+	unsigned i;
+	grub_dprintf("ohci", "OHCI is owned by SMM\n");
+	/* Do change of ownership */
+	/* Ownership change request */
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic.  */
+	/* Waiting for SMM deactivation */
+	for (i=0; i < 10; i++)
+	  {
+	    if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0)
+	      {
+		grub_dprintf("ohci", "Ownership changed normally.\n");
+		break;
+	      }
+	    grub_millisleep (100);
+          }
+	if (i >= 10)
+	  {
+	    grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+				  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100);
+	    grub_dprintf("ohci", "Ownership changing timeout, change forced !\n");
+	  }
+      }
+    else if (((control & 0x100) == 0) && 
+	     ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */
+      {
+	grub_dprintf("ohci", "OHCI is owned by BIOS\n");
+	/* Do change of ownership - not implemented yet... */
+	/* In fact we probably need to do nothing ...? */
+      }
+    else
+      {
+	grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n");
+	/* We can setup OHCI. */
+      }  
+  }
 
   /* Suspend the OHCI by issuing a reset.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
@@ -232,15 +275,58 @@
 			GRUB_OHCI_PERIODIC_START);
 
   /* Setup the HCCA.  */
+  o->hcca->donehead = 0;
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
   grub_dprintf ("ohci", "OHCI HCCA\n");
 
+  /* Misc. pre-sets. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  /* Check OHCI Legacy Support */
+  if ((revision & 0x100) != 0)
+    {
+      grub_dprintf ("ohci", "Legacy Support registers detected\n");
+      grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n",
+		    grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL));
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL,
+			    (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1);
+      grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
+    }
+
   /* Enable the OHCI.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
 			(2 << 6));
   grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
 		(grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
 
+  /* Power on all ports */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
+                       (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
+                        & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
+                       | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
+  /* Wait for stable power (100ms) and stable attachment (100ms) */
+  /* I.e. minimum wait time should be probably 200ms. */
+  /* We assume that device is attached when ohci is loaded. */
+  /* Some devices take long time to power-on or indicate attach. */
+  /* Here is some experimental value which should probably mostly work. */
+  /* Cameras with manual USB mode selection and maybe some other similar
+   * devices will not work in some cases - they are repowered during
+   * ownership change and then they are starting slowly and mostly they
+   * are wanting select proper mode again...
+   * The same situation can be on computers where BIOS not set-up OHCI
+   * to be at least powered USB bus (maybe it is Yeelong case...?)
+   * Possible workaround could be for example some prompt
+   * for user with confirmation of proper USB device connection.
+   * Another workaround - "rmmod usbms", "rmmod ohci", proper start
+   * and configuration of USB device and then "insmod ohci"
+   * and "insmod usbms". */
+  grub_millisleep (500);	
+
   /* Link to ohci now that initialisation is successful.  */
   o->next = ohci;
   ohci = o;
@@ -317,13 +403,29 @@
   token |= toggle << 24;
   token |= 1 << 25;
 
+  /* Set "Not accessed" error code */
+  token |= 15 << 28;
+
   buffer = data;
   buffer_end = buffer + size - 1;
 
+  /* Set correct buffer values in TD if zero transfer occurs */
+  if (size)
+    {
+      buffer = (grub_uint32_t) data;
+      buffer_end = buffer + size - 1;
+      td->buffer = grub_cpu_to_le32 (buffer);
+      td->buffer_end = grub_cpu_to_le32 (buffer_end);
+    }
+  else 
+    {
+      td->buffer = 0;
+      td->buffer_end = 0;
+    }
+
+  /* Set the rest of TD */
   td->token = grub_cpu_to_le32 (token);
-  td->buffer = grub_cpu_to_le32 (buffer);
   td->next_td = 0;
-  td->buffer_end = grub_cpu_to_le32 (buffer_end);
 }
 
 static grub_usb_err_t
@@ -342,7 +444,9 @@
   grub_uint32_t status;
   grub_uint32_t control;
   grub_usb_err_t err;
-  int i;
+  int i, j;
+  grub_uint64_t maxtime;
+  int err_timeout = 0;
 
   /* Allocate an Endpoint Descriptor.  */
   ed_chunk = grub_memalign_dma32 (256, sizeof (*ed));
@@ -375,13 +479,25 @@
 					     + (i + 1) * sizeof (td_list[0]));
     }
 
+  /* The last-1 TD token we should change to enable interrupt when TD finishes.
+   * As OHCI interrupts are disabled, it does only setting of WDH bit in
+   * HcInterruptStatus register - and that is what we want to safely detect
+   * normal end of all transactions. */
+  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
+
+  td_list[transfer->transcnt].token = 0;
+  td_list[transfer->transcnt].buffer = 0;
+  td_list[transfer->transcnt].buffer_end = 0;
+  td_list[transfer->transcnt].next_td =
+    (grub_uint32_t) &td_list[transfer->transcnt];
+  
   /* Setup the Endpoint Descriptor.  */
 
   /* Set the device address.  */
   target = transfer->devaddr;
 
-  /* Set the endpoint.  */
-  target |= transfer->endpoint << 7;
+  /* Set the endpoint. It should be masked, we need 4 bits only. */
+  target |= (transfer->endpoint & 15) << 7;
 
   /* Set the device speed.  */
   target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
@@ -400,6 +516,30 @@
 
   grub_dprintf ("ohci", "program OHCI\n");
 
+  /* Disable the Control and Bulk lists.  */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  control &= ~(3 << 4);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+
+  /* Clear BulkListFilled and ControlListFilled.  */
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  status &= ~(3 << 1);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+
+  /* Now we should wait for start of next frame. Because we are not using
+   * interrupt, we reset SF bit and wait when it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+
+  /* This we do for safety's sake - it should be done in previous call
+   * of grub_ohci_transfer and nobody should change it in meantime...
+   * It should be done before start of control or bulk OHCI list. */   
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+
   /* Program the OHCI to actually transfer.  */
   switch (transfer->type)
     {
@@ -407,24 +547,17 @@
       {
 	grub_dprintf ("ohci", "add to bulk list\n");
 
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear BulkListFilled.  */
-	status &= ~(1 << 2);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set BulkList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
 	/* Enable the Bulk list.  */
+	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
 	control |= 1 << 5;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
 
 	/* Set BulkListFilled.  */
+	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	status |= 1 << 2;
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
 
@@ -433,21 +566,9 @@
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-
-	/* Disable the Control and Bulk lists.  */
-	control &= ~(3 << 4);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
-	/* Clear ControlListFilled.  */
-	status &= ~(1 << 1);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	/* Set ControlList Head and Current */
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD+1,
-			      ed_addr);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
 
 	/* Enable the Control list.  */
 	control |= 1 << 4;
@@ -465,36 +586,77 @@
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
 		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
 
+  /* Safety measure to avoid a hang. */
+  maxtime = grub_get_time_ms () + 1000;
+	
   /* Wait until the transfer is completed or STALLs.  */
-  while ((ed->td_head & ~0xf) != (ed->td_tail & ~0xf))
+  do
     {
       grub_cpu_idle ();
 
-      grub_dprintf ("ohci", "head=0x%02x tail=0x%02x\n", ed->td_head, ed->td_tail);
-
-      /* Detected a STALL.  */
-      if (ed->td_head & 1)
+      /* Detected a HALT.  */
+      if (grub_le_to_cpu32 (ed->td_head) & 1)
+        break;
+  
+      if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+        {
+          if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
+	      == td_list_addr + (transfer->transcnt - 1) * sizeof (td_list[0]))
+	    break;
+
+          /* Done Head can be updated on some another place if ED is halted. */          
+          if (grub_le_to_cpu32 (ed->td_head) & 1)
+            break;
+
+          /* If there is not HALT in ED, it is not correct, so debug it, reset
+           * donehead and WDH and continue waiting. */
+          grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+                        o->hcca->donehead);
+          o->hcca->donehead = 0;
+          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          continue;
+        }
+      /* Timeout ? */
+      if (grub_get_time_ms () > maxtime)
+      	{
+	  /* Disable the Control and Bulk lists.  */
+  	  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+	  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4));
+      	  err_timeout = 1;
+      	  break;
+      	}
+
+      if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
 	break;
     }
-
-  grub_dprintf ("ohci", "complete\n");
-
-/*   if (ed->td_head & 1) */
-/*     err = GRUB_USB_ERR_STALL; */
-/*   else if (ed->td */
-
-
-  if (ed->td_head & 1)
-    {
+  while (1);
+
+  if (err_timeout)
+    {
+      err = GRUB_ERR_TIMEOUT;
+      grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+      grub_le_to_cpu32(ed->target),
+      grub_le_to_cpu32(ed->td_head),
+      grub_le_to_cpu32(ed->td_tail),
+      grub_le_to_cpu32(ed->next_ed));
+    }
+  else if (grub_le_to_cpu32 (ed->td_head) & 1)
+    {
+      grub_uint32_t td_err_addr;
       grub_uint8_t errcode;
-      grub_ohci_td_t tderr;
-      grub_uint32_t td_err_addr;
+      grub_ohci_td_t tderr = NULL;
 
-      td_err_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD);
+      td_err_addr = (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+      if (td_err_addr == 0)
+        /* If DONEHEAD==0 it means that correct address is in HCCA.
+         * It should be always now! */
+        td_err_addr = (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
 
       tderr = (grub_ohci_td_t) ((char *) td_list
 				+ (td_err_addr - td_list_addr));
-      errcode = tderr->token >> 28;
+ 
+      errcode = grub_le_to_cpu32 (tderr->token) >> 28;      
+      grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode);
 
       switch (errcode)
 	{
@@ -540,11 +702,17 @@
 	case 8:
 	  /* XXX: Data overrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 9:
 	  /* XXX: Data underrun error.  */
 	  err = GRUB_USB_ERR_DATA;
+	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
+	                1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer));
+	  j = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n", tderr, j);
 	  break;
 
 	case 10:
@@ -582,42 +750,74 @@
 
   /* Clear BulkListFilled and ControlListFilled.  */
   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~((1 << 2) | (1 << 3));
+  status &= ~(3 << 1);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+  
+  /* Set ED to be skipped - for safety */
+  ed->target |= grub_cpu_to_le32 (1 << 14);
+
+  /* Now we should wait for start of next frame.
+   * It is necessary because we will invalidate pointer to ED and it
+   * can be on OHCI active till SOF!
+   * Because we are not using interrupt, we reset SF bit and wait when
+   * it goes to 1. */
+  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+  /* Wait for new SOF */
+  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+  /* Now it should be safe to change CONTROL and BULK lists. */
+  
+  /* Important cleaning. */
+  o->hcca->donehead = 0;
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n",
+                err);
+  
   /* XXX */
-  grub_free (td_list);
-  grub_free (ed);
+  grub_dma_free (td_list_chunk);
+  grub_dma_free (ed_chunk);
 
   return err;
 }
 
+#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1)
+#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0)
+#define GRUB_OHCI_SET_PORT_RESET (1 << 4)
+#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)
+
 static grub_err_t
 grub_ohci_portstatus (grub_usb_controller_t dev,
 		      unsigned int port, unsigned int enable)
 {
    struct grub_ohci *o = (struct grub_ohci *) dev->data;
-   grub_uint32_t status;
-
-   /* Reset the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (1 << 4); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-   grub_millisleep (100);
+
+   grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
+                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
+
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			 GRUB_OHCI_SET_PORT_RESET);
+   grub_millisleep (50); /* For root hub should be nominaly 50ms */
 
    /* End the reset signaling.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (1 << 20); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-   grub_millisleep (10);
-
-   /* Enable the port.  */
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   status |= (enable << 1); /* XXX: Magic.  */
-   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-
-   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
-   grub_dprintf ("ohci", "portstatus=0x%02x\n", status);
+   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			 GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
+   grub_millisleep (10);
+
+   if (enable)
+     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			   GRUB_OHCI_SET_PORT_ENABLE);
+   else
+     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+			   GRUB_OHCI_CLEAR_PORT_ENABLE);
+   grub_millisleep (10);
+
+   grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
+		 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
 
    return GRUB_ERR_NONE;
 }
@@ -654,6 +854,39 @@
   return portinfo & 0xFF;
 }
 
+static grub_err_t
+grub_ohci_fini_hw (int noreturn __attribute__ ((unused)))
+{
+  struct grub_ohci *o;
+
+  for (o = ohci; o; o = o->next)
+    {
+      int i, nports = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) & 0xff;
+      for (i = 0; i < nports; i++)
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + i,
+			      GRUB_OHCI_CLEAR_PORT_ENABLE);
+
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0);
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_ohci_restore_hw (void)
+{
+  struct grub_ohci *o;
+
+  for (o = ohci; o; o = o->next)
+    grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
+
+  return GRUB_ERR_NONE;
+}
 
 \f
 static struct grub_usb_controller_dev usb_controller =
@@ -670,9 +903,12 @@
 {
   grub_ohci_inithw ();
   grub_usb_controller_dev_register (&usb_controller);
+  grub_loader_register_preboot_hook (grub_ohci_fini_hw, grub_ohci_restore_hw,
+				     GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
 }
 
 GRUB_MOD_FINI(ohci)
 {
+  grub_ohci_fini_hw (0);
   grub_usb_controller_dev_unregister (&usb_controller);
 }

=== modified file 'bus/usb/uhci.c'
--- bus/usb/uhci.c	2010-05-05 08:40:48 +0000
+++ bus/usb/uhci.c	2010-05-23 13:08:06 +0000
@@ -174,14 +174,15 @@
     return 1;
 
   u->iobase = base & GRUB_UHCI_IOMASK;
-  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x\n",
-		class, subclass, interf, u->iobase);
 
   /* Reserve a page for the frame list.  */
   u->framelist = grub_memalign (4096, 4096);
   if (! u->framelist)
     goto fail;
 
+  grub_dprintf ("uhci", "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n",
+		class, subclass, interf, u->iobase, u->framelist);
+
   /* The framelist pointer of UHCI is only 32 bits, make sure this
      code works on on 64 bits architectures.  */
 #if GRUB_CPU_SIZEOF_VOID_P == 8
@@ -221,6 +222,9 @@
     }
 #endif
 
+  grub_dprintf ("uhci", "QH=%p, TD=%p\n",
+		u->qh, u->td);
+
   /* Link all Transfer Descriptors in a list of available Transfer
      Descriptors.  */
   for (i = 0; i < 256; i++)
@@ -441,6 +445,8 @@
   if (! qh)
     return grub_errno;
 
+  grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase);
+  
   for (i = 0; i < transfer->transcnt; i++)
     {
       grub_usb_transaction_t tr = &transfer->transactions[i];
@@ -548,7 +554,8 @@
 
  fail:
 
-  grub_dprintf ("uhci", "transaction failed\n");
+  if (err != GRUB_USB_ERR_NONE)
+    grub_dprintf ("uhci", "transaction failed\n");
 
   /* Place the QH back in the free list and deallocate the associated
      TDs.  */
@@ -583,6 +590,8 @@
   unsigned int status;
   grub_uint64_t endtime;
 
+  grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase);
+  
   grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
 
   if (port == 0)
@@ -631,6 +640,8 @@
   int reg;
   unsigned int status;
 
+  grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase);
+  
   if (port == 0)
     reg = GRUB_UHCI_REG_PORTSC1;
   else if (port == 1)

=== modified file 'bus/usb/usb.c'
--- bus/usb/usb.c	2009-11-09 17:43:53 +0000
+++ bus/usb/usb.c	2010-05-23 13:08:06 +0000
@@ -107,7 +107,7 @@
 {
   int i;
 
-  for (i = 0; i < 16; i++)
+  for (i = 0; i < 256; i++)
     dev->toggle[i] = 0;
 
   return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
@@ -163,6 +163,16 @@
   grub_usb_err_t err;
   int i;
 
+  /* First we have to read first 8 bytes only and determine
+   * max. size of packet */
+  dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be removed if it is sure it is zero here */
+  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
+				 0, 8, (char *) &dev->descdev);
+  if (err)
+    return err;
+
+  /* Now we have valid value in dev->descdev.maxsize0,
+   * so we can read whole device descriptor */
   err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
 				 0, sizeof (struct grub_usb_desc_device),
 				 (char *) &dev->descdev);

=== modified file 'bus/usb/usbtrans.c'
--- bus/usb/usbtrans.c	2010-05-22 22:13:37 +0000
+++ bus/usb/usbtrans.c	2010-05-23 13:10:07 +0000
@@ -138,7 +138,7 @@
   /* End with an empty OUT transaction.  */
   transfer->transactions[datablocks + 1].size = 0;
   transfer->transactions[datablocks + 1].data = 0;
-  if (reqtype & 128)
+  if ((reqtype & 128) && datablocks)
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_OUT;
   else
     transfer->transactions[datablocks + 1].pid = GRUB_USB_TRANSFER_TYPE_IN;
@@ -148,6 +148,7 @@
   err = dev->controller.dev->transfer (&dev->controller, transfer);
 
   grub_free (transfer->transactions);
+  
   grub_free (transfer);
   grub_dma_free (data_chunk);
   grub_dma_free (setupdata_chunk);
@@ -207,7 +208,7 @@
   datablocks = ((size + max - 1) / max);
   transfer->transcnt = datablocks;
   transfer->size = size - 1;
-  transfer->endpoint = endpoint;
+  transfer->endpoint = endpoint & 15;
   transfer->devaddr = dev->addr;
   transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
   transfer->max = max;

=== modified file 'commands/usbtest.c'
--- commands/usbtest.c	2010-05-23 12:37:28 +0000
+++ commands/usbtest.c	2010-05-23 13:41:32 +0000
@@ -148,6 +148,8 @@
 
   grub_printf ("%s speed device\n", usb_devspeed[dev->speed]);
 
+  return 0;
+
   for (i = 0; i < descdev->configcnt; i++)
     {
       struct grub_usb_desc_config *config;

=== modified file 'disk/scsi.c'
--- disk/scsi.c	2010-03-05 14:29:28 +0000
+++ disk/scsi.c	2010-05-23 13:08:06 +0000
@@ -25,6 +25,7 @@
 #include <grub/types.h>
 #include <grub/scsi.h>
 #include <grub/scsicmd.h>
+#include <grub/time.h>
 
 \f
 static grub_scsi_dev_t grub_scsi_dev_list;
@@ -50,7 +51,62 @@
 }
 
 \f
-/* Determine the the device is removable and the type of the device
+/* Check result of previous operation.  */
+static grub_err_t
+grub_scsi_request_sense (grub_scsi_t scsi)
+{
+  struct grub_scsi_request_sense rs;
+  struct grub_scsi_request_sense_data rsd;
+  grub_err_t err;
+
+  rs.opcode = grub_scsi_cmd_request_sense;
+  rs.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  rs.reserved1 = 0;
+  rs.reserved2 = 0;
+  rs.alloc_length = 0x12; /* XXX: Hardcoded for now */
+  rs.control = 0;
+  grub_memset (rs.pad, 0, sizeof(rs.pad));
+
+  err = scsi->dev->read (scsi, sizeof (rs), (char *) &rs,
+			 sizeof (rsd), (char *) &rsd);
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+/* Self commenting... */
+static grub_err_t
+grub_scsi_test_unit_ready (grub_scsi_t scsi)
+{
+  struct grub_scsi_test_unit_ready tur;
+  grub_err_t err;
+  grub_err_t err_sense;
+  
+  tur.opcode = grub_scsi_cmd_test_unit_ready;
+  tur.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  tur.reserved1 = 0;
+  tur.reserved2 = 0;
+  tur.reserved3 = 0;
+  tur.control = 0;
+  grub_memset (tur.pad, 0, sizeof(tur.pad));
+
+  err = scsi->dev->read (scsi, sizeof (tur), (char *) &tur,
+			 0, NULL);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+  
+  if (err)
+    return err;
+
+  return GRUB_ERR_NONE;
+}
+
+/* Determine if the device is removable and the type of the device
    SCSI.  */
 static grub_err_t
 grub_scsi_inquiry (grub_scsi_t scsi)
@@ -58,15 +114,26 @@
   struct grub_scsi_inquiry iq;
   struct grub_scsi_inquiry_data iqd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   iq.opcode = grub_scsi_cmd_inquiry;
   iq.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  iq.page = 0;
   iq.reserved = 0;
   iq.alloc_length = 0x24; /* XXX: Hardcoded for now */
-  iq.reserved2 = 0;
+  iq.control = 0;
+  grub_memset (iq.pad, 0, sizeof(iq.pad));
 
   err = scsi->dev->read (scsi, sizeof (iq), (char *) &iq,
 			 sizeof (iqd), (char *) &iqd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -83,13 +150,27 @@
   struct grub_scsi_read_capacity rc;
   struct grub_scsi_read_capacity_data rcd;
   grub_err_t err;
+  grub_err_t err_sense;
 
   rc.opcode = grub_scsi_cmd_read_capacity;
   rc.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
-  grub_memset (rc.reserved, 0, sizeof (rc.reserved));
-
+  rc.logical_block_addr = 0;
+  rc.reserved1 = 0;
+  rc.reserved2 = 0;
+  rc.PMI = 0;
+  rc.control = 0;
+  rc.pad = 0;
+	
   err = scsi->dev->read (scsi, sizeof (rc), (char *) &rc,
 			 sizeof (rcd), (char *) &rcd);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+/* err_sense is ignored for now and Request Sense Data also... */
+
   if (err)
     return err;
 
@@ -107,6 +188,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read10 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -118,7 +201,16 @@
   rd.reserved2 = 0;
   rd.pad = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: read SIZE sectors starting with
@@ -129,6 +221,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_read12 rd;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -139,7 +233,16 @@
   rd.reserved = 0;
   rd.control = 0;
 
-  return scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+  err = scsi->dev->read (scsi, sizeof (rd), (char *) &rd, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 #if 0
@@ -151,6 +254,8 @@
 {
   grub_scsi_t scsi;
   struct grub_scsi_write10 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -162,7 +267,16 @@
   wr.reserved2 = 0;
   wr.pad = 0;
 
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 
 /* Send a SCSI request for DISK: write the data stored in BUF to SIZE
@@ -172,7 +286,9 @@
 		   grub_size_t size, char *buf)
 {
   grub_scsi_t scsi;
-  struct grub_scsi_write10 wr;
+  struct grub_scsi_write12 wr;
+  grub_err_t err;
+  grub_err_t err_sense;
 
   scsi = disk->data;
 
@@ -181,9 +297,18 @@
   wr.lba = grub_cpu_to_be32 (sector);
   wr.size = grub_cpu_to_be32 (size);
   wr.reserved = 0;
-  wr.pad = 0;
-
-  return scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+  wr.control = 0;
+
+  err = scsi->dev->write (scsi, sizeof (wr), (char *) &wr, size * scsi->blocksize, buf);
+
+  /* Each SCSI command should be followed by Request Sense.
+     If not so, many devices STALLs or definitely freezes. */
+  err_sense = grub_scsi_request_sense (scsi);
+  if (err_sense != GRUB_ERR_NONE)
+  	grub_errno = err;
+  /* err_sense is ignored for now and Request Sense Data also... */
+
+  return err;
 }
 #endif
 
@@ -235,6 +360,7 @@
   grub_err_t err;
   int len;
   int lun;
+  grub_uint64_t maxtime;
 
   scsi = grub_malloc (sizeof (*scsi));
   if (! scsi)
@@ -292,6 +418,31 @@
       else
 	disk->has_partitions = 1;
 
+
+      /* According to USB MS tests specification, issue Test Unit Ready
+       * until OK */
+      maxtime = grub_get_time_ms () + 1000;
+      do
+        {
+	  /* Timeout is necessary - for example in case when we have
+	   * universal card reader with more LUNs and we have only
+	   * one card inserted (or none), so only one LUN (or none)
+	   * will be ready - and we want not to hang... */
+	  if (grub_get_time_ms () > maxtime)
+            {
+              err = GRUB_ERR_READ_ERROR;
+              grub_free (scsi);
+              grub_dprintf ("scsi", "LUN is not ready - timeout\n");
+              return err;
+            }
+          err = grub_scsi_test_unit_ready (scsi);
+        }
+      while (err == GRUB_ERR_READ_ERROR);
+      /* Reset grub_errno !
+       * It is set to some error code in loop before... */
+      grub_errno = GRUB_ERR_NONE;
+
+      /* Read capacity of media */
       err = grub_scsi_read_capacity (scsi);
       if (err)
 	{
@@ -302,12 +453,14 @@
 
       /* SCSI blocks can be something else than 512, although GRUB
 	 wants 512 byte blocks.  */
-      disk->total_sectors = ((scsi->size * scsi->blocksize)
-			     << GRUB_DISK_SECTOR_BITS);
+      disk->total_sectors = ((grub_uint64_t)scsi->size
+                             * (grub_uint64_t)scsi->blocksize)
+			    >> GRUB_DISK_SECTOR_BITS;
 
-      grub_dprintf ("scsi", "capacity=%llu, blksize=%d\n",
-		    (unsigned long long) disk->total_sectors,
-		    scsi->blocksize);
+      grub_dprintf ("scsi", "blocks=%u, blocksize=%u\n",
+		    scsi->size, scsi->blocksize);
+      grub_dprintf ("scsi", "Disk total 512 sectors = %llu\n",
+		    disk->total_sectors);
 
       return GRUB_ERR_NONE;
     }

=== modified file 'disk/usbms.c'
--- disk/usbms.c	2010-01-20 08:12:47 +0000
+++ disk/usbms.c	2010-05-23 14:08:52 +0000
@@ -84,7 +84,8 @@
       struct grub_usb_desc_device *descdev = &usbdev->descdev;
       int i;
 
-      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0)
+      if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0
+	  || descdev->configcnt == 0)
 	return 0;
 
       /* XXX: Just check configuration 0 for now.  */
@@ -93,19 +94,31 @@
 	  struct grub_usbms_dev *usbms;
 	  struct grub_usb_desc_if *interf;
 	  int j;
-	  grub_uint8_t luns;
+	  grub_uint8_t luns = 0;
+
+	  grub_dprintf ("usbms", "alive\n");
 
 	  interf = usbdev->config[0].interf[i].descif;
 
 	  /* If this is not a USB Mass Storage device with a supported
 	     protocol, just skip it.  */
+	  grub_dprintf ("usbms", "iterate: interf=%d, class=%d, subclass=%d, protocol=%d\n",
+	                i, interf->class, interf->subclass, interf->protocol);
+
 	  if (interf->class != GRUB_USB_CLASS_MASS_STORAGE
-	      || interf->subclass != GRUB_USBMS_SUBCLASS_BULK
+	      || ( interf->subclass != GRUB_USBMS_SUBCLASS_BULK &&
+	    /* Experimental support of RBC, MMC-2, UFI, SFF-8070i devices */
+	           interf->subclass != GRUB_USBMS_SUBCLASS_RBC &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_MMC2 &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_UFI &&
+	           interf->subclass != GRUB_USBMS_SUBCLASS_SFF8070 )
 	      || interf->protocol != GRUB_USBMS_PROTOCOL_BULK)
 	    {
 	      continue;
 	    }
 
+	  grub_dprintf ("usbms", "alive\n");
+
 	  devcnt++;
 	  usbms = grub_zalloc (sizeof (struct grub_usbms_dev));
 	  if (! usbms)
@@ -114,6 +127,8 @@
 	  usbms->dev = usbdev;
 	  usbms->interface = i;
 
+	  grub_dprintf ("usbms", "alive\n");
+
 	  /* Iterate over all endpoints of this interface, at least a
 	     IN and OUT bulk endpoint are required.  */
 	  for (j = 0; j < interf->endpointcnt; j++)
@@ -125,14 +140,16 @@
 		{
 		  /* Bulk IN endpoint.  */
 		  usbms->in = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->in_maxsz = endp->maxpacket;
 		}
 	      else if (!(endp->endp_addr & 128) && (endp->attrib & 3) == 2)
 		{
 		  /* Bulk OUT endpoint.  */
 		  usbms->out = endp;
-		  grub_usb_clear_halt (usbdev, endp->endp_addr & 128);
+		  /* Clear Halt is not possible yet! */
+		  /* grub_usb_clear_halt (usbdev, endp->endp_addr); */
 		  usbms->out_maxsz = endp->maxpacket;
 		}
 	    }
@@ -143,51 +160,63 @@
 	      return 0;
 	    }
 
+	  grub_dprintf ("usbms", "alive\n");
+
+	  /* XXX: Activate the first configuration.  */
+	  grub_usb_set_configuration (usbdev, 1);
+
 	  /* Query the amount of LUNs.  */
 	  err = grub_usb_control_msg (usbdev, 0xA1, 254,
 				      0, i, 1, (char *) &luns);
+		
 	  if (err)
 	    {
 	      /* In case of a stall, clear the stall.  */
 	      if (err == GRUB_USB_ERR_STALL)
 		{
-		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr & 3);
-		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr & 3);
+		  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+		  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
 		}
-
 	      /* Just set the amount of LUNs to one.  */
 	      grub_errno = GRUB_ERR_NONE;
 	      usbms->luns = 1;
 	    }
 	  else
-	    usbms->luns = luns;
-
-	  /* XXX: Check the magic values, does this really make
-	     sense?  */
-	  grub_usb_control_msg (usbdev, (1 << 6) | 1, 255,
-				0, i, 0, 0);
-
-	  /* XXX: To make Qemu work?  */
-	  if (usbms->luns == 0)
-	    usbms->luns = 1;
+            /* luns = 0 means one LUN with ID 0 present ! */
+            /* We get from device not number of LUNs but highest
+             * LUN number. LUNs are numbered from 0, 
+             * i.e. number of LUNs is luns+1 ! */
+	    usbms->luns = luns + 1;
+
+	  grub_dprintf ("usbms", "alive\n");
 
 	  usbms->next = grub_usbms_dev_list;
 	  grub_usbms_dev_list = usbms;
 
-	  /* XXX: Activate the first configuration.  */
-	  grub_usb_set_configuration (usbdev, 1);
-
+#if 0 /* All this part should be probably deleted.
+     * This make trouble on some devices if they are not in
+     * Phase Error state - and there they should be not in such state...
+     * Bulk only mass storage reset procedure should be used only
+     * on place and in time when it is really necessary. */
+	  /* Reset recovery procedure */
 	  /* Bulk-Only Mass Storage Reset, after the reset commands
 	     will be accepted.  */
 	  grub_usbms_reset (usbdev, i);
+	  grub_usb_clear_halt (usbdev, usbms->in->endp_addr);
+	  grub_usb_clear_halt (usbdev, usbms->out->endp_addr);
+#endif
 
 	  return 0;
 	}
 
+      grub_dprintf ("usbms", "alive\n");
       return 0;
     }
+  grub_dprintf ("usbms", "alive\n");
 
   grub_usb_iterate (usb_iterate);
+  grub_dprintf ("usbms", "alive\n");
+
 }
 
 \f
@@ -225,6 +254,7 @@
   static grub_uint32_t tag = 0;
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
+  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -237,73 +267,89 @@
   cbw.tag = tag++;
   cbw.transfer_length = grub_cpu_to_le32 (size);
   cbw.flags = (!read_write) << GRUB_USBMS_DIRECTION_BIT;
-  cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+  cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place than in SCSI CDB, both should be set correctly. */
   cbw.length = cmdsize;
   grub_memcpy (cbw.cbwcb, cmd, cmdsize);
+  
+  /* Debug print of CBW content. */
+  grub_dprintf ("usb", "CBW: sign=0x%08x tag=0x%08x len=0x%08x\n",
+  	cbw.signature, cbw.tag, cbw.transfer_length);
+  grub_dprintf ("usb", "CBW: flags=0x%02x lun=0x%02x CB_len=0x%02x\n",
+  	cbw.flags, cbw.lun, cbw.length);
+  grub_dprintf ("usb", "CBW: cmd:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	cbw.cbwcb[ 0], cbw.cbwcb[ 1], cbw.cbwcb[ 2], cbw.cbwcb[ 3],
+  	cbw.cbwcb[ 4], cbw.cbwcb[ 5], cbw.cbwcb[ 6], cbw.cbwcb[ 7],
+  	cbw.cbwcb[ 8], cbw.cbwcb[ 9], cbw.cbwcb[10], cbw.cbwcb[11],
+  	cbw.cbwcb[12], cbw.cbwcb[13], cbw.cbwcb[14], cbw.cbwcb[15]);
 
-  /* Write the request.  */
-  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr & 15,
+  /* Write the request.
+   * XXX: Error recovery is maybe still not fully correct. */
+  err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr,
 			     sizeof (cbw), (char *) &cbw);
   if (err)
     {
       if (err == GRUB_USB_ERR_STALL)
 	{
+	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
 	  grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
 	}
       return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
     }
 
-  /* Read/write the data.  */
-  if (read_write == 0)
+  /* Read/write the data, (maybe) according to specification.  */
+  if (size && (read_write == 0))
     {
-      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15, size, buf);
-      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_READ_ERROR,
-			     "can't read from USB Mass Storage device");
-	}
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf);
+      grub_dprintf ("usb", "read: %d %d\n", err, GRUB_USB_ERR_STALL); 
+      if (err) goto CheckCSW;
+      /* Debug print of received data. */
+      grub_dprintf ("usb", "buf:\n");
+      if (size <= 64)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
-  else
+  else if (size)
     {
-      err = grub_usb_bulk_write (dev->dev, dev->in->endp_addr & 15, size, buf);
+      err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      if (err)
-	{
-	  if (err == GRUB_USB_ERR_STALL)
-	    {
-	      grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
-	      goto retry;
-	    }
-	  return grub_error (GRUB_ERR_WRITE_ERROR,
-			     "can't write to USB Mass Storage device");
-	}
+      grub_dprintf ("usb", "buf:\n");
+      /* Debug print of sent data. */
+      if (size <= 256)
+        for (i=0; i<size; i++)
+          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
+      else
+          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
 
-  /* Read the status.  */
-  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr & 15,
-			    sizeof (status), (char *) &status);
+  /* Read the status - (maybe) according to specification.  */
+CheckCSW:
+  err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+		    sizeof (status), (char *) &status);
   if (err)
     {
-      if (err == GRUB_USB_ERR_STALL)
-	{
-	  grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+      err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+			        sizeof (status), (char *) &status);
+      if (err)
+        { /* Bulk-only reset device. */
+          grub_usbms_reset (dev->dev, dev->interface);
+          grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+          grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
 	  goto retry;
-	}
-      return grub_error (GRUB_ERR_READ_ERROR,
-			 "can't read status from USB Mass Storage device");
+        }
     }
 
-  /* XXX: Magic and check this code.  */
+  /* Debug print of CSW content. */
+  grub_dprintf ("usb", "CSW: sign=0x%08x tag=0x%08x resid=0x%08x\n",
+  	status.signature, status.tag, status.residue);
+  grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
+  
+  /* If phase error, do bulk-only reset device. */
   if (status.status == 2)
     {
-      /* XXX: Phase error, reset device.  */
       grub_usbms_reset (dev->dev, dev->interface);
       grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
       grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@@ -384,8 +430,11 @@
 
 GRUB_MOD_INIT(usbms)
 {
+  grub_dprintf ("usbms", "alive\n");
   grub_usbms_finddevs ();
+  grub_dprintf ("usbms", "alive\n");
   grub_scsi_dev_register (&grub_usbms_dev);
+  grub_dprintf ("usbms", "alive\n");
 }
 
 GRUB_MOD_FINI(usbms)

=== modified file 'hello/hello.c'
--- hello/hello.c	2010-01-03 18:50:51 +0000
+++ hello/hello.c	2010-05-25 21:24:37 +0000
@@ -31,7 +31,12 @@
 		int argc __attribute__ ((unused)),
 		char **args __attribute__ ((unused)))
 {
-  grub_printf ("Hello World\n");
+  grub_uint32_t r0;
+  asm volatile (".set mips64");
+  asm volatile ("mfc0 %0, $16, 0" : "=r" (r0));
+  asm volatile (".set mips3");
+
+  grub_printf ("%08x\n", r0);
   return 0;
 }
 

=== modified file 'include/grub/scsicmd.h'
--- include/grub/scsicmd.h	2008-08-27 15:05:00 +0000
+++ include/grub/scsicmd.h	2010-05-23 13:08:06 +0000
@@ -25,14 +25,26 @@
 #define GRUB_SCSI_REMOVABLE_BIT	7
 #define GRUB_SCSI_LUN_SHIFT	5
 
+struct grub_scsi_test_unit_ready
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t reserved3;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
 struct grub_scsi_inquiry
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint16_t reserved;
-  grub_uint16_t alloc_length;
-  grub_uint8_t reserved2;
-  grub_uint8_t pad[5];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 EVPD */
+  grub_uint8_t page; /* page code if EVPD=1 */
+  grub_uint8_t reserved;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_inquiry_data
@@ -47,12 +59,42 @@
   char prodrev[4];
 } __attribute__((packed));
 
+struct grub_scsi_request_sense
+{
+  grub_uint8_t opcode;
+  grub_uint8_t lun; /* 7-5 LUN, 4-0 reserved */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t alloc_length;
+  grub_uint8_t control;
+  grub_uint8_t pad[6]; /* To be ATAPI compatible */
+} __attribute__((packed));
+
+struct grub_scsi_request_sense_data
+{
+  grub_uint8_t error_code; /* 7 Valid, 6-0 Err. code */
+  grub_uint8_t segment_number;
+  grub_uint8_t sense_key; /*7 FileMark, 6 EndOfMedia, 5 ILI, 4-0 sense key */
+  grub_uint32_t information;
+  grub_uint8_t additional_sense_length;
+  grub_uint32_t cmd_specific_info;
+  grub_uint8_t additional_sense_code;
+  grub_uint8_t additional_sense_code_qualifier;
+  grub_uint8_t field_replaceable_unit_code;
+  grub_uint8_t sense_key_specific[3];
+  /* there can be additional sense field */
+} __attribute__((packed));
+
 struct grub_scsi_read_capacity
 {
   grub_uint8_t opcode;
-  grub_uint8_t lun;
-  grub_uint8_t reserved[8];
-  grub_uint8_t pad[2];
+  grub_uint8_t lun; /* 7-5 LUN, 4-1 reserved, 0 reserved */
+  grub_uint32_t logical_block_addr; /* only if PMI=1 */
+  grub_uint8_t reserved1;
+  grub_uint8_t reserved2;
+  grub_uint8_t PMI;
+  grub_uint8_t control;
+  grub_uint16_t pad; /* To be ATAPI compatible */
 } __attribute__((packed));
 
 struct grub_scsi_read_capacity_data
@@ -106,11 +148,13 @@
 typedef enum
   {
     grub_scsi_cmd_inquiry = 0x12,
+    grub_scsi_cmd_test_unit_ready = 0x00,
     grub_scsi_cmd_read_capacity = 0x25,
     grub_scsi_cmd_read10 = 0x28,
     grub_scsi_cmd_write10 = 0x2a,
     grub_scsi_cmd_read12 = 0xa8,
-    grub_scsi_cmd_write12 = 0xaa
+    grub_scsi_cmd_write12 = 0xaa,
+    grub_scsi_cmd_request_sense = 0x03
   } grub_scsi_cmd_t;
 
 typedef enum

=== modified file 'include/grub/usb.h'
--- include/grub/usb.h	2009-11-09 17:43:53 +0000
+++ include/grub/usb.h	2010-05-23 13:08:06 +0000
@@ -156,7 +156,7 @@
   int initialized;
 
   /* Data toggle values (used for bulk transfers only).  */
-  int toggle[16];
+  int toggle[256];
 
   /* Device-specific data.  */
   void *data;
@@ -184,7 +184,12 @@
 
 typedef enum
   {
-    GRUB_USBMS_SUBCLASS_BULK = 0x06
+    GRUB_USBMS_SUBCLASS_BULK = 0x06,
+  	/* Experimental support for non-pure SCSI devices */
+    GRUB_USBMS_SUBCLASS_RBC = 0x01,
+    GRUB_USBMS_SUBCLASS_MMC2 = 0x02,
+    GRUB_USBMS_SUBCLASS_UFI = 0x04,
+    GRUB_USBMS_SUBCLASS_SFF8070 = 0x05
   } grub_usbms_subclass_t;
 
 typedef enum

=== modified file 'include/grub/usbtrans.h'
--- include/grub/usbtrans.h	2010-05-05 08:40:48 +0000
+++ include/grub/usbtrans.h	2010-05-23 13:08:06 +0000
@@ -86,9 +86,9 @@
 
 #define GRUB_USB_REQ_HUB_GET_PORT_STATUS 0x00
 
-#define GRUB_USB_FEATURE_ENDP_HALT	0x01
-#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x02
-#define GRUB_USB_FEATURE_TEST_MODE	0x04
+#define GRUB_USB_FEATURE_ENDP_HALT	0x00
+#define GRUB_USB_FEATURE_DEV_REMOTE_WU	0x01
+#define GRUB_USB_FEATURE_TEST_MODE	0x02
 
 #define GRUB_USB_HUB_STATUS_CONNECTED	(1 << 0)
 #define GRUB_USB_HUB_STATUS_LOWSPEED	(1 << 9)

=== modified file 'loader/multiboot_elfxx.c'
--- loader/multiboot_elfxx.c	2010-04-03 12:29:11 +0000
+++ loader/multiboot_elfxx.c	2010-05-26 20:38:23 +0000
@@ -74,7 +74,11 @@
   if (ehdr->e_phoff + ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH)
     return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
 
-#ifdef MULTIBOOT_LOAD_ELF64
+#if defined (MULTIBOOT_LOAD_ELF64) && defined (__mips)
+  /* We still in 32-bit mode.  */
+  if (ehdr->e_entry < 0xffffffff80000000ULL)
+    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+#else
   /* We still in 32-bit mode.  */
   if (ehdr->e_entry > 0xffffffff)
     return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");

=== modified file 'util/grub-mkimage.c'
--- util/grub-mkimage.c	2010-05-22 14:58:45 +0000
+++ util/grub-mkimage.c	2010-05-26 20:37:05 +0000
@@ -1015,14 +1015,17 @@
       boot_size = grub_util_get_image_size (boot_path);
       boot_img = grub_util_read_image (boot_path);
 
-      rom_size = core_size + boot_size;
+      rom_size = ALIGN_UP (core_size + boot_size, 512 * 1024);
 
       rom_img = xmalloc (rom_size);
       memset (rom_img, 0, rom_size); 
 
       memcpy (rom_img, boot_img, boot_size);
 
-      memcpy (rom_img + boot_size, core_img, core_size);      
+      memcpy (rom_img + boot_size, core_img, core_size);
+
+      memset (rom_img + boot_size + core_size, 0,
+	      rom_size - (boot_size + core_size));
 
       free (core_img);
       core_img = rom_img;


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-30 22:26                             ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-31  4:16                               ` Seth Goldberg
  2010-05-31 12:23                               ` Vladimir 'φ-coder/phcoder' Serbinenko
  1 sibling, 0 replies; 41+ messages in thread
From: Seth Goldberg @ 2010-05-31  4:16 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: TEXT/PLAIN, Size: 4720 bytes --]


  USB devices are notoriously difficult to get right.  During development of 
USB device support (mainly mass-storage devices), we discovered numerous 
devices, usually super-cheap devices, that were not spec compliant or that 
didn't implement this-or-that command, etc.  It's truly amazing that they work 
at all (they clearly only test on Windows, and that's all they care about). 
So prepare yourself for a LOT of working trying to figure this stuff out (or 
at least try to use Linux's implementation as a technical guide to avoid 
rediscovering all of this horribleness.

  --S

Quoting Vladimir 'φ-coder/phcoder' Serbinenko, who wrote the following on...:

> Aleš Nesrsta wrote:
> Hello, Aleš. I've seen that your assignment was completed. I added you
> to grub contributors. Feel free to create a branch in branches/usb.
> Right now I'm busy but next week I should be able to assist. Perhaps
> even this week. Right now I send uncommitted changes sitting in my
> yeeloongfw branch. It's largely your rebased changes + few other things
> like shutting the controller down before booting OS to avoid memory used
> by OS to be clobbered by e.g. HCCA.
> Branch usb should contain pci improvements + your usb changes. If you
> have trouble with bzr or are short on time me (or perhaps someone else)
> can give a hand.
> This branch can go into mainline. Since mainline usb is in deplorable
> state there is no need to pass this changes through experimental at all.
> Merging into mainline doesn't mean that no further developpement should
> be done on usb, just that this part is already a huge improvement.
>
>>
>> Remaining problems:
>>
>> 1.
>> Some devices (at least my BUFFALO USB clip drive flash disk, more
>> precisely "ID 0ea0:2168 Ours Technology, Inc. Transcend JetFlash 2.0 /
>> Astone USB Drive") cannot transfer 4KB data blocks independent on
>> controller OHCI/UHCI - my workaround is not good.
>> But it is probably problem with low priority - device is working finally
>> but it is slower than another device.
>>
>>
> Agreed
>> 2.
>> Some devices (at least my APACER cardreader "ID 058f:6366 Alcor Micro
>> Corp. Multi Flash Reader") are not working on UHCI. Such device does not
>> accept any control message and UHCI returns status 0x450007 - it means
>> STALL during sending SETUP packet.
>> It looks to be the same problem as described by Vladimir: "I have
>> somewhat similar issue with Geode OHCI controller right now: devices and
>> speeds are correctly seen but trying to send a message results in a halt
>> in first TD and error code 5.".
>>
> The problem was that the port was plainly off due to Geode reaction on
> incorrect writes differring from that of other controllers.
>> But I have problem on UHCI, not on OHCI - on computer with OHCI is this
>> device working well (it is normal USB Mass Storage Bulk-Only device with
>> SCSI subclass)! Maybe it depends on "combined controllers" UHCI-EHCI,
>> OHCI-EHCI (?) - device is working on computer with OHCI only computer
>> and it is not working on computer with UHCI-EHCI controller. But any
>> other device is working well on both computers... I don't understand, I
>> currently have no idea what can be wrong. Does anybody know...?
>>
>>
> Does this device work under OS on this computer? Some ports are plainly
> underpowered.
>> 3.
>> There is not working USB hub support, GRUB does not see device connected
>> via USB hub - does anybody know some details or have some specification
>> of USB Hub class ? I cannot find it on USB site (maybe I have not
>> sufficient patience...).
>>
>>
>> I will probably focus in OHCI speed-up now, i.e. I try to do some other
>> handling of ED to prevent changes in OHCI registers which are slowing
>> down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
>> this reason).
>>
> How useful would the interrupts be for this matter? If the answer is
> "very useful" I can implement them. Also we need some restructuring of
> code of both ohci and grub in general to decrease the wait time. I think
> specifically all the waits in init sequence. If system has 2 OHCI
> controllers currently you need to wait twice as long as with 1
> controllers while it's possible to init them in parallel and not wait
> longer than in the case of one controller.
>>
>> Best regards
>> Ales
>>
>>
>> ------------------------------------------------------------------------
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>
> -- 
> Regards
> Vladimir 'φ-coder/phcoder' Serbinenko
>
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-30 22:26                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-31  4:16                               ` Seth Goldberg
@ 2010-05-31 12:23                               ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-05-31 21:54                                 ` Aleš Nesrsta
  1 sibling, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-31 12:23 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 4352 bytes --]

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> Aleš Nesrsta wrote:
> Hello, Aleš. I've seen that your assignment was completed. I added you
> to grub contributors. Feel free to create a branch in branches/usb.
> Right now I'm busy but next week I should be able to assist. Perhaps
> even this week. Right now I send uncommitted changes sitting in my
> yeeloongfw branch. It's largely your rebased changes + few other things
> like shutting the controller down before booting OS to avoid memory used
> by OS to be clobbered by e.g. HCCA.
> Branch usb should contain pci improvements + your usb changes. If you
> have trouble with bzr or are short on time me (or perhaps someone else)
> can give a hand.
> This branch can go into mainline. Since mainline usb is in deplorable
> state there is no need to pass this changes through experimental at all.
> Merging into mainline doesn't mean that no further developpement should
> be done on usb, just that this part is already a huge improvement.
>
>   
I've created a branch "usb" where I put all the yeeloong work and
previous version of your patch. Could you merge your latest patch into
it and test the whole on your systems?
>> Remaining problems:
>>
>> 1.
>> Some devices (at least my BUFFALO USB clip drive flash disk, more
>> precisely "ID 0ea0:2168 Ours Technology, Inc. Transcend JetFlash 2.0 /
>> Astone USB Drive") cannot transfer 4KB data blocks independent on
>> controller OHCI/UHCI - my workaround is not good.
>> But it is probably problem with low priority - device is working finally
>> but it is slower than another device.
>>
>>   
>>     
> Agreed
>   
>> 2.
>> Some devices (at least my APACER cardreader "ID 058f:6366 Alcor Micro
>> Corp. Multi Flash Reader") are not working on UHCI. Such device does not
>> accept any control message and UHCI returns status 0x450007 - it means
>> STALL during sending SETUP packet.
>> It looks to be the same problem as described by Vladimir: "I have
>> somewhat similar issue with Geode OHCI controller right now: devices and
>> speeds are correctly seen but trying to send a message results in a halt
>> in first TD and error code 5.".
>>   
>>     
> The problem was that the port was plainly off due to Geode reaction on
> incorrect writes differring from that of other controllers.
>   
>> But I have problem on UHCI, not on OHCI - on computer with OHCI is this
>> device working well (it is normal USB Mass Storage Bulk-Only device with
>> SCSI subclass)! Maybe it depends on "combined controllers" UHCI-EHCI,
>> OHCI-EHCI (?) - device is working on computer with OHCI only computer
>> and it is not working on computer with UHCI-EHCI controller. But any
>> other device is working well on both computers... I don't understand, I
>> currently have no idea what can be wrong. Does anybody know...?
>>
>>   
>>     
> Does this device work under OS on this computer? Some ports are plainly
> underpowered.
>   
>> 3.
>> There is not working USB hub support, GRUB does not see device connected
>> via USB hub - does anybody know some details or have some specification
>> of USB Hub class ? I cannot find it on USB site (maybe I have not
>> sufficient patience...).
>>
>>
>> I will probably focus in OHCI speed-up now, i.e. I try to do some other
>> handling of ED to prevent changes in OHCI registers which are slowing
>> down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
>> this reason).
>>   
>>     
> How useful would the interrupts be for this matter? If the answer is
> "very useful" I can implement them. Also we need some restructuring of
> code of both ohci and grub in general to decrease the wait time. I think
> specifically all the waits in init sequence. If system has 2 OHCI
> controllers currently you need to wait twice as long as with 1
> controllers while it's possible to init them in parallel and not wait
> longer than in the case of one controller.
>   
>> Best regards
>> Ales
>>
>>   
>> ------------------------------------------------------------------------
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> http://lists.gnu.org/mailman/listinfo/grub-devel
>>     
>
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-31 12:23                               ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-05-31 21:54                                 ` Aleš Nesrsta
  2010-06-01  0:18                                   ` [RFT] " Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-05-31 21:54 UTC (permalink / raw)
  To: The development of GNU GRUB

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> > Hello, Aleš. I've seen that your assignment was completed. I added you
> > to grub contributors. Feel free to create a branch in branches/usb.
> > Right now I'm busy but next week I should be able to assist. Perhaps
> > even this week. Right now I send uncommitted changes sitting in my
> > yeeloongfw branch. It's largely your rebased changes + few other things
> > like shutting the controller down before booting OS to avoid memory used
> > by OS to be clobbered by e.g. HCCA.

Yes, thanks, I was also sometimes thinking what happens when ohci module
is unloaded or OS booting started, but I never had time to check it, I
have little bit another priorities...

> > Branch usb should contain pci improvements + your usb changes. If you
> > have trouble with bzr or are short on time me (or perhaps someone else)
> > can give a hand.
> > This branch can go into mainline. Since mainline usb is in deplorable
> > state there is no need to pass this changes through experimental at all.
> > Merging into mainline doesn't mean that no further developpement should
> > be done on usb, just that this part is already a huge improvement.
> >
> >   
> I've created a branch "usb" where I put all the yeeloong work and
> previous version of your patch. Could you merge your latest patch into
> it and test the whole on your systems?

Ufff, I will try to do it but I am also busy probably whole this week
and maybe I will need a help with bzr, I will see... So don't expect it
soon.

> >> 2.
> >> Some devices (at least my APACER cardreader "ID 058f:6366 Alcor Micro
> >> Corp. Multi Flash Reader") are not working on UHCI. Such device does not
> >> accept any control message and UHCI returns status 0x450007 - it means
> >> STALL during sending SETUP packet.
> >> It looks to be the same problem as described by Vladimir: "I have
> >> somewhat similar issue with Geode OHCI controller right now: devices and
> >> speeds are correctly seen but trying to send a message results in a halt
> >> in first TD and error code 5.".
> >>   
> >>     
> > The problem was that the port was plainly off due to Geode reaction on
> > incorrect writes differring from that of other controllers.
> >   
> >> But I have problem on UHCI, not on OHCI - on computer with OHCI is this
> >> device working well (it is normal USB Mass Storage Bulk-Only device with
> >> SCSI subclass)! Maybe it depends on "combined controllers" UHCI-EHCI,
> >> OHCI-EHCI (?) - device is working on computer with OHCI only computer
> >> and it is not working on computer with UHCI-EHCI controller. But any
> >> other device is working well on both computers... I don't understand, I
> >> currently have no idea what can be wrong. Does anybody know...?
> >>
> >>   
> >>     
> > Does this device work under OS on this computer? Some ports are plainly
> > underpowered.

This device works fine under Linux and Windows in the same UHCI port.
But the underpower can be good idea - OSes are using EHCI, not UHCI and
there could be some difference in power handling. On UHCI I cannot find
any power handling...
And You remember to me that we are not handling overcurrent on OHCI -
but I have no idea what to do in this case.

> >   
> >> 3.
> >> There is not working USB hub support, GRUB does not see device connected
> >> via USB hub - does anybody know some details or have some specification
> >> of USB Hub class ? I cannot find it on USB site (maybe I have not
> >> sufficient patience...).
> >>
> >>
> >> I will probably focus in OHCI speed-up now, i.e. I try to do some other
> >> handling of ED to prevent changes in OHCI registers which are slowing
> >> down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
> >> this reason).
> >>   
> >>     
> > How useful would the interrupts be for this matter? If the answer is
> > "very useful" I can implement them. Also we need some restructuring of
> > code of both ohci and grub in general to decrease the wait time. I think
> > specifically all the waits in init sequence. If system has 2 OHCI
> > controllers currently you need to wait twice as long as with 1
> > controllers while it's possible to init them in parallel and not wait
> > longer than in the case of one controller.

I think interrupt is not important in this case, don't hurry. Why:
A.
Wait in initialization phase is not so critical from my point of view -
it happens only once when ohci module is loaded - for example compare it
with the time consumed by some BIOS starts...
But nothing against such improving, it can be done when interrupts will
be available.

B.
The main problem is changing of Control or Bulk Head registers on begin
and end of each transfer because for each transfer we have another
allocated memory for ED and after transfer we de-allocate it. It was one
of thing which little bit surprised me when I first read OHCI
specification and then I saw GRUB source code - it is different than
ED/TD handling suggested in OHCI specification.
Registry change and ED de-allocation have to be done on really disabled
and inactive ED - and such situation is only after SOF (when related
list is disabled before, of course).
Such waiting can be avoided if EDs memory will not change - we can
allocate necessary EDs only once when device is detected and
enumerated / configured and do all necessary work only with TDs - in
this case, if we will not change ED addresses, it is not necessary to do
changes in OHCI registers on each transfer (with exception of "list
filled" bit setting but it can be done asynchronously without waiting
for SOF in this case) and all should be more faster, I hope as on UHCI.
Also TDs have to be handled in little bit different way in this case
because each ED must point to one valid memory address of TD structure
even if there is no transfer and queue is empty - and this empty TD have
to be used as "starting" (first) TD of next transfer - in fact we should
do it in the similar way as it is described in OHCI specification in HCD
sample.

Seth Goldberg wrote:
> USB devices are notoriously difficult to get right.  During development of 
> USB device support (mainly mass-storage devices), we discovered numerous 
> devices, usually super-cheap devices, that were not spec compliant or that 
> didn't implement this-or-that command, etc.  It's truly amazing that they work 
> at all (they clearly only test on Windows, and that's all they care about). 
> So prepare yourself for a LOT of working trying to figure this stuff out (or 
> at least try to use Linux's implementation as a technical guide to avoid 
> rediscovering all of this horribleness.

Yes, I agree, it is never ending story... I am sometimes looking into
Linux (for example, issuing the Request Sense after each SCSI command is
idea taken from Linux source). Maybe I need to look there more often...
But Linux has little bit different "working environment" for USB device
driver than GRUB2.
Sometimes I am also looking into some USB development forums /
development mail lists - but there is lot of unsorted information and it
is not easy to find the right case and related solution there... - it is
often wasting of time.

But, from my point of view, current status is not so bad - I have 10
fairly different USB mass storage devices and all devices are working
well on OHCI and 9 of them are working well on UHCI.
GRUB2, when it is installed on HD or booting CD, is now able to boot
Linux from USB flash on my older computer without BIOS USB booting
support. That is what I needed some months ago, i.e. I am satisfied...

After all, GRUB2 is bootloader only, so probably nobody can expect full
USB functionality as in Linux/Windows... :-)

Best regards
Ales




^ permalink raw reply	[flat|nested] 41+ messages in thread

* [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-05-31 21:54                                 ` Aleš Nesrsta
@ 2010-06-01  0:18                                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-06-01 21:14                                     ` Aleš Nesrsta
  0 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-06-01  0:18 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 5863 bytes --]

Aleš Nesrsta wrote:
> Vladimir 'φ-coder/phcoder' Serbinenko wrote:
>   
>>> Hello, Aleš. I've seen that your assignment was completed. I added you
>>> to grub contributors. Feel free to create a branch in branches/usb.
>>> Right now I'm busy but next week I should be able to assist. Perhaps
>>> even this week. Right now I send uncommitted changes sitting in my
>>> yeeloongfw branch. It's largely your rebased changes + few other things
>>> like shutting the controller down before booting OS to avoid memory used
>>> by OS to be clobbered by e.g. HCCA.
>>>       
>
> Yes, thanks, I was also sometimes thinking what happens when ohci module
> is unloaded or OS booting started, but I never had time to check it, I
> have little bit another priorities...
>
>   
I discovered it the hard way.
>>> Branch usb should contain pci improvements + your usb changes. If you
>>> have trouble with bzr or are short on time me (or perhaps someone else)
>>> can give a hand.
>>> This branch can go into mainline. Since mainline usb is in deplorable
>>> state there is no need to pass this changes through experimental at all.
>>> Merging into mainline doesn't mean that no further developpement should
>>> be done on usb, just that this part is already a huge improvement.
>>>
>>>   
>>>       
>> I've created a branch "usb" where I put all the yeeloong work and
>> previous version of your patch. Could you merge your latest patch into
>> it and test the whole on your systems?
>>     
>
> Ufff, I will try to do it but I am also busy probably whole this week
> and maybe I will need a help with bzr, I will see... So don't expect it
> soon.
>   
I've merged your latest patch into usb branch too, fixing some problems
it would have on yeeloong. Compile tested only.
Can someone give it a good test? If it works on both yeeloong and pc,
I'll merge it into mainline ASAP.
>>>   
>>>       
>>>> 3.
>>>> There is not working USB hub support, GRUB does not see device connected
>>>> via USB hub - does anybody know some details or have some specification
>>>> of USB Hub class ? I cannot find it on USB site (maybe I have not
>>>> sufficient patience...).
>>>>
>>>>
>>>> I will probably focus in OHCI speed-up now, i.e. I try to do some other
>>>> handling of ED to prevent changes in OHCI registers which are slowing
>>>> down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
>>>> this reason).
>>>>   
>>>>     
>>>>         
>>> How useful would the interrupts be for this matter? If the answer is
>>> "very useful" I can implement them. Also we need some restructuring of
>>> code of both ohci and grub in general to decrease the wait time. I think
>>> specifically all the waits in init sequence. If system has 2 OHCI
>>> controllers currently you need to wait twice as long as with 1
>>> controllers while it's possible to init them in parallel and not wait
>>> longer than in the case of one controller.
>>>       
>
> I think interrupt is not important in this case, don't hurry. Why:
> A.
> Wait in initialization phase is not so critical from my point of view -
> it happens only once when ohci module is loaded - for example compare it
> with the time consumed by some BIOS starts...
> But nothing against such improving, it can be done when interrupts will
> be available.
>
>   
In the case of yeeloong this results in twice bigger time from power on
to console. On yeeloong we don't have to wait through BIOS and booting
time is an important goal. This slowdown is annoying because it happens
even if user boots from internal disk
> B.
> The main problem is changing of Control or Bulk Head registers on begin
> and end of each transfer because for each transfer we have another
> allocated memory for ED and after transfer we de-allocate it. It was one
> of thing which little bit surprised me when I first read OHCI
> specification and then I saw GRUB source code - it is different than
> ED/TD handling suggested in OHCI specification.
> Registry change and ED de-allocation have to be done on really disabled
> and inactive ED - and such situation is only after SOF (when related
> list is disabled before, of course).
> Such waiting can be avoided if EDs memory will not change - we can
> allocate necessary EDs only once when device is detected and
> enumerated / configured and do all necessary work only with TDs - in
> this case, if we will not change ED addresses, it is not necessary to do
> changes in OHCI registers on each transfer (with exception of "list
> filled" bit setting but it can be done asynchronously without waiting
> for SOF in this case) and all should be more faster, I hope as on UHCI.
> Also TDs have to be handled in little bit different way in this case
> because each ED must point to one valid memory address of TD structure
> even if there is no transfer and queue is empty - and this empty TD have
> to be used as "starting" (first) TD of next transfer - in fact we should
> do it in the similar way as it is described in OHCI specification in HCD
> sample.
>
>   
For this you can add a new field in "o".
> After all, GRUB2 is bootloader only, so probably nobody can expect full
> USB functionality as in Linux/Windows... :-)
>
>   
You don't know what our users sometimes ask us. In any case even what we
have now if we forget the lack of EHCI support is better than most
firmwares available including pmon. I was able to boot from SD card on
yeeloong, something I wasn't able to do before.
Anyway crap devices are low priority
> Best regards
> Ales
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-01  0:18                                   ` [RFT] " Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-06-01 21:14                                     ` Aleš Nesrsta
  2010-06-02  0:27                                       ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-01 21:14 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi Vladimir,

even I am too busy to do any development or "research", I tried at least
to shortly test the usb branch code, as you wrote here:

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> I've merged your latest patch into usb branch too, fixing some
> problems
> it would have on yeeloong. Compile tested only.
> Can someone give it a good test? If it works on both yeeloong and pc,
> I'll merge it into mainline ASAP.

I tested 9 devices which all were working on both HCs before.
Result of my testing:
UHCI - 7 devices from 9 are working
OHCI - 7 devices from 9 are working, some of them were very slow,
probably lot of timeouts happened

So some mistakes are somewhere...

On both HCs was not working device which has max. packet length for
control endpoint lower than 64. As I remember that this patch was
relative simple, I shortly looked into usb.c and usbtrans.c and there is
mistake:

--- usbtrans.c
+++ usbtrans_corrected.c	
@@ -76,7 +76,7 @@
   setupdata_addr = grub_dma_get_phys (setupdata_chunk);
 
   /* Determine the maximum packet size.  */
-  if (dev->initialized && dev->descdev.maxsize0)
+  if (dev->descdev.maxsize0)
     max = dev->descdev.maxsize0;
   else
     max = 64;

Why:
dev->initialized is set to TRUE too late (after address setting
transaction) so we have to ignore it in control transfers - we must use
value from dev->descdev.maxsize0 immediately after successful reading of
first 8 bytes of device descriptor (where is this value included) - as
is (probably not clearly, sorry) described by me in this part of usb.c
code:

 /* First we have to read first 8 bytes only and determine
   * max. size of packet */
  dev->descdev.maxsize0 = 0; /* invalidating, for safety only, can be
removed if it is sure it is zero here */
  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
				 0, 8, (char *) &dev->descdev);
  if (err)
    return err;

  /* Now we have valid value in dev->descdev.maxsize0,
   * so we can read whole device descriptor */
  err = grub_usb_get_descriptor (dev, GRUB_USB_DESCRIPTOR_DEVICE,
				 0, sizeof (struct grub_usb_desc_device),
				 (char *) &dev->descdev);


There is probably some new problem in OHCI because some devices are now
very slow on this controller. Unfortunately I currently have no time to
discover why - I am expecting some mistake probably in "toggling" and
related code in ohci.c.

On UHCI does not work my mobile phone. But I am not 100% sure if it
worked before on UHCI and I have no time to try it now. I am 100% sure
it worked on OHCI - so maybe it is similar problem as with my card
reader.

And some old (but probably not critical) problem remains in UHCI - there
is still not properly working sequence:
rmmod usbms, rmmod uhci, <removing of old device and connecting of new
device>, insmod uhci, insmod usbms
- it works on OHCI but on UHCI it does not work - for first look it is
working (for example commands "ls" and "ls (usb0,1)" are working if
these commands are used as first commands after device change) but for
second look does not (for example command "ls (usb0,1)/<some directory>"
reports USB mass storage stalled).

Best regards
Ales



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-01 21:14                                     ` Aleš Nesrsta
@ 2010-06-02  0:27                                       ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-06-02  3:02                                         ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-06-02  0:27 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 2616 bytes --]

On 06/01/2010 11:14 PM, Aleš Nesrsta wrote:
> On both HCs was not working device which has max. packet length for
> control endpoint lower than 64. As I remember that this patch was
> relative simple, I shortly looked into usb.c and usbtrans.c and there is
> mistake:
>
> --- usbtrans.c
> +++ usbtrans_corrected.c	
> @@ -76,7 +76,7 @@
>    setupdata_addr = grub_dma_get_phys (setupdata_chunk);
>  
>    /* Determine the maximum packet size.  */
> -  if (dev->initialized && dev->descdev.maxsize0)
> +  if (dev->descdev.maxsize0)
>      max = dev->descdev.maxsize0;
>    else
>      max = 64;
>
>   
Thanks, applied.
> Why:
> dev->initialized is set to TRUE too late (after address setting
> transaction) so we have to ignore it in control transfers - we must use
> value from dev->descdev.maxsize0 immediately after successful reading of
> first 8 bytes of device descriptor (where is this value included) - as
> is (probably not clearly, sorry) described by me in this part of usb.c
> code:
>
>   
Actualy I was confused because I had a similar change in the same place
to avoid division by zero.
> There is probably some new problem in OHCI because some devices are now
> very slow on this controller. Unfortunately I currently have no time to
> discover why - I am expecting some mistake probably in "toggling" and
> related code in ohci.c.
>   
I've managed to recreate it with one of my USB sticks. Investigating.
> On UHCI does not work my mobile phone. But I am not 100% sure if it
> worked before on UHCI and I have no time to try it now. I am 100% sure
> it worked on OHCI - so maybe it is similar problem as with my card
> reader.
>
> And some old (but probably not critical) problem remains in UHCI - there
> is still not properly working sequence:
> rmmod usbms, rmmod uhci, <removing of old device and connecting of new
> device>, insmod uhci, insmod usbms
>   
I haven't retried UHCI on my laptop but I think it will fail because
BIOS has a bug in EHCI handover.
> - it works on OHCI but on UHCI it does not work - for first look it is
> working (for example commands "ls" and "ls (usb0,1)" are working if
> these commands are used as first commands after device change) but for
> second look does not (for example command "ls (usb0,1)/<some directory>"
> reports USB mass storage stalled).
>
> Best regards
> Ales
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-02  0:27                                       ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-06-02  3:02                                         ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-06-02 19:39                                           ` Aleš Nesrsta
  0 siblings, 1 reply; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-06-02  3:02 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 555 bytes --]

On 06/02/2010 02:27 AM, Vladimir 'φ-coder/phcoder' Serbinenko wrote:
>> very slow on this controller. Unfortunately I currently have no time to
>> discover why - I am expecting some mistake probably in "toggling" and
>> related code in ohci.c.
>>   
>>     
> >There is probably some new problem in OHCI because some devices are now
> I've managed to recreate it with one of my USB sticks. Investigating.
>   
There were few special cases. (Hopefully) fixed and ccomitted into "usb".

-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-02  3:02                                         ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-06-02 19:39                                           ` Aleš Nesrsta
  2010-06-02 20:14                                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-02 19:39 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi Vladimir,


Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> There were few special cases. (Hopefully) fixed and ccomitted into "usb".

You are successful, great work, all devices are working (with some known
exceptions on UHCI - see below in point 2.).

But better will be if somebody else also tests more devices on both
controllers.

Some additional informations:

1.
I found multi-LUN relatedproblem: I have one multi-LUN device - card
reader - and I tried plug into it more than one card. In this case "ls"
command reports bad number of filesystems (and also bad type of listed
partition filesystem) on another than first LUN.

It looks like that (not true copy of console):
(usb0a) (usb0,msdos1) (usb0b,msdos1)
It is bad because LUN usb0b has four partitions with ext2 filesystem.

When I try for example "ls (usb0b,4)/", grub returns for the first try
"error: unknown filesystem.", for second try grub does normal file list.
(the same for another partitions, i.e. (usb0b,1), (usb0b,2), (usb0b,3))

The same configuration works fine under Linux and Windows.

Oh, I find probably the mistake right now - there is missing my older
small patch in usbms.c - there is fast "hand made" "patch", I have no
more time, sorry:
usbms.c, line 240:
-   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
+   cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place
than in SCSI CDB, both should be set correctly. */


2.
I found some other devices to test, now I have 13 USB MS bulk-only
devices, each is from another manufacturer and there are approx. five
different kinds of devices - flash disk, hard disk, card reader, camera,
mobile phone.
What is known but maybe interesting, three devices are not working at
all on UHCI - but are working on OHCI (and under Linux / Windows they
are also working) - as we discussed previously, it can be some problem
with power or EHCI influence because it looks like device is not
properly powered.
UHCI-problematic devices  are: Transcend SDHC card reader and two flash
disks - EMTEC "lollipop" and PRETEC Bullet-Proof.
One curiosity - cheap EMTEC "lollipop" USB flash disk has USB ID
0000:0000... (but it works on OHCI).


3.
In Linux USB mass storage source is interesting link about problems with
USB mass storage devices:
http://www.one-eyed-alien.net/~mdharm/linux-usb (and inside
http://www2.one-eyed-alien.net/~mdharm/linux-usb/target_offenses.txt )

In Linux source You can find also one interesting information - from
some Intel chipset version there are new bits in UHCI hub port registers
related to overcurrent (bits 10 & 11). But we currently don't care about
overcurrent at all.


4.
One really stupid question related to your older e-mail:

What does mean "For this you can add a new field in "o"." ?

It is something related to Bazaar branch ? Or to something in source
code configuration etc. ?
(I am totally unexperienced with bazaar and similar tools, the same with
autogen and similar..., sorry.)


That's all for now, there will be probably silent from me for some
days...

Best regards
Ales





^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-02 19:39                                           ` Aleš Nesrsta
@ 2010-06-02 20:14                                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-06-03 11:47                                               ` Aleš Nesrsta
                                                                 ` (2 more replies)
  0 siblings, 3 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-06-02 20:14 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 4769 bytes --]

Hello, Aleš. Today I merged usb branch into mainline. Now I've started
some work towards hotplugging and module autoloading. Currently it just
checks portsstatus to see newly connected devices. Touble is that if you
disconnect a device and plug a new one at its place this routine won't
notice anything because ports are polled only when enumerating
usbdevices. We need a more reliable way to detect new devices. Perhaps
we should try to assign addresses to any device without address and see
if it responds to configuration request. If noone responds assume that
no devices were newly connected.
On Yeeloong 'usb' command lists hotplugged devices correctly but bulk
transfer fails with donehead=0.
> Vladimir 'φ-coder/phcoder' Serbinenko wrote:
>   
>> There were few special cases. (Hopefully) fixed and ccomitted into "usb".
>>     
> You are successful, great work, all devices are working (with some known
> exceptions on UHCI - see below in point 2.).
>
>   
It's mostly your work ;).
> But better will be if somebody else also tests more devices on both
> controllers.
>
>   
Now when it's merge to mainline and will be propagated, it should
recevie some light.
> Some additional informations:
>
> 1.
> I found multi-LUN relatedproblem: I have one multi-LUN device - card
> reader - and I tried plug into it more than one card. In this case "ls"
> command reports bad number of filesystems (and also bad type of listed
> partition filesystem) on another than first LUN.
>
> It looks like that (not true copy of console):
> (usb0a) (usb0,msdos1) (usb0b,msdos1)
> It is bad because LUN usb0b has four partitions with ext2 filesystem.
>
> When I try for example "ls (usb0b,4)/", grub returns for the first try
> "error: unknown filesystem.", for second try grub does normal file list.
> (the same for another partitions, i.e. (usb0b,1), (usb0b,2), (usb0b,3))
>
>   
Perhaps there are strill problems with toggle bit. Problem is that if it
gets out of sync it can only hard get in-sync again. Hence strange
problems. But it would appear on single-LUN devices too.
> The same configuration works fine under Linux and Windows.
>
> Oh, I find probably the mistake right now - there is missing my older
> small patch in usbms.c - there is fast "hand made" "patch", I have no
> more time, sorry:
> usbms.c, line 240:
> -   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
> +   cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place
> than in SCSI CDB, both should be set correctly. */
>
>   
Perhaps (scsi->lun << GRUB_SCSI_LUN_SHIFT) | scsi->lun ?
> 2.
> I found some other devices to test, now I have 13 USB MS bulk-only
> devices, each is from another manufacturer and there are approx. five
> different kinds of devices - flash disk, hard disk, card reader, camera,
> mobile phone.
> What is known but maybe interesting, three devices are not working at
> all on UHCI - but are working on OHCI (and under Linux / Windows they
> are also working) - as we discussed previously, it can be some problem
> with power or EHCI influence because it looks like device is not
> properly powered.
> UHCI-problematic devices  are: Transcend SDHC card reader 
I have a cardreader and it may be Transcend.
> and two flash
> disks - EMTEC "lollipop" and PRETEC Bullet-Proof.
> One curiosity - cheap EMTEC "lollipop" USB flash disk has USB ID
> 0000:0000... (but it works on OHCI).
>
>
> 3.
> In Linux USB mass storage source is interesting link about problems with
> USB mass storage devices:
> http://www.one-eyed-alien.net/~mdharm/linux-usb (and inside
> http://www2.one-eyed-alien.net/~mdharm/linux-usb/target_offenses.txt )
>
> In Linux source You can find also one interesting information - from
> some Intel chipset version there are new bits in UHCI hub port registers
> related to overcurrent (bits 10 & 11). But we currently don't care about
> overcurrent at all.
>
>
> 4.
> One really stupid question related to your older e-mail:
>
> What does mean "For this you can add a new field in "o"." ?
>
> It is something related to Bazaar branch ? Or to something in source
> code configuration etc. ?
> (I am totally unexperienced with bazaar and similar tools, the same with
> autogen and similar..., sorry.)
>
>   
I meant this:
  struct grub_ohci *o = (struct grub_ohci *) dev->data;

> That's all for now, there will be probably silent from me for some
> days...
>   
That's of course no problem.
> Best regards
> Ales
>
>
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-02 20:14                                             ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-06-03 11:47                                               ` Aleš Nesrsta
  2010-06-12 17:05                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices - faster OHCI Aleš Nesrsta
  2010-06-13 17:47                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices Aleš Nesrsta
  2 siblings, 0 replies; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-03 11:47 UTC (permalink / raw)
  To: The development of GNU GRUB

Hello Vladimir,

only shortly:

> > Oh, I find probably the mistake right now - there is missing my older
> > small patch in usbms.c - there is fast "hand made" "patch", I have no
> > more time, sorry:
> > usbms.c, line 240:
> > -   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
> > +   cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place
> > than in SCSI CDB, both should be set correctly. */
> >
> >   
> Perhaps (scsi->lun << GRUB_SCSI_LUN_SHIFT) | scsi->lun ?

I think no, simply in this way:

cbw.lun = scsi->lun;

Look in http://www.usb.org/developers/devclass_docs/usbmassbulk_10.pdf
("Universal Serial Bus Mass Storage Class Bulk-Only Transport, Revision
1.0, September 31, 1999"), chapter 5.1 Command Block Wrapper (CBW).
Field bCBWLUN is in 13-th byte in low four bytes, i.e. it should be
directly GRUB cbw.lun.

This small patch was included in my some previous patch (I think
usb_patch_100523_0) and it worked. But if somebody have multi-LUN
device, please test it (usually some "X in one" card readers are
multi-LUN, I tested it on "Trust CR-1200 16-in-1" with SD and CF cards
inserted in).

Best regards
Ales



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices - faster OHCI
  2010-06-02 20:14                                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-06-03 11:47                                               ` Aleš Nesrsta
@ 2010-06-12 17:05                                               ` Aleš Nesrsta
  2010-06-12 18:59                                                 ` Aleš Nesrsta
  2010-06-13 17:47                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices Aleš Nesrsta
  2 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-12 17:05 UTC (permalink / raw)
  To: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 7549 bytes --]

Hi Vladimir,

in attachment you find patch which speed-up OHCI to (approx.) the same
speed as UHCI.
It is huge change of ohci.c file, so I attach also whole ohci.c file.
Changes in usbms and usbtrans are not important and can be omitted, they
are related only to debugging, as far as I remember...

Patch is done according current usb branch rev. 2426.
I did not commit anything into bazaar repository yet, at first, I have
some trouble with SSH, I cannot access repository via SFTP, and at
second, I am still not so familiar with bazaar...


Some maybe important things:
1.
It may not work on 64-bits computers.
Also it should be tested on Yeeloong and possibly also on computers with
Big Endian memory organization.

2.
I am missing functions which make physical address from virtual and vice
versa, there are only functions which are doing chunk->phys and
chunk->virt. I made some workaround macros in ohci.c to solve my needs
but it is probably not too nice and systematic.

3.
I met some peculiar problems in this faster version and I found that the
problem is related probably to PCI posted writes, as mentioned by Seth
Goldberg (Sun, 23 May 2010 13:41:01 -0700 (22:41 CEST)).
So I added some workarounds for that in transfer function (read back of
written register) - it solved these problems. (It was not necessary in
"slow" version, because there is waiting for SOF after each transfer.)

4.
I added some additional things into grub_ohci_fini_hw. But I am not sure
with de-allocating of HCCA and EDs/TDs. I am little bit confused by
grub_ohci_fini_hw / grub_ohci_restore_hw - I don't know when it can be
called and what to do with memory allocation.
Probably we should de-allocate memory in grub_ohci_fini_hw for the case
if "rmmod ohci" is used - but if we de-allocate memory there, we should
probably allocate the memory again in grub_ohci_restore_hw. (?)

5.
"Allocation" and "de-allocation" of TDs is similar to uhci.c but not the
same as they are used in little bit another way than in UHCI.
There is missing "de-allocation" of EDs - I think it is currently not
necessary (there is no hot plugging, no (working) hub support), normally
we need only few EDs:
- one control with address 0
- one control with address 1
- two bulk
And bulk endpoints have often numbers 1 and 2, so even if hot plugging
will be implemented, once "allocated" EDs can be reused. But it can be
further enhancement in future, I focused only on speed-up of OHCI.

6.
Some answers:

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> Hello, Aleš. Today I merged usb branch into mainline. Now I've started
> some work towards hotplugging and module autoloading. Currently it just
> checks portsstatus to see newly connected devices. Touble is that if you
> disconnect a device and plug a new one at its place this routine won't
> notice anything because ports are polled only when enumerating
> usbdevices. We need a more reliable way to detect new devices. Perhaps
> we should try to assign addresses to any device without address and see
> if it responds to configuration request. If noone responds assume that
> no devices were newly connected.
> On Yeeloong 'usb' command lists hotplugged devices correctly but bulk
> transfer fails with donehead=0.

OHCI (but not only) related idea:
Did you are using the RootHubStatusChange bit in HcInterruptStatus
register ? It can be helpful - maybe.
But probably more useful will be ConnectStatusChange bit in
HcRhPortstatus[1:NDP] register (UHCI has also similar bit in PORTSC). If
this bit is set it is sure that device was disconnected and connected
again and needs to be re-addressed. And after re-addressing this bit
will be cleared. It could work.

> > Some additional informations:
> >
> > 1.
> > I found multi-LUN relatedproblem: I have one multi-LUN device - card
> > reader - and I tried plug into it more than one card. In this case "ls"
> > command reports bad number of filesystems (and also bad type of listed
> > partition filesystem) on another than first LUN.
> >
> > It looks like that (not true copy of console):
> > (usb0a) (usb0,msdos1) (usb0b,msdos1)
> > It is bad because LUN usb0b has four partitions with ext2 filesystem.
> >
> > When I try for example "ls (usb0b,4)/", grub returns for the first try
> > "error: unknown filesystem.", for second try grub does normal file list.
> > (the same for another partitions, i.e. (usb0b,1), (usb0b,2), (usb0b,3))
> >
> >   
> Perhaps there are strill problems with toggle bit. Problem is that if it
> gets out of sync it can only hard get in-sync again. Hence strange
> problems. But it would appear on single-LUN devices too.

I think the multi-LUN problem is not related to toggling. I looked into
debug messages and there is no problem on USB level - all transactions
are working well in my case.

> >
> > Oh, I find probably the mistake right now - there is missing my older
> > small patch in usbms.c - there is fast "hand made" "patch", I have no
> > more time, sorry:
> > usbms.c, line 240:
> > -   cbw.lun = scsi->lun << GRUB_SCSI_LUN_SHIFT;
> > +   cbw.lun = scsi->lun; /* In USB MS CBW are LUN bits on another place
> > than in SCSI CDB, both should be set correctly. */
> >
> >   
> Perhaps (scsi->lun << GRUB_SCSI_LUN_SHIFT) | scsi->lun ?

As I answered before, it should be "scsi->lun" only (according to
specification - reality can be different...).
But this problem remains even when I tested corrected usbms.c version.
As I wrote above, on USB level it looks fine - as far as I can see, it
looks like ohci.c/uhci.c, usbtrans.c, usbms.c, scsi.c are working well.
Could you check if something can be wrong in higher level for multi-LUN
devices ?

> > 2.
> > I found some other devices to test, now I have 13 USB MS bulk-only
> > devices, each is from another manufacturer and there are approx. five
> > different kinds of devices - flash disk, hard disk, card reader, camera,
> > mobile phone.
> > What is known but maybe interesting, three devices are not working at
> > all on UHCI - but are working on OHCI (and under Linux / Windows they
> > are also working) - as we discussed previously, it can be some problem
> > with power or EHCI influence because it looks like device is not
> > properly powered.
> > UHCI-problematic devices  are: Transcend SDHC card reader 
> I have a cardreader and it may be Transcend.

Yes, as we discussed before, it can be some power related problem on my
computer or something else... Maybe I try investigate it again.

> > 4.
> > One really stupid question related to your older e-mail:
> >
> > What does mean "For this you can add a new field in "o"." ?
> >
> > It is something related to Bazaar branch ? Or to something in source
> > code configuration etc. ?
> > (I am totally unexperienced with bazaar and similar tools, the same with
> > autogen and similar..., sorry.)
> >
> >   
> I meant this:
>   struct grub_ohci *o = (struct grub_ohci *) dev->data;
> 

Thanks :-), sometimes I don't see evident things...

One question at the end but not so important - I am thinking about some
"resarch" of EHCI - at least in possible relation between
UHCI-not-working devices and some EHCI power settings. So, if I will
have some time, I maybe try to do something with EHCI. 
But I am not familiar with atomake, autogen and similar tools - how to
add correctly new module "ehci" - i.e. file bus/usb/ehci.c into
makefile ?

Best regards
Ales

[-- Attachment #2: usb_fast_patch_100612 --]
[-- Type: text/x-patch, Size: 48898 bytes --]

diff -urB ./usb/bus/usb/ohci.c ./usb_fast/bus/usb/ohci.c
--- ./usb/bus/usb/ohci.c	2010-06-12 17:40:14.000000000 +0200
+++ ./usb_fast/bus/usb/ohci.c	2010-06-12 17:08:30.000000000 +0200
@@ -46,6 +46,23 @@
   grub_uint8_t reserved[116];
 } __attribute__((packed));
 
+/* OHCI General Transfer Descriptor */
+struct grub_ohci_td
+{
+  /* Information used to construct the TOKEN packet.  */
+  grub_uint32_t token;
+  grub_uint32_t buffer; /* LittleEndian physical address */
+  grub_uint32_t next_td; /* LittleEndian physical address */
+  grub_uint32_t buffer_end; /* LittleEndian physical address */
+  /* next values are not for OHCI HW */
+  grub_uint32_t prev_td_phys; /* we need it to find previous TD
+                               * physical address in CPU endian */
+  grub_uint32_t link_td; /* pointer to next free/chained TD
+                          * pointer as uint32 */
+  grub_uint32_t tr_index; /* index of TD in transfer */
+  grub_uint8_t pad[4]; /* padding to 32 bytes */
+} __attribute__((packed));
+
 /* OHCI Endpoint Descriptor.  */
 struct grub_ohci_ed
 {
@@ -55,26 +72,33 @@
   grub_uint32_t next_ed;
 } __attribute__((packed));
 
-struct grub_ohci_td
-{
-  /* Information used to construct the TOKEN packet.  */
-  grub_uint32_t token;
-
-  grub_uint32_t buffer;
-  grub_uint32_t next_td;
-  grub_uint32_t buffer_end;
-} __attribute__((packed));
-
 typedef volatile struct grub_ohci_td *grub_ohci_td_t;
 typedef volatile struct grub_ohci_ed *grub_ohci_ed_t;
 
+/* Experimental change of ED/TD allocation */
+/* Little bit similar as in UHCI */
+/* Implementation assumes:
+ *      32-bits architecture - XXX: fix for 64-bits
+ *      memory allocated by grub_memalign_dma32 must be continuous
+ *      in virtual and also in physical memory */
 struct grub_ohci
 {
   volatile grub_uint32_t *iobase;
   volatile struct grub_ohci_hcca *hcca;
   grub_uint32_t hcca_addr;
   struct grub_pci_dma_chunk *hcca_chunk;
+  grub_ohci_ed_t ed_ctrl; /* EDs for CONTROL */
+  grub_uint32_t ed_ctrl_addr;
+  struct grub_pci_dma_chunk *ed_ctrl_chunk;
+  grub_ohci_ed_t ed_bulk; /* EDs for BULK */
+  grub_uint32_t ed_bulk_addr;
+  struct grub_pci_dma_chunk *ed_bulk_chunk;
+  grub_ohci_td_t td; /* TDs */
+  grub_uint32_t td_addr;
+  struct grub_pci_dma_chunk *td_chunk;
   struct grub_ohci *next;
+  grub_ohci_td_t td_free; /* Pointer to first free TD */
+  int bad_OHCI;
 };
 
 static struct grub_ohci *ohci;
@@ -121,6 +145,34 @@
 #define GRUB_OHCI_SET_PORT_RESET (1 << 4)
 #define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)
 
+#define GRUB_OHCI_REG_CONTROL_BULK_ENABLE (1 << 5)
+#define GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE (1 << 4)
+
+#define GRUB_OHCI_CTRL_EDS 16
+#define GRUB_OHCI_BULK_EDS 16
+#define GRUB_OHCI_TDS 256
+
+#define GRUB_OHCI_ED_ADDR_MASK 0x7ff
+
+#define GRUB_OHCI_ED_PHYS2VIRT(o, bulk, x) ( !(x) ? NULL : ( \
+  (bulk) ? \
+  (grub_ohci_ed_t)((x) - (o)->ed_bulk_addr + (grub_uint32_t)(o)->ed_bulk) \
+  : \
+  (grub_ohci_ed_t)((x) - (o)->ed_ctrl_addr + (grub_uint32_t)(o)->ed_ctrl) ) )
+  
+#define GRUB_OHCI_ED_VIRT2PHYS(o, bulk, x) ( !(x) ? 0 : ( \
+  (bulk) ? \
+  ((grub_uint32_t)(x) - (grub_uint32_t)(o)->ed_bulk + (o)->ed_bulk_addr) \
+  : \
+  ((grub_uint32_t)(x) - (grub_uint32_t)(o)->ed_ctrl + (o)->ed_ctrl_addr) ) )
+
+#define GRUB_OHCI_TD_PHYS2VIRT(o, x) ( !(x) ? NULL : \
+  (grub_ohci_td_t)((x) - (o)->td_addr + (grub_uint32_t)(o)->td) )
+
+#define GRUB_OHCI_TD_VIRT2PHYS(o, x) ( !(x) ? 0 : \
+  ((grub_uint32_t)(x) - (grub_uint32_t)(o)->td + (o)->td_addr) )
+
+  
 static grub_uint32_t
 grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
 {
@@ -148,7 +200,8 @@
   struct grub_ohci *o;
   grub_uint32_t revision;
   int cs5536;
-
+  int j;
+  
   /* Determine IO base address.  */
   grub_dprintf ("ohci", "pciid = %x\n", pciid);
 
@@ -204,7 +257,7 @@
   o = grub_malloc (sizeof (*o));
   if (! o)
     return 1;
-
+  grub_memset ((void*)o, 0, sizeof (*o));
   o->iobase = grub_pci_device_map_range (dev, base, 0x800);
 
   grub_dprintf ("ohci", "base=%p\n", o->iobase);
@@ -212,9 +265,57 @@
   /* Reserve memory for the HCCA.  */
   o->hcca_chunk = grub_memalign_dma32 (256, 256);
   if (! o->hcca_chunk)
-    return 1;
+    goto fail;
   o->hcca = grub_dma_get_virt (o->hcca_chunk);
   o->hcca_addr = grub_dma_get_phys (o->hcca_chunk);
+  grub_memset ((void*)o->hcca, 0, sizeof(*o->hcca));
+  grub_dprintf ("ohci", "hcca: chunk=%p, virt=%p, phys=0x%02x\n",
+                o->hcca_chunk, o->hcca, o->hcca_addr);
+
+  /* Reserve memory for ctrl EDs.  */
+  o->ed_ctrl_chunk = grub_memalign_dma32 (16, sizeof(struct grub_ohci_ed)*GRUB_OHCI_CTRL_EDS);
+  if (! o->ed_ctrl_chunk)
+    goto fail;
+  o->ed_ctrl = grub_dma_get_virt (o->ed_ctrl_chunk);
+  o->ed_ctrl_addr = grub_dma_get_phys (o->ed_ctrl_chunk);
+  /* Preset EDs */
+  grub_memset ((void*)o->ed_ctrl, 0, sizeof(struct grub_ohci_ed) * GRUB_OHCI_CTRL_EDS);
+  for (j=0; j < GRUB_OHCI_CTRL_EDS; j++)
+    o->ed_ctrl[j].target = grub_cpu_to_le32 (1 << 14); /* skip */
+    
+  grub_dprintf ("ohci", "EDs-C: chunk=%p, virt=%p, phys=0x%02x\n",
+                o->ed_ctrl_chunk, o->ed_ctrl, o->ed_ctrl_addr);
+
+  /* Reserve memory for bulk EDs.  */
+  o->ed_bulk_chunk = grub_memalign_dma32 (16, sizeof(struct grub_ohci_ed)*GRUB_OHCI_BULK_EDS);
+  if (! o->ed_bulk_chunk)
+    goto fail;
+  o->ed_bulk = grub_dma_get_virt (o->ed_bulk_chunk);
+  o->ed_bulk_addr = grub_dma_get_phys (o->ed_bulk_chunk);
+  /* Preset EDs */
+  grub_memset ((void*)o->ed_bulk, 0, sizeof(struct grub_ohci_ed) * GRUB_OHCI_BULK_EDS);
+  for (j=0; j < GRUB_OHCI_BULK_EDS; j++)
+    o->ed_bulk[j].target = grub_cpu_to_le32 (1 << 14); /* skip */
+
+  grub_dprintf ("ohci", "EDs-B: chunk=%p, virt=%p, phys=0x%02x\n",
+                o->ed_bulk_chunk, o->ed_bulk, o->ed_bulk_addr);
+
+  /* Reserve memory for TDs.  */
+  o->td_chunk = grub_memalign_dma32 (32, sizeof(struct grub_ohci_td)*GRUB_OHCI_TDS);
+  /* Why is it aligned on 32 boundary if spec. says 16 ?
+   * We have structure 32 bytes long and we don't want cross
+   * 4K boundary inside structure. */
+  if (! o->td_chunk)
+    goto fail;
+  o->td_free = o->td = grub_dma_get_virt (o->td_chunk);
+  o->td_addr = grub_dma_get_phys (o->td_chunk);
+  /* Preset free TDs chain in TDs */
+  grub_memset ((void*)o->td, 0, sizeof(struct grub_ohci_td) * GRUB_OHCI_TDS);
+  for (j=0; j < (GRUB_OHCI_TDS-1); j++)
+    o->td[j].link_td = (grub_uint32_t)&o->td[j+1];
+
+  grub_dprintf ("ohci", "TDs: chunk=%p, virt=%p, phys=0x%02x\n",
+                o->td_chunk, o->td, o->td_addr);
 
   /* Check if the OHCI revision is actually 1.0 as supported.  */
   revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION);
@@ -286,9 +387,13 @@
   /* Misc. pre-sets. */
   o->hcca->donehead = 0;
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
+  /* We don't want modify CONTROL/BULK HEAD registers.
+   * So we assign to HEAD registers zero ED from related array
+   * and we will not use this ED, it will be always skipped.
+   * It should not produce notable performance penalty (I hope). */
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
 
   /* Check OHCI Legacy Support */
@@ -302,9 +407,11 @@
       grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
     }
 
-  /* Enable the OHCI.  */
+  /* Enable the OHCI + enable CONTROL and BULK LIST.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
-			(2 << 6));
+			(2 << 6)
+			| GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
+			| GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
   grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
 		(grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);
 
@@ -331,6 +438,10 @@
    * and "insmod usbms". */
   grub_millisleep (500);	
 
+
+/* Test - remove it !!! */
+o->bad_OHCI = 1;
+
   /* Link to ohci now that initialisation is successful.  */
   o->next = ohci;
   ohci = o;
@@ -339,10 +450,13 @@
 
  fail:
   if (o)
+    grub_dma_free (o->td_chunk);
+    grub_dma_free (o->ed_bulk_chunk);
+    grub_dma_free (o->ed_ctrl_chunk);
     grub_dma_free (o->hcca_chunk);
   grub_free (o);
 
-  return 0;
+  return 1;
 }
 
 
@@ -370,6 +484,114 @@
   return 0;
 }
 
+static grub_ohci_ed_t
+grub_ohci_find_ed (struct grub_ohci *o, int bulk, grub_uint32_t target)
+{
+  grub_ohci_ed_t ed, ed_next;
+  grub_uint32_t target_addr = target & GRUB_OHCI_ED_ADDR_MASK;
+  int count;
+  int i;
+
+  /* Use proper values and structures. */
+  if (bulk)
+    {    
+      count = GRUB_OHCI_BULK_EDS;
+      ed = o->ed_bulk;
+      ed_next = GRUB_OHCI_ED_PHYS2VIRT(o, bulk,
+                  grub_le_to_cpu32 (ed->next_ed) );
+    }
+  else
+    {
+      count = GRUB_OHCI_CTRL_EDS;
+      ed = o->ed_ctrl;
+      ed_next = GRUB_OHCI_ED_PHYS2VIRT(o, bulk,
+                  grub_le_to_cpu32 (ed->next_ed) );
+    }
+
+   /* First try to find existing ED with proper target address */
+  for (i = 0; ; )
+    {
+      if (i && /* We ignore zero ED */
+           ((ed->target & GRUB_OHCI_ED_ADDR_MASK) == target_addr))
+        return ed; /* Found proper existing ED */
+      i++;
+      if (ed_next && (i < count))
+        {
+          ed = ed_next;
+          ed_next = GRUB_OHCI_ED_PHYS2VIRT(o, bulk,
+                      grub_le_to_cpu32 (ed->next_ed) );
+          continue;
+        }
+      break;
+    }
+  /* ED with target_addr does not exist, we have to add it */
+  /* Have we any free ED in array ? */
+  if (i >= count) /* No. */
+    return NULL;
+  /* Currently we simply take next ED in array, no allocation
+   * function is used. It should be no problem until hot-plugging
+   * will be implemented, i.e. until we will need to de-allocate EDs
+   * of unplugged devices. */
+  /* We can link new ED to previous ED safely as the new ED should
+   * still have set skip bit. */
+  ed->next_ed = grub_cpu_to_le32 ( GRUB_OHCI_ED_VIRT2PHYS (o,
+                                     bulk, &ed[1]));
+  return &ed[1];
+}
+
+static grub_ohci_td_t
+grub_ohci_alloc_td (struct grub_ohci *o)
+{
+  grub_ohci_td_t ret;
+
+  /* Check if there is a Transfer Descriptor available.  */
+  if (! o->td_free)
+    return NULL;
+
+  ret = o->td_free; /* Take current free TD */
+  o->td_free = (grub_ohci_td_t)ret->link_td; /* Advance to next free TD in chain */
+  ret->link_td = 0; /* Reset link_td in allocated TD */
+  return ret;
+}
+
+static void
+grub_ohci_free_td (struct grub_ohci *o, grub_ohci_td_t td)
+{
+  grub_memset ( (void*)td, 0, sizeof(struct grub_ohci_td) ); 
+  td->link_td = (grub_uint32_t) o->td_free; /* Cahin new free TD & rest */
+  o->td_free = td; /* Change address of first free TD */
+}
+
+static void
+grub_ohci_free_tds (struct grub_ohci *o, grub_ohci_td_t td)
+{
+  if (!td)
+    return;
+    
+  /* Unchain first TD from previous TD if it is chained */
+  if (td->prev_td_phys)
+    {
+      grub_ohci_td_t td_prev_virt = GRUB_OHCI_TD_PHYS2VIRT(o,
+                                      td->prev_td_phys);
+
+      if (td == (grub_ohci_td_t) td_prev_virt->link_td)
+        td_prev_virt->link_td = 0;
+    }
+  
+  /* Free all TDs from td  (chained by link_td) */
+  while (td)
+    {
+      grub_ohci_td_t tdprev;
+      
+      /* Unlink the queue.  */
+      tdprev = td;
+      td = (grub_ohci_td_t) td->link_td;
+
+      /* Free the TD.  */
+      grub_ohci_free_td (o, tdprev);
+    }
+}
+
 static void
 grub_ohci_transaction (grub_ohci_td_t td,
 		       grub_transfer_type_t type, unsigned int toggle,
@@ -398,12 +620,7 @@
       break;
     }
 
-#if 0 /* Always generate interrupt */
-  /* Generate no interrupts.  */
-  token |= 7 << 21;
-#endif
-
-  /* Set the token.  */
+  /* Set the token (Always generate interrupt - bits 21-23 = 0).  */
   token |= toggle << 24;
   token |= 1 << 25;
 
@@ -437,80 +654,36 @@
 		    grub_usb_transfer_t transfer)
 {
   struct grub_ohci *o = (struct grub_ohci *) dev->data;
-  grub_ohci_ed_t ed;
-  grub_uint32_t ed_addr;
-  struct grub_pci_dma_chunk *ed_chunk, *td_list_chunk;
-  grub_ohci_td_t td_list;
-  grub_uint32_t td_list_addr;
+  grub_ohci_ed_t ed_virt;
+  int bulk = 0;
+  grub_ohci_td_t td_head_virt;
+  grub_ohci_td_t td_current_virt;
+  grub_ohci_td_t td_next_virt;
+  grub_ohci_td_t tderr_virt = NULL;
   grub_uint32_t target;
-  grub_uint32_t td_tail;
-  grub_uint32_t td_head;
+  grub_uint32_t td_head_phys;
+  grub_uint32_t td_tail_phys;
+  grub_uint32_t td_last_phys;
+  grub_uint32_t tderr_phys = 0;
   grub_uint32_t status;
   grub_uint32_t control;
-  grub_usb_err_t err;
+  grub_uint8_t errcode = 0;
+  grub_usb_err_t err = GRUB_USB_ERR_NONE;
   int i;
   grub_uint64_t maxtime;
+  grub_uint64_t bad_OHCI_delay = 0;
+  int err_halt = 0;
   int err_timeout = 0;
   int err_unrec = 0;
   grub_uint32_t intstatus;
-  grub_uint32_t tderr_addr = 0;
-
-  /* Allocate an Endpoint Descriptor.  */
-  ed_chunk = grub_memalign_dma32 (256, sizeof (*ed));
-  if (! ed_chunk)
-    return GRUB_USB_ERR_INTERNAL;
-  ed = grub_dma_get_virt (ed_chunk);
-  ed_addr = grub_dma_get_phys (ed_chunk);
-
-  td_list_chunk = grub_memalign_dma32 (256, sizeof (*td_list)
-				       * (transfer->transcnt + 1));
-  if (! td_list_chunk)
-    {
-      grub_dma_free (ed_chunk);
-      return GRUB_USB_ERR_INTERNAL;
-    }
-  td_list = grub_dma_get_virt (td_list_chunk);
-  td_list_addr = grub_dma_get_phys (td_list_chunk);
-
-  grub_dprintf ("ohci", "alloc=%p/0x%x\n", td_list, td_list_addr);
-
-  /* Setup all Transfer Descriptors.  */
-  for (i = 0; i < transfer->transcnt; i++)
-    {
-      grub_usb_transaction_t tr = &transfer->transactions[i];
-
-      grub_ohci_transaction (&td_list[i], tr->pid, tr->toggle,
-			     tr->size, tr->data);
-
-      td_list[i].next_td = grub_cpu_to_le32 (td_list_addr
-					     + (i + 1) * sizeof (td_list[0]));
-    }
-
-#if 0 /* Better will be enable interrupt on all TDs. */
-  /* The last-1 TD token we should change to enable interrupt when TD finishes.
-   * As OHCI interrupts are disabled, it does only setting of WDH bit in
-   * HcInterruptStatus register - and that is what we want to safely detect
-   * normal end of all transactions. */
-  td_list[transfer->transcnt - 1].token &= ~(7 << 21);
-#endif
-
-  td_list[transfer->transcnt].token = 0;
-  td_list[transfer->transcnt].buffer = 0;
-  td_list[transfer->transcnt].buffer_end = 0;
-  td_list[transfer->transcnt].next_td =
-    (grub_uint32_t) &td_list[transfer->transcnt];
-  
-  /* Setup the Endpoint Descriptor.  */
 
+  /* Pre-set target for ED - we need it to find proper ED */
   /* Set the device address.  */
   target = transfer->devaddr;
-
   /* Set the endpoint. It should be masked, we need 4 bits only. */
   target |= (transfer->endpoint & 15) << 7;
-
   /* Set the device speed.  */
   target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
-
   /* Set the maximum packet size.  */
   target |= transfer->max << 16;
 
@@ -514,97 +687,147 @@
   /* Set the maximum packet size.  */
   target |= transfer->max << 16;
 
-  td_head = td_list_addr;
+  /* Determine if transfer type is bulk - we need to select proper ED */
+  switch (transfer->type)
+    {
+      case GRUB_USB_TRANSACTION_TYPE_BULK:
+        bulk = 1;
+	break;
 
-  td_tail = td_list_addr + transfer->transcnt * sizeof (*td_list);
+      case GRUB_USB_TRANSACTION_TYPE_CONTROL:
+        break;
 
-  ed->target = grub_cpu_to_le32 (target);
-  ed->td_head = grub_cpu_to_le32 (td_head);
-  ed->td_tail = grub_cpu_to_le32 (td_tail);
-  ed->next_ed = grub_cpu_to_le32 (0);
+      default :
+        return GRUB_USB_ERR_INTERNAL;
+    }
 
-  grub_dprintf ("ohci", "program OHCI\n");
+  /* Find proper ED or add new ED */
+  ed_virt = grub_ohci_find_ed (o, bulk, target);
+  if (!ed_virt)
+    {
+      grub_dprintf ("ohci","Fatal: No free ED !\n");
+      return GRUB_USB_ERR_INTERNAL;
+    }
+  
+  /* Take pointer to first TD from ED */
+  td_head_phys = grub_le_to_cpu32 (ed_virt->td_head) & ~0xf;
+  td_tail_phys = grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf;
 
-  /* Disable the Control and Bulk lists.  */
-  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-  control &= ~(3 << 4);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+  /* Sanity check - td_head should be equal to td_tail */
+  if (td_head_phys != td_tail_phys) /* Should never happen ! */
+    {
+      grub_dprintf ("ohci", "Fatal: HEAD is not equal to TAIL !\n");
+      grub_dprintf ("ohci", "HEAD = 0x%02x, TAIL = 0x%02x\n",
+                    td_head_phys, td_tail_phys);
+      /* XXX: Fix: What to do ? */
+      return GRUB_USB_ERR_INTERNAL;
+    }
+  
+  /* Now we should handle first TD. If ED is newly allocated,
+   * we must allocate the first TD. */
+  if (!td_head_phys)
+    {
+      td_head_virt = grub_ohci_alloc_td (o);
+      if (!td_head_virt)
+        return GRUB_USB_ERR_INTERNAL; /* We don't need de-allocate ED */
+      /* We can set td_head only when ED is not active, i.e.
+       * when it is newly allocated. */
+      ed_virt->td_head = grub_cpu_to_le32 ( GRUB_OHCI_TD_VIRT2PHYS (o,
+                                              td_head_virt) );
+      ed_virt->td_tail = ed_virt->td_head;
+    }
+  else
+    td_head_virt = GRUB_OHCI_TD_PHYS2VIRT ( o, td_head_phys );
+    
+  /* Set TDs */
+  td_last_phys = td_head_phys; /* initial value to make compiler happy... */
+  for (i = 0, td_current_virt = td_head_virt;
+       i < transfer->transcnt; i++)
+    {
+      grub_usb_transaction_t tr = &transfer->transactions[i];
 
-  /* Clear BulkListFilled and ControlListFilled.  */
-  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~(3 << 1);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+      grub_ohci_transaction (td_current_virt, tr->pid, tr->toggle,
+			     tr->size, tr->data);
 
-  /* Now we should wait for start of next frame. Because we are not using
-   * interrupt, we reset SF bit and wait when it goes to 1. */
-  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
-  /* Wait for new SOF */
-  while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
-  /* Now it should be safe to change CONTROL and BULK lists. */
-
-  /* This we do for safety's sake - it should be done in previous call
-   * of grub_ohci_transfer and nobody should change it in meantime...
-   * It should be done before start of control or bulk OHCI list. */   
-  o->hcca->donehead = 0;
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
+      /* Set index of TD in transfer */
+      td_current_virt->tr_index = (grub_uint32_t) i;
+
+      /* No IRQ request in TD if bad_OHCI set */
+      if (o->bad_OHCI)
+        td_current_virt->token |= grub_cpu_to_le32 ( 7 << 21);
+      
+      /* Remember last used (processed) TD phys. addr. */
+      td_last_phys = GRUB_OHCI_TD_VIRT2PHYS (o, td_current_virt);
+      
+      /* Allocate next TD */
+      td_next_virt = grub_ohci_alloc_td (o);
+      if (!td_next_virt) /* No free TD, cancel transfer and free TDs except head TD */
+        {
+          if (i) /* if i==0 we have nothing to free... */
+            grub_ohci_free_tds (o,
+              GRUB_OHCI_TD_PHYS2VIRT(o,
+                grub_le_to_cpu32 (td_head_virt->next_td) ) );
+          /* Reset head TD */
+          grub_memset ( (void*)td_head_virt, 0,
+                        sizeof(struct grub_ohci_td) );
+          grub_dprintf ("ohci", "Fatal: No free TD !");
+          return GRUB_USB_ERR_INTERNAL;
+        }
+
+      /* Chain TDs */
+      td_current_virt->link_td = (grub_uint32_t) td_next_virt;
+      td_current_virt->next_td = grub_cpu_to_le32 (
+                                   GRUB_OHCI_TD_VIRT2PHYS (o,
+                                     td_next_virt) );
+      td_next_virt->prev_td_phys = GRUB_OHCI_TD_VIRT2PHYS (o,
+                                td_current_virt);
+      td_current_virt = td_next_virt;
+    }
+
+  grub_dprintf ("ohci", "Tail TD (not processed) = %p\n",
+                td_current_virt);
+  
+  /* Setup the Endpoint Descriptor for transfer.  */
+  /* First set necessary fields in TARGET but keep (or set) skip bit */
+  /* Note: It could be simpler if speed, format and max. packet
+   * size never change after first allocation of ED.
+   * But unfortunately max. packet size may change during initial
+   * setup sequence and we must handle it. */
+  ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
+  /* Set td_tail */
+  ed_virt->td_tail = grub_cpu_to_le32 ( GRUB_OHCI_TD_VIRT2PHYS (o,
+                                          td_current_virt) );
+  /* Now reset skip bit */
+  ed_virt->target = grub_cpu_to_le32 (target);
+  /* ed_virt->td_head = grub_cpu_to_le32 (td_head); Must not be changed, it is maintained by OHCI */
+  /* ed_virt->next_ed = grub_cpu_to_le32 (0); Handled by grub_ohci_find_ed, do not change ! */
+
+  grub_dprintf ("ohci", "program OHCI\n");
 
   /* Program the OHCI to actually transfer.  */
   switch (transfer->type)
     {
     case GRUB_USB_TRANSACTION_TYPE_BULK:
       {
-	grub_dprintf ("ohci", "add to bulk list\n");
-
-	/* Set BulkList Head and Current */
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, ed_addr);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
-
-#define GRUB_OHCI_REG_CONTROL_BULK_ENABLE (1 << 5)
-#define GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE (1 << 4)
-
-	/* Enable the Bulk list.  */
-	control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-	control |= GRUB_OHCI_REG_CONTROL_BULK_ENABLE;
-	control &= ~GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE;
-
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
+	grub_dprintf ("ohci", "BULK list filled\n");
 	/* Set BulkListFilled.  */
-	status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-	status |= 1 << 2;
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1 << 2);
+	/* Read back of register should ensure it is really written */
+	grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	break;
       }
 
     case GRUB_USB_TRANSACTION_TYPE_CONTROL:
       {
-	grub_dprintf ("ohci", "add to control list\n");
-
-	/* Set ControlList Head and Current */
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, ed_addr);
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
-
-	/* Enable the Control list.  */
-	control |= GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE;
-	control &= ~GRUB_OHCI_REG_CONTROL_BULK_ENABLE;
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
-
+	grub_dprintf ("ohci", "CONTROL list filled\n");
 	/* Set ControlListFilled.  */
-	status |= 1 << 1;
-	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
+	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1 << 1);
+	/* Read back of register should ensure it is really written */
+	grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
 	break;
       }
     }
 
-  grub_dprintf ("ohci", "wait for completion\n");
-  grub_dprintf ("ohci", "begin: control=0x%02x status=0x%02x\n",
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
-  grub_dprintf ("ohci","intstatus=0x%02x\n",
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
-
   /* Safety measure to avoid a hang. */
   maxtime = grub_get_time_ms () + 1000;
 	
@@ -613,19 +836,22 @@
     {
       /* Check transfer status */
       intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
-      if ((intstatus & 0x2) != 0)
+      if (!o->bad_OHCI && (intstatus & 0x2) != 0)
         {
-          grub_dprintf ("ohci", "Current HccaDoneHead=0x%08x\n",
-			o->hcca->donehead);
           /* Remember last successful TD */
-          tderr_addr = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
+          tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
           /* Reset DoneHead */
 	  o->hcca->donehead = 0;
           grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+          /* Read back of register should ensure it is really written */
+          grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
           /* if TD is last, finish */
-          if (tderr_addr == td_list_addr
-	      + sizeof (td_list[0]) * (transfer->transcnt - 1))
-            break;
+          if (tderr_phys == td_last_phys)
+            {
+              if (grub_le_to_cpu32 (ed_virt->td_head) & 1)
+                err_halt = 1;
+              break;
+            }
 	  continue;
         }
 
@@ -636,102 +862,157 @@
         }
 
       /* Detected a HALT.  */
-      if (grub_le_to_cpu32 (ed->td_head) & 1)
-        break;
+      if (err_halt || (grub_le_to_cpu32 (ed_virt->td_head) & 1))
+        {
+          err_halt = 1;
+          /* ED is halted, but donehead event can happened in the meantime */
+          intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+          if (!o->bad_OHCI && (intstatus & 0x2) != 0)
+            /* Don't break loop now, first do donehead action(s) */
+            continue;
+          break;
+        }
 
+      /* bad OHCI handling */
+      if ( (grub_le_to_cpu32 (ed_virt->td_head) & ~0xf) ==
+           (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf) ) /* Empty ED */
+        {
+          if (o->bad_OHCI) /* Bad OHCI detected previously */
+            {
+              /* Try get last successful TD. */
+              tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
+              if (tderr_phys)/* Reset DoneHead if we were successful */
+                {
+                  o->hcca->donehead = 0;
+                  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+                  /* Read back of register should ensure it is really written */
+                  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+                }
+              break;
+            }
+          else /* Detection of bad OHCI */
+            /* We should wait short time (~2ms) before we say that
+             * it is bad OHCI to prevent some hazard -
+             * donehead can react in the meantime. This waiting is done
+             * only once per OHCI driver "live cycle". */
+            if (!bad_OHCI_delay) /* Set delay time */
+              bad_OHCI_delay = grub_get_time_ms () + 2;
+            else if (grub_get_time_ms () >= bad_OHCI_delay)
+              o->bad_OHCI = 1;
+          continue;
+        }
+        
       /* Timeout ? */
       if (grub_get_time_ms () > maxtime)
       	{
-	  /* Disable the Control and Bulk lists.  */
-  	  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
-	  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~(3 << 4));
       	  err_timeout = 1;
       	  break;
       	}
 
-      if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
-	break;
-
       grub_cpu_idle ();
     }
   while (1);
 
-  grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n",
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
-  grub_dprintf ("ohci", "intstatus=0x%02x\n",
-		grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
-
-  if (!tderr_addr)
-    {
-      /* It means that something wrong happened,
-       * it could be:
-       * - timeout and no TD processed
-       * - some or unrecoverable error and no TD processed
-       * - something unexpected... :-( */
-      /* Try look into DONEHEAD reg., but there should be also zero */
-      grub_dprintf("ohci", "HCCA DoneHead is zero, something is bad!\n");
-      tderr_addr = grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf;
-    }
-  
-  /* Remember last processed transaction (TD) - it is necessary for
-   * proper setting of toggle bit in next transaction. */
-  transfer->last_trans = ((tderr_addr - td_list_addr) / sizeof (*td_list));
-  grub_dprintf("ohci", "tderr_addr=0x%x, td_list_addr=0x%x,\n",
-	       tderr_addr, td_list_addr);
-
-  if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
-    transfer->last_trans = transfer->transcnt - 1;
-
-  /* Check correct value in last_trans */
-  /* It could happen if timeout happens and no TD was retired */
-  if (transfer->last_trans >= transfer->transcnt || !tderr_addr)
-    {
-      grub_dprintf("ohci", "tderr==0 or out of TDs range!\n");
-      grub_dprintf("ohci", "last_trans=%d, transcnt=%d\n",
-		   transfer->last_trans, transfer->transcnt);
-
-      /* We should set something valid... */
-      transfer->last_trans = -1; /* Probably no TD done */
-      tderr_addr = td_list_addr;
-    }
- 
-  /* In case of timeout do not detect error from TD */    
-  if (err_timeout)
+  /* There are many ways how the loop above can finish:
+   * - normally without any error via INTSTATUS WDH bit
+   *   : tderr_phys == td_last_phys, td_head == td_tail
+   * - normally with error via HALT bit in ED TD HEAD
+   *   : td_head = next TD after TD with error
+   *   : tderr_phys = last processed and retired TD with error,
+   *     i.e. should be != 0
+   *   : if bad_OHCI == TRUE, tderr_phys will be probably invalid
+   * - unrecoverable error - I never seen it but it could be
+   *   : err_unrec == TRUE, other values can contain anything...
+   * - timeout, it can be caused by:
+   *  -- bad USB device - some devices have some bugs, see Linux source
+   *     and related links
+   *  -- bad OHCI controller - e.g. lost interrupts or does not set
+   *     proper bits in INTSTATUS when real IRQ not enabled etc.,
+   *     see Linux source and related links
+   *     One known bug is handled - if transfer finished
+   *     successfully (i.e. HEAD==TAIL, last transfer TD is retired,
+   *     HALT bit is not set) and WDH bit is not set in INTSTATUS - in
+   *     this case we set o->bad_OHCI=TRUE and do alternate loop
+   *     and error handling - but there is problem how to find retired
+   *     TD with error code if HALT occurs and if DONEHEAD is not
+   *     working - we need to find TD previous to current ED HEAD
+   *  -- bad code of this driver or some unknown reasons - :-(
+   *     it can be e.g. bad handling of EDs/TDs/toggle bit...
+   */
+
+  /* Remember target for debug and set skip flag in ED */
+  /* It should be normaly not necessary but we need it at least
+   * in case of timeout */
+  target = grub_le_to_cpu32 ( ed_virt->target );
+  ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
+  /* Read registers for debug - they should be read now because
+   * debug prints case unwanted delays, so something can happen
+   * in the meantime... */
+  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
+  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+  intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+  /* Now print debug values - to have full info what happened */
+  grub_dprintf ("ohci", "loop finished: control=0x%02x status=0x%02x\n",
+		control, status);
+  grub_dprintf ("ohci", "intstatus=0x%02x \n\t\t tderr_phys=0x%02x, td_last_phys=0x%02x\n",
+		intstatus, tderr_phys, td_last_phys);
+  grub_dprintf ("ohci", "err_unrec=%d, err_timeout=%d \n\t\t err_halt=%d, bad_OHCI=%d\n",
+		err_unrec, err_timeout, err_halt, o->bad_OHCI);
+  grub_dprintf ("ohci", "TARGET=0x%02x, HEAD=0x%02x, TAIL=0x%02x\n",
+                target,
+                grub_le_to_cpu32 (ed_virt->td_head),
+                grub_le_to_cpu32 (ed_virt->td_tail) );
+
+  if (!err_halt && !err_unrec && !err_timeout) /* normal finish */
     {
-      err = GRUB_ERR_TIMEOUT;
-      grub_dprintf("ohci", "Timeout, target=%08x, head=%08x\n",
-		   grub_le_to_cpu32(ed->target),
-		   grub_le_to_cpu32(ed->td_head));
-      grub_dprintf("ohci", "tail=%08x, next=%08x\n",
-		   grub_le_to_cpu32(ed->td_tail),
-		   grub_le_to_cpu32(ed->next_ed));
+      /* Simple workaround if donehead is not working */
+      if (o->bad_OHCI &&
+           ( !tderr_phys || (tderr_phys != td_last_phys) ) )
+        {
+          grub_dprintf ("ohci", "normal finish, but tderr_phys corrected\n");
+          tderr_phys = td_last_phys;
+          /* I hope we can do it as transfer (most probably) finished OK */
+        }
+      /* Prepare pointer to last processed TD */
+      tderr_virt = GRUB_OHCI_TD_PHYS2VIRT (o, tderr_phys);
+      /* Set index of last processed TD */
+      if (tderr_virt)
+        transfer->last_trans = tderr_virt->tr_index;
+      else
+        transfer->last_trans = -1;
     }
-  /* In case of unrecoverable error do not detect error from TD */    
-  else if (err_unrec)
-    {
-      err = GRUB_USB_ERR_UNRECOVERABLE;
-      grub_dprintf("ohci",
-		   "Unrecoverable error, target=%08x, head=%08x\n",
-		   grub_le_to_cpu32(ed->target),
-		   grub_le_to_cpu32(ed->td_head));
-      grub_dprintf("ohci", "tail=%08x, next=%08x\n",
-		   grub_le_to_cpu32(ed->td_tail),
-		   grub_le_to_cpu32(ed->next_ed));
-    }
-  else if (grub_le_to_cpu32 (ed->td_head) & 1)
-    {
-      grub_uint8_t errcode;
-      grub_ohci_td_t tderr = NULL;
 
-      transfer->last_trans--;
+  else if (err_halt) /* error, ED is halted by OHCI, i.e. can be modified */
+    {
+      /* First we must get proper tderr_phys value */
+      if (o->bad_OHCI) /* In case of bad_OHCI tderr_phys can be wrong */
+        {
+          if ( tderr_phys ) /* check if tderr_phys points to TD with error */
+            errcode = grub_le_to_cpu32 ( GRUB_OHCI_TD_PHYS2VIRT (o,
+                                           tderr_phys)->token )
+                      >> 28;
+          if ( !tderr_phys || !errcode ) /* tderr_phys not valid or points to wrong TD */
+            { /* Retired TD with error should be previous TD to ED->td_head */
+              tderr_phys = GRUB_OHCI_TD_PHYS2VIRT (o,
+                             grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf )
+                           ->prev_td_phys;
+            }
+        }
 
-      tderr = (grub_ohci_td_t) ((char *) td_list
-				+ (tderr_addr - td_list_addr));
- 
-      errcode = grub_le_to_cpu32 (tderr->token) >> 28;      
-      grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode);
+      /* Prepare pointer to last processed TD and get error code */
+      tderr_virt = GRUB_OHCI_TD_PHYS2VIRT (o, tderr_phys);
+      /* Set index of last processed TD */
+      if (tderr_virt)
+        {
+          errcode = grub_le_to_cpu32 ( tderr_virt->token ) >> 28;
+          transfer->last_trans = tderr_virt->tr_index;
+        }
+      else
+        transfer->last_trans = -1;
 
+      /* Evaluation of error code */
+      grub_dprintf ("ohci", "OHCI tderr_phys=0x%02x, errcode=0x%02x\n",
+                    tderr_phys, errcode);
       switch (errcode)
 	{
 	case 0:
@@ -777,16 +1058,17 @@
 	  /* XXX: Data overrun error.  */
 	  err = GRUB_USB_ERR_DATA;
 	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n",
-	                tderr, transfer->last_trans);
+	                tderr_virt, tderr_virt->tr_index);
 	  break;
 
 	case 9:
 	  /* XXX: Data underrun error.  */
 	  err = GRUB_USB_ERR_DATA;
 	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
-	                tderr, transfer->last_trans);
+	                tderr_virt, tderr_virt->tr_index);
 	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
-	                1 + grub_le_to_cpu32 (tderr->buffer_end) - grub_le_to_cpu32 (tderr->buffer));
+	                1 + grub_le_to_cpu32 (tderr_virt->buffer_end)
+	                  - grub_le_to_cpu32 (tderr_virt->buffer));
 	  break;
 
 	case 10:
@@ -813,69 +1095,101 @@
 	  err = GRUB_USB_ERR_NAK;
 	  break;
 	}
+
+      /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
+      ed_virt->td_head = ed_virt->td_tail & ~0xf; 
     }
-  else
-    err = GRUB_USB_ERR_NONE;
+        
+  else if (err_unrec)      
+    {
+      /* Don't try to get error code and last processed TD for proper
+       * toggle bit value - anything can be invalid */
+      err = GRUB_USB_ERR_UNRECOVERABLE;
+      grub_dprintf("ohci", "Unrecoverable error!");
 
-  /* Disable the Control and Bulk lists.  */
-  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
-  control &= ~(3 << 4);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, control);
+      /* Do OHCI reset in case of unrecoverable error - maybe we will need
+       * do more - re-enumerate bus etc. (?) */
 
-  /* Clear BulkListFilled and ControlListFilled.  */
-  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
-  status &= ~(3 << 1);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, status);
-   
-  /* Set ED to be skipped - for safety */
-  ed->target |= grub_cpu_to_le32 (1 << 14);
- 
-  /* Now we should wait for start of next frame.
-   * It is necessary because we will invalidate pointer to ED and it
-   * can be on OHCI active till SOF!
-   * Because we are not using interrupt, we reset SF bit and wait when
-   * it goes to 1. */
-  /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
-  /* Wait for new SOF */
-  while (((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0)
-         && !err_unrec);
-  /* Now it should be safe to change CONTROL and BULK lists. */
-   
-  /* Important cleaning. */
+      /* Suspend the OHCI by issuing a reset.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
+      /* Read back of register should ensure it is really written */
+      grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
+      grub_millisleep (1);
+      grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
+
+      /* Misc. resets. */
+      ed_virt->td_head = ed_virt->td_tail & ~0xf; /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
+      o->hcca->donehead = 0;
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+      /* Read back of register should ensure it is really written */
+      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+
+      /* Enable the OHCI.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+                            (2 << 6)
+                            | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
+                            | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
+    } 
+
+  else if (err_timeout)
+    {
+      /* In case of timeout do not detect error from TD */    
+      err = GRUB_ERR_TIMEOUT;
+      grub_dprintf("ohci", "Timeout !\n");
+
+      /* We should wait for next SOF to be sure that ED is unaccessed
+       * by OHCI */
+      /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+      /* Wait for new SOF */
+      while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);
+
+      /* Now we must find last processed TD if bad_OHCI == TRUE */
+      if (o->bad_OHCI)
+        { /* Retired TD with error should be previous TD to ED->td_head */
+          tderr_phys = GRUB_OHCI_TD_PHYS2VIRT (o,
+                         grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf)
+                       ->prev_td_phys;
+        }
+      tderr_virt = GRUB_OHCI_TD_PHYS2VIRT (o, tderr_phys);
+      if (tderr_virt)
+        transfer->last_trans = tderr_virt->tr_index;
+      else
+        transfer->last_trans = -1;
+
+      /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
+      ed_virt->td_head = ed_virt->td_tail & ~0xf; 
+    }
+
+  /* At this point always should be:
+   * ED has skip bit set and halted or empty or after next SOF,
+   *    i.e. it is safe to free all TDs except last not processed
+   * ED HEAD == TAIL == phys. addr. of td_current_virt */
+
+  /* Reset DoneHead - sanity cleanup */
   o->hcca->donehead = 0;
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1)); /* Clears WDH */
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
-  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+  /* Read back of register should ensure it is really written */
+  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
 
- if (err_unrec)
-   {
-     /* Do OHCI reset in case of unrecoverable error - maybe we will need
-      * do more - re-enumerate bus etc. (?) */
-
-     /* Suspend the OHCI by issuing a reset.  */
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
-     grub_millisleep (1);
-     grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
-
-     /* Misc. resets. */
-     o->hcca->donehead = 0;
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
-
-     /* Enable the OHCI.  */
-     grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6));
-   } 
-
-  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err);
-   
-  grub_dma_free (td_list_chunk);
-  grub_dma_free (ed_chunk);
+  /* Un-chainig of last TD */
+  if (td_current_virt->prev_td_phys)
+    {
+      grub_ohci_td_t td_prev_virt
+                = GRUB_OHCI_TD_PHYS2VIRT (o, td_current_virt->prev_td_phys);
+
+      td_next_virt = (grub_ohci_td_t) td_prev_virt->link_td;
+      if (td_current_virt == td_next_virt)
+        td_prev_virt->link_td = 0;
+    }
+
+  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x, errcode=0x%02x\n",
+                err, errcode);
+  grub_ohci_free_tds (o, td_head_virt);
 
   return err;
 }
@@ -952,10 +1266,32 @@
   for (o = ohci; o; o = o->next)
     {
       int i, nports = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) & 0xff;
+      grub_uint64_t maxtime;
+
+      /* Set skip in all EDs */
+      if (o->ed_bulk)
+        for (i=0; i < GRUB_OHCI_BULK_EDS; i++)
+          o->ed_bulk[i].target |= grub_cpu_to_le32 (1 << 14); /* skip */
+      if (o->ed_ctrl)
+        for (i=0; i < GRUB_OHCI_CTRL_EDS; i++)
+          o->ed_ctrl[i].target |= grub_cpu_to_le32 (1 << 14); /* skip */
+
+      /* We should wait for next SOF to be sure that all EDs are
+       * unaccessed by OHCI. But OHCI can be non-functional, so
+       * more than 1ms timeout have to be applied. */
+      /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
+      maxtime = grub_get_time_ms () + 2;
+      /* Wait for new SOF or timeout */
+      while ( ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4)
+                 == 0) || (grub_get_time_ms () >= maxtime) );
+
       for (i = 0; i < nports; i++)
 	grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + i,
 			      GRUB_OHCI_CLEAR_PORT_ENABLE);
 
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1);
+      grub_millisleep (1);
       grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, 0);
       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
@@ -963,7 +1299,22 @@
       grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
       grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0);
       grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, 0);
-      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1);
+      /* Read back of register should ensure it is really written */
+      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+
+#if 0 /* Is this necessary before booting? Probably not .(?)
+       * But it must be done if module is removed ! (Or not ?)
+       * How to do it ? - Probably grub_ohci_restore_hw should be more
+       * complicated. (?)
+       * (If we do it, we need to reallocate EDs and TDs in function
+       * grub_ohci_restore_hw ! */
+
+      /* Free allocated EDs and TDs */
+      grub_dma_free (o->td_chunk);
+      grub_dma_free (o->ed_bulk_chunk);
+      grub_dma_free (o->ed_ctrl_chunk);
+      grub_dma_free (o->hcca_chunk);
+#endif
     }
   grub_millisleep (10);
 
@@ -976,7 +1327,23 @@
   struct grub_ohci *o;
 
   for (o = ohci; o; o = o->next)
-    grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
+    {
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
+      o->hcca->donehead = 0;
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+      /* Read back of register should ensure it is really written */
+      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+
+      /* Enable the OHCI.  */
+      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
+                            (2 << 6)
+                            | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
+                            | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
+    }
 
   return GRUB_ERR_NONE;
 }
diff -urB ./usb/bus/usb/usbtrans.c ./usb_fast/bus/usb/usbtrans.c
--- ./usb/bus/usb/usbtrans.c	2010-06-12 17:40:15.000000000 +0200
+++ ./usb_fast/bus/usb/usbtrans.c	2010-06-11 22:47:32.000000000 +0200
@@ -81,7 +81,9 @@
   else
     max = 64;
 
-  grub_dprintf ("usb", "transfer = %p, dev = %p\n", transfer, dev);
+  /*
+  grub_dprintf ("usb", "control: transfer = %p, dev = %p\n", transfer, dev);
+  */
 
   datablocks = (size + max - 1) / max;
 
@@ -146,6 +148,7 @@
   transfer->transactions[datablocks + 1].toggle = 1;
 
   err = dev->controller.dev->transfer (&dev->controller, transfer);
+  grub_dprintf ("usb", "control: err=%d\n", err);
 
   grub_free (transfer->transactions);
   
@@ -174,6 +177,8 @@
   struct grub_pci_dma_chunk *data_chunk;
   grub_size_t size = size0;
 
+  grub_dprintf ("usb", "bulk: size=0x%02x type=%d\n", size, type);
+
   /* FIXME: avoid allocation any kind of buffer in a first place.  */
   data_chunk = grub_memalign_dma32 (128, size);
   if (!data_chunk)
@@ -248,7 +253,7 @@
     toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1;
   else
     toggle = dev->toggle[endpoint]; /* Nothing done, take original */
-  grub_dprintf ("usb", "toggle=%d\n", toggle);
+  grub_dprintf ("usb", "bulk: err=%d, toggle=%d\n", err, toggle);
   dev->toggle[endpoint] = toggle;
 
   grub_free (transfer->transactions);
diff -urB ./usb/disk/usbms.c ./usb_fast/disk/usbms.c
--- ./usb/disk/usbms.c	2010-06-12 17:40:15.000000000 +0200
+++ ./usb_fast/disk/usbms.c	2010-06-12 16:29:06.000000000 +0200
@@ -239,7 +239,6 @@
   grub_usb_err_t err = GRUB_USB_ERR_NONE;
   grub_usb_err_t errCSW = GRUB_USB_ERR_NONE;
   int retrycnt = 3 + 1;
-  grub_size_t i;
 
  retry:
   retrycnt--;
@@ -293,30 +292,27 @@
           goto CheckCSW;
         }
       /* Debug print of received data. */
-      grub_dprintf ("usb", "buf:\n");
-      if (size <= 64)
-        for (i=0; i<size; i++)
-          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
-      else
-          grub_dprintf ("usb", "Too much data for debug print...\n");
+      grub_dprintf ("usb", "First 16 bytes of received data:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	buf[ 0], buf[ 1], buf[ 2], buf[ 3],
+  	buf[ 4], buf[ 5], buf[ 6], buf[ 7],
+  	buf[ 8], buf[ 9], buf[10], buf[11],
+  	buf[12], buf[13], buf[14], buf[15]);
     }
   else if (size)
     {
       err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, size, buf);
       grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
-      grub_dprintf ("usb", "buf:\n");
+      grub_dprintf ("usb", "First 16 bytes of sent data:\n %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+  	buf[ 0], buf[ 1], buf[ 2], buf[ 3],
+  	buf[ 4], buf[ 5], buf[ 6], buf[ 7],
+  	buf[ 8], buf[ 9], buf[10], buf[11],
+  	buf[12], buf[13], buf[14], buf[15]);
       if (err)
         {
           if (err == GRUB_USB_ERR_STALL)
 	    grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
           goto CheckCSW;
         }
-      /* Debug print of sent data. */
-      if (size <= 256)
-        for (i=0; i<size; i++)
-          grub_dprintf ("usb", "0x%02x: 0x%02x\n", i, buf[i]);
-      else
-          grub_dprintf ("usb", "Too much data for debug print...\n");
     }
 
   /* Read the status - (maybe) according to specification.  */

[-- Attachment #3: ohci.c --]
[-- Type: text/x-csrc, Size: 45710 bytes --]

/* ohci.c - OHCI Support.  */
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2008  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/dl.h>
#include <grub/mm.h>
#include <grub/usb.h>
#include <grub/usbtrans.h>
#include <grub/misc.h>
#include <grub/pci.h>
#include <grub/cpu/pci.h>
#include <grub/cpu/io.h>
#include <grub/time.h>
#include <grub/cs5536.h>
#include <grub/loader.h>

struct grub_ohci_hcca
{
  /* Pointers to Interrupt Endpoint Descriptors.  Not used by
     GRUB.  */
  grub_uint32_t inttable[32];

  /* Current frame number.  */
  grub_uint16_t framenumber;

  grub_uint16_t pad;

  /* List of completed TDs.  */
  grub_uint32_t donehead;

  grub_uint8_t reserved[116];
} __attribute__((packed));

/* OHCI General Transfer Descriptor */
struct grub_ohci_td
{
  /* Information used to construct the TOKEN packet.  */
  grub_uint32_t token;
  grub_uint32_t buffer; /* LittleEndian physical address */
  grub_uint32_t next_td; /* LittleEndian physical address */
  grub_uint32_t buffer_end; /* LittleEndian physical address */
  /* next values are not for OHCI HW */
  grub_uint32_t prev_td_phys; /* we need it to find previous TD
                               * physical address in CPU endian */
  grub_uint32_t link_td; /* pointer to next free/chained TD
                          * pointer as uint32 */
  grub_uint32_t tr_index; /* index of TD in transfer */
  grub_uint8_t pad[4]; /* padding to 32 bytes */
} __attribute__((packed));

/* OHCI Endpoint Descriptor.  */
struct grub_ohci_ed
{
  grub_uint32_t target;
  grub_uint32_t td_tail;
  grub_uint32_t td_head;
  grub_uint32_t next_ed;
} __attribute__((packed));

typedef volatile struct grub_ohci_td *grub_ohci_td_t;
typedef volatile struct grub_ohci_ed *grub_ohci_ed_t;

/* Experimental change of ED/TD allocation */
/* Little bit similar as in UHCI */
/* Implementation assumes:
 *      32-bits architecture - XXX: fix for 64-bits
 *      memory allocated by grub_memalign_dma32 must be continuous
 *      in virtual and also in physical memory */
struct grub_ohci
{
  volatile grub_uint32_t *iobase;
  volatile struct grub_ohci_hcca *hcca;
  grub_uint32_t hcca_addr;
  struct grub_pci_dma_chunk *hcca_chunk;
  grub_ohci_ed_t ed_ctrl; /* EDs for CONTROL */
  grub_uint32_t ed_ctrl_addr;
  struct grub_pci_dma_chunk *ed_ctrl_chunk;
  grub_ohci_ed_t ed_bulk; /* EDs for BULK */
  grub_uint32_t ed_bulk_addr;
  struct grub_pci_dma_chunk *ed_bulk_chunk;
  grub_ohci_td_t td; /* TDs */
  grub_uint32_t td_addr;
  struct grub_pci_dma_chunk *td_chunk;
  struct grub_ohci *next;
  grub_ohci_td_t td_free; /* Pointer to first free TD */
  int bad_OHCI;
};

static struct grub_ohci *ohci;

typedef enum
{
  GRUB_OHCI_REG_REVISION = 0x00,
  GRUB_OHCI_REG_CONTROL,
  GRUB_OHCI_REG_CMDSTATUS,
  GRUB_OHCI_REG_INTSTATUS,
  GRUB_OHCI_REG_INTENA,
  GRUB_OHCI_REG_INTDIS,
  GRUB_OHCI_REG_HCCA,
  GRUB_OHCI_REG_PERIODIC,
  GRUB_OHCI_REG_CONTROLHEAD,
  GRUB_OHCI_REG_CONTROLCURR,
  GRUB_OHCI_REG_BULKHEAD,
  GRUB_OHCI_REG_BULKCURR,
  GRUB_OHCI_REG_DONEHEAD,
  GRUB_OHCI_REG_FRAME_INTERVAL,
  GRUB_OHCI_REG_PERIODIC_START = 16,
  GRUB_OHCI_REG_RHUBA = 18,
  GRUB_OHCI_REG_RHUBPORT = 21,
  GRUB_OHCI_REG_LEGACY_CONTROL = 0x100,
  GRUB_OHCI_REG_LEGACY_INPUT = 0x104,
  GRUB_OHCI_REG_LEGACY_OUTPUT = 0x108,
  GRUB_OHCI_REG_LEGACY_STATUS = 0x10c
} grub_ohci_reg_t;

#define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
#define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200

#define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_MASK 0x8fff0000
#define GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT 16
#define GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT 0

/* XXX: Is this choice of timings sane?  */
#define GRUB_OHCI_FSMPS 0x2778
#define GRUB_OHCI_PERIODIC_START 0x257f
#define GRUB_OHCI_FRAME_INTERVAL 0x2edf

#define GRUB_OHCI_SET_PORT_ENABLE (1 << 1)
#define GRUB_OHCI_CLEAR_PORT_ENABLE (1 << 0)
#define GRUB_OHCI_SET_PORT_RESET (1 << 4)
#define GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE (1 << 20)

#define GRUB_OHCI_REG_CONTROL_BULK_ENABLE (1 << 5)
#define GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE (1 << 4)

#define GRUB_OHCI_CTRL_EDS 16
#define GRUB_OHCI_BULK_EDS 16
#define GRUB_OHCI_TDS 256

#define GRUB_OHCI_ED_ADDR_MASK 0x7ff

#define GRUB_OHCI_ED_PHYS2VIRT(o, bulk, x) ( !(x) ? NULL : ( \
  (bulk) ? \
  (grub_ohci_ed_t)((x) - (o)->ed_bulk_addr + (grub_uint32_t)(o)->ed_bulk) \
  : \
  (grub_ohci_ed_t)((x) - (o)->ed_ctrl_addr + (grub_uint32_t)(o)->ed_ctrl) ) )
  
#define GRUB_OHCI_ED_VIRT2PHYS(o, bulk, x) ( !(x) ? 0 : ( \
  (bulk) ? \
  ((grub_uint32_t)(x) - (grub_uint32_t)(o)->ed_bulk + (o)->ed_bulk_addr) \
  : \
  ((grub_uint32_t)(x) - (grub_uint32_t)(o)->ed_ctrl + (o)->ed_ctrl_addr) ) )

#define GRUB_OHCI_TD_PHYS2VIRT(o, x) ( !(x) ? NULL : \
  (grub_ohci_td_t)((x) - (o)->td_addr + (grub_uint32_t)(o)->td) )

#define GRUB_OHCI_TD_VIRT2PHYS(o, x) ( !(x) ? 0 : \
  ((grub_uint32_t)(x) - (grub_uint32_t)(o)->td + (o)->td_addr) )

  
static grub_uint32_t
grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
{
  return grub_le_to_cpu32 (*(o->iobase + reg));
}

static void
grub_ohci_writereg32 (struct grub_ohci *o,
		      grub_ohci_reg_t reg, grub_uint32_t val)
{
  *(o->iobase + reg) = grub_cpu_to_le32 (val);
}

\f

/* Iterate over all PCI devices.  Determine if a device is an OHCI
   controller.  If this is the case, initialize it.  */
static int NESTED_FUNC_ATTR
grub_ohci_pci_iter (grub_pci_device_t dev,
		    grub_pci_id_t pciid)
{
  grub_uint32_t interf;
  grub_uint32_t base;
  grub_pci_address_t addr;
  struct grub_ohci *o;
  grub_uint32_t revision;
  int cs5536;
  int j;
  
  /* Determine IO base address.  */
  grub_dprintf ("ohci", "pciid = %x\n", pciid);

  if (pciid == GRUB_CS5536_PCIID)
    {
      grub_uint64_t basereg;

      cs5536 = 1;
      basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE);
      if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE))
	{
	  /* Shouldn't happen.  */
	  grub_dprintf ("ohci", "No OHCI address is assigned\n");
	  return 0;
	}
      base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK);
      basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER;
      basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED;
      basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS;
      grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_OHCI_BASE, basereg);
    }
  else
    {
      grub_uint32_t class_code;
      grub_uint32_t class;
      grub_uint32_t subclass;

      addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
      class_code = grub_pci_read (addr) >> 8;
      
      interf = class_code & 0xFF;
      subclass = (class_code >> 8) & 0xFF;
      class = class_code >> 16;

      /* If this is not an OHCI controller, just return.  */
      if (class != 0x0c || subclass != 0x03 || interf != 0x10)
	return 0;

      addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
      base = grub_pci_read (addr);

#if 0
      /* Stop if there is no IO space base address defined.  */
      if (! (base & 1))
	return 0;
#endif

      grub_dprintf ("ohci", "class=0x%02x 0x%02x interface 0x%02x\n",
		    class, subclass, interf);
    }

  /* Allocate memory for the controller and register it.  */
  o = grub_malloc (sizeof (*o));
  if (! o)
    return 1;
  grub_memset ((void*)o, 0, sizeof (*o));
  o->iobase = grub_pci_device_map_range (dev, base, 0x800);

  grub_dprintf ("ohci", "base=%p\n", o->iobase);

  /* Reserve memory for the HCCA.  */
  o->hcca_chunk = grub_memalign_dma32 (256, 256);
  if (! o->hcca_chunk)
    goto fail;
  o->hcca = grub_dma_get_virt (o->hcca_chunk);
  o->hcca_addr = grub_dma_get_phys (o->hcca_chunk);
  grub_memset ((void*)o->hcca, 0, sizeof(*o->hcca));
  grub_dprintf ("ohci", "hcca: chunk=%p, virt=%p, phys=0x%02x\n",
                o->hcca_chunk, o->hcca, o->hcca_addr);

  /* Reserve memory for ctrl EDs.  */
  o->ed_ctrl_chunk = grub_memalign_dma32 (16, sizeof(struct grub_ohci_ed)*GRUB_OHCI_CTRL_EDS);
  if (! o->ed_ctrl_chunk)
    goto fail;
  o->ed_ctrl = grub_dma_get_virt (o->ed_ctrl_chunk);
  o->ed_ctrl_addr = grub_dma_get_phys (o->ed_ctrl_chunk);
  /* Preset EDs */
  grub_memset ((void*)o->ed_ctrl, 0, sizeof(struct grub_ohci_ed) * GRUB_OHCI_CTRL_EDS);
  for (j=0; j < GRUB_OHCI_CTRL_EDS; j++)
    o->ed_ctrl[j].target = grub_cpu_to_le32 (1 << 14); /* skip */
    
  grub_dprintf ("ohci", "EDs-C: chunk=%p, virt=%p, phys=0x%02x\n",
                o->ed_ctrl_chunk, o->ed_ctrl, o->ed_ctrl_addr);

  /* Reserve memory for bulk EDs.  */
  o->ed_bulk_chunk = grub_memalign_dma32 (16, sizeof(struct grub_ohci_ed)*GRUB_OHCI_BULK_EDS);
  if (! o->ed_bulk_chunk)
    goto fail;
  o->ed_bulk = grub_dma_get_virt (o->ed_bulk_chunk);
  o->ed_bulk_addr = grub_dma_get_phys (o->ed_bulk_chunk);
  /* Preset EDs */
  grub_memset ((void*)o->ed_bulk, 0, sizeof(struct grub_ohci_ed) * GRUB_OHCI_BULK_EDS);
  for (j=0; j < GRUB_OHCI_BULK_EDS; j++)
    o->ed_bulk[j].target = grub_cpu_to_le32 (1 << 14); /* skip */

  grub_dprintf ("ohci", "EDs-B: chunk=%p, virt=%p, phys=0x%02x\n",
                o->ed_bulk_chunk, o->ed_bulk, o->ed_bulk_addr);

  /* Reserve memory for TDs.  */
  o->td_chunk = grub_memalign_dma32 (32, sizeof(struct grub_ohci_td)*GRUB_OHCI_TDS);
  /* Why is it aligned on 32 boundary if spec. says 16 ?
   * We have structure 32 bytes long and we don't want cross
   * 4K boundary inside structure. */
  if (! o->td_chunk)
    goto fail;
  o->td_free = o->td = grub_dma_get_virt (o->td_chunk);
  o->td_addr = grub_dma_get_phys (o->td_chunk);
  /* Preset free TDs chain in TDs */
  grub_memset ((void*)o->td, 0, sizeof(struct grub_ohci_td) * GRUB_OHCI_TDS);
  for (j=0; j < (GRUB_OHCI_TDS-1); j++)
    o->td[j].link_td = (grub_uint32_t)&o->td[j+1];

  grub_dprintf ("ohci", "TDs: chunk=%p, virt=%p, phys=0x%02x\n",
                o->td_chunk, o->td, o->td_addr);

  /* Check if the OHCI revision is actually 1.0 as supported.  */
  revision = grub_ohci_readreg32 (o, GRUB_OHCI_REG_REVISION);
  grub_dprintf ("ohci", "OHCI revision=0x%02x\n", revision & 0xFF);
  if ((revision & 0xFF) != 0x10)
    goto fail;

  {
    grub_uint32_t control;
    /* Check SMM/BIOS ownership of OHCI (SMM = USB Legacy Support driver for BIOS) */
    control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
    if ((control & 0x100) != 0)
      {
	unsigned i;
	grub_dprintf("ohci", "OHCI is owned by SMM\n");
	/* Do change of ownership */
	/* Ownership change request */
	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, (1<<3)); /* XXX: Magic.  */
	/* Waiting for SMM deactivation */
	for (i=0; i < 10; i++)
	  {
	    if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & 0x100) == 0)
	      {
		grub_dprintf("ohci", "Ownership changed normally.\n");
		break;
	      }
	    grub_millisleep (100);
          }
	if (i >= 10)
	  {
	    grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
				  grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) & ~0x100);
	    grub_dprintf("ohci", "Ownership changing timeout, change forced !\n");
	  }
      }
    else if (((control & 0x100) == 0) && 
	     ((control & 0xc0) != 0)) /* Not owned by SMM nor reset */
      {
	grub_dprintf("ohci", "OHCI is owned by BIOS\n");
	/* Do change of ownership - not implemented yet... */
	/* In fact we probably need to do nothing ...? */
      }
    else
      {
	grub_dprintf("ohci", "OHCI is not owned by SMM nor BIOS\n");
	/* We can setup OHCI. */
      }  
  }

  /* Suspend the OHCI by issuing a reset.  */
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
  grub_millisleep (1);
  grub_dprintf ("ohci", "OHCI reset\n");

  grub_ohci_writereg32 (o, GRUB_OHCI_REG_FRAME_INTERVAL,
			(GRUB_OHCI_FSMPS
			 << GRUB_OHCI_REG_FRAME_INTERVAL_FSMPS_SHIFT)
			| (GRUB_OHCI_FRAME_INTERVAL
			   << GRUB_OHCI_REG_FRAME_INTERVAL_FI_SHIFT));

  grub_ohci_writereg32 (o, GRUB_OHCI_REG_PERIODIC_START,
			GRUB_OHCI_PERIODIC_START);

  /* Setup the HCCA.  */
  o->hcca->donehead = 0;
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
  grub_dprintf ("ohci", "OHCI HCCA\n");

  /* Misc. pre-sets. */
  o->hcca->donehead = 0;
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
  /* We don't want modify CONTROL/BULK HEAD registers.
   * So we assign to HEAD registers zero ED from related array
   * and we will not use this ED, it will be always skipped.
   * It should not produce notable performance penalty (I hope). */
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);

  /* Check OHCI Legacy Support */
  if ((revision & 0x100) != 0)
    {
      grub_dprintf ("ohci", "Legacy Support registers detected\n");
      grub_dprintf ("ohci", "Current state of legacy control reg.: 0x%04x\n",
		    grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL));
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL,
			    (grub_ohci_readreg32 (o, GRUB_OHCI_REG_LEGACY_CONTROL)) & ~1);
      grub_dprintf ("ohci", "OHCI Legacy Support disabled.\n");
    }

  /* Enable the OHCI + enable CONTROL and BULK LIST.  */
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
			(2 << 6)
			| GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
			| GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
  grub_dprintf ("ohci", "OHCI enable: 0x%02x\n",
		(grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL) >> 6) & 3);

  /* Power on all ports */
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBA,
                       (grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA)
                        & ~GRUB_OHCI_RHUB_PORT_POWER_MASK)
                       | GRUB_OHCI_RHUB_PORT_ALL_POWERED);
  /* Wait for stable power (100ms) and stable attachment (100ms) */
  /* I.e. minimum wait time should be probably 200ms. */
  /* We assume that device is attached when ohci is loaded. */
  /* Some devices take long time to power-on or indicate attach. */
  /* Here is some experimental value which should probably mostly work. */
  /* Cameras with manual USB mode selection and maybe some other similar
   * devices will not work in some cases - they are repowered during
   * ownership change and then they are starting slowly and mostly they
   * are wanting select proper mode again...
   * The same situation can be on computers where BIOS not set-up OHCI
   * to be at least powered USB bus (maybe it is Yeelong case...?)
   * Possible workaround could be for example some prompt
   * for user with confirmation of proper USB device connection.
   * Another workaround - "rmmod usbms", "rmmod ohci", proper start
   * and configuration of USB device and then "insmod ohci"
   * and "insmod usbms". */
  grub_millisleep (500);	


/* Test - remove it !!! */
o->bad_OHCI = 1;

  /* Link to ohci now that initialisation is successful.  */
  o->next = ohci;
  ohci = o;

  return 0;

 fail:
  if (o)
    grub_dma_free (o->td_chunk);
    grub_dma_free (o->ed_bulk_chunk);
    grub_dma_free (o->ed_ctrl_chunk);
    grub_dma_free (o->hcca_chunk);
  grub_free (o);

  return 1;
}


static void
grub_ohci_inithw (void)
{
  grub_pci_iterate (grub_ohci_pci_iter);
}

\f

static int
grub_ohci_iterate (int (*hook) (grub_usb_controller_t dev))
{
  struct grub_ohci *o;
  struct grub_usb_controller dev;

  for (o = ohci; o; o = o->next)
    {
      dev.data = o;
      if (hook (&dev))
	return 1;
    }

  return 0;
}

static grub_ohci_ed_t
grub_ohci_find_ed (struct grub_ohci *o, int bulk, grub_uint32_t target)
{
  grub_ohci_ed_t ed, ed_next;
  grub_uint32_t target_addr = target & GRUB_OHCI_ED_ADDR_MASK;
  int count;
  int i;

  /* Use proper values and structures. */
  if (bulk)
    {    
      count = GRUB_OHCI_BULK_EDS;
      ed = o->ed_bulk;
      ed_next = GRUB_OHCI_ED_PHYS2VIRT(o, bulk,
                  grub_le_to_cpu32 (ed->next_ed) );
    }
  else
    {
      count = GRUB_OHCI_CTRL_EDS;
      ed = o->ed_ctrl;
      ed_next = GRUB_OHCI_ED_PHYS2VIRT(o, bulk,
                  grub_le_to_cpu32 (ed->next_ed) );
    }

   /* First try to find existing ED with proper target address */
  for (i = 0; ; )
    {
      if (i && /* We ignore zero ED */
           ((ed->target & GRUB_OHCI_ED_ADDR_MASK) == target_addr))
        return ed; /* Found proper existing ED */
      i++;
      if (ed_next && (i < count))
        {
          ed = ed_next;
          ed_next = GRUB_OHCI_ED_PHYS2VIRT(o, bulk,
                      grub_le_to_cpu32 (ed->next_ed) );
          continue;
        }
      break;
    }
  /* ED with target_addr does not exist, we have to add it */
  /* Have we any free ED in array ? */
  if (i >= count) /* No. */
    return NULL;
  /* Currently we simply take next ED in array, no allocation
   * function is used. It should be no problem until hot-plugging
   * will be implemented, i.e. until we will need to de-allocate EDs
   * of unplugged devices. */
  /* We can link new ED to previous ED safely as the new ED should
   * still have set skip bit. */
  ed->next_ed = grub_cpu_to_le32 ( GRUB_OHCI_ED_VIRT2PHYS (o,
                                     bulk, &ed[1]));
  return &ed[1];
}

static grub_ohci_td_t
grub_ohci_alloc_td (struct grub_ohci *o)
{
  grub_ohci_td_t ret;

  /* Check if there is a Transfer Descriptor available.  */
  if (! o->td_free)
    return NULL;

  ret = o->td_free; /* Take current free TD */
  o->td_free = (grub_ohci_td_t)ret->link_td; /* Advance to next free TD in chain */
  ret->link_td = 0; /* Reset link_td in allocated TD */
  return ret;
}

static void
grub_ohci_free_td (struct grub_ohci *o, grub_ohci_td_t td)
{
  grub_memset ( (void*)td, 0, sizeof(struct grub_ohci_td) ); 
  td->link_td = (grub_uint32_t) o->td_free; /* Cahin new free TD & rest */
  o->td_free = td; /* Change address of first free TD */
}

static void
grub_ohci_free_tds (struct grub_ohci *o, grub_ohci_td_t td)
{
  if (!td)
    return;
    
  /* Unchain first TD from previous TD if it is chained */
  if (td->prev_td_phys)
    {
      grub_ohci_td_t td_prev_virt = GRUB_OHCI_TD_PHYS2VIRT(o,
                                      td->prev_td_phys);

      if (td == (grub_ohci_td_t) td_prev_virt->link_td)
        td_prev_virt->link_td = 0;
    }
  
  /* Free all TDs from td  (chained by link_td) */
  while (td)
    {
      grub_ohci_td_t tdprev;
      
      /* Unlink the queue.  */
      tdprev = td;
      td = (grub_ohci_td_t) td->link_td;

      /* Free the TD.  */
      grub_ohci_free_td (o, tdprev);
    }
}

static void
grub_ohci_transaction (grub_ohci_td_t td,
		       grub_transfer_type_t type, unsigned int toggle,
		       grub_size_t size, grub_uint32_t data)
{
  grub_uint32_t token;
  grub_uint32_t buffer;
  grub_uint32_t buffer_end;

  grub_dprintf ("ohci", "OHCI transaction td=%p type=%d, toggle=%d, size=%d\n",
		td, type, toggle, size);

  switch (type)
    {
    case GRUB_USB_TRANSFER_TYPE_SETUP:
      token = 0 << 19;
      break;
    case GRUB_USB_TRANSFER_TYPE_IN:
      token = 2 << 19;
      break;
    case GRUB_USB_TRANSFER_TYPE_OUT:
      token = 1 << 19;
      break;
    default:
      token = 0;
      break;
    }

  /* Set the token (Always generate interrupt - bits 21-23 = 0).  */
  token |= toggle << 24;
  token |= 1 << 25;

  /* Set "Not accessed" error code */
  token |= 15 << 28;

  buffer = data;
  buffer_end = buffer + size - 1;

  /* Set correct buffer values in TD if zero transfer occurs */
  if (size)
    {
      buffer = (grub_uint32_t) data;
      buffer_end = buffer + size - 1;
      td->buffer = grub_cpu_to_le32 (buffer);
      td->buffer_end = grub_cpu_to_le32 (buffer_end);
    }
  else 
    {
      td->buffer = 0;
      td->buffer_end = 0;
    }

  /* Set the rest of TD */
  td->token = grub_cpu_to_le32 (token);
  td->next_td = 0;
}

static grub_usb_err_t
grub_ohci_transfer (grub_usb_controller_t dev,
		    grub_usb_transfer_t transfer)
{
  struct grub_ohci *o = (struct grub_ohci *) dev->data;
  grub_ohci_ed_t ed_virt;
  int bulk = 0;
  grub_ohci_td_t td_head_virt;
  grub_ohci_td_t td_current_virt;
  grub_ohci_td_t td_next_virt;
  grub_ohci_td_t tderr_virt = NULL;
  grub_uint32_t target;
  grub_uint32_t td_head_phys;
  grub_uint32_t td_tail_phys;
  grub_uint32_t td_last_phys;
  grub_uint32_t tderr_phys = 0;
  grub_uint32_t status;
  grub_uint32_t control;
  grub_uint8_t errcode = 0;
  grub_usb_err_t err = GRUB_USB_ERR_NONE;
  int i;
  grub_uint64_t maxtime;
  grub_uint64_t bad_OHCI_delay = 0;
  int err_halt = 0;
  int err_timeout = 0;
  int err_unrec = 0;
  grub_uint32_t intstatus;

  /* Pre-set target for ED - we need it to find proper ED */
  /* Set the device address.  */
  target = transfer->devaddr;
  /* Set the endpoint. It should be masked, we need 4 bits only. */
  target |= (transfer->endpoint & 15) << 7;
  /* Set the device speed.  */
  target |= (transfer->dev->speed == GRUB_USB_SPEED_LOW) << 13;
  /* Set the maximum packet size.  */
  target |= transfer->max << 16;

  /* Determine if transfer type is bulk - we need to select proper ED */
  switch (transfer->type)
    {
      case GRUB_USB_TRANSACTION_TYPE_BULK:
        bulk = 1;
	break;

      case GRUB_USB_TRANSACTION_TYPE_CONTROL:
        break;

      default :
        return GRUB_USB_ERR_INTERNAL;
    }

  /* Find proper ED or add new ED */
  ed_virt = grub_ohci_find_ed (o, bulk, target);
  if (!ed_virt)
    {
      grub_dprintf ("ohci","Fatal: No free ED !\n");
      return GRUB_USB_ERR_INTERNAL;
    }
  
  /* Take pointer to first TD from ED */
  td_head_phys = grub_le_to_cpu32 (ed_virt->td_head) & ~0xf;
  td_tail_phys = grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf;

  /* Sanity check - td_head should be equal to td_tail */
  if (td_head_phys != td_tail_phys) /* Should never happen ! */
    {
      grub_dprintf ("ohci", "Fatal: HEAD is not equal to TAIL !\n");
      grub_dprintf ("ohci", "HEAD = 0x%02x, TAIL = 0x%02x\n",
                    td_head_phys, td_tail_phys);
      /* XXX: Fix: What to do ? */
      return GRUB_USB_ERR_INTERNAL;
    }
  
  /* Now we should handle first TD. If ED is newly allocated,
   * we must allocate the first TD. */
  if (!td_head_phys)
    {
      td_head_virt = grub_ohci_alloc_td (o);
      if (!td_head_virt)
        return GRUB_USB_ERR_INTERNAL; /* We don't need de-allocate ED */
      /* We can set td_head only when ED is not active, i.e.
       * when it is newly allocated. */
      ed_virt->td_head = grub_cpu_to_le32 ( GRUB_OHCI_TD_VIRT2PHYS (o,
                                              td_head_virt) );
      ed_virt->td_tail = ed_virt->td_head;
    }
  else
    td_head_virt = GRUB_OHCI_TD_PHYS2VIRT ( o, td_head_phys );
    
  /* Set TDs */
  td_last_phys = td_head_phys; /* initial value to make compiler happy... */
  for (i = 0, td_current_virt = td_head_virt;
       i < transfer->transcnt; i++)
    {
      grub_usb_transaction_t tr = &transfer->transactions[i];

      grub_ohci_transaction (td_current_virt, tr->pid, tr->toggle,
			     tr->size, tr->data);

      /* Set index of TD in transfer */
      td_current_virt->tr_index = (grub_uint32_t) i;

      /* No IRQ request in TD if bad_OHCI set */
      if (o->bad_OHCI)
        td_current_virt->token |= grub_cpu_to_le32 ( 7 << 21);
      
      /* Remember last used (processed) TD phys. addr. */
      td_last_phys = GRUB_OHCI_TD_VIRT2PHYS (o, td_current_virt);
      
      /* Allocate next TD */
      td_next_virt = grub_ohci_alloc_td (o);
      if (!td_next_virt) /* No free TD, cancel transfer and free TDs except head TD */
        {
          if (i) /* if i==0 we have nothing to free... */
            grub_ohci_free_tds (o,
              GRUB_OHCI_TD_PHYS2VIRT(o,
                grub_le_to_cpu32 (td_head_virt->next_td) ) );
          /* Reset head TD */
          grub_memset ( (void*)td_head_virt, 0,
                        sizeof(struct grub_ohci_td) );
          grub_dprintf ("ohci", "Fatal: No free TD !");
          return GRUB_USB_ERR_INTERNAL;
        }

      /* Chain TDs */
      td_current_virt->link_td = (grub_uint32_t) td_next_virt;
      td_current_virt->next_td = grub_cpu_to_le32 (
                                   GRUB_OHCI_TD_VIRT2PHYS (o,
                                     td_next_virt) );
      td_next_virt->prev_td_phys = GRUB_OHCI_TD_VIRT2PHYS (o,
                                td_current_virt);
      td_current_virt = td_next_virt;
    }

  grub_dprintf ("ohci", "Tail TD (not processed) = %p\n",
                td_current_virt);
  
  /* Setup the Endpoint Descriptor for transfer.  */
  /* First set necessary fields in TARGET but keep (or set) skip bit */
  /* Note: It could be simpler if speed, format and max. packet
   * size never change after first allocation of ED.
   * But unfortunately max. packet size may change during initial
   * setup sequence and we must handle it. */
  ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
  /* Set td_tail */
  ed_virt->td_tail = grub_cpu_to_le32 ( GRUB_OHCI_TD_VIRT2PHYS (o,
                                          td_current_virt) );
  /* Now reset skip bit */
  ed_virt->target = grub_cpu_to_le32 (target);
  /* ed_virt->td_head = grub_cpu_to_le32 (td_head); Must not be changed, it is maintained by OHCI */
  /* ed_virt->next_ed = grub_cpu_to_le32 (0); Handled by grub_ohci_find_ed, do not change ! */

  grub_dprintf ("ohci", "program OHCI\n");

  /* Program the OHCI to actually transfer.  */
  switch (transfer->type)
    {
    case GRUB_USB_TRANSACTION_TYPE_BULK:
      {
	grub_dprintf ("ohci", "BULK list filled\n");
	/* Set BulkListFilled.  */
	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1 << 2);
	/* Read back of register should ensure it is really written */
	grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
	break;
      }

    case GRUB_USB_TRANSACTION_TYPE_CONTROL:
      {
	grub_dprintf ("ohci", "CONTROL list filled\n");
	/* Set ControlListFilled.  */
	grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1 << 1);
	/* Read back of register should ensure it is really written */
	grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
	break;
      }
    }

  /* Safety measure to avoid a hang. */
  maxtime = grub_get_time_ms () + 1000;
	
  /* Wait until the transfer is completed or STALLs.  */
  do
    {
      /* Check transfer status */
      intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
      if (!o->bad_OHCI && (intstatus & 0x2) != 0)
        {
          /* Remember last successful TD */
          tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
          /* Reset DoneHead */
	  o->hcca->donehead = 0;
          grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
          /* Read back of register should ensure it is really written */
          grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
          /* if TD is last, finish */
          if (tderr_phys == td_last_phys)
            {
              if (grub_le_to_cpu32 (ed_virt->td_head) & 1)
                err_halt = 1;
              break;
            }
	  continue;
        }

      if ((intstatus & 0x10) != 0)
        { /* Unrecoverable error - only reset can help...! */
          err_unrec = 1;
          break;
        }

      /* Detected a HALT.  */
      if (err_halt || (grub_le_to_cpu32 (ed_virt->td_head) & 1))
        {
          err_halt = 1;
          /* ED is halted, but donehead event can happened in the meantime */
          intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
          if (!o->bad_OHCI && (intstatus & 0x2) != 0)
            /* Don't break loop now, first do donehead action(s) */
            continue;
          break;
        }

      /* bad OHCI handling */
      if ( (grub_le_to_cpu32 (ed_virt->td_head) & ~0xf) ==
           (grub_le_to_cpu32 (ed_virt->td_tail) & ~0xf) ) /* Empty ED */
        {
          if (o->bad_OHCI) /* Bad OHCI detected previously */
            {
              /* Try get last successful TD. */
              tderr_phys = grub_le_to_cpu32 (o->hcca->donehead) & ~0xf;
              if (tderr_phys)/* Reset DoneHead if we were successful */
                {
                  o->hcca->donehead = 0;
                  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
                  /* Read back of register should ensure it is really written */
                  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
                }
              break;
            }
          else /* Detection of bad OHCI */
            /* We should wait short time (~2ms) before we say that
             * it is bad OHCI to prevent some hazard -
             * donehead can react in the meantime. This waiting is done
             * only once per OHCI driver "live cycle". */
            if (!bad_OHCI_delay) /* Set delay time */
              bad_OHCI_delay = grub_get_time_ms () + 2;
            else if (grub_get_time_ms () >= bad_OHCI_delay)
              o->bad_OHCI = 1;
          continue;
        }
        
      /* Timeout ? */
      if (grub_get_time_ms () > maxtime)
      	{
      	  err_timeout = 1;
      	  break;
      	}

      grub_cpu_idle ();
    }
  while (1);

  /* There are many ways how the loop above can finish:
   * - normally without any error via INTSTATUS WDH bit
   *   : tderr_phys == td_last_phys, td_head == td_tail
   * - normally with error via HALT bit in ED TD HEAD
   *   : td_head = next TD after TD with error
   *   : tderr_phys = last processed and retired TD with error,
   *     i.e. should be != 0
   *   : if bad_OHCI == TRUE, tderr_phys will be probably invalid
   * - unrecoverable error - I never seen it but it could be
   *   : err_unrec == TRUE, other values can contain anything...
   * - timeout, it can be caused by:
   *  -- bad USB device - some devices have some bugs, see Linux source
   *     and related links
   *  -- bad OHCI controller - e.g. lost interrupts or does not set
   *     proper bits in INTSTATUS when real IRQ not enabled etc.,
   *     see Linux source and related links
   *     One known bug is handled - if transfer finished
   *     successfully (i.e. HEAD==TAIL, last transfer TD is retired,
   *     HALT bit is not set) and WDH bit is not set in INTSTATUS - in
   *     this case we set o->bad_OHCI=TRUE and do alternate loop
   *     and error handling - but there is problem how to find retired
   *     TD with error code if HALT occurs and if DONEHEAD is not
   *     working - we need to find TD previous to current ED HEAD
   *  -- bad code of this driver or some unknown reasons - :-(
   *     it can be e.g. bad handling of EDs/TDs/toggle bit...
   */

  /* Remember target for debug and set skip flag in ED */
  /* It should be normaly not necessary but we need it at least
   * in case of timeout */
  target = grub_le_to_cpu32 ( ed_virt->target );
  ed_virt->target = grub_cpu_to_le32 (target | (1 << 14));
  /* Read registers for debug - they should be read now because
   * debug prints case unwanted delays, so something can happen
   * in the meantime... */
  control = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL);
  status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
  intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
  /* Now print debug values - to have full info what happened */
  grub_dprintf ("ohci", "loop finished: control=0x%02x status=0x%02x\n",
		control, status);
  grub_dprintf ("ohci", "intstatus=0x%02x \n\t\t tderr_phys=0x%02x, td_last_phys=0x%02x\n",
		intstatus, tderr_phys, td_last_phys);
  grub_dprintf ("ohci", "err_unrec=%d, err_timeout=%d \n\t\t err_halt=%d, bad_OHCI=%d\n",
		err_unrec, err_timeout, err_halt, o->bad_OHCI);
  grub_dprintf ("ohci", "TARGET=0x%02x, HEAD=0x%02x, TAIL=0x%02x\n",
                target,
                grub_le_to_cpu32 (ed_virt->td_head),
                grub_le_to_cpu32 (ed_virt->td_tail) );

  if (!err_halt && !err_unrec && !err_timeout) /* normal finish */
    {
      /* Simple workaround if donehead is not working */
      if (o->bad_OHCI &&
           ( !tderr_phys || (tderr_phys != td_last_phys) ) )
        {
          grub_dprintf ("ohci", "normal finish, but tderr_phys corrected\n");
          tderr_phys = td_last_phys;
          /* I hope we can do it as transfer (most probably) finished OK */
        }
      /* Prepare pointer to last processed TD */
      tderr_virt = GRUB_OHCI_TD_PHYS2VIRT (o, tderr_phys);
      /* Set index of last processed TD */
      if (tderr_virt)
        transfer->last_trans = tderr_virt->tr_index;
      else
        transfer->last_trans = -1;
    }

  else if (err_halt) /* error, ED is halted by OHCI, i.e. can be modified */
    {
      /* First we must get proper tderr_phys value */
      if (o->bad_OHCI) /* In case of bad_OHCI tderr_phys can be wrong */
        {
          if ( tderr_phys ) /* check if tderr_phys points to TD with error */
            errcode = grub_le_to_cpu32 ( GRUB_OHCI_TD_PHYS2VIRT (o,
                                           tderr_phys)->token )
                      >> 28;
          if ( !tderr_phys || !errcode ) /* tderr_phys not valid or points to wrong TD */
            { /* Retired TD with error should be previous TD to ED->td_head */
              tderr_phys = GRUB_OHCI_TD_PHYS2VIRT (o,
                             grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf )
                           ->prev_td_phys;
            }
        }

      /* Prepare pointer to last processed TD and get error code */
      tderr_virt = GRUB_OHCI_TD_PHYS2VIRT (o, tderr_phys);
      /* Set index of last processed TD */
      if (tderr_virt)
        {
          errcode = grub_le_to_cpu32 ( tderr_virt->token ) >> 28;
          transfer->last_trans = tderr_virt->tr_index;
        }
      else
        transfer->last_trans = -1;

      /* Evaluation of error code */
      grub_dprintf ("ohci", "OHCI tderr_phys=0x%02x, errcode=0x%02x\n",
                    tderr_phys, errcode);
      switch (errcode)
	{
	case 0:
	  /* XXX: Should not happen!  */
	  grub_error (GRUB_ERR_IO, "OHCI without reporting the reason");
	  err = GRUB_USB_ERR_INTERNAL;
	  break;

	case 1:
	  /* XXX: CRC error.  */
	  err = GRUB_USB_ERR_TIMEOUT;
	  break;

	case 2:
	  err = GRUB_USB_ERR_BITSTUFF;
	  break;

	case 3:
	  /* XXX: Data Toggle error.  */
	  err = GRUB_USB_ERR_DATA;
	  break;

	case 4:
	  err = GRUB_USB_ERR_STALL;
	  break;

	case 5:
	  /* XXX: Not responding.  */
	  err = GRUB_USB_ERR_TIMEOUT;
	  break;

	case 6:
	  /* XXX: PID Check bits failed.  */
	  err = GRUB_USB_ERR_BABBLE;
	  break;

	case 7:
	  /* XXX: PID unexpected failed.  */
	  err = GRUB_USB_ERR_BABBLE;
	  break;

	case 8:
	  /* XXX: Data overrun error.  */
	  err = GRUB_USB_ERR_DATA;
	  grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n",
	                tderr_virt, tderr_virt->tr_index);
	  break;

	case 9:
	  /* XXX: Data underrun error.  */
	  err = GRUB_USB_ERR_DATA;
	  grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
	                tderr_virt, tderr_virt->tr_index);
	  grub_dprintf ("ohci", "Underrun, number of not transferred bytes: %d\n",
	                1 + grub_le_to_cpu32 (tderr_virt->buffer_end)
	                  - grub_le_to_cpu32 (tderr_virt->buffer));
	  break;

	case 10:
	  /* XXX: Reserved.  */
	  err = GRUB_USB_ERR_NAK;
	  break;

	case 11:
	  /* XXX: Reserved.  */
	  err = GRUB_USB_ERR_NAK;
	  break;

	case 12:
	  /* XXX: Buffer overrun.  */
	  err = GRUB_USB_ERR_DATA;
	  break;

	case 13:
	  /* XXX: Buffer underrun.  */
	  err = GRUB_USB_ERR_DATA;
	  break;

	default:
	  err = GRUB_USB_ERR_NAK;
	  break;
	}

      /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
      ed_virt->td_head = ed_virt->td_tail & ~0xf; 
    }
        
  else if (err_unrec)      
    {
      /* Don't try to get error code and last processed TD for proper
       * toggle bit value - anything can be invalid */
      err = GRUB_USB_ERR_UNRECOVERABLE;
      grub_dprintf("ohci", "Unrecoverable error!");

      /* Do OHCI reset in case of unrecoverable error - maybe we will need
       * do more - re-enumerate bus etc. (?) */

      /* Suspend the OHCI by issuing a reset.  */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic.  */
      /* Read back of register should ensure it is really written */
      grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS);
      grub_millisleep (1);
      grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");

      /* Misc. resets. */
      ed_virt->td_head = ed_virt->td_tail & ~0xf; /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
      o->hcca->donehead = 0;
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
      /* Read back of register should ensure it is really written */
      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);

      /* Enable the OHCI.  */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
                            (2 << 6)
                            | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
                            | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
    } 

  else if (err_timeout)
    {
      /* In case of timeout do not detect error from TD */    
      err = GRUB_ERR_TIMEOUT;
      grub_dprintf("ohci", "Timeout !\n");

      /* We should wait for next SOF to be sure that ED is unaccessed
       * by OHCI */
      /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
      /* Wait for new SOF */
      while ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0);

      /* Now we must find last processed TD if bad_OHCI == TRUE */
      if (o->bad_OHCI)
        { /* Retired TD with error should be previous TD to ED->td_head */
          tderr_phys = GRUB_OHCI_TD_PHYS2VIRT (o,
                         grub_le_to_cpu32 ( ed_virt->td_head) & ~0xf)
                       ->prev_td_phys;
        }
      tderr_virt = GRUB_OHCI_TD_PHYS2VIRT (o, tderr_phys);
      if (tderr_virt)
        transfer->last_trans = tderr_virt->tr_index;
      else
        transfer->last_trans = -1;

      /* Set empty ED - set HEAD = TAIL = last (not processed) TD */
      ed_virt->td_head = ed_virt->td_tail & ~0xf; 
    }

  /* At this point always should be:
   * ED has skip bit set and halted or empty or after next SOF,
   *    i.e. it is safe to free all TDs except last not processed
   * ED HEAD == TAIL == phys. addr. of td_current_virt */

  /* Reset DoneHead - sanity cleanup */
  o->hcca->donehead = 0;
  grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
  /* Read back of register should ensure it is really written */
  grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);

  /* Un-chainig of last TD */
  if (td_current_virt->prev_td_phys)
    {
      grub_ohci_td_t td_prev_virt
                = GRUB_OHCI_TD_PHYS2VIRT (o, td_current_virt->prev_td_phys);

      td_next_virt = (grub_ohci_td_t) td_prev_virt->link_td;
      if (td_current_virt == td_next_virt)
        td_prev_virt->link_td = 0;
    }

  grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x, errcode=0x%02x\n",
                err, errcode);
  grub_ohci_free_tds (o, td_head_virt);

  return err;
}

static grub_err_t
grub_ohci_portstatus (grub_usb_controller_t dev,
		      unsigned int port, unsigned int enable)
{
   struct grub_ohci *o = (struct grub_ohci *) dev->data;

   grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
                 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));

   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
			 GRUB_OHCI_SET_PORT_RESET);
   grub_millisleep (50); /* For root hub should be nominaly 50ms */
 
    /* End the reset signaling.  */
   grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
			 GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
   grub_millisleep (10);

   if (enable)
     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
			   GRUB_OHCI_SET_PORT_ENABLE);
   else
     grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
			   GRUB_OHCI_CLEAR_PORT_ENABLE);
   grub_millisleep (10);

   grub_dprintf ("ohci", "end of portstatus=0x%02x\n",
		 grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
 
   return GRUB_ERR_NONE;
}

static grub_usb_speed_t
grub_ohci_detect_dev (grub_usb_controller_t dev, int port)
{
   struct grub_ohci *o = (struct grub_ohci *) dev->data;
   grub_uint32_t status;

   status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);

   grub_dprintf ("ohci", "detect_dev status=0x%02x\n", status);

   if (! (status & 1))
     return GRUB_USB_SPEED_NONE;
   else if (status & (1 << 9))
     return GRUB_USB_SPEED_LOW;
   else
     return GRUB_USB_SPEED_FULL;
}

static int
grub_ohci_hubports (grub_usb_controller_t dev)
{
  struct grub_ohci *o = (struct grub_ohci *) dev->data;
  grub_uint32_t portinfo;

  portinfo = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA);

  grub_dprintf ("ohci", "root hub ports=%d\n", portinfo & 0xFF);

  /* The root hub has exactly two ports.  */
  return portinfo & 0xFF;
}

static grub_err_t
grub_ohci_fini_hw (int noreturn __attribute__ ((unused)))
{
  struct grub_ohci *o;

  for (o = ohci; o; o = o->next)
    {
      int i, nports = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBA) & 0xff;
      grub_uint64_t maxtime;

      /* Set skip in all EDs */
      if (o->ed_bulk)
        for (i=0; i < GRUB_OHCI_BULK_EDS; i++)
          o->ed_bulk[i].target |= grub_cpu_to_le32 (1 << 14); /* skip */
      if (o->ed_ctrl)
        for (i=0; i < GRUB_OHCI_CTRL_EDS; i++)
          o->ed_ctrl[i].target |= grub_cpu_to_le32 (1 << 14); /* skip */

      /* We should wait for next SOF to be sure that all EDs are
       * unaccessed by OHCI. But OHCI can be non-functional, so
       * more than 1ms timeout have to be applied. */
      /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
      maxtime = grub_get_time_ms () + 2;
      /* Wait for new SOF or timeout */
      while ( ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4)
                 == 0) || (grub_get_time_ms () >= maxtime) );

      for (i = 0; i < nports; i++)
	grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + i,
			      GRUB_OHCI_CLEAR_PORT_ENABLE);

      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1);
      grub_millisleep (1);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_DONEHEAD, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, 0);
      /* Read back of register should ensure it is really written */
      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);

#if 0 /* Is this necessary before booting? Probably not .(?)
       * But it must be done if module is removed ! (Or not ?)
       * How to do it ? - Probably grub_ohci_restore_hw should be more
       * complicated. (?)
       * (If we do it, we need to reallocate EDs and TDs in function
       * grub_ohci_restore_hw ! */

      /* Free allocated EDs and TDs */
      grub_dma_free (o->td_chunk);
      grub_dma_free (o->ed_bulk_chunk);
      grub_dma_free (o->ed_ctrl_chunk);
      grub_dma_free (o->hcca_chunk);
#endif
    }
  grub_millisleep (10);

  return GRUB_ERR_NONE;
}

static grub_err_t
grub_ohci_restore_hw (void)
{
  struct grub_ohci *o;

  for (o = ohci; o; o = o->next)
    {
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_HCCA, o->hcca_addr);
      o->hcca->donehead = 0;
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLHEAD, o->ed_ctrl_addr);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 0);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, o->ed_bulk_addr);
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
      /* Read back of register should ensure it is really written */
      grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);

      /* Enable the OHCI.  */
      grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL,
                            (2 << 6)
                            | GRUB_OHCI_REG_CONTROL_CONTROL_ENABLE
                            | GRUB_OHCI_REG_CONTROL_BULK_ENABLE );
    }

  return GRUB_ERR_NONE;
}

\f
static struct grub_usb_controller_dev usb_controller =
{
  .name = "ohci",
  .iterate = grub_ohci_iterate,
  .transfer = grub_ohci_transfer,
  .hubports = grub_ohci_hubports,
  .portstatus = grub_ohci_portstatus,
  .detect_dev = grub_ohci_detect_dev
};

GRUB_MOD_INIT(ohci)
{
  grub_ohci_inithw ();
  grub_usb_controller_dev_register (&usb_controller);
  grub_loader_register_preboot_hook (grub_ohci_fini_hw, grub_ohci_restore_hw,
				     GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
}

GRUB_MOD_FINI(ohci)
{
  grub_ohci_fini_hw (0);
  grub_usb_controller_dev_unregister (&usb_controller);
}

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices - faster OHCI
  2010-06-12 17:05                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices - faster OHCI Aleš Nesrsta
@ 2010-06-12 18:59                                                 ` Aleš Nesrsta
  0 siblings, 0 replies; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-12 18:59 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi,

I am sorry, there is small mistake inside ohci.c - I forgot delete
testing code in lines 442-443 in ohci.c, code

442 /* Test - remove it !!! */
443 o->bad_OHCI = 1;

should be removed !
(Code is functional also with this mistake but in "emergency" mode,
o->bad_OHCI == TRUE enables workarounds for broken OHCI reported by
Vladimir.)

Best regards
Ales



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-02 20:14                                             ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-06-03 11:47                                               ` Aleš Nesrsta
  2010-06-12 17:05                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices - faster OHCI Aleš Nesrsta
@ 2010-06-13 17:47                                               ` Aleš Nesrsta
  2010-06-21 20:24                                                 ` Aleš Nesrsta
  2 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-13 17:47 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi Vladimir,

one more idea:

Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> Hello, Aleš. Today I merged usb branch into mainline. Now I've started
> some work towards hotplugging and module autoloading. Currently it just
> checks portsstatus to see newly connected devices. Touble is that if you
> disconnect a device and plug a new one at its place this routine won't
> notice anything because ports are polled only when enumerating
> usbdevices. We need a more reliable way to detect new devices. Perhaps
> we should try to assign addresses to any device without address and see
> if it responds to configuration request. If noone responds assume that
> no devices were newly connected.
> On Yeeloong 'usb' command lists hotplugged devices correctly but bulk
> transfer fails with donehead=0.

It can be result of bad toggling. If device does not accept
configuration, it does not reset toggle bits in its bulk endpoints.
But function grub_usb_set_configuration does reset of toggle bits in any
case. Perhaps function grub_usb_set_configuration should reset toggling
only if configuration is accepted by device.

Best regards
Ales




^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-13 17:47                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices Aleš Nesrsta
@ 2010-06-21 20:24                                                 ` Aleš Nesrsta
  2010-06-25 18:10                                                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 41+ messages in thread
From: Aleš Nesrsta @ 2010-06-21 20:24 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi Vladimir,

now I have working SSH, so I committed my last work into usb branch
(commented as "Faster OHCI, USB hub support, UHCI portstatus corr.",
revision 2427).

(Sorry if it is not the right way how to cooperate - should I make
another, my own branch? Or wait first for your agreement about notified
patches? Or...?)

Best regards
Ales

> Hi Vladimir,
> 
> one more idea:
> 
> Vladimir 'φ-coder/phcoder' Serbinenko wrote:
> > Hello, Aleš. Today I merged usb branch into mainline. Now I've started
> > some work towards hotplugging and module autoloading. Currently it just
> > checks portsstatus to see newly connected devices. Touble is that if you
> > disconnect a device and plug a new one at its place this routine won't
> > notice anything because ports are polled only when enumerating
> > usbdevices. We need a more reliable way to detect new devices. Perhaps
> > we should try to assign addresses to any device without address and see
> > if it responds to configuration request. If noone responds assume that
> > no devices were newly connected.
> > On Yeeloong 'usb' command lists hotplugged devices correctly but bulk
> > transfer fails with donehead=0.
> 
> It can be result of bad toggling. If device does not accept
> configuration, it does not reset toggle bits in its bulk endpoints.
> But function grub_usb_set_configuration does reset of toggle bits in any
> case. Perhaps function grub_usb_set_configuration should reset toggling
> only if configuration is accepted by device.
> 
> Best regards
> Ales
> 
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
> 



^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices
  2010-06-21 20:24                                                 ` Aleš Nesrsta
@ 2010-06-25 18:10                                                   ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 0 replies; 41+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-06-25 18:10 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 2417 bytes --]

>
> Hi Vladimir,
>
> now I have working SSH, so I committed my last work into usb branch
> (commented as "Faster OHCI, USB hub support, UHCI portstatus corr.",
> revision 2427).
>
> (Sorry if it is not the right way how to cooperate - should I make
> another, my own branch? Or wait first for your agreement about notified
> patches? Or...?)
>
>   
It's the right way. If something is in branches/ as opposed to personal
directory it means that every dev is welcome to commit but it's highly
recommended to be present at IRC at the moment of commit and ping
original author if he's only too. The only exception is experimental
branch which is reviewed.
If you want more comments on your changes please send patches to ML
> Best regards
> Ales
>
>   
>> Hi Vladimir,
>>
>> one more idea:
>>
>> Vladimir 'φ-coder/phcoder' Serbinenko wrote:
>>     
>>> Hello, Aleš. Today I merged usb branch into mainline. Now I've started
>>> some work towards hotplugging and module autoloading. Currently it just
>>> checks portsstatus to see newly connected devices. Touble is that if you
>>> disconnect a device and plug a new one at its place this routine won't
>>> notice anything because ports are polled only when enumerating
>>> usbdevices. We need a more reliable way to detect new devices. Perhaps
>>> we should try to assign addresses to any device without address and see
>>> if it responds to configuration request. If noone responds assume that
>>> no devices were newly connected.
>>> On Yeeloong 'usb' command lists hotplugged devices correctly but bulk
>>> transfer fails with donehead=0.
>>>       
>> It can be result of bad toggling. If device does not accept
>> configuration, it does not reset toggle bits in its bulk endpoints.
>> But function grub_usb_set_configuration does reset of toggle bits in any
>> case. Perhaps function grub_usb_set_configuration should reset toggling
>> only if configuration is accepted by device.
>>
>> Best regards
>> Ales
>>
>>
>>
>> _______________________________________________
>> Grub-devel mailing list
>> Grub-devel@gnu.org
>> http://lists.gnu.org/mailman/listinfo/grub-devel
>>
>>     
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel
>
>   


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 294 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2010-06-25 18:11 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-14 22:23 [Patch] [bug #26237] multiple problems with usb devices Aleš Nesrsta
2010-04-02 20:46 ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-04-07 19:49   ` Aleš Nesrsta
2010-04-07 20:39     ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-04-08 21:27       ` Aleš Nesrsta
2010-04-16 16:39         ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-04-20 20:14           ` Aleš Nesrsta
2010-04-24 19:50             ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-04-28  8:20               ` Aleš Nesrsta
2010-05-09 13:50                 ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-21 23:46             ` Aleš Nesrsta
2010-05-22  1:20               ` Grégoire Sutre
2010-05-22  9:35               ` Colin Watson
2010-05-22 23:13               ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-23 10:27                 ` Aleš Nesrsta
2010-05-23 14:41                   ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-23 17:42                     ` Aleš Nesrsta
2010-05-23 18:40                       ` Thomas Schmitt
2010-05-23 17:51                     ` [Patch] [bug #26237] " Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-23 14:44                   ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-23 17:16                     ` Aleš Nesrsta
2010-05-23 19:35                       ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-23 20:41                         ` seth.goldberg
2010-05-25 18:58                         ` Aleš Nesrsta
2010-05-30 16:51                           ` Aleš Nesrsta
2010-05-30 22:26                             ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-31  4:16                               ` Seth Goldberg
2010-05-31 12:23                               ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-05-31 21:54                                 ` Aleš Nesrsta
2010-06-01  0:18                                   ` [RFT] " Vladimir 'φ-coder/phcoder' Serbinenko
2010-06-01 21:14                                     ` Aleš Nesrsta
2010-06-02  0:27                                       ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-06-02  3:02                                         ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-06-02 19:39                                           ` Aleš Nesrsta
2010-06-02 20:14                                             ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-06-03 11:47                                               ` Aleš Nesrsta
2010-06-12 17:05                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices - faster OHCI Aleš Nesrsta
2010-06-12 18:59                                                 ` Aleš Nesrsta
2010-06-13 17:47                                               ` [RFT] Re: [Patch] [bug #26237] multiple problems with usb devices Aleš Nesrsta
2010-06-21 20:24                                                 ` Aleš Nesrsta
2010-06-25 18:10                                                   ` Vladimir 'φ-coder/phcoder' Serbinenko

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.