Currently I'm writing a UHCI and a USB 1.x driver. Both are working so far with full and low speed devices ("working so far" means enumerating, showing the manufacturer's name and all that cute stuff). I also get high speed devices to work as far as they are behind a full speed hub (I'm beginning a mass storage driver which uses a generic SCSI driver, it's able to do INQUIRY, print the device's vendor and name and is also able to read one sector in QEMU (the last doesn't work on real hardware so far or with real hardware in QEMU)).
But all my high speed devices obviously refuse to work when they're connected to a root hub. If I disable the legacy support on my computer, I get a CRC/time out error together with a stall (maybe STALL or NAK in response to my SETUP packet), if I enable it, I had the same problem at the beginning but now there are page faults (though I don't know the exact reason). So, in general: My high speed devices work behind a full speed hub. Fine. But they refuse to work on a root port.
No, I don't ask your (explicitly) for a manual on how to disable EHCI (though I won't reject them), I'm already doing that (disabling the EHCI). I started by reading the forum post about it (see here), but I read the specification carefully while following those steps because there are obviously some mistakes in it. I continued by reading most parts of the manual, and I ended up with this code (if you'd like to have it):
(Note: this is not my actual code as I removed some unnecessary parts (PCI stuff, etc.) and changed some variable and function names (and added comments))
Code: Select all
// Get the MMIO base
// Enable bus master capability and the MMIO
pci_write_config_word(/*Offset: Command register*/ 0x04, /*Value*/ 0x06);
hc->capregs = map_uncached(mmio_begin, length_of_mmio_space);
hc->opregs = (void *)((uintptr_t)hc->capregs + hc->capregs->cap_length);
// Find legacy support EECP
int eecp = (hc->usbbase->hcc_params & 0x0000FF00) >> 8;
if (eecp >= 0x40)
{
int eecp_id;
while (eecp)
{
eecp_id = pci_read_config_byte(eecp);
// Legacy support EECP
if (eecp_id == 1)
break;
// Not legacy support EECP, so skip this one
eecp = pci_read_config_byte(eecp + 0x01);
}
// Check if a legacy support extended capability has been found and if the BIOS semaphore is set
if ((eecp_id == 1) && pci_read_config_byte(eecp + 0x02) & 0x01)
{
// Set OS semaphore
pci_write_config_byte(eecp + 0x03, 0x01);
failed = 1;
// One second ("tick count" is measured in nanoseconds)
unsigned long long timeout = tick_count + 1000000000LL;
// Wait until BIOS semaphore isn't set anymore
while ((pci_read_config_byte(eecp + 0x02) & 0x01) && (tick_count < timeout));
if (!(pci_read_config_byte(eecp + 0x02) & 0x01))
{
// One second again
unsigned long long timeout = tick_count + 1000000000LL;
// Wait until OS semaphore is set
while (!(pci_read_config_byte(eecp + 0x03) & 0x01) && (tick_count < timeout));
if (pci_read_config_byte(eecp + 0x03) & 0x01)
failed = 0;
}
if (failed)
{
// Deactivate legacy support by hand
pci_write_config_dword(eecp + 0x04, 0x0000);
}
}
}
// Well, now the EHCI is owned by us and we're free to use it.
hc->opregs->usb_cmd = 0x000002;
while (hc->opregs->usb_cmd & 0x00000002);
hc->opregs->usb_sts = hc->opregs->usb_sts;
hc->opregs->ctrl_ds_segment = 0x00000000;
hc->opregs->usb_intr = 0x00;
hc->opregs->usb_cmd = 0x00080000;
// All devices to the companion controllers
hc->opregs->config_flag = 0;
My main problem is that the devices must be routed to the companion controller, because I'm able to detect them there. But they don't response to my requests, appearently (though they also might STALL or NAK, which wouldn't make any sense, either).
Does anyone know about that kind of problem? If no, can you guess which could be the cause of it?
Any help is much appreciated.
PS: I should add that I have driver dependencies. Thus, all the EHCIs are initialized before the UHCIs (or OHCIs later on).