Merge Aleš' latest patch
This commit is contained in:
parent
778ff32476
commit
7d4873c26a
8 changed files with 206 additions and 57 deletions
154
bus/usb/ohci.c
154
bus/usb/ohci.c
|
@ -116,6 +116,11 @@ typedef enum
|
|||
#define GRUB_OHCI_PERIODIC_START 0x257f
|
||||
#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
|
||||
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. */
|
||||
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);
|
||||
|
@ -396,8 +401,10 @@ grub_ohci_transaction (grub_ohci_td_t td,
|
|||
break;
|
||||
}
|
||||
|
||||
#if 0 /* Always generate interrupt */
|
||||
/* Generate no interrupts. */
|
||||
token |= 7 << 21;
|
||||
#endif
|
||||
|
||||
/* Set the token. */
|
||||
token |= toggle << 24;
|
||||
|
@ -444,9 +451,12 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
grub_uint32_t status;
|
||||
grub_uint32_t control;
|
||||
grub_usb_err_t err;
|
||||
int i, j;
|
||||
int i;
|
||||
grub_uint64_t maxtime;
|
||||
int err_timeout = 0;
|
||||
int err_unrec = 0;
|
||||
grub_uint32_t intstatus;
|
||||
grub_uint32_t tderr_addr = 0;
|
||||
|
||||
/* Allocate an Endpoint Descriptor. */
|
||||
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]));
|
||||
}
|
||||
|
||||
#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;
|
||||
|
@ -582,9 +594,11 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
}
|
||||
|
||||
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));
|
||||
|
||||
/* Safety measure to avoid a hang. */
|
||||
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. */
|
||||
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. */
|
||||
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)
|
||||
{
|
||||
|
@ -628,9 +646,45 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
|
||||
if ((ed->td_head & ~0xf) == (ed->td_tail & ~0xf))
|
||||
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_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)
|
||||
{
|
||||
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->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)
|
||||
{
|
||||
grub_uint32_t td_err_addr;
|
||||
grub_uint8_t errcode;
|
||||
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
|
||||
+ (td_err_addr - td_list_addr));
|
||||
+ (tderr_addr - td_list_addr));
|
||||
|
||||
errcode = grub_le_to_cpu32 (tderr->token) >> 28;
|
||||
grub_dprintf ("ohci", "OHCI errcode=0x%02x\n", errcode);
|
||||
|
@ -702,17 +761,17 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
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:
|
||||
|
@ -764,7 +823,8 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
/* 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. */
|
||||
|
@ -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_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_dma_free (td_list_chunk);
|
||||
|
|
|
@ -332,13 +332,20 @@ grub_free_td (struct grub_uhci *u, grub_uhci_td_t td)
|
|||
}
|
||||
|
||||
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 @@ grub_uhci_transfer (grub_usb_controller_t dev,
|
|||
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 @@ grub_uhci_transfer (grub_usb_controller_t dev,
|
|||
/* 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_portstatus (grub_usb_controller_t dev,
|
|||
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");
|
||||
|
|
|
@ -213,6 +213,7 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
|
|||
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
|
||||
|
@ -240,6 +241,13 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
|
|
33
disk/scsi.c
33
disk/scsi.c
|
@ -421,7 +421,7 @@ grub_scsi_open (const char *name, grub_disk_t disk)
|
|||
|
||||
/* 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 @@ grub_scsi_read (grub_disk_t disk, grub_disk_addr_t sector,
|
|||
|
||||
/* 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
|
||||
|
|
40
disk/usbms.c
40
disk/usbms.c
|
@ -253,6 +253,7 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
|
|||
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;
|
||||
|
||||
|
@ -290,9 +291,8 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
|
|||
{
|
||||
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");
|
||||
}
|
||||
|
@ -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);
|
||||
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)
|
||||
|
@ -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);
|
||||
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++)
|
||||
|
@ -326,15 +337,16 @@ grub_usbms_transfer (struct grub_scsi *scsi, grub_size_t cmdsize, char *cmd,
|
|||
|
||||
/* 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);
|
||||
|
@ -347,9 +359,11 @@ CheckCSW:
|
|||
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);
|
||||
|
@ -357,9 +371,13 @@ CheckCSW:
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ typedef enum
|
|||
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
|
||||
|
|
|
@ -58,6 +58,9 @@ struct grub_usb_transfer
|
|||
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;
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
|
||||
#include <grub/term.h>
|
||||
#include <grub/machine/console.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/cpu/io.h>
|
||||
#include <grub/misc.h>
|
||||
|
|
Loading…
Reference in a new issue