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

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>
 
 
 static grub_scsi_dev_t grub_scsi_dev_list;
@@ -50,7 +51,62 @@
 }
 
 
-/* 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)
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to