[SOLVED] EHCI Interrupt Weird Firing Problem

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
Hoozim
Member
Member
Posts: 53
Joined: Fri Jul 23, 2010 8:26 am

[SOLVED] EHCI Interrupt Weird Firing Problem

Post by Hoozim »

Hi,

I wrote an EHCI driver a number of years ago that works just fine with a 10 year old computer and VirtualBox, but not on my 6 month old netbook. This netbook contains two EHCI controllers (each with their own OHCI companion). One pair handles the USB ports the other the SD card reader. There is also a single XHCI port which I am not using. When I reset the controller and start it, I often get an EHCI interrupt with a zero USBSTS or a frame list rollover. I have the frame list rollover interrupt turned off. After that, I never receive any EHCI interrupts. Why is it that I don't receive any interrupts? My interrupt handler sends the EOI bytes and does the IRET stuff.

Here is the code:

Code: Select all

void EHCI::ConfigureEHCI(dword baseAddressPhysical, byte interrupt)
{
	/*static bool configonce = true;
	if (configonce == true)
	{
		configonce = false;
		return;
	}*/

	EHCI::Private::BaseAddressPhysical = baseAddressPhysical;
	EHCI::Private::Interrupt = interrupt;
	
	dword baseAddress = (dword)VirtualMemory::PCIMemory(baseAddressPhysical, 1);
	EHCI::Private::BaseAddress = baseAddress;


	EHCI::Private::CapabilityRegisters* capRegs = (EHCI::Private::CapabilityRegisters*)baseAddress;
	EHCI::Private::CapRegister = capRegs;
	EHCI::Private::NumberOfPorts = capRegs->HCSPARAMS.N_PORTS;
	

	
	if (capRegs->HCCPARAMS.Addressing64Bit)
	{
		EHCI::Private::Addressing64Bit = 1;
		//return;  Virtual Box Uses a 64 Bit EHCI
	}


	if (capRegs->HCSPARAMS.PortPowerControl)
	{
		EHCI::Private::PortPowerControl = 1;
	}

	EHCI::Private::OperationalRegistersBase = EHCI::Private::BaseAddress + capRegs->CAPLENGTH;
	EHCI::Private::OperationalRegisters* opRegs = (EHCI::Private::OperationalRegisters*)EHCI::Private::OperationalRegistersBase;
	EHCI::Private::OpRegister = (EHCI::Private::OperationalRegisters*)EHCI::Private::OperationalRegistersBase;
	EHCI::Private::PORTSCBaseAddress = ((EHCI::Private::OperationalRegistersBase) + 0x44);
	Video::PrintNumber(EHCI::Private::NumberOfPorts, 16);
	IDT::InstallIRQ(32 + interrupt, &EHCI::Private::EHCIIRQHandler);
	USB20::ConfigureUSB20();



	EHCI::Private::ResetEHCI();


	for (int i = 0; i < EHCI::Private::NumberOfPorts; i++)
	{
		EHCI::Private::PORTSC* port = EHCI::Private::SelectPortSC(i);
		if (EHCI::Private::PortPowerControl)
		{
			port->PortPower = 1;
		}
	}

	rerun:
	for (byte j = 0; j < EHCI::Private::NumberOfPorts; j ++)
	{

		EHCI::Private::PORTSC* port = EHCI::Private::SelectPortSC(j);
		
		port->PortPower = 1;
		port->PortEnabled = 0;
		EHCI::Private::OpRegister->USBSTS.PortChangeDetect = 0;

		if (EHCI::Private::OpRegister->USBSTS.HCHalted)
		{
			Video::Print("Error: hchalted");
			for(;;);
		}

		
		EHCI::Private::ResetPort(port);
		

		Video::PrintNumber(port->PortEnabled, 10);Video::NewLine();
		if (port->PortEnabled)
			USB20::ConfigureDevice(j);
	}

}


