MODULE UsbUhci;
IMPORT SYSTEM, KernelLog, Machine, PCI, Kernel, Objects, Modules, UsbHcdi, Usbdi, Debug := UsbDebug;
CONST
Description = "UHCI USB Host Controller";
NumberOfPorts = 2;
UsbCmd = 0H;
UsbSts = 2H;
UsbIntr = 4H;
FrNum = 6H;
FlBaseAdr = 8H;
SofMod = 0CH;
PortSc1 = 10H;
PortSc2 = 12H;
CmdMaxPacket = {7};
CmdConfigureFlag = {6};
CmdSoftwareDebug = {5};
CmdForceGlobalResume = {4};
CmdEnterGlobalSuspend = {3};
CmdGlobalReset = {2};
CmdHostControllerReset = {1};
CmdRunStop = {0};
StatusHChalted = {5};
StatusProcessError = {4};
StatusSystemError = {3};
StatusResumeDetect = {2};
StatusErrorInt = {1};
StatusInt = {0};
IntShortPacket = {3};
IntIOC = {2};
IntResume = {1};
IntTimeoutCRC = {0};
PortSuspend = {12};
PortReset = {9};
PortLowSpeed = {8};
PortResumeDetect = {6};
PortLineStatus = {4,5};
PortEnabledChange = {3};
PortEnabled = {2};
PortConnectStatusChange = {1};
PortCurrentConnectStatus = {0};
PortChangeMask = {1,3};
QhLinkPointer = 0;
QhElementLinkPointer = 4;
QhTerminate = {0};
QhTdSelect = {1};
TDLinkPointer = 0;
TDControlStatus = 4;
TDToken = 8;
TDBufferPointer = 12;
TDTerminate = {0};
TDQHSelect = {1};
TDDepthFirst = {2};
TDBitStuff = {17};
TDCrcTimeout = {18};
TDNak = {19};
TDBabble = {20};
TDDataBuffer = {21};
TDStalled = {22};
TDActive = {23};
TDIOC = {24};
TDIOS = {25};
TDLS = {26};
TDERR0 = {}; TDERR1 = {27}; TDERR2 = {28}; TDERR3 = {27,28};
TDSPD = {29};
TDDataToggle = {19};
PidOut = 0E1H;
PidIn = 069H;
PidSetup = 02DH;
TdListSize = 3 + 11 + 1;
ShowScheduleMaxQH = 25;
TYPE
UhciFrameList = POINTER TO RECORD
index : SYSTEM.SIZE;
framelistbase : Machine.Address32;
field : ARRAY 2*1024 OF LONGINT;
END;
UhciController = OBJECT (UsbHcdi.Hcd)
VAR
bcdUSB : LONGINT;
framelist : UhciFrameList;
controlTD : Machine.Address32;
bulkTD : Machine.Address32;
isochronousTD : Machine.Address32;
interruptTD : ARRAY 11 OF Machine.Address32;
tdlist : POINTER TO ARRAY OF CHAR;
tdlistbase : Machine.Address32;
PROCEDURE EnablePortPower*(port : LONGINT);
END EnablePortPower;
PROCEDURE DisablePortPower*(port : LONGINT);
END DisablePortPower;
PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
VAR status : INTEGER; mtimer : Kernel.MilliTimer;
BEGIN
Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortReset - PortChangeMask));
Wait(UsbHcdi.PortResetTime);
Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) - PortReset - PortChangeMask));
Wait(2);
Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
IF SYSTEM.VAL(SET, status) * PortReset # {} THEN RETURN FALSE; END;
Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortEnabled - PortChangeMask));
Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
REPEAT
Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
UNTIL (SYSTEM.VAL(SET, status) * PortEnabled # {}) OR Kernel.Expired(mtimer);
Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortChangeMask));
RETURN SYSTEM.VAL(SET, status) * PortEnabled # {};
END ResetAndEnablePort;
PROCEDURE DisablePort*(port : LONGINT);
VAR status : INTEGER; mtimer : Kernel.MilliTimer;
BEGIN
Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) - PortEnabled - PortChangeMask));
Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
REPEAT
Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, status));
UNTIL (SYSTEM.VAL(SET, status) * PortEnabled = {}) OR Kernel.Expired(mtimer);
Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, status) + PortChangeMask));
END DisablePort;
PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN):SET;
VAR status, s : SET;
BEGIN
Machine.Portin16(ports[port], SYSTEM.VAL(INTEGER, s));
IF ack & (s * PortChangeMask # {}) THEN Machine.Portout16(ports[port], SYSTEM.VAL(INTEGER, s)); END;
status := UsbHcdi.PortStatusPowered;
IF s * PortCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent END;
IF s * PortConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange; END;
IF s * PortEnabled # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
IF s * PortEnabledChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange; END;
IF s * PortReset # {} THEN status := status + UsbHcdi.PortStatusReset; END;
IF s * PortSuspend # {} THEN status := status + UsbHcdi.PortStatusSuspended; END;
IF s * PortLowSpeed # {} THEN
status := status + UsbHcdi.PortStatusLowSpeed;
ELSE
status := status + UsbHcdi.PortStatusFullSpeed;
END;
RETURN status;
END GetPortStatus;
PROCEDURE SetStatusChangeHandler*(handler : UsbHcdi.StatusChangeHandler) : BOOLEAN;
BEGIN
RETURN FALSE;
END SetStatusChangeHandler;
PROCEDURE Reset;
BEGIN {EXCLUSIVE}
Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, CmdGlobalReset) ); Wait (50);
Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, 0) ); Wait (10);
END Reset;
PROCEDURE GetFrameNumber*() : LONGINT;
VAR frameNumber : INTEGER;
BEGIN
Machine.Portin16(iobase + FrNum, frameNumber);
frameNumber := SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, frameNumber) * {0..10});
RETURN SYSTEM.VAL(LONGINT, frameNumber);
END GetFrameNumber;
PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
BEGIN {EXCLUSIVE}
UnlinkTDsInternal(pipe);
END UnlinkTDs;
PROCEDURE UnlinkTDsInternal*(pipe : UsbHcdi.Pipe);
VAR td : Machine.Address32; dword : SET; timer : Kernel.Timer;
BEGIN
IF pipe.firstTD = 0 THEN RETURN END;
td := Machine.Ensure32BitAddress (pipe.firstTD);
ASSERT(SYSTEM.VAL(SET, td) * {0..3} = {});
WHILE td < pipe.lastTD DO
dword := SYSTEM.VAL(SET, SYSTEM.GET32(td + TDControlStatus));
SYSTEM.PUT32(td + TDControlStatus, dword - TDActive);
td := td + 16;
END;
NEW(timer); timer.Sleep(10);
SYSTEM.PUT32(pipe.qh + QhElementLinkPointer, QhTerminate);
END UnlinkTDsInternal;
PROCEDURE ClearHalt*(pipe : UsbHcdi.Pipe);
VAR dword : SET;
BEGIN
ASSERT((pipe # NIL) & (pipe.qh # 0));
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhElementLinkPointer));
ASSERT(dword * QhTdSelect = {});
dword := dword + QhTerminate;
SYSTEM.PUT32(pipe.qh + QhElementLinkPointer, dword);
END ClearHalt;
PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
VAR qhtmp, slots, index, queueslots : LONGINT;
BEGIN
ASSERT((pipe # NIL) & (pipe.qh # 0) & (SYSTEM.VAL(SET, pipe.qh) * {0..3} = {}));
BuildQH(pipe.qh, 3, 1, pipe, 0);
CASE pipe.type OF
UsbHcdi.PipeControl : pipe.queue := controlTD;
| UsbHcdi.PipeBulk : pipe.queue := bulkTD;
| UsbHcdi.PipeIsochronous : pipe.queue := isochronousTD;
| UsbHcdi.PipeInterrupt :
BEGIN
slots := 1024 DIV pipe.irqInterval;
index := 0; queueslots := 1024;
LOOP
IF queueslots = 0 THEN index := 10; EXIT; END;
IF slots >= queueslots THEN EXIT END;
queueslots := queueslots DIV 2; INC (index)
END;
pipe.queue := interruptTD[index]
END;
ELSE
RETURN FALSE;
END;
qhtmp := SYSTEM.GET32(pipe.queue);
SYSTEM.PUT32(pipe.qh, qhtmp);
SYSTEM.PUT32(pipe.queue, SYSTEM.VAL(SET, pipe.qh) + {1});
RETURN TRUE;
END InsertQH;
PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
VAR qhtmp, queue, delthis : Machine.Address32; timer : Kernel.Timer;
BEGIN
UnlinkTDsInternal(pipe);
delthis := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, pipe.qh) + {1});
queue := Machine.Ensure32BitAddress (pipe.queue);
LOOP
qhtmp := SYSTEM.GET32(queue);
IF qhtmp = 1 THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbUhci: Fatal error, DeleteQH cannot find qh entry in queue"); KernelLog.Ln; END;
EXIT;
END;
IF qhtmp = delthis THEN
qhtmp := SYSTEM.GET32(pipe.qh);
SYSTEM.PUT32(queue, qhtmp);
EXIT;
END;
queue := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, qhtmp) - {1})
END;
NEW(timer); timer.Sleep(10);
END RemoveQH;
PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
BEGIN {EXCLUSIVE}
RETURN TRUE;
END LinkTDsAllowed;
PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; td : Machine.Address32);
BEGIN {EXCLUSIVE}
ASSERT(SYSTEM.VAL(SET, td) * {0..3} = {});
SYSTEM.PUT32(pipe.qh + QhElementLinkPointer, Machine.Ensure32BitAddress (td));
END LinkTDs;
PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
VAR td : Machine.Address32; pid, restlen, databufferAdr, curlen : LONGINT; tdStatus : SET;
BEGIN
pipe.firstTD := pipe.tdBase; td := pipe.tdBase;
IF Debug.StrongChecks THEN DoStrongChecks(pipe, bufferLen, 0, buffer); END;
tdStatus := TDActive + TDERR3;
IF pipe.speed = UsbHcdi.LowSpeed THEN tdStatus := tdStatus + TDLS; END;
BuildTDLinkPointer(td, TDDepthFirst, td + 16);
BuildTDControlStatus(td, tdStatus);
BuildTDToken(td, PidSetup, pipe.address, pipe.endpoint MOD 16, FALSE, 8);
SYSTEM.PUT32(td + TDBufferPointer, SYSTEM.ADR(msg[0]));
pipe.dataToggle := TRUE;
IF bufferLen # 0 THEN
IF direction = UsbHcdi.In THEN pid := PidIn; ELSE pid := PidOut; END;
databufferAdr := Machine.Ensure32BitAddress (pipe.physBufferAdr);
restlen := bufferLen;
WHILE restlen > 0 DO
td := td + 16;
IF restlen > pipe.maxPacketSize THEN
curlen := pipe.maxPacketSize;
ELSE
curlen := restlen;
END;
BuildTDLinkPointer(td, TDDepthFirst, td + 16);
BuildTDControlStatus(td, tdStatus);
BuildTDToken(td, pid, pipe.address, pipe.endpoint MOD 16, pipe.dataToggle, curlen);
SYSTEM.PUT32(td + TDBufferPointer, databufferAdr);
pipe.dataToggle := ~pipe.dataToggle;
databufferAdr := databufferAdr + curlen;
restlen := restlen - curlen;
END;
END;
tdStatus := tdStatus - TDSPD;
IF pipe.ioc THEN tdStatus := tdStatus + TDIOC; END;
IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
pid := PidIn;
ELSE
pid := PidOut;
END;
td := td + 16;
BuildTDLinkPointer(td, TDTerminate, 0);
BuildTDControlStatus(td, tdStatus);
BuildTDToken(td, pid, pipe.address, pipe.endpoint MOD 16, TRUE, 0);
SYSTEM.PUT32(td + TDBufferPointer, 0);
pipe.lastTD := td;
END ScheduleControl;
PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
VAR td : Machine.Address32; pid, restlen, curlen, databuffer : LONGINT; tdStatus : SET;
BEGIN
pipe.firstTD := pipe.tdBase;
IF Debug.StrongChecks THEN DoStrongChecks(pipe, bufferLen, offset, buffer); END;
IF pipe.firstTD + 16*((pipe.transferLen DIV pipe.maxPacketSize) +1) > SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
KernelLog.String("UsbUhci: TD buffer too small to support requested tranfer size."); KernelLog.Ln;
pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.OutOfTDs;
RETURN;
END;
tdStatus := TDActive + TDERR3;
IF pipe.speed = UsbHcdi.LowSpeed THEN tdStatus := tdStatus + TDLS; END;
IF pipe.direction = UsbHcdi.In THEN
tdStatus := tdStatus + TDSPD;
pid := PidIn;
ELSE
pid := PidOut;
END;
restlen := bufferLen;
databuffer := Machine.Ensure32BitAddress (pipe.physBufferAdr);
td := pipe.firstTD - 16;
WHILE restlen > 0 DO
td := td + 16;
IF restlen > pipe.maxPacketSize THEN
curlen := pipe.maxPacketSize;
BuildTDLinkPointer(td, TDDepthFirst, td + 16);
ELSE
curlen := restlen;
IF restlen < pipe.maxPacketSize THEN tdStatus := tdStatus - TDSPD; ELSE tdStatus := tdStatus + TDSPD; END;
IF pipe.ioc THEN tdStatus := tdStatus + TDIOC; ELSE tdStatus := tdStatus - TDIOC; END;
BuildTDLinkPointer(td, TDTerminate, 0);
END;
BuildTDControlStatus(td, tdStatus);
BuildTDToken(td, pid, pipe.address, pipe.endpoint MOD 16, pipe.dataToggle, curlen);
SYSTEM.PUT32(td + TDBufferPointer, databuffer);
pipe.dataToggle := ~ pipe.dataToggle;
databuffer := databuffer + curlen;
restlen := restlen - curlen;
END;
pipe.lastTD := td;
END Schedule;
PROCEDURE InterruptHandler;
VAR s : SET;
BEGIN
IF Debug.Stats THEN INC(NnofInterrupts); END;
IF state >= UsbHcdi.Initialized THEN
Machine.Portin16(iobase + UsbSts, SYSTEM.VAL(INTEGER, s));
IF s # {} THEN
IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
IF Debug.Trace & Debug.traceInterrupts THEN ShowInterrupts(s); END;
Machine.Portout16(iobase + UsbSts, SYSTEM.VAL(INTEGER, s));
IF s * StatusHChalted # {} THEN KernelLog.String("UsbUhci: Error: Host Controller halted"); KernelLog.Ln; SetState(UsbHcdi.Halted); END;
IF s * StatusProcessError # {} THEN KernelLog.String("UsbUhci: Host Controller process error"); KernelLog.Ln; END;
IF s * StatusSystemError # {} THEN KernelLog.String("UsbUhci: Host system error"); KernelLog.Ln; END;
IF s * (StatusErrorInt + StatusInt) # {} THEN
NotifyCompletionHandlers;
END;
END;
END;
END InterruptHandler;
PROCEDURE GetTransferredLen(pipe : UsbHcdi.Pipe; VAR errors : SET);
VAR td, thislen, actlen, maxlen, val : LONGINT; addr : Machine.Address32; s : SET;
BEGIN
actlen := 0; addr := pipe.firstTD;
LOOP
val := SYSTEM.GET32(addr + TDControlStatus);
s := SYSTEM.VAL(SET, val);
IF s * TDActive # {} THEN EXIT; END;
thislen := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val+1) * {0..10});
val := SYSTEM.GET32(addr + TDToken);
IF SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..7}) # PidSetup THEN actlen := actlen + thislen; END;
maxlen := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ASH(val, -21) + 1) * {0..10});
IF thislen < maxlen THEN
errors := errors + UsbHcdi.ShortPacket;
END;
IF s * (TDCrcTimeout + TDDataBuffer + TDStalled + TDBitStuff + TDBabble + TDNak) # {} THEN EXIT END;
td := SYSTEM.GET32(addr + TDLinkPointer);
IF SYSTEM.VAL(SET, td) * {0} # {} THEN EXIT; END;
addr := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, td) * {4..31});
END;
pipe.actLen := actlen;
END GetTransferredLen;
PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
VAR
val, pid : LONGINT;
td, addr : Machine.Address32;
errors, s : SET;
datatoggle : BOOLEAN;
last : BOOLEAN;
BEGIN
td := SYSTEM.GET32(pipe.qh + QhElementLinkPointer);
IF td = 1 THEN
td := pipe.lastTD;
ELSE
td := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, td) * {4..31});
END;
IF td = pipe.lastTD THEN last := TRUE; END;
addr := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, td) * {4..31});
val := SYSTEM.GET32(addr + TDControlStatus);
s := SYSTEM.VAL(SET, val);
errors := UsbHcdi.NoErrors;
IF s * TDActive # {} THEN RETURN; END;
IF s * TDNak # {} THEN errors := errors + UsbHcdi.Nak; END;
val := SYSTEM.GET32(addr + TDToken);
pid := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..7});
IF SYSTEM.VAL(SET, val) * TDDataToggle # {} THEN datatoggle := TRUE ELSE datatoggle := FALSE END;
IF (s * (TDCrcTimeout + TDDataBuffer + TDStalled + TDBitStuff + TDBabble)) # {} THEN
IF s * TDCrcTimeout # {} THEN errors := errors + UsbHcdi.CrcTimeout; END;
IF s * TDDataBuffer # {} THEN errors := errors + UsbHcdi.Databuffer; END;
IF s * TDStalled # {} THEN errors := errors + UsbHcdi.Stalled; END;
IF s * TDBitStuff # {} THEN errors := errors + UsbHcdi.BitStuff; END;
IF s * TDBabble # {} THEN errors := errors + UsbHcdi.Babble; END;
END;
IF ~last & (errors - UsbHcdi.Nak = {}) THEN RETURN; END;
td := SYSTEM.GET32(pipe.qh + QhElementLinkPointer);
GetTransferredLen(pipe, errors);
pipe.errors := errors;
IF errors = UsbHcdi.NoErrors THEN
pipe.status := Usbdi.Ok;
ELSIF errors = UsbHcdi.ShortPacket THEN
pipe.status := Usbdi.ShortPacket;
ELSE
IF errors * UsbHcdi.Stalled # {} THEN
pipe.status := Usbdi.Stalled;
ELSE
pipe.status := Usbdi.Error;
END;
END;
IF (pipe.type = UsbHcdi.PipeBulk) OR (pipe.type = UsbHcdi.PipeInterrupt) THEN
IF (pipe.status = Usbdi.Ok) OR (pipe.status = Usbdi.ShortPacket) THEN
pipe.dataToggle := ~datatoggle;
END;
END;
END UpdatePipeStatus;
PROCEDURE BuildQH(td: Machine.Address32; f1, f2: LONGINT; f3 : ANY; f4 : LONGINT);
BEGIN
SYSTEM.PUT32(td, f1);
SYSTEM.PUT32(td + 4, f2);
SYSTEM.PUT32(td + 8, f3);
SYSTEM.PUT32(td + 12, f4);
END BuildQH;
PROCEDURE BuildTD(td : Machine.Address32; f1, f2, f3, f4 : LONGINT);
BEGIN
SYSTEM.PUT32(td + TDLinkPointer, f1);
SYSTEM.PUT32(td + TDControlStatus, f2);
SYSTEM.PUT32(td + TDToken, f3);
SYSTEM.PUT32(td + TDBufferPointer, f4);
END BuildTD;
PROCEDURE BuildTDLinkPointer (VAR td : Machine.Address32; flags : SET; LinkPointer : LONGINT);
BEGIN
ASSERT( SYSTEM.VAL( SET, LinkPointer) * {0..3} = {});
SYSTEM.PUT32(td, SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LinkPointer) + flags));
END BuildTDLinkPointer;
PROCEDURE BuildTDControlStatus (VAR td : Machine.Address32; status : SET);
BEGIN
status := status + {0..10}; SYSTEM.PUT32(td + TDControlStatus, status);
END BuildTDControlStatus;
PROCEDURE BuildTDToken (VAR td: Machine.Address32; PID, DeviceAddress, EndPoint : LONGINT; DataToggle : BOOLEAN; maxLength : LONGINT);
VAR s : SET;
BEGIN
ASSERT(((maxLength>=0000H) & (maxLength<=04FFH)) OR (maxLength=07FFH));
IF maxLength = 0 THEN maxLength := 07FFH;
ELSE maxLength := maxLength - 1;
END;
s := {}; IF DataToggle THEN s := s + TDDataToggle END;
SYSTEM.PUT32(td + TDToken, SYSTEM.VAL(SET, PID + ASH(DeviceAddress, 8) + ASH(EndPoint, 15) + ASH(maxLength, 21)) + s);
END BuildTDToken;
PROCEDURE Init(base: Machine.Address32; IRQ : LONGINT) : BOOLEAN;
VAR i, k, j : LONGINT; dword : SET;
BEGIN
iobase := base; irq := IRQ; DMAchaining := FALSE;
portCount := NumberOfPorts; NEW(ports, portCount);
FOR i := 0 TO portCount - 1 DO ports[i] := iobase + PortSc1 + i*2; END;
NEW(hubDescriptor, 8);
hubDescriptor[0] := CHR(7);
hubDescriptor[1] := CHR(29H);
hubDescriptor[2] := CHR(portCount);
dword := dword + {1};
dword := dword + {4};
hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
hubDescriptor[4] := CHR(0);
hubDescriptor[5] := CHR(10);
hubDescriptor[6] := CHR(0);
NEW(framelist);
framelist.framelistbase := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, SYSTEM.ADR(framelist.field[0]) + 1024*4 - 1) * {12..31});
ASSERT (Machine.Is32BitAddress (framelist.framelistbase));
framelist.index := ( framelist.framelistbase - SYSTEM.ADR(framelist.field[0]) ) DIV 4;
NEW(tdlist, 16*TdListSize);
tdlistbase := SYSTEM.VAL(Machine.Address32, SYSTEM.VAL(SET, SYSTEM.ADR(tdlist[0])+15) * {4..31});
ASSERT (Machine.Is32BitAddress (tdlistbase));
ASSERT(SYSTEM.VAL(SET, tdlistbase) * {0..3} = {});
ASSERT((tdlistbase >= SYSTEM.ADR(tdlist[0])) & (tdlistbase < SYSTEM.ADR(tdlist[16*TdListSize-1])));
controlTD := tdlistbase;
bulkTD := tdlistbase + 16;
isochronousTD := tdlistbase + 32;
FOR i := 0 TO 10 DO
ASSERT(tdlistbase + 48 + i*16 <= SYSTEM.ADR(tdlist[16*TdListSize-1]));
interruptTD[i] := tdlistbase + 48 + i*16;
END;
BuildTD(bulkTD, 1, 1, 0, 0);
BuildTD(controlTD, SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, bulkTD) + {1} ), 1, 0, 0);
BuildTD(isochronousTD, SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, controlTD) + {1}), 1, 0, 0);
BuildTD(interruptTD[0], SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, isochronousTD) + {1} ), 1, 0, 0);
FOR i:=1 TO 10 DO
BuildTD(interruptTD[i], SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptTD[i-1]) + {1} ), 1, 0, 0);
END;
FOR i := 0 TO 1023 DO
k := 0; j := i;
LOOP
IF (SYSTEM.VAL(SET, j) * {0}) = {} THEN EXIT; END;
INC(k); j := j DIV 2;
END;
framelist.field[framelist.index + i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptTD[k]) + {1} );
END;
Reset;
IF Start() = FALSE THEN
Reset; KernelLog.String("UsbUhci: Couldn't start controller."); KernelLog.Ln; RETURN FALSE;
END;
Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
RETURN TRUE;
END Init;
PROCEDURE Start():BOOLEAN;
VAR t : LONGINT; s : SET;
BEGIN
Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, CmdHostControllerReset) );
t := 10000;
REPEAT
Machine.Portin16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, s));
DEC( t );
UNTIL ((t = 0) OR ( ~(1 IN s)));
IF t = 0 THEN RETURN FALSE END;
Machine.Portout16(iobase + UsbIntr, SYSTEM.VAL(INTEGER, IntShortPacket + IntIOC + IntResume + IntTimeoutCRC) );
Machine.Portout16(iobase + FrNum, SYSTEM.VAL(INTEGER, 0) );
Machine.Portout32(iobase + FlBaseAdr, SYSTEM.VAL(LONGINT, framelist.framelistbase));
Machine.Portout16(iobase + UsbCmd, SYSTEM.VAL(INTEGER, CmdRunStop + CmdConfigureFlag + CmdMaxPacket) );
SetState(UsbHcdi.Operational);
RETURN TRUE;
END Start;
PROCEDURE Cleanup;
BEGIN
IF state >= UsbHcdi.Initialized THEN Objects.RemoveHandler(InterruptHandler, Machine.IRQ0 + irq); END;
Cleanup^;
Reset;
END Cleanup;
PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
BEGIN
HumanTD(pipe.firstTD);
END ShowPipe;
PROCEDURE ShowSchedule*;
BEGIN
IF Debug.Trace THEN
KernelLog.String("Host Controller Data Structures of ");
KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
HumanQH(controlTD, SELF);
HumanQH(interruptTD[10], SELF);
END;
END ShowSchedule;
PROCEDURE DoStrongChecks(pipe : UsbHcdi.Pipe; len, ofs : LONGINT; VAR buffer : Usbdi.Buffer);
VAR i : SYSTEM.ADDRESS;
BEGIN
IF len > 0 THEN
i := Machine.PhysicalAdr(SYSTEM.ADR(buffer[0]), len);
IF i = -1 THEN
KernelLog.String("UsbUhci: Buffers must be physically contiguoues"); KernelLog.Ln;
HALT(99)
END;
END;
IF pipe.type = UsbHcdi.PipeControl THEN
ASSERT((pipe.firstTD + 16*((pipe.transferLen DIV pipe.maxPacketSize) +3)) <= SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]));
ELSE
ASSERT((pipe.firstTD + 16*((pipe.transferLen DIV pipe.maxPacketSize) +1)) <= SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]));
END;
END DoStrongChecks;
PROCEDURE Diag;
VAR s : SET; framenum : LONGINT;
BEGIN
IF Debug.Trace THEN
Diag^;
Machine.Portin16 (iobase + FrNum, SYSTEM.VAL(INTEGER, framenum));
framenum := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, framenum) * {0..10});
KernelLog.String(" Frame 0"); KernelLog.Hex(framenum, 8);
KernelLog.String(" LastStatus: ");
Machine.Portin16 (iobase + UsbSts, SYSTEM.VAL(INTEGER, s));
IF s # {} THEN
IF s * StatusHChalted # {} THEN KernelLog.String ("[HChalted]"); END;
IF s * StatusProcessError # {} THEN KernelLog.String ("[Process Error]"); END;
IF s * StatusSystemError # {} THEN KernelLog.String ("[System Error]"); END;
IF s * StatusResumeDetect # {} THEN KernelLog.String ("[Resume Detect]"); END;
IF s * StatusErrorInt # {} THEN KernelLog.String ("[ErrorInt]"); END;
IF s * StatusInt # {} THEN KernelLog.String ("[Int]"); END;
KernelLog.Ln;
ELSE
KernelLog.String("[ok]"); KernelLog.Ln;
END;
KernelLog.String (" IRQ enable status: ");
Machine.Portin16 (iobase + UsbIntr, SYSTEM.VAL(INTEGER, s));
IF s * IntShortPacket # {} THEN KernelLog.String("[Short Packet]"); END;
IF s * IntIOC # {} THEN KernelLog.String ("[IOC]"); END;
IF s * IntResume # {} THEN KernelLog.String ("[Resume]"); END;
IF s * IntTimeoutCRC # {} THEN KernelLog.String ("[Timeout/CRC]"); END;
KernelLog.Ln;
END;
END Diag;
END UhciController;
VAR
qhCounter : LONGINT;
PROCEDURE HumanQH(qh : LONGINT; controller : UhciController);
VAR val,addr : LONGINT; s : SET; pipe : UsbHcdi.Pipe; ptr : ANY;
BEGIN
IF Debug.Trace THEN
IF qhCounter > ShowScheduleMaxQH THEN
KernelLog.String("UsbUhci: HumanQH: UsbUhci.ShowScheduleMaxQH showed... aborting."); KernelLog.Ln;
RETURN;
ELSE
INC(qhCounter);
END;
KernelLog.String("QH at address: "); KernelLog.Hex(qh, 8); KernelLog.String("H");
IF qh = controller.controlTD THEN KernelLog.String(" (Controller Control Queue)");
ELSIF qh = controller.bulkTD THEN KernelLog.String(" (Controller Bulk Queue)");
ELSIF qh = controller. isochronousTD THEN KernelLog.String(" (Controller Isochronous Queue)");
ELSIF qh = controller. interruptTD[0] THEN KernelLog.String(" (Controller 1ms-Interrupt Queue)");
ELSIF qh = controller. interruptTD[1] THEN KernelLog.String(" (Controller 2ms-Interrupt Queue)");
ELSIF qh = controller. interruptTD[2] THEN KernelLog.String(" (Controller 4ms-Interrupt Queue)");
ELSIF qh = controller. interruptTD[3] THEN KernelLog.String(" (Controller 8ms-Interrupt Queue)");
ELSIF qh = controller. interruptTD[4] THEN KernelLog.String(" (Controller 16ms-Interrupt Queue)");
ELSIF qh = controller. interruptTD[5] THEN KernelLog.String(" (Controller 32ms-Interrupt Queue)");
ELSIF qh = controller.interruptTD[6] THEN KernelLog.String(" (Controller 64ms-Interrupt Queue)");
ELSIF qh = controller.interruptTD[7] THEN KernelLog.String(" (Controller 128ms-Interrupt Queue)");
ELSIF qh = controller.interruptTD[8] THEN KernelLog.String(" (Controller 256ms-Interrupt Queue)");
ELSIF qh = controller.interruptTD[9] THEN KernelLog.String(" (Controller 512ms-Interrupt Queue)");
ELSIF qh = controller.interruptTD[10] THEN KernelLog.String(" (Controller 1024ms-Interrupt Queue)");
ELSE
ptr := SYSTEM.VAL(ANY, SYSTEM.GET32(qh + 8));
IF ptr # NIL THEN
pipe := ptr (UsbHcdi.Pipe);
CASE pipe.type OF
UsbHcdi.PipeBulk : KernelLog.String(" (Bulk");
|UsbHcdi.PipeControl : KernelLog.String(" (Control");
|UsbHcdi.PipeInterrupt : KernelLog.String(" (Interrupt");
|UsbHcdi.PipeIsochronous : KernelLog.String(" (Isochronous");
ELSE
KernelLog.String(" (Unknown");
END;
KernelLog.String(" Pipe Adr: "); KernelLog.Int(pipe.address, 0);
KernelLog.String(" Ep: "); KernelLog.Int(pipe.endpoint, 0);
KernelLog.String(")"); KernelLog.Ln;
ELSE
KernelLog.String("(unknown)");
END;
END;
KernelLog.Ln;
KernelLog.String(" QH link pointer: ");
val := SYSTEM.GET32(qh); s := SYSTEM.VAL(SET, val);
IF s * TDQHSelect # {} THEN KernelLog.String(" [QH]"); ELSE KernelLog.String(" [TD]"); END;
IF s * TDTerminate # {} THEN KernelLog.String(" [Terminate]"); END;
val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {4..31});
addr := val;
KernelLog.String("[PTR val: "); KernelLog.Hex(val, 8); KernelLog.String("H]"); KernelLog.Ln;
KernelLog.String(" QH Element Link Pointer: ");
val := SYSTEM.GET32(qh + 4); s := SYSTEM.VAL(SET,val);
IF s * TDQHSelect # {} THEN KernelLog.String(" [QH]"); ELSE KernelLog.String(" [TD]"); END;
IF s * TDTerminate # {} THEN KernelLog.String(" [Terminate]"); END;
val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {4..31});
KernelLog.String("[PTR val: "); KernelLog.Hex(val, 8); KernelLog.String("G]"); KernelLog.Ln;
IF s * TDTerminate = {} THEN
IF s * TDQHSelect # {} THEN
HumanQH(val, controller);
ELSE
HumanTD(val);
END;
END;
IF addr # 0 THEN
KernelLog.String("next queue head in queue:"); KernelLog.Ln; KernelLog.Ln;
HumanQH(addr, controller);
END;
qhCounter := 0;
END;
END HumanQH;
PROCEDURE HumanTD(nexttd: Machine.Address32);
VAR addr, val : LONGINT; s : SET;
BEGIN
IF Debug.Trace THEN
LOOP
addr := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, nexttd) * {4..31});
KernelLog.String("TD at address: "); KernelLog.Hex(addr, 8); KernelLog.Ln;
val := SYSTEM.GET32(addr); s := SYSTEM.VAL(SET, val);
KernelLog.String(" LinkPTR:");
IF s * TDDepthFirst # {} THEN KernelLog.String(" [Depthfirst]"); ELSE KernelLog.String("[BreathFirst]"); END;
IF s * TDQHSelect # {} THEN KernelLog.String(" [QH]"); ELSE KernelLog.String(" [TD]"); END;
IF s * TDTerminate # {} THEN KernelLog.String(" [Terminate]"); END;
KernelLog.String(" [PTR Val: "); KernelLog.Hex((val DIV 16) * 16, 8); KernelLog.String("H]");
KernelLog.Ln;
val := SYSTEM.GET32(addr + 4); s := SYSTEM.VAL(SET, val);
KernelLog.String(" Control&Status:");
IF s * TDActive # {} THEN KernelLog.String(" [Active]"); END;
IF s * TDIOC # {} THEN KernelLog.String(" [IOC]"); END;
IF s * TDIOS # {} THEN KernelLog.String(" [IOS]"); END;
IF s * TDLS # {} THEN KernelLog.String(" [LowSpeed]"); END;
IF s * TDERR1 # {} THEN KernelLog.String(" [Err1]"); END;
IF s * TDERR2 # {} THEN KernelLog.String(" [Err2]"); END;
IF s * TDSPD # {} THEN KernelLog.String(" [SPD]"); END;
IF s * TDCrcTimeout # {} THEN KernelLog.String(" [CRC/Timeout]"); END;
IF s * TDNak # {} THEN KernelLog.String(" [NAK]"); END;
IF s * TDDataBuffer # {} THEN KernelLog.String(" [DataBuffer]"); END;
IF s * TDStalled # {} THEN KernelLog.String(" [Stalled]"); END;
IF s * TDBitStuff # {} THEN KernelLog.String(" [BitStuff]"); END;
IF s * TDBabble # {} THEN KernelLog.String(" [Babble]"); END;
KernelLog.String (" [Actlen: "); val := SYSTEM.VAL(LONGINT, s * {0..10});
IF val = 07FFH THEN val := 0; ELSE INC(val); END; KernelLog.Int(val, 0); KernelLog.String(" ]");
KernelLog.String(" [MaxLen: "); val := SYSTEM.GET32(addr + 8);
val := SYSTEM.LSH(val, -21); IF val = 7FFH THEN val := 0; ELSE INC(val); END;
KernelLog.Int(val, 0); KernelLog.String(" ]"); KernelLog.Ln;
KernelLog.String(" PID: ");
val := SYSTEM.GET32(addr + 8); val := val MOD 256;
CASE val OF
PidOut : KernelLog.String("OUT");
| PidIn : KernelLog.String("IN");
| PidSetup : KernelLog.String("SETUP");
ELSE
KernelLog.String("Unkown ("); KernelLog.Hex(val, 8); KernelLog.String("H)");
END;
KernelLog.String(" DataToggle: ");
val := SYSTEM.GET32(addr + 8);
s := SYSTEM.VAL(SET,val);
IF s * TDDataToggle # {} THEN KernelLog.String("DATA1"); ELSE KernelLog.String("DATA0"); END;
KernelLog.String(" Device Adr: "); KernelLog.Int(SYSTEM.LSH(SYSTEM.VAL(LONGINT, s * {8..14}), -8),0);
KernelLog.String(" Endpoint: "); KernelLog.Int(SYSTEM.LSH(SYSTEM.VAL(LONGINT, s * {15..18}), -15),0);
KernelLog.Ln;
nexttd := SYSTEM.GET32(addr);
IF SYSTEM.VAL(SET, nexttd) * {0} # {} THEN EXIT; END;
IF nexttd = 0 THEN KernelLog.String("ERROR! Terminate was not set but address is 0"); KernelLog.Ln; EXIT; END;
END;
END;
END HumanTD;
PROCEDURE ShowInterrupts(s : SET);
BEGIN
KernelLog.String("UsbUhci: Interrupt: ");
IF s * StatusHChalted # {} THEN KernelLog.String("[HcHalted]"); END;
IF s * StatusProcessError # {} THEN KernelLog.String("[ProcessError]"); END;
IF s * StatusSystemError # {} THEN KernelLog.String("[SystemError]"); END;
IF s * StatusErrorInt # {} THEN KernelLog.String("[ErrorInt]"); END;
IF s * StatusInt # {} THEN KernelLog.String("[Int]"); END;
KernelLog.Ln;
END ShowInterrupts;
PROCEDURE PCIFindUhci;
CONST
UhciClassCode = 0C0300H;
VAR
hostController : UhciController;
bus, device, function : LONGINT;
iobase, irqline : LONGINT;
index : LONGINT;
res : LONGINT;
BEGIN
index := 0;
WHILE PCI.FindPCIClassCode(UhciClassCode, index, bus, device, function) = PCI.Done DO
res := PCI.ReadConfigByte(bus, device, function, PCI.IntlReg, irqline); ASSERT(res = PCI.Done);
res := PCI.ReadConfigDword(bus, device, function, PCI.Adr4Reg, iobase); ASSERT(res = PCI.Done);
iobase := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, iobase) * {1..31});
IF irqline # 0 THEN
res := PCI.WriteConfigWord(bus, device, function, 0C0H, 2000H);
IF res = PCI.Done THEN
NEW(hostController, bus, device, function);
IF hostController.Init(iobase, irqline) THEN
res := PCI.ReadConfigByte(bus, device, function, 60H, hostController.bcdUSB);
IF Debug.Verbose THEN
KernelLog.Enter;
KernelLog.String("UsbUhci: Initialised USB UHCI controller at base 0"); KernelLog.Hex(iobase, 8);
KernelLog.String("H, Irq: "); KernelLog.Int(irqline, 0);
KernelLog.String(" USB version: "); KernelLog.Hex(SYSTEM.LSH(hostController.bcdUSB, -4), -2); KernelLog.Char(".");
KernelLog.Hex(hostController.bcdUSB MOD 10H, -2);
KernelLog.Exit;
END;
UsbHcdi.RegisterHostController(hostController, Description);
ELSE
KernelLog.Enter;
KernelLog.String("UsbUhci: ERROR: Cannot init USB UHCI controller at base 0"); KernelLog.Hex(iobase, 8);
KernelLog.String("H, Irq: "); KernelLog.Int(irqline, 0);
KernelLog.Exit;
END;
ELSE KernelLog.String("UsbUhci: Error when accessing PCI configuration space (2)"); KernelLog.Ln;
END;
ELSE
KernelLog.Enter;
KernelLog.String("UsbUhci: Please enable USB-Interrupt in BIOS for UHCI host controller at base 0");
KernelLog.Hex(iobase, 8); KernelLog.Char("H"); KernelLog.Ln;
KernelLog.Exit;
END;
INC(index);
END;
END PCIFindUhci;
PROCEDURE Install*;
END Install;
PROCEDURE Cleanup;
BEGIN
UsbHcdi.UnRegisterHostControllers(Description);
END Cleanup;
BEGIN
Modules.InstallTermHandler(Cleanup);
PCIFindUhci;
END UsbUhci.
UsbUhci.Install ~ SystemTools.Free UsbUhci ~