Fix a bunch of problems with USB.
This commit is contained in:
parent
7fd08f56ae
commit
778ff32476
10 changed files with 637 additions and 167 deletions
342
bus/usb/ohci.c
342
bus/usb/ohci.c
|
@ -27,6 +27,7 @@
|
|||
#include <grub/cpu/io.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/cs5536.h>
|
||||
#include <grub/loader.h>
|
||||
|
||||
struct grub_ohci_hcca
|
||||
{
|
||||
|
@ -96,7 +97,11 @@ typedef enum
|
|||
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 +200,7 @@ grub_ohci_pci_iter (grub_pci_device_t dev,
|
|||
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 +217,48 @@ grub_ohci_pci_iter (grub_pci_device_t dev,
|
|||
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 +275,58 @@ grub_ohci_pci_iter (grub_pci_device_t dev,
|
|||
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 +403,29 @@ grub_ohci_transaction (grub_ohci_td_t td,
|
|||
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 +444,9 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
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 +479,25 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
+ (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 +516,30 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
|
||||
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 +547,17 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
{
|
||||
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 +566,9 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
|
||||
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 +586,77 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
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 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;
|
||||
|
||||
/* Detected a STALL. */
|
||||
if (ed->td_head & 1)
|
||||
/* 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;
|
||||
}
|
||||
while (1);
|
||||
|
||||
grub_dprintf ("ohci", "complete\n");
|
||||
|
||||
/* if (ed->td_head & 1) */
|
||||
/* err = GRUB_USB_ERR_STALL; */
|
||||
/* else if (ed->td */
|
||||
|
||||
|
||||
if (ed->td_head & 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_uint8_t errcode;
|
||||
grub_ohci_td_t tderr;
|
||||
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);
|
||||
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 +702,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);
|
||||
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,43 +750,73 @@ grub_ohci_transfer (grub_usb_controller_t dev,
|
|||
|
||||
/* 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);
|
||||
|
||||
/* XXX */
|
||||
|
||||
/* 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);
|
||||
|
||||
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));
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -174,14 +174,15 @@ grub_uhci_pci_iter (grub_pci_device_t dev,
|
|||
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 @@ grub_uhci_pci_iter (grub_pci_device_t dev,
|
|||
}
|
||||
#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 @@ grub_uhci_transfer (grub_usb_controller_t dev,
|
|||
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 @@ grub_uhci_transfer (grub_usb_controller_t dev,
|
|||
|
||||
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 @@ grub_uhci_portstatus (grub_usb_controller_t dev,
|
|||
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 @@ grub_uhci_detect_dev (grub_usb_controller_t dev, int port)
|
|||
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)
|
||||
|
|
|
@ -107,7 +107,7 @@ grub_usb_set_configuration (grub_usb_device_t dev, int configuration)
|
|||
{
|
||||
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_device_initialize (grub_usb_device_t dev)
|
|||
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);
|
||||
|
|
|
@ -138,7 +138,7 @@ grub_usb_control_msg (grub_usb_device_t dev,
|
|||
/* 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 @@ grub_usb_control_msg (grub_usb_device_t dev,
|
|||
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 @@ grub_usb_bulk_readwrite (grub_usb_device_t dev,
|
|||
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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue