Merge Aleš' latest patch

This commit is contained in:
Aleš Nesrsta 2010-06-01 02:10:19 +02:00 committed by Vladimir 'phcoder' Serbinenko
parent 778ff32476
commit 7d4873c26a
8 changed files with 206 additions and 57 deletions

View file

@ -116,6 +116,11 @@ typedef enum
#define GRUB_OHCI_PERIODIC_START 0x257f #define GRUB_OHCI_PERIODIC_START 0x257f
#define GRUB_OHCI_FRAME_INTERVAL 0x2edf #define GRUB_OHCI_FRAME_INTERVAL 0x2edf
#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 static grub_uint32_t
grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg) grub_ohci_readreg32 (struct grub_ohci *o, grub_ohci_reg_t reg)
{ {
@ -281,7 +286,7 @@ grub_ohci_pci_iter (grub_pci_device_t dev,
/* Misc. pre-sets. */ /* Misc. pre-sets. */
o->hcca->donehead = 0; 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_CONTROLHEAD, 0);
grub_ohci_writereg32 (o, GRUB_OHCI_REG_CONTROLCURR, 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_BULKHEAD, 0);
@ -396,8 +401,10 @@ grub_ohci_transaction (grub_ohci_td_t td,
break; break;
} }
#if 0 /* Always generate interrupt */
/* Generate no interrupts. */ /* Generate no interrupts. */
token |= 7 << 21; token |= 7 << 21;
#endif
/* Set the token. */ /* Set the token. */
token |= toggle << 24; token |= toggle << 24;
@ -444,9 +451,12 @@ grub_ohci_transfer (grub_usb_controller_t dev,
grub_uint32_t status; grub_uint32_t status;
grub_uint32_t control; grub_uint32_t control;
grub_usb_err_t err; grub_usb_err_t err;
int i, j; int i;
grub_uint64_t maxtime; grub_uint64_t maxtime;
int err_timeout = 0; int err_timeout = 0;
int err_unrec = 0;
grub_uint32_t intstatus;
grub_uint32_t tderr_addr = 0;
/* Allocate an Endpoint Descriptor. */ /* Allocate an Endpoint Descriptor. */
ed_chunk = grub_memalign_dma32 (256, sizeof (*ed)); ed_chunk = grub_memalign_dma32 (256, sizeof (*ed));
@ -479,11 +489,13 @@ grub_ohci_transfer (grub_usb_controller_t dev,
+ (i + 1) * sizeof (td_list[0])); + (i + 1) * sizeof (td_list[0]));
} }
#if 0 /* Better will be enable interrupt on all TDs. */
/* The last-1 TD token we should change to enable interrupt when TD finishes. /* 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 * As OHCI interrupts are disabled, it does only setting of WDH bit in
* HcInterruptStatus register - and that is what we want to safely detect * HcInterruptStatus register - and that is what we want to safely detect
* normal end of all transactions. */ * normal end of all transactions. */
td_list[transfer->transcnt - 1].token &= ~(7 << 21); td_list[transfer->transcnt - 1].token &= ~(7 << 21);
#endif
td_list[transfer->transcnt].token = 0; td_list[transfer->transcnt].token = 0;
td_list[transfer->transcnt].buffer = 0; td_list[transfer->transcnt].buffer = 0;
@ -582,9 +594,11 @@ grub_ohci_transfer (grub_usb_controller_t dev,
} }
grub_dprintf ("ohci", "wait for completion\n"); 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_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));
/* Safety measure to avoid a hang. */ /* Safety measure to avoid a hang. */
maxtime = grub_get_time_ms () + 1000; maxtime = grub_get_time_ms () + 1000;
@ -592,30 +606,34 @@ grub_ohci_transfer (grub_usb_controller_t dev,
/* Wait until the transfer is completed or STALLs. */ /* Wait until the transfer is completed or STALLs. */
do do
{ {
grub_cpu_idle (); /* Check transfer status */
intstatus = grub_ohci_readreg32 (o, GRUB_OHCI_REG_INTSTATUS);
if ((intstatus & 0x2) != 0)
{
grub_dprintf ("ohci", "Current HccaDoneHead=0x%08x\n",
o->hcca->donehead);
/* Remember last successful TD */
tderr_addr = 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_addr == td_list_addr
+ sizeof (td_list[0]) * (transfer->transcnt - 1))
break;
continue;
}
if ((intstatus & 0x10) != 0)
{ /* Unrecoverable error - only reset can help...! */
err_unrec = 1;
break;
}
/* Detected a HALT. */ /* Detected a HALT. */
if (grub_le_to_cpu32 (ed->td_head) & 1) if (grub_le_to_cpu32 (ed->td_head) & 1)
break; 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 ? */ /* Timeout ? */
if (grub_get_time_ms () > maxtime) if (grub_get_time_ms () > maxtime)
{ {
@ -628,9 +646,45 @@ grub_ohci_transfer (grub_usb_controller_t dev,
if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf)) if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
break; break;
grub_cpu_idle ();
} }
while (1); 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_addr)
{
/* 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_addr = 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 = (tderr_addr - td_list_addr) / sizeof (*td_list);
/* Check correct value in last_trans */
/* It could happen if timeout happens and no TD was retired */
if (transfer->last_trans >= transfer->transcnt || !tderr_addr)
{
grub_dprintf("ohci", "tder==0 or out of TDs range!\n");
grub_dprintf("ohci", "tderr_addr=0x%x, td_list=%p,\n\t\t last_trans=%d, transcnt=%d\n",
tderr_addr, td_list, transfer->last_trans, transfer->transcnt);
/* We should set something valid... */
transfer->last_trans = -1; /* Probably no TD done */
tderr_addr = td_list_addr;
}
/* In case of timeout do not detect error from TD */
if (err_timeout) if (err_timeout)
{ {
err = GRUB_ERR_TIMEOUT; err = GRUB_ERR_TIMEOUT;
@ -640,20 +694,25 @@ grub_ohci_transfer (grub_usb_controller_t dev,
grub_le_to_cpu32(ed->td_tail), grub_le_to_cpu32(ed->td_tail),
grub_le_to_cpu32(ed->next_ed)); 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) else if (grub_le_to_cpu32 (ed->td_head) & 1)
{ {
grub_uint32_t td_err_addr;
grub_uint8_t errcode; grub_uint8_t errcode;
grub_ohci_td_t tderr = NULL; grub_ohci_td_t tderr = NULL;
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 tderr = (grub_ohci_td_t) ((char *) td_list
+ (td_err_addr - td_list_addr)); + (tderr_addr - td_list_addr));
errcode = grub_le_to_cpu32 (tderr->token) >> 28; errcode = grub_le_to_cpu32 (tderr->token) >> 28;
grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode); grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode);
@ -702,17 +761,17 @@ grub_ohci_transfer (grub_usb_controller_t dev,
case 8: case 8:
/* XXX: Data overrun error. */ /* XXX: Data overrun error. */
err = GRUB_USB_ERR_DATA; 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",
grub_dprintf ("ohci", "Overrun, failed TD address: %p, index: %d\n", tderr, j); tderr, transfer->last_trans);
break; break;
case 9: case 9:
/* XXX: Data underrun error. */ /* XXX: Data underrun error. */
err = GRUB_USB_ERR_DATA; 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", 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)); 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; break;
case 10: case 10:
@ -764,7 +823,8 @@ grub_ohci_transfer (grub_usb_controller_t dev,
/* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */ /* SF bit reset. (SF bit indicates Start Of Frame (SOF) packet) */
grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2)); grub_ohci_writereg32 (o, GRUB_OHCI_REG_INTSTATUS, (1<<2));
/* Wait for new SOF */ /* 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. */ /* Now it should be safe to change CONTROL and BULK lists. */
/* Important cleaning. */ /* Important cleaning. */
@ -775,6 +835,28 @@ grub_ohci_transfer (grub_usb_controller_t dev,
grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKHEAD, 0);
grub_ohci_writereg32 (o, GRUB_OHCI_REG_BULKCURR, 0); grub_ohci_writereg32 (o, GRUB_OHCI_REG_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\n", err); grub_dprintf ("ohci", "OHCI finished, freeing, err=0x%02x\n", err);
grub_dma_free (td_list_chunk); grub_dma_free (td_list_chunk);

View file

@ -332,13 +332,20 @@ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td)
} }
static void 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. */ int i; /* Index of TD in transfer */
while (td)
/* Free the TDs in this queue and set last_trans. */
for (i=0; td; i++)
{ {
grub_uhci_td_t tdprev; 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. */ /* Unlink the queue. */
tdprev = td; tdprev = td;
td = (grub_uhci_td_t) td->linkptr2; td = (grub_uhci_td_t) td->linkptr2;
@ -461,7 +468,7 @@ grub_uhci_transfer (grub_usb_controller_t dev,
td_prev->linkptr = 1; td_prev->linkptr = 1;
if (td_first) if (td_first)
grub_free_queue (u, td_first); grub_free_queue (u, td_first, NULL);
return GRUB_USB_ERR_INTERNAL; return GRUB_USB_ERR_INTERNAL;
} }
@ -560,7 +567,7 @@ grub_uhci_transfer (grub_usb_controller_t dev,
/* Place the QH back in the free list and deallocate the associated /* Place the QH back in the free list and deallocate the associated
TDs. */ TDs. */
qh->elinkptr = 1; qh->elinkptr = 1;
grub_free_queue (u, td_first); grub_free_queue (u, td_first, transfer);
return err; return err;
} }
@ -609,7 +616,7 @@ grub_uhci_portstatus (grub_usb_controller_t dev,
grub_uhci_writereg16 (u, reg, enable << 9); grub_uhci_writereg16 (u, reg, enable << 9);
/* Wait for the reset to complete. XXX: How long exactly? */ /* 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); status = grub_uhci_readreg16 (u, reg);
grub_uhci_writereg16 (u, reg, status & ~(1 << 9)); grub_uhci_writereg16 (u, reg, status & ~(1 << 9));
grub_dprintf ("uhci", "reset completed\n"); grub_dprintf ("uhci", "reset completed\n");

View file

@ -213,6 +213,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK; transfer->type = GRUB_USB_TRANSACTION_TYPE_BULK;
transfer->max = max; transfer->max = max;
transfer->dev = dev; transfer->dev = dev;
transfer->last_trans = -1; /* Reset index of last processed transaction (TD) */
/* Allocate an array of transfer data structures. */ /* Allocate an array of transfer data structures. */
transfer->transactions = grub_malloc (transfer->transcnt transfer->transactions = grub_malloc (transfer->transcnt
@ -240,6 +241,13 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
} }
err = dev->controller.dev->transfer (&dev->controller, transfer); 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); grub_dprintf ("usb", "toggle=%d\n", toggle);
dev->toggle[endpoint] = toggle; dev->toggle[endpoint] = toggle;

