I'm working on making a UHCI driver, and got stuck.
I've been scavenging information about USB from all around, OSDev wiki, UHCI docs, Linux sources, random places on the net, but there seems to be no complete and correct documentation on how to get started.
In emulators all is perfect, on real PC (eeepc, got 4 UHCI and one EHCI) nothing works.
Nothing works can be defined as no transactions working.
The simple test of filling a frame list with $00000001 and printing frame counter works nicely (all said about real HW if not said otherwise), but anything more complex does not.
Trying to request a descriptor straight away or assign address to a device results in $0030 or $0028 being returned in USBSTS, with TD left unchanged in active state.
frame counter vary between 0 and 17, each test giving different values and different status.
Tried it with a bunch of USB1 devices - mouses, keypad, old BT stick, floppy pad, no significant differences.
PCI status on each attempt is either $0280 or $2280, not sure what it means.
No apparent link between UHCI status and PCI status, they can come in any pair.
Can anyone help?
What have i missed or misunderstood?
Outline of the code:
Code: Select all
SR_GET_STATUS =$00;
SR_SET_ADDRESS =$05;
SR_GET_DESCRIPTOR=$06;
DSC_DEVICE=$01;
DSC_STRING=$03;
UHCI_REG_USBCMD =$00;
UHCI_REG_USBSTS =$02;
UHCI_REG_USBINTR =$04;
UHCI_REG_FRNUM =$06;
UHCI_REG_FRBASEADD=$08;
UHCI_REG_PORTBASE =$10;
UHCI_REG_SOFMOD =$0C;
Code: Select all
//--Acquire device dev--
//--Legacy off, keyboards stop working at this point, so i guess that's done right--
pci_write_word(dev,$C0,$8F00);
//--HCRESET--
rd:=readregw(dev,UHCI_REG_USBCMD);
rd:=rd or 2;
writeregw(dev,UHCI_REG_USBCMD,rd);
//--Wait one second or until (readregw(dev,UHCI_REG_USBCMD) and 2)=0 ---
//--Clear all--
writeregw(uhci.dev,UHCI_REG_USBINTR,0);
writeregw(uhci.dev,UHCI_REG_USBCMD,0);
//--Set frame list, TDs, QHs, page-aligned, physical memory--
//--Set frame list base--
writeregd(uhci.dev,UHCI_REG_FRBASEADD,uhci.frs);
//--Set timing--
writeregb(uhci.dev,UHCI_REG_SOFMOD,64);
//--Flag configured, 64kb--
writeregw(uhci.dev,UHCI_REG_USBCMD,$C0);
//--Install interrupt handler--
irq_install_handler(dev.irq,@uhci_intr);
//Without dummy IRQ the all-1 frame list test hangs on random frame count.
//If IRQ handle is installed earlier, nothing else works, despite no interrupts received. Weird.
Without it the test with frame list filled with $00000001's hangs on random frame count, and keeps calling the interrupt.
Code: Select all
rd:=readregw(uhci.dev,UHCI_REG_USBSTS);
writeregw(uhci.dev,UHCI_REG_USBSTS,rd or 1);
Code: Select all
//--Disable, reset, enable the port, then check if something is connected to it. If it is, assume pipe 0 is it, and proceed.
for i:=uhci.lastport to uhci.ports-1 do begin
writeregw(uhci.dev,UHCI_REG_PORTBASE+i*2,0);//Disable
wide_delay(100); //millisecs
writeregw(uhci.dev,UHCI_REG_PORTBASE+i*2,$0204);//Reset
wide_delay(50);
writeregw(uhci.dev,UHCI_REG_PORTBASE+i*2,$0004);//Enable
wide_delay(10);
rd:=readregw(uhci.dev,UHCI_REG_PORTBASE+i*2);
if(rd and 1)<>0 then //--select this, device present, update uhci.lastport--
end;
//---Try to assign address, addr - next free address---
//Setup and status TDs.
uhci_xfer(uhci,0, 1,$2D,0,0,0,8,uhci.buffer); //uhci struct, TD to write to,LINK to next td,PID,dev,endpoint,D,len,buffer
mk_setup_buf(@uhci.buffer[0],$00,SR_SET_ADDRESS,addr,0,0); //buffer, RequestType, Request, Value, Index, Length
uhci_xfer(uhci,1,-1,$69,0,0,1,4,@uhci.buffer[8]); //Link=-1 means set T=1.
//--Clean, set run, wait till completed, stop, USBSTS=$0020 at this point--
uhci_reinit_q(uhci); //--Fill the frame list with 1's, make one QH with TDs list above referenced in it, put that into frame 0.---
writeregw(uhci.dev,UHCI_REG_FRNUM,0);
writeregw(uhci.dev,UHCI_REG_USBCMD,$C1);
if not uhci_wait_q(uhci,2) then exit;
writeregw(uhci.dev,UHCI_REG_USBCMD,$C0);
wide_delay(62);
//--Here comes the first fault, the TD[0] is never set as complete, status is either $30 or $28--