[Top][All Lists]
[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))
{
signature.asc
Description: OpenPGP digital signature
- Re: [Patch] [bug #26237] multiple problems with usb devices, Vladimir 'φ-coder/phcoder' Serbinenko, 2010/05/09
- Re: [Patch] [bug #26237] multiple problems with usb devices, Aleš Nesrsta, 2010/05/21
- Re: [Patch] [bug #26237] multiple problems with usb devices, Vladimir 'φ-coder/phcoder' Serbinenko, 2010/05/23
- Re: [Patch] [bug #26237] multiple problems with usb devices, Aleš Nesrsta, 2010/05/23
- Re: [Patch] [bug #26237] multiple problems with usb devices, Vladimir 'φ-coder/phcoder' Serbinenko, 2010/05/23
- Re: [Patch] [bug #26237] multiple problems with usb devices, address@hidden, 2010/05/23
- Re: [Patch] [bug #26237] multiple problems with usb devices, Aleš Nesrsta, 2010/05/25
- Re: [Patch] [bug #26237] multiple problems with usb devices, Aleš Nesrsta, 2010/05/30
- Re: [Patch] [bug #26237] multiple problems with usb devices, Vladimir 'φ-coder/phcoder' Serbinenko, 2010/05/30
- Re: [Patch] [bug #26237] multiple problems with usb devices, Seth Goldberg, 2010/05/31
- Re: [Patch] [bug #26237] multiple problems with usb devices, Vladimir 'φ-coder/phcoder' Serbinenko, 2010/05/31
- Re: [Patch] [bug #26237] multiple problems with usb devices, Aleš Nesrsta, 2010/05/31