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 19:51:09 +0200
User-agent: Mozilla-Thunderbird 2.0.0.22 (X11/20091109)

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

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

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

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

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

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

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

   return GRUB_ERR_NONE;
}




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

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

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

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

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

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

=== modified file 'disk/scsi.c'
--- disk/scsi.c 2010-03-05 14:29:28 +0000
+++ disk/scsi.c 2010-05-23 13:08:06 +0000
@@ -25,6 +25,7 @@
 #include <grub/types.h>
 #include <grub/scsi.h>
 #include <grub/scsicmd.h>
+#include <grub/time.h>
 
 
 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 17:32:21 +0000
@@ -189,7 +189,7 @@
   const char *debug = grub_env_get ("debug");
 
   if (! debug)
-    return;
+    debug = "all";
 
   if (grub_strword (debug, "all") || grub_strword (debug, condition))
     {

Attachment: signature.asc
Description: OpenPGP digital signature


reply via email to

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