View file

@ -421,7 +421,7 @@ grub_scsi_open (const char *name, grub_disk_t disk)
/* According to USB MS tests specification, issue Test Unit Ready /* According to USB MS tests specification, issue Test Unit Ready
* until OK */ * until OK */
maxtime = grub_get_time_ms () + 1000; maxtime = grub_get_time_ms () + 5000; /* It is safer value */
do do
{ {
/* Timeout is necessary - for example in case when we have /* Timeout is necessary - for example in case when we have
@ -519,6 +519,37 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
/* XXX: Never reached. */ /* XXX: Never reached. */
return GRUB_ERR_NONE; 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 static grub_err_t

View file

@ -253,6 +253,7 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
struct grub_usbms_csw status; struct grub_usbms_csw status;
static grub_uint32_t tag = 0; static grub_uint32_t tag = 0;
grub_usb_err_t err = GRUB_USB_ERR_NONE; grub_usb_err_t err = GRUB_USB_ERR_NONE;
grub_usb_err_t errCSW = GRUB_USB_ERR_NONE;
int retrycnt = 3 + 1; int retrycnt = 3 + 1;
grub_size_t i; grub_size_t i;
@ -290,9 +291,8 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
{ {
if (err == GRUB_USB_ERR_STALL) 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); 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"); return grub_error (GRUB_ERR_IO, "USB Mass Storage request failed");
} }
@ -302,7 +302,12 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
{ {
err = grub_usb_bulk_read (dev->dev, dev->in->endp_addr, size, buf); 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); 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. */ /* Debug print of received data. */
grub_dprintf ("usb", "buf:\n"); grub_dprintf ("usb", "buf:\n");
if (size <= 64) if (size <= 64)
@ -316,6 +321,12 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
err = grub_usb_bulk_write (dev->dev, dev->out->endp_addr, 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); grub_dprintf ("usb", "write: %d %d\n", err, GRUB_USB_ERR_STALL);
grub_dprintf ("usb", "buf:\n"); 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. */ /* Debug print of sent data. */
if (size <= 256) if (size <= 256)
for (i=0; i<size; i++) for (i=0; i<size; i++)
@ -326,15 +337,16 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
/* Read the status - (maybe) according to specification. */ /* Read the status - (maybe) according to specification. */
CheckCSW: 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); sizeof (status), (char *) &status);
if (err) if (errCSW)
{ {
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, errCSW = grub_usb_bulk_read (dev->dev, dev->in->endp_addr,
sizeof (status), (char *) &status); sizeof (status), (char *) &status);
if (err) if (errCSW)
{ /* Bulk-only reset device. */ { /* Bulk-only reset device. */
grub_dprintf ("usb", "Bulk-only reset device - errCSW\n");
grub_usbms_reset (dev->dev, dev->interface); grub_usbms_reset (dev->dev, dev->interface);
grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
grub_usb_clear_halt (dev->dev, dev->out->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@ -347,9 +359,11 @@ CheckCSW:
status.signature, status.tag, status.residue); status.signature, status.tag, status.residue);
grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status); grub_dprintf ("usb", "CSW: status=0x%02x\n", status.status);
/* If phase error, do bulk-only reset device. */ /* If phase error or not valid signature, do bulk-only reset device. */
if (status.status == 2) 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_usbms_reset (dev->dev, dev->interface);
grub_usb_clear_halt (dev->dev, dev->in->endp_addr); grub_usb_clear_halt (dev->dev, dev->in->endp_addr);
grub_usb_clear_halt (dev->dev, dev->out->endp_addr); grub_usb_clear_halt (dev->dev, dev->out->endp_addr);
@ -357,9 +371,13 @@ CheckCSW:
goto retry; 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, return grub_error (GRUB_ERR_READ_ERROR,
"error communication with USB Mass Storage device"); "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; return GRUB_ERR_NONE;
} }

View file

@ -35,7 +35,8 @@ typedef enum
GRUB_USB_ERR_NAK, GRUB_USB_ERR_NAK,
GRUB_USB_ERR_BABBLE, GRUB_USB_ERR_BABBLE,
GRUB_USB_ERR_TIMEOUT, GRUB_USB_ERR_TIMEOUT,
GRUB_USB_ERR_BITSTUFF GRUB_USB_ERR_BITSTUFF,
GRUB_USB_ERR_UNRECOVERABLE
} grub_usb_err_t; } grub_usb_err_t;
typedef enum typedef enum

View file

@ -58,6 +58,9 @@ struct grub_usb_transfer
struct grub_usb_device *dev; struct grub_usb_device *dev;
struct grub_usb_transaction *transactions; 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; typedef struct grub_usb_transfer *grub_usb_transfer_t;

View file

@ -18,7 +18,6 @@
*/ */
#include <grub/term.h> #include <grub/term.h>
#include <grub/machine/console.h>
#include <grub/time.h> #include <grub/time.h>
#include <grub/cpu/io.h> #include <grub/cpu/io.h>
#include <grub/misc.h> #include <grub/misc.h>