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: Mon, 31 May 2010 00:26:18 +0200
User-agent: Mozilla-Thunderbird 2.0.0.22 (X11/20091109)

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

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


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

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

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

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

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

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

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

=== modified file 'disk/scsi.c'
--- disk/scsi.c 2010-03-05 14:29:28 +0000
+++ disk/scsi.c 2010-05-23 13:08:06 +0000
@@ -25,6 +25,7 @@
 #include <grub/types.h>
 #include <grub/scsi.h>
 #include <grub/scsicmd.h>
+#include <grub/time.h>
 
 
 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 'hello/hello.c'
--- hello/hello.c       2010-01-03 18:50:51 +0000
+++ hello/hello.c       2010-05-25 21:24:37 +0000
@@ -31,7 +31,12 @@
                int argc __attribute__ ((unused)),
                char **args __attribute__ ((unused)))
 {
-  grub_printf ("Hello World\n");
+  grub_uint32_t r0;
+  asm volatile (".set mips64");
+  asm volatile ("mfc0 %0, $16, 0" : "=r" (r0));
+  asm volatile (".set mips3");
+
+  grub_printf ("%08x\n", r0);
   return 0;
 }
 

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

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

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

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

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

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

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