void EHCI::Private::ResetEHCI()
{
	EHCI::Private::OpRegister->USBCMD.RunStop = 0;
	while (EHCI::Private::OpRegister->USBSTS.HCHalted == 0)
	{
		PIT::WaitMilliseconds(30);
	}
	EHCI::Private::OpRegister->USBCMD.HostControllerReset = 1;
	while (EHCI::Private::OpRegister->USBCMD.HostControllerReset == 1)
	{
		PIT::WaitMilliseconds(20);
	}
	

	EHCI::Private::OpRegister->CTRLDSSEGMENT = 0;
	memset((void*)&EHCI::Private::OpRegister->USBSTS, 0, sizeof(EHCI::Private::OperationalRegisters::USBSTS));
	EHCI::Private::EnableInterrupts();

	EHCI::Private::OpRegister->PERIODICLISTBASE = 0;
	EHCI::Private::OpRegister->USBCMD.InterruptThresholdControl = 8;
	EHCI::Private::OpRegister->ASYNCLISTADDR = 0;
	Video::Print("standby");
	Keyboard::WaitForKeyDown();
	EHCI::Private::OpRegister->USBCMD.RunStop = 1;
	while (EHCI::Private::OpRegister->USBSTS.HCHalted == 1)
	{
		PIT::WaitMilliseconds(20);
	}
	Video::Print("Reset");
	Keyboard::WaitForKeyDown();
	Video::Print("pro");
	Keyboard::WaitForKeyDown();
	EHCI::Private::OpRegister->CONFIGFLAG.ConfigureFlag = 1;
	Video::Print("seed");
	Keyboard::WaitForKeyDown();
	Video::Print("on");

	PIT::WaitMilliseconds(50);
}

void EHCI::Private::EnableInterrupts()
{
	EHCI::Private::OpRegister->USBINTR.HostSystemErrorEnable = 1;
	EHCI::Private::OpRegister->USBINTR.InterruptOnAsyncAdvanceEnable = 1;
	EHCI::Private::OpRegister->USBINTR.PortChangeInterruptEnable = 1;
	EHCI::Private::OpRegister->USBINTR.USBErrorInterruptEnable = 1;
	EHCI::Private::OpRegister->USBINTR.USBInterruptEnable = 1;
}

void EHCI::Private::DisableInterrupts()
{
	EHCI::Private::OpRegister->USBINTR.HostSystemErrorEnable = 0;
	EHCI::Private::OpRegister->USBINTR.InterruptOnAsyncAdvanceEnable = 0;
	EHCI::Private::OpRegister->USBINTR.PortChangeInterruptEnable = 0;
	EHCI::Private::OpRegister->USBINTR.USBErrorInterruptEnable = 0;
	EHCI::Private::OpRegister->USBINTR.USBInterruptEnable = 0;
}


void EHCI::Private::EHCIIRQHandler(IDT::Registers* r)
{
	standby:
	dword usbsts = 0;
	memcpy ((void*)&usbsts, (void*)&EHCI::Private::OpRegister->USBSTS, 4);
	Video::NewLine();Video::PrintNumber(usbsts, 16);Video::NewLine();
	if (usbsts == 0)
	{

		goto standby;
	}

	Video::Print("ehciinterrupt\n");
	if (EHCI::Private::OpRegister->USBSTS.USBINT)
	{
		Video::Print("\nintr\n");
		EHCI::Private::USBInterrupt = true;
		EHCI::Private::OpRegister->USBSTS.USBINT = 1;
	}
	if (EHCI::Private::OpRegister->USBSTS.USBERRINT)
	{
		Video::Print("\nUSB Error Interrupt\n");
		EHCI::Private::OpRegister->USBSTS.USBERRINT = 1;
	}
	if (EHCI::Private::OpRegister->USBSTS.PortChangeDetect)
	{
		Video::Print("port change");
		EHCI::Private::OpRegister->USBSTS.PortChangeDetect = 1;
		OutPort(0xA0, 0x20);
		OutPort(0x20, 0x20);
		//_asm sti;
		asm volatile("sti");
		PIT::WaitMilliseconds(30);
		EHCI::Private::ResetPorts();
	}
	if (EHCI::Private::OpRegister->USBSTS.FrameListRollover)
	{
		Video::Print("\nFrame List Rollover\n");
		EHCI::Private::OpRegister->USBSTS.FrameListRollover = 1;
	}
	if (EHCI::Private::OpRegister->USBSTS.HostSystemError)
	{
		Video::Print("\nHost System Error\n");
		Video::Print("Halting system...");
		EHCI::Private::OpRegister->USBSTS.HostSystemError = 1;
		asm volatile("cli");
		asm volatile("hlt");
	}
	if (EHCI::Private::OpRegister->USBSTS.InterruptOnAsyncAdvance)
	{
		Video::Print("\nInterrupt on Async Advance\n");
		EHCI::Private::OpRegister->USBSTS.InterruptOnAsyncAdvance = 1;
	}
}

