Hi,
there is some new patch which includes:
- OHCI power and configuration counter check patch sent by Vladimir in
the meantime
- simple handling of unrecoverable OHCI error (not tested yet - I cannot
set OHCI into unrec. error state...)
- (I hope) proper toggle bit handling also in error states - ohci.c,
uhci.c, usbtrans.c, usbtrans.h
- some changes in OHCI DoneHead handling (it is partialy related to
toggle bit handling)
- some changes in bulk transfers error handling in usbms, should be more
close to specification now
- scsi.c simple workaround related to problem with device which cannot
transfer 4KB blocks - but it is disabled. It works but transfer is very
slow when this workaround is active - I don't know why. It is little bit
surprising - device works better without this workaround... So I
disabled it.
Patch is made against files taken from revision 2391 (should be the same
as in 1.98 release) patched by my previous patch usb_patch_100523_0.
Don't use my intermediate "toggle" patch from 25 May 2010.
Remaining problems:
1.
Some devices (at least my BUFFALO USB clip drive flash disk, more
precisely "ID 0ea0:2168 Ours Technology, Inc. Transcend JetFlash 2.0 /
Astone USB Drive") cannot transfer 4KB data blocks independent on
controller OHCI/UHCI - my workaround is not good.
But it is probably problem with low priority - device is working finally
but it is slower than another device.
2.
Some devices (at least my APACER cardreader "ID 058f:6366 Alcor Micro
Corp. Multi Flash Reader") are not working on UHCI. Such device does not
accept any control message and UHCI returns status 0x450007 - it means
STALL during sending SETUP packet.
It looks to be the same problem as described by Vladimir: "I have
somewhat similar issue with Geode OHCI controller right now: devices and
speeds are correctly seen but trying to send a message results in a halt
in first TD and error code 5.".
But I have problem on UHCI, not on OHCI - on computer with OHCI is this
device working well (it is normal USB Mass Storage Bulk-Only device with
SCSI subclass)! Maybe it depends on "combined controllers" UHCI-EHCI,
OHCI-EHCI (?) - device is working on computer with OHCI only computer
and it is not working on computer with UHCI-EHCI controller. But any
other device is working well on both computers... I don't understand, I
currently have no idea what can be wrong. Does anybody know...?
3.
There is not working USB hub support, GRUB does not see device connected
via USB hub - does anybody know some details or have some specification
of USB Hub class ? I cannot find it on USB site (maybe I have not
sufficient patience...).
I will probably focus in OHCI speed-up now, i.e. I try to do some other
handling of ED to prevent changes in OHCI registers which are slowing
down OHCI performance (OHCI is approx. 3 times slower than UHCI now from
this reason).
Best regards
Ales
diff -urB ./grub/bus/usb/ohci.c ./grub_patched/bus/usb/ohci.c
--- ./grub/bus/usb/ohci.c 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/bus/usb/ohci.c 2010-05-28 19:02:38.000000000 +0200
@@ -103,6 +103,11 @@
#define GRUB_OHCI_RHUB_PORT_POWER_MASK 0x300
#define GRUB_OHCI_RHUB_PORT_ALL_POWERED 0x200
+#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_uint32_t
grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
{
@@ -234,7 +239,7 @@
/* 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_INTSTATUS, 0x7f); /* Clears everything */
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);
@@ -351,8 +356,10 @@
break;
}
+#if 0 /* Always generate interrupt */
/* Generate no interrupts. */
token |= 7 << 21;
+#endif
/* Set the token. */
token |= toggle << 24;
@@ -395,9 +402,11 @@
grub_usb_err_t err;
grub_uint8_t errcode = 0;
grub_ohci_td_t tderr = NULL;
- int i, j;
+ int i;
grub_uint64_t maxtime;
int err_timeout = 0;
+ int err_unrec = 0;
+ grub_uint32_t intstatus;
/* Allocate an Endpoint Descriptor. */
ed = grub_memalign (16, sizeof (*ed));
@@ -424,11 +433,13 @@
td_list[i].next_td = grub_cpu_to_le32 (&td_list[i + 1]);
}
+#if 0 /* Better will be enable interrupt on all TDs. */
/* 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);
+#endif
td_list[transfer->transcnt].token = 0;
td_list[transfer->transcnt].buffer = 0;
@@ -530,44 +541,49 @@
}
grub_dprintf ("ohci", "wait for completion\n");
- grub_dprintf ("ohci", "control=0x%02x status=0x%02x\n",
+ grub_dprintf ("ohci", "begin: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n",
grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
- grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS));
+ grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS),
+ grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
/* Starting time for timeout - feel free to change the value,
* I have no idea about correct value ...
* It is workaround only because it looks like my OHCI does not
- * inicate properly STALL or NAK (or both...) - or there is
+ * indicate properly STALL or NAK (or both...) - or there is
* some mistake somewhere... */
maxtime = grub_get_time_ms () + 1000;
-
+
/* Wait until the transfer is completed or STALLs. */
do
{
- grub_cpu_idle ();
-
- /* Detected a HALT. */
- if (grub_le_to_cpu32 (ed->td_head) & 1)
- break;
-
- if ((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x2) != 0)
+ /* Check transfer status */
+ intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
+ if ((intstatus & 0x2) != 0)
{
- if ((grub_le_to_cpu32 (o->hcca->donehead) & ~0xf)
- == (grub_uint32_t) &td_list[transfer->transcnt - 1])
- break;
-
- /* Done Head can be updated on some another place if ED is halted. */
- if (grub_le_to_cpu32 (ed->td_head) & 1)
- break;
-
- /* If there is not HALT in ED, it is not correct, so debug it, reset
- * donehead and WDH and continue waiting. */
- grub_dprintf ("ohci", "Incorrect HccaDoneHead=0x%08x\n",
+ grub_dprintf ("ohci", "Current HccaDoneHead=0x%08x\n",
o->hcca->donehead);
+ /* Remember last successful TD */
+ tderr = (grub_ohci_td_t)
+ (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
+ /* Reset DoneHead */
o->hcca->donehead = 0;
grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1 << 1));
+ /* if TD is last, finish */
+ if (tderr == (grub_ohci_td_t) &td_list[transfer->transcnt - 1])
+ break;
continue;
}
+
+ if ((intstatus & 0x10) != 0)
+ { /* Unrecoverable error - only reset can help...! */
+ err_unrec = 1;
+ break;
+ }
+
+ /* Detected a HALT. */
+ if (grub_le_to_cpu32 (ed->td_head) & 1)
+ break;
+
/* Timeout ? */
if (grub_get_time_ms () > maxtime)
{
@@ -577,9 +593,49 @@
err_timeout = 1;
break;
}
+
+ grub_cpu_idle ();
}
while (1);
+
+ grub_dprintf ("ohci", "end: control=0x%02x status=0x%02x\n\t\t intstatus=0x%02x\n",
+ grub_ohci_readreg32 (o, GRUB_OHCI_REG_CONTROL),
+ grub_ohci_readreg32 (o, GRUB_OHCI_REG_CMDSTATUS),
+ grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS));
+
+ if (!tderr)
+ {
+ /* It means that something wrong happened,
+ * it could be:
+ * - timeout and no TD processed
+ * - some or unrecoverable error and no TD processed
+ * - something unexpected... :-( */
+ /* Try look into DONEHEAD reg., but there should be also zero */
+ grub_dprintf("ohci", "HCCA DoneHead is zero, something is bad!\n");
+ tderr = (grub_ohci_td_t)
+ (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
+ }
+
+ /* Remember last processed transaction (TD) - it is necessary for
+ * proper setting of toggle bit in next transaction. */
+ transfer->last_trans = ((grub_uint32_t)tderr - (grub_uint32_t)td_list) / sizeof (*td_list);
+ /* The expression above maybe will not work on Yeeloong because
+ * of DMA memory mapping. (?) I.e., maybe we will need to have some
+ * additional informations in TDs. */
+
+ /* Check correct value in last_trans */
+ /* It could happen if timeout happens and no TD was retired */
+ if (transfer->last_trans >= transfer->transcnt || !tderr)
+ {
+ grub_dprintf("ohci", "tder==0 or out of TDs range!\n");
+ grub_dprintf("ohci", "tderr=%p, td_list=%p,\n\t\t last_trans=%d, transcnt=%d\n",
+ tderr, td_list, transfer->last_trans, transfer->transcnt);
+ /* We should set something valid... */
+ transfer->last_trans = -1; /* Probably no TD done */
+ tderr = &td_list[0];
+ }
+ /* In case of timeout do not detect error from TD */
if (err_timeout)
{
err = GRUB_ERR_TIMEOUT;
@@ -590,15 +646,19 @@
grub_le_to_cpu32(ed->next_ed));
}
+ /* In case of unrecoverable error do not detect error from TD */
+ else if (err_unrec)
+ {
+ err = GRUB_USB_ERR_UNRECOVERABLE;
+ grub_dprintf("ohci", "Unrecoverable error, target=%08x, head=%08x\n\t\ttail=%08x, next=%08x\n",
+ grub_le_to_cpu32(ed->target),
+ grub_le_to_cpu32(ed->td_head),
+ grub_le_to_cpu32(ed->td_tail),
+ grub_le_to_cpu32(ed->next_ed));
+ }
+
else if (grub_le_to_cpu32 (ed->td_head) & 1)
{
- tderr = (grub_ohci_td_t)
- (grub_ohci_readreg32 (o, GRUB_OHCI_REG_DONEHEAD) & ~0xf);
- if (tderr == 0)
- /* If DONEHEAD==0 it means that correct address is in HCCA.
- * It should be always now! */
- tderr = (grub_ohci_td_t) (grub_le_to_cpu32 (o->hcca->donehead) & ~0xf);
-
errcode = grub_le_to_cpu32 (tderr->token) >> 28;
switch (errcode)
@@ -645,17 +705,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);
+ grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n",
+ tderr, transfer->last_trans);
break;
case 9:
/* XXX: Data underrun error. */
err = GRUB_USB_ERR_DATA;
+ grub_dprintf ("ohci", "Underrun, failed TD address: %p, index: %d\n",
+ tderr, transfer->last_trans);
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:
@@ -707,7 +767,8 @@
/* 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);
+ while (((grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS) & 0x4) == 0)
+ && !err_unrec);
/* Now it should be safe to change CONTROL and BULK lists. */
/* Important cleaning. */
@@ -718,6 +779,28 @@
grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0);
+ if (err_unrec)
+ {
+ /* Do OHCI reset in case of unrecoverable error - maybe we will need
+ * do more - re-enumerate bus etc. (?) */
+
+ /* Suspend the OHCI by issuing a reset. */
+ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CMDSTATUS, 1); /* XXX: Magic. */
+ grub_millisleep (1);
+ grub_dprintf ("ohci", "Unrecoverable error - OHCI reset\n");
+
+ /* Misc. resets. */
+ o->hcca->donehead = 0;
+ grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, 0x7f); /* Clears everything */
+ 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);
+
+ /* Enable the OHCI. */
+ grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROL, (2 << 6));
+ }
+
grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x (OHCI errcode=0x%02x)\n",
err, errcode);
@@ -730,34 +813,32 @@
static grub_err_t
grub_ohci_portstatus (grub_usb_controller_t dev,
- unsigned int port, unsigned int enable)
+ unsigned int port, unsigned int enable)
{
struct grub_ohci *o = (struct grub_ohci *) dev->data;
- grub_uint32_t status;
- status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
grub_dprintf ("ohci", "begin of portstatus=0x%02x\n",
grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port));
- /* Enable the port. */
- status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
- status |= (enable << 1); /* XXX: Magic. */
- grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
-
- /* Reset the port. */
- status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
- status |= (1 << 4); /* XXX: Magic. */
- grub_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port, status);
+ grub_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_ohci_writereg32 (o, GRUB_OHCI_REG_RHUBPORT + port,
+ GRUB_OHCI_SET_PORT_RESET_STATUS_CHANGE);
grub_millisleep (10);
- status = grub_ohci_readreg32 (o, GRUB_OHCI_REG_RHUBPORT + port);
- grub_dprintf ("ohci", "end of portstatus=0x%02x\n", status);
+ 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;
}
diff -urB ./grub/bus/usb/uhci.c ./grub_patched/bus/usb/uhci.c
--- ./grub/bus/usb/uhci.c 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/bus/usb/uhci.c 2010-05-28 21:26:39.000000000 +0200
@@ -332,13 +332,20 @@
}
static void
-grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td)
+grub_free_queue (struct grub_uhci *u, grub_uhci_td_t td,
+ grub_usb_transfer_t transfer)
{
- /* Free the TDs in this queue. */
- while (td)
+ int i; /* Index of TD in transfer */
+
+ /* Free the TDs in this queue and set last_trans. */
+ for (i=0; td; i++)
{
grub_uhci_td_t tdprev;
+ /* Check state of TD and possibly set last_trans */
+ if (transfer && (td->linkptr & 1))
+ transfer->last_trans = i;
+
/* Unlink the queue. */
tdprev = td;
td = (grub_uhci_td_t) td->linkptr2;
@@ -461,7 +468,7 @@
td_prev->linkptr = 1;
if (td_first)
- grub_free_queue (u, td_first);
+ grub_free_queue (u, td_first, NULL);
return GRUB_USB_ERR_INTERNAL;
}
@@ -560,7 +567,7 @@
/* Place the QH back in the free list and deallocate the associated
TDs. */
qh->elinkptr = 1;
- grub_free_queue (u, td_first);
+ grub_free_queue (u, td_first, transfer);
return err;
}
@@ -609,7 +616,7 @@
grub_uhci_writereg16 (u, reg, enable << 9);
/* Wait for the reset to complete. XXX: How long exactly? */
- grub_millisleep (10);
+ grub_millisleep (50); /* For root hub should be nominaly 50ms */
status = grub_uhci_readreg16 (u, reg);
grub_uhci_writereg16 (u, reg, status & ~(1 << 9));
grub_dprintf ("uhci", "reset completed\n");
diff -urB ./grub/bus/usb/usbtrans.c ./grub_patched/bus/usb/usbtrans.c
--- ./grub/bus/usb/usbtrans.c 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/bus/usb/usbtrans.c 2010-05-28 16:05:04.000000000 +0200
@@ -167,6 +167,7 @@
transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
transfer->max = max;
transfer->dev = dev;
+ transfer->last_trans = -1; /* Reset index of last processed transaction (TD) */
/* Allocate an array of transfer data structures. */
transfer->transactions = grub_malloc (transfer->transcnt
@@ -193,6 +194,13 @@
}
err = dev->controller.dev->transfer (&dev->controller, transfer);
+ /* We must remember proper toggle value even if some transactions
+ * were not processed - correct value should be inversion of last
+ * processed transaction (TD). */
+ if (transfer->last_trans >= 0)
+ toggle = transfer->transactions[transfer->last_trans].toggle ? 0 : 1;
+ else
+ toggle = dev->toggle[endpoint]; /* Nothing done, take original */
grub_dprintf ("usb", "toggle=%d\n", toggle);
dev->toggle[endpoint] = toggle;
diff -urB ./grub/disk/scsi.c ./grub_patched/disk/scsi.c
--- ./grub/disk/scsi.c 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/disk/scsi.c 2010-05-30 15:59:32.000000000 +0200
@@ -421,7 +421,7 @@
/* According to USB MS tests specification, issue Test Unit Ready
* until OK */
- maxtime = grub_get_time_ms () + 1000;
+ maxtime = grub_get_time_ms () + 5000; /* It is safer value */
do
{
/* Timeout is necessary - for example in case when we have
@@ -519,6 +519,37 @@
/* XXX: Never reached. */
return GRUB_ERR_NONE;
+
+#if 0 /* Workaround - it works - but very slowly, from some reason
+ * unknown to me (specially on OHCI). Do not use it. */
+ /* Split transfer requests to device sector size because */
+ /* some devices are not able to transfer more than 512-1024 bytes */
+ grub_err_t err = GRUB_ERR_NONE;
+
+ for ( ; size; size--)
+ {
+ /* Depending on the type, select a read function. */
+ switch (scsi->devtype)
+ {
+ case grub_scsi_devtype_direct:
+ err = grub_scsi_read10 (disk, sector, 1, buf);
+ break;
+
+ case grub_scsi_devtype_cdrom:
+ err = grub_scsi_read12 (disk, sector, 1, buf);
+ break;
+
+ default: /* This should not happen */
+ return GRUB_ERR_READ_ERROR;
+ }
+ if (err)
+ return err;
+ sector++;
+ buf += scsi->blocksize;
+ }
+
+ return err;
+#endif
}
static grub_err_t
diff -urB ./grub/disk/usbms.c ./grub_patched/disk/usbms.c
--- ./grub/disk/usbms.c 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/disk/usbms.c 2010-05-24 20:38:23.000000000 +0200
@@ -84,9 +84,10 @@
struct grub_usb_desc_device *descdev = &usbdev->descdev;
int i;
- if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0)
- return 0;
-
+ if (descdev->class != 0 || descdev->subclass || descdev->protocol != 0
+ || descdev->configcnt == 0)
+ return 0;
+
/* XXX: Just check configuration 0 for now. */
for (i = 0; i < usbdev->config[0].descconf->numif; i++)
{
@@ -238,6 +239,7 @@
struct grub_usbms_csw status;
static grub_uint32_t tag = 0;
grub_usb_err_t err = GRUB_USB_ERR_NONE;
+ grub_usb_err_t errCSW = GRUB_USB_ERR_NONE;
int retrycnt = 3 + 1;
grub_size_t i;
@@ -275,9 +277,8 @@
{
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;
+ goto CheckCSW;
}
return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
}
@@ -287,7 +288,12 @@
{
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;
+ if (err)
+ {
+ if (err == GRUB_USB_ERR_STALL)
+ grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
+ goto CheckCSW;
+ }
/* Debug print of received data. */
grub_dprintf ("usb", "buf:\n");
if (size <= 64)
@@ -301,6 +307,12 @@
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);
grub_dprintf ("usb", "buf:\n");
+ if (err)
+ {
+ if (err == GRUB_USB_ERR_STALL)
+ grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
+ goto CheckCSW;
+ }
/* Debug print of sent data. */
if (size <= 256)
for (i=0; i<size; i++)
@@ -311,15 +323,16 @@
/* Read the status - (maybe) according to specification. */
CheckCSW:
- err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+ errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
sizeof (status), (char *) &status);
- if (err)
+ if (errCSW)
{
grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
- err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
+ errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
sizeof (status), (char *) &status);
- if (err)
+ if (errCSW)
{ /* Bulk-only reset device. */
+ grub_dprintf ("usb", "Bulk-only reset device - errCSW\n");
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);
@@ -332,9 +345,11 @@
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)
- {
+ /* If phase error or not valid signature, do bulk-only reset device. */
+ if ((status.status == 2) ||
+ (status.signature != grub_cpu_to_le32(0x53425355)))
+ { /* Bulk-only reset device. */
+ grub_dprintf ("usb", "Bulk-only reset device - bad status\n");
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);
@@ -342,9 +356,13 @@
goto retry;
}
- if (status.status)
+ /* If "command failed" status or data transfer failed -> error */
+ if ((status.status || err) && !read_write)
return grub_error (GRUB_ERR_READ_ERROR,
"error communication with USB Mass Storage device");
+ else if ((status.status || err) && read_write)
+ return grub_error (GRUB_ERR_WRITE_ERROR,
+ "error communication with USB Mass Storage device");
return GRUB_ERR_NONE;
}
diff -urB ./grub/include/grub/usb.h ./grub_patched/include/grub/usb.h
--- ./grub/include/grub/usb.h 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/include/grub/usb.h 2010-05-24 19:49:11.000000000 +0200
@@ -35,7 +35,8 @@
GRUB_USB_ERR_NAK,
GRUB_USB_ERR_BABBLE,
GRUB_USB_ERR_TIMEOUT,
- GRUB_USB_ERR_BITSTUFF
+ GRUB_USB_ERR_BITSTUFF,
+ GRUB_USB_ERR_UNRECOVERABLE
} grub_usb_err_t;
typedef enum
diff -urB ./grub/include/grub/usbtrans.h ./grub_patched/include/grub/usbtrans.h
--- ./grub/include/grub/usbtrans.h 2010-05-30 15:09:07.000000000 +0200
+++ ./grub_patched/include/grub/usbtrans.h 2010-05-24 19:52:48.000000000 +0200
@@ -58,6 +58,9 @@
struct grub_usb_device *dev;
struct grub_usb_transaction *transactions;
+
+ int last_trans;
+ /* Index of last processed transaction in OHCI/UHCI driver. */
};
typedef struct grub_usb_transfer *grub_usb_transfer_t;
_______________________________________________
Grub-devel mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/grub-devel