grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Patch] [bug #26237] multiple problems with usb devices


From: Vladimir 'φ-coder/phcoder' Serbinenko
Subject: Re: [Patch] [bug #26237] multiple problems with usb devices
Date: Sun, 23 May 2010 16:41:19 +0200
User-agent: Mozilla-Thunderbird 2.0.0.22 (X11/20091109)

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
> address@hidden
> http://lists.gnu.org/mailman/listinfo/grub-devel


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

=== 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>
 
 
 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;
-
-  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");
+
 }
 
 
@@ -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: signature.asc
Description: OpenPGP digital signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]