EHCI::Private::PORTSC* EHCI::Private::SelectPortSC(byte port)
{
	dword* NewAddress = (dword*)(EHCI::Private::PORTSCBaseAddress + (port * 4));
	return (EHCI::Private::PORTSC*)NewAddress;
}

void EHCI::Private::ResetPort(EHCI::Private::PORTSC* port)
{
		EHCI::Private::DisableInterrupts();
		port->PortReset = 1;
		PIT::WaitMilliseconds(300);
		port->PortReset = 0;
		while (port->PortReset == 1)
		{
			PIT::WaitMilliseconds(20);
		}
		EHCI::Private::EnableInterrupts();
}

void EHCI::ResetPort(byte i)
{
	EHCI::Private::PORTSC* port = EHCI::Private::SelectPortSC(i);
	EHCI::Private::ResetPort(port);
}

void EHCI::Private::ResetPorts()
{
	for (int i = 0; i < EHCI::Private::NumberOfPorts; i++)
	{
		EHCI::Private::PORTSC* port = EHCI::Private::SelectPortSC(i);
		if (port->CurrentConnectStatus)
		{
			Video::PrintNumber(i, 10);Video::NewLine();
		
			if (EHCI::Private::PortConnectStatus[i] == 0)
			{
				EHCI::Private::ResetPort(port);
				
				EHCI::Private::PortConnectStatus[i] = USB20::ConfigureDevice(i);
			}
		}
		else
		{
			if (EHCI::Private::PortConnectStatus[i] != 0)
			{
				USB20::DeConfigureDevice(EHCI::Private::PortConnectStatus[i]);
				EHCI::Private::PortConnectStatus[i] = 0;
			}
		}
	}
}
I did not include transaction specific code in this.

Jacob
Last edited by Hoozim on Mon Dec 09, 2013 6:27 pm, edited 1 time in total.
Hoozim
Member
Member
Posts: 53
Joined: Fri Jul 23, 2010 8:26 am

Re: EHCI Interrupt Weird Firing Problem

Post by Hoozim »

That USBSTS = 0 interrupt seems to occur right after I set the configure flag in the CONFIGUREFLAG register.

Jacob
Nable
Member
Member
Posts: 453
Joined: Tue Nov 08, 2011 11:35 am

Re: EHCI Interrupt Weird Firing Problem

Post by Nable »

I wonder if that's related to your problem: http://forum.osdev.org/viewtopic.php?f= ... hilit=EHCI
When you've mentioned CONFIG...FLAG, I've remembered that thread. Although it's full of Rdos, you might find some usefull pieces of information.
Hoozim
Member
Member
Posts: 53
Joined: Fri Jul 23, 2010 8:26 am

Re: EHCI Interrupt Weird Firing Problem

Post by Hoozim »

No, it's not that, the config flag is being set and the EHCI is getting port ownership. Its the IRQ which is behaving very weirdly. I'm trying a rewrite of the driver (which it needed anyway to make it faster and more reliable), maybe I will uncover something there. Thanks anyway though. :)

Jacob
rdos
Member
Member
Posts: 3306
Joined: Wed Oct 01, 2008 1:55 pm

Re: EHCI Interrupt Weird Firing Problem

Post by rdos »

Hoozim wrote:No, it's not that, the config flag is being set and the EHCI is getting port ownership. Its the IRQ which is behaving very weirdly. I'm trying a rewrite of the driver (which it needed anyway to make it faster and more reliable), maybe I will uncover something there. Thanks anyway though. :)

Jacob
I wouldn't be too sure that the problem is not that the BIOS still has SMIs active on the EHCI controller, something that could cause a multitude of problems. You should make sure you have turned of BIOS SMI notifications through PCI-registers.
Hoozim
Member
Member
Posts: 53
Joined: Fri Jul 23, 2010 8:26 am

Re: EHCI Interrupt Weird Firing Problem

Post by Hoozim »

rdos, YOU ARE AWESOME!!!!! It worked!!!!! I just had to flip the semaphores as per the specification and it works beautifully on both controllers!

Jacob
Post Reply