MODULE UsbOhci;
IMPORT SYSTEM, KernelLog, Machine, PCI, Kernel, Objects, Modules, UsbHcdi, Usbdi, Debug := UsbDebug;
CONST
Description = "USB Open Host Controller";
ScatterGatherListSize = 4000;
PageSize = 4096;
HcRevision = 0H;
HcControl = 4H;
HcCommandStatus = 8H;
HcInterruptStatus = 0CH;
HcInterruptEnable = 10H;
HcInterruptDisable = 14H;
HcHCCA = 18H;
HcPeriodCurrentED = 1CH;
HcControlHeadED = 20H;
HcControlCurrentED = 24H;
HcBulkHeadED = 28H;
HcBulkCurrentED = 2CH;
HcDoneHead = 30H;
HcFmInterval = 34H;
HcFmRemaining = 38H;
HcFmNumber = 3CH;
HcPeriodicStart = 40H;
HcLSThreshold = 44H;
HcRhDescriptorA = 48H;
HcRhDescriptorB = 4CH;
HcRhStatus = 50H;
HcRhPortStatus1 = 54H;
HcRevRevision = {0..7};
HcRevLegacySupport = {8};
HceControl = 100H;
HceInput = 104H;
HceOutput = 108H;
HceStatus = 10CH;
HceControlReserved = {9..31};
HcConControlBulkServiceRatio = {0..1};
HcConPeriodicListEnable = {2};
HcConIsochronousEnable = {3};
HcConControlListEnable = {4};
HcConBulkListEnable = {5};
HcConHcFunctionalState = {6..7};
HcConInterruptRouting = {8};
HcConRemoteWakeupConnected = {9};
HcConRemoteWakeupEnable = {10};
HcConReserved = {11..31};
UsbReset = 0;
UsbResume = 1;
UsbOperational = 2;
UsbSuspend = 3;
HcCmdHostControllerReset = {0};
HcCmdControlListFilled = {1};
HcCmdBulkListFilled = {2};
HcCmdOwnershipChangeRequest = {3};
HcCmdSchedulingOverrunCount = {16,17};
HcIntSchedulingOverrun = {0};
HcIntWriteBackDoneHead = {1};
HcIntStartOfFrame = {2};
HcIntResumeDetected = {3};
HcIntUnrecoverableError = {4};
HcIntFrameNumberOverflow = {5};
HcIntRootHubStatusChange = {6};
HcIntReserved = {7..29} + {31};
HcIntOwnerShipChange = {30};
IntSchedulingOverrun = {0};
IntHcDoneHeadWriteback = {1};
IntStartOfFrame = {2};
IntResumeDetect = {3};
IntUnrecoverableError = {4};
IntFrameNumberOverflow = {5};
IntRootHubStatusChange = {6};
IntOwnerShipChange = {30};
IntMasterInterruptEnable = {31};
IntReservedMask = {7..29};
HcFmiFrameInterval = {0..13};
HcFmiFsLargestDataPacket = {16..30};
HcFmiFrameIntervalToggle = {31};
HcFmiReserved = {14,15};
HcPerPeriodicStart = {0..13};
HcPerReserved = {14..31};
HcRhaNumberDownstreamPorts = {0..7};
HcRhaNoPowerSwitching = {9};
HcRhaPowerSwitchingMode = {8};
HcRhaDeviceType = {10};
HcRhaOverCurrentProtectionMode = {11};
HcRhaNoOverCurrentProtection = {12};
HcRhaPowerOnToPowerGoodTime = {24..31};
HcRhbDeviceRemovable = {1..15};
HcRhbPortPowerControlMask = {17..31};
HcRhbReserved = {0,16};
HcRhsLocalPowerStatus = {0};
HcRhsOverCurrentIndicator = {1};
HcRhsDeviceRemoteWakeupEnable = {15};
HcRhsLocalPowerStatusChange = {16};
HcRhsOverCurrentIndicatorChange = {17};
HcRhsClearRemoteWakeupEnable = {31};
HcRhsReservedMask = {2..14} + {18..30};
HcRhsClearGlobalPower = {0};
HcRhsSetRemoteWakeupEnable = {15};
HcRhsSetGlobalPower = {16};
HcPsCurrentConnectStatus = {0};
HcPsPortEnableStatus = {1};
HcPsPortSuspendStatus = {2};
HcPsPortOverCurrentIndicator = {3};
HcPsPortResetStatus = {4};
HcPsPortPowerStatus = {8};
HcPsLowSpeedDeviceAttached = {9};
HcPsClearPortEnable = {0};
HcPsSetPortEnable = {1};
HcPsSetPortSuspend = {2};
HcPsClearSuspendStatus = {3};
HcPsSetPortReset = {4};
HcPsSetPortPower = {8};
HcPsClearPortPower = {9};
HcPsConnectStatusChange = {16};
HcPsPortEnableStatusChange = {17};
HcPsSuspendStatusChange = {18};
HcPsOverCurrentIndicatorChange = {19};
HcPsPortResetStatusChange = {20};
HcPsReserved = {5..7} + {10..15} + {21..31};
HcPsChangeMask = {16..20};
EdControlStatus = 0;
EdTailP = 4;
EdHeadP = 8;
EdNextEdP = 12;
EdFunctionAddress = {0..6};
EdEndpointNumber = {7..10};
EdDirection = {11..12};
EdSpeed = {13};
EdSkip = {14};
EdFormat = {15};
EdMaximumPacketSize = {16..26};
EdHalted = {0};
EdToggleCarry = {1};
PidSetup = 0;
PidOut = 1;
PidIn = 2;
TdCommand = 0;
TdCurrentBufferP = 4;
TdNextTdP = 8;
TdBufferEndP = 12;
TdTransferSize = {0..17};
TdBufferRounding = {18};
TdDirectionPid = {19..20};
TdDelayInterrupt = {21..23};
TdDataToggle = {24};
TdDataToggleFromTd = {25};
TdErrorCount = {26..27};
TdConditionCode = {28..31};
TdCurrentBufferPointer = {0..31};
TdNextTd = {4..31};
TdBufferEnd = {0..31};
TdNoError = 0;
TdCrc = 1;
TdBitStuffing = 2;
TdDataToggleMismatch = 3;
TdStall = 4;
TdDeviceNotResponding = 5;
TdPidCheckFailure = 6;
TdUnexpectedPid = 7;
TdDataOverrun = 8;
TdDataUnderrun = 9;
TdBufferOverrun = 12;
TdBufferUnderrun = 13;
TdNotAccessed1 = 14;
TdNotAccessed2 = 15;
TdListSize = 3 + 6 + 1 + 1;
HccaInterruptTable = 0;
HccaFrameNumber = 32;
HccaDoneHead = 36;
ShowScheduleMaxQH = 1000;
HccFSLargestDataPacket = 1000*8 ;
HccFrameInterval = 2EDFH;
HccPeriodicStart = 3E67H;
TYPE
OpenHostController = OBJECT (UsbHcdi.Hcd)
VAR
hcca : UsbHcdi.AlignedMemSpace;
revision : LONGINT;
legacySupport : BOOLEAN;
controlED : LONGINT;
bulkED : LONGINT;
isochronousED : LONGINT;
interruptED : POINTER TO ARRAY 6 OF LONGINT;
nullTD : LONGINT;
tdlist : UsbHcdi.AlignedMemSpace;
globalPowerSwitching : BOOLEAN;
PROCEDURE EnablePortPower*(port : LONGINT);
BEGIN
IF ~globalPowerSwitching THEN
SYSTEM.PUT32(ports[port], HcPsSetPortPower);
ELSE
SYSTEM.PUT32(iobase + HcRhStatus, HcRhsSetGlobalPower);
END;
FlushPCI;
END EnablePortPower;
PROCEDURE DisablePortPower*(port : LONGINT);
BEGIN
IF ~globalPowerSwitching THEN
SYSTEM.PUT32(ports[port], HcPsClearPortPower);
ELSE
SYSTEM.PUT32(iobase + HcRhStatus, HcRhsClearGlobalPower);
END;
FlushPCI;
END DisablePortPower;
PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
VAR status : SET; mtimer : Kernel.MilliTimer;
BEGIN
SYSTEM.PUT32(ports[port], HcPsSetPortReset); FlushPCI;
Wait(UsbHcdi.PortResetTime);
Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
REPEAT
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
UNTIL (status * HcPsPortEnableStatus # {}) OR Kernel.Expired(mtimer);
RETURN status * HcPsPortEnableStatus # {};
END ResetAndEnablePort;
PROCEDURE DisablePort*(port : LONGINT);
BEGIN
SYSTEM.PUT32(ports[port], HcPsClearPortEnable); FlushPCI;
SYSTEM.PUT32(ports[port], HcPsChangeMask); FlushPCI;
END DisablePort;
PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN):SET;
VAR status, s : SET;
BEGIN
s := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
IF ack & ((s * HcPsChangeMask) # {}) THEN SYSTEM.PUT32(ports[port], HcPsChangeMask * s); FlushPCI; END;
status := {};
IF s * HcPsCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent; END;
IF s * HcPsPortEnableStatus # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
IF s * HcPsPortSuspendStatus # {} THEN status := status + UsbHcdi.PortStatusSuspended END;
IF s * HcPsPortOverCurrentIndicator # {} THEN status := status + UsbHcdi.PortStatusOverCurrent END;
IF s * HcPsPortResetStatus # {} THEN status := status + UsbHcdi.PortStatusReset END;
IF s * HcPsPortPowerStatus # {} THEN status := status + UsbHcdi.PortStatusPowered END;
IF s * HcPsConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange END;
IF s * HcPsPortEnableStatusChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange END;
IF s * HcPsSuspendStatusChange # {} THEN status := status + UsbHcdi.PortStatusSuspendChange END;
IF s * HcPsOverCurrentIndicatorChange # {} THEN status := status + UsbHcdi.PortStatusOverCurrentChange END;
IF s * HcPsPortResetStatusChange # {} THEN status := status + UsbHcdi.PortStatusResetChange END;
IF s * HcPsLowSpeedDeviceAttached # {} THEN
status := status + UsbHcdi.PortStatusLowSpeed;
ELSE
status := status + UsbHcdi.PortStatusFullSpeed;
END;
RETURN status;
END GetPortStatus;
PROCEDURE GetFrameNumber*() : INTEGER;
BEGIN
RETURN SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, hcca.data[hcca.base + HccaFrameNumber]) * {0..15});
END GetFrameNumber;
PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
VAR nextED : LONGINT; dword : SET;
BEGIN
ASSERT((pipe#NIL) & (pipe.qh#0) & (SYSTEM.VAL(SET, pipe.qh) * {0..3} = {}));
ASSERT((pipe.maxPacketSize > 0));
CASE pipe.type OF
| UsbHcdi.PipeControl : pipe.queue := controlED;
| UsbHcdi.PipeBulk : pipe.queue := bulkED;
| UsbHcdi.PipeIsochronous : pipe.queue := isochronousED;
| UsbHcdi.PipeInterrupt :
BEGIN
IF pipe.irqInterval = 1 THEN
pipe.queue := interruptED[0];
ELSIF pipe.irqInterval < 4 THEN
pipe.queue := interruptED[1];
ELSIF pipe.irqInterval < 8 THEN
pipe.queue := interruptED[2];
ELSIF pipe.irqInterval < 16 THEN
pipe.queue := interruptED[3];
ELSIF pipe.irqInterval < 32 THEN
pipe.queue := interruptED[4];
ELSE
pipe.queue := interruptED[5];
END;
END;
ELSE
RETURN FALSE;
END;
dword := SYSTEM.VAL(SET, pipe.address) * EdFunctionAddress + (SYSTEM.VAL(SET, SYSTEM.LSH(pipe.endpoint, 7)) * EdEndpointNumber);
IF pipe.type = UsbHcdi.PipeControl THEN
ELSE
IF pipe.direction = UsbHcdi.In THEN
INCL(dword, 12);
ELSIF pipe.direction = UsbHcdi.Out THEN
INCL(dword, 11);
ELSE
HALT(90);
END;
END;
IF pipe.speed = UsbHcdi.LowSpeed THEN dword := dword + EdSpeed; END;
IF pipe.type = UsbHcdi.PipeIsochronous THEN dword := dword + EdFormat; END;
dword := dword + (SYSTEM.VAL(SET, SYSTEM.LSH(pipe.maxPacketSize, 16)) * EdMaximumPacketSize);
dword := dword + EdSkip;
SYSTEM.PUT32(pipe.qh + EdControlStatus, dword);
SYSTEM.PUT32(pipe.qh + EdTailP , nullTD);
SYSTEM.PUT32(pipe.qh + EdHeadP , nullTD);
nextED := SYSTEM.GET32(pipe.queue + EdNextEdP);
SYSTEM.PUT32(pipe.qh + EdNextEdP, SYSTEM.VAL(SET, nextED) * {4..31});
SYSTEM.PUT32(pipe.queue + EdNextEdP, pipe.qh);
RETURN TRUE;
END InsertQH;
PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
VAR prev, temp : LONGINT; dword : SET;
BEGIN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh)) + EdSkip;
SYSTEM.PUT32(pipe.qh, dword);
prev := pipe.queue;
LOOP
temp := SYSTEM.GET32(prev + EdNextEdP);
IF (temp = pipe.qh) OR (temp = 0) THEN EXIT; END;
prev := temp;
ASSERT(SYSTEM.VAL(SET, prev) * {0..3} = {});
END;
IF temp = 0 THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbOhci: DeleteQH: Pipe not found."); KernelLog.Ln; END;
RETURN;
END;
SYSTEM.PUT32(prev + EdNextEdP, SYSTEM.GET32(pipe.qh + EdNextEdP));
CASE pipe.type OF
| UsbHcdi.PipeControl:
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl)) - HcConControlListEnable;
SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
temp := SYSTEM.GET32(iobase + HcControlCurrentED);
IF temp = pipe.qh THEN
SYSTEM.PUT32(iobase + HcControlCurrentED, 0); FlushPCI;
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl)) + HcConControlListEnable;
SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
| UsbHcdi.PipeBulk :
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl)) - HcConBulkListEnable;
SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
temp := SYSTEM.GET32(iobase + HcBulkCurrentED);
IF temp = pipe.qh THEN
SYSTEM.PUT32(iobase + HcBulkCurrentED, 0); FlushPCI;
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcControl))+HcConBulkListEnable;
SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
ELSE
END;
END RemoveQH;
PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
VAR headP, tailP : SET;
BEGIN {EXCLUSIVE}
headP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP)) * {4..31};
tailP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdTailP)) * {4..31};
IF headP # tailP THEN
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
RETURN FALSE;
ELSE
RETURN TRUE;
END;
END LinkTDsAllowed;
PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; td : LONGINT);
VAR dword : SET;
BEGIN {EXCLUSIVE}
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus)) + EdSkip;
SYSTEM.PUT32(pipe.qh + EdControlStatus, dword);
SYSTEM.PUT32(pipe.qh + EdTailP, nullTD);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP));
SYSTEM.PUT32(pipe.qh + EdHeadP, SYSTEM.VAL(SET, td) * {4..31} + dword * {1..3});
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
SYSTEM.PUT32(pipe.qh + EdControlStatus, dword - EdSkip);
IF pipe.type = UsbHcdi.PipeControl THEN
SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdControlListFilled);
ELSIF pipe.type = UsbHcdi.PipeBulk THEN
SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdBulkListFilled);
END;
FlushPCI;
END LinkTDs;
PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
VAR dword : SET;
BEGIN {EXCLUSIVE}
IF pipe.firstTD = 0 THEN RETURN END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
SYSTEM.PUT32(pipe.qh + EdControlStatus, dword + EdSkip);
SYSTEM.PUT32(pipe.qh+ EdTailP, nullTD);
SYSTEM.PUT32(pipe.qh + EdHeadP, nullTD);
END UnlinkTDs;
PROCEDURE ClearHalt*(pipe : UsbHcdi.Pipe);
VAR dword : SET; temp : SET;
BEGIN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP));
IF dword * EdHalted # {} THEN
temp := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
temp := temp + EdSkip;
SYSTEM.PUT32(pipe.qh + EdControlStatus, temp);
dword := dword - EdHalted - EdToggleCarry;
SYSTEM.PUT32(pipe.qh + EdHeadP, dword);
ELSE
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbOhci: ClearHalt: Pipe is not halted."); KernelLog.Ln; END;
END;
END ClearHalt;
PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
VAR ranges : ARRAY ScatterGatherListSize OF Machine.Range; td, numRanges : LONGINT; dword : SET;
BEGIN
pipe.firstTD := pipe.tdBase; td := pipe.tdBase;
SYSTEM.PUT32(td + TdCommand, TdDelayInterrupt + TdDataToggleFromTd + {29,30,31});
SYSTEM.PUT32(td + TdNextTdP, td + 16);
Machine.TranslateVirtual(SYSTEM.ADR(msg[0]), 8, numRanges, ranges);
IF numRanges = 0 THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.TransferTooLarge; RETURN;
END;
SYSTEM.PUT32(td + TdCurrentBufferP, ranges[0].adr);
IF numRanges = 1 THEN
SYSTEM.PUT32(td + TdBufferEndP, ranges[0].adr + 8 - 1);
ELSE
SYSTEM.PUT32(td + TdBufferEndP, ranges[1].adr + ranges[1].size - 1);
END;
ASSERT(SYSTEM.GET32(td + TdBufferEndP) - SYSTEM.GET32(td + TdCurrentBufferP) + 1 = 8);
pipe.dataToggle := TRUE;
IF bufferLen # 0 THEN
IF ~CreateTDList(pipe, direction, bufferLen, 0, buffer, td + 16, td, TRUE) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
END;
END;
td := td + 16;
IF td + 15 > SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: TD buffer too small"); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs; RETURN;
END;
dword := TdDataToggle + TdDataToggleFromTd + {29,30,31};
IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
INCL(dword, 20);
ELSE
INCL(dword, 19);
END;
IF pipe.ioc THEN
ELSE
dword := dword + TdDelayInterrupt;
END;
SYSTEM.PUT32(td + TdCommand, dword);
SYSTEM.PUT32(td + TdCurrentBufferP, 0);
SYSTEM.PUT32(td + TdNextTdP, nullTD);
SYSTEM.PUT32(td + TdBufferEndP, 0);
pipe.lastTD := td;
END ScheduleControl;
PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
VAR dword : SET;
BEGIN
pipe.firstTD := pipe.tdBase;
IF ~CreateTDList(pipe, pipe.direction, bufferLen, offset, buffer, pipe.firstTD, pipe.lastTD, FALSE) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
END;
SYSTEM.PUT32(pipe.lastTD + TdNextTdP, nullTD);
IF pipe.ioc THEN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + TdCommand));
dword := dword - TdDelayInterrupt;
SYSTEM.PUT32(pipe.lastTD + TdCommand, dword);
END;
END Schedule;
PROCEDURE CreateTDList(pipe : UsbHcdi.Pipe; direction, len, ofs : LONGINT; VAR buffer : Usbdi.Buffer; firstTD : LONGINT; VAR lastTD : LONGINT; tdToggle : BOOLEAN) : BOOLEAN;
VAR
restlen, curlen : LONGINT;
numRanges, idx, offset : LONGINT;
td, temp : LONGINT;
dword : SET;
BEGIN
Machine.TranslateVirtual(SYSTEM.ADR(buffer[ofs]), len, numRanges, pipe.sgList^);
IF numRanges = 0 THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Scatter/Gather list too small."); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.TransferTooLarge; RETURN FALSE;
END;
td := firstTD - 16;
restlen := len; idx := 0; offset := 0;
WHILE restlen > 0 DO
td := td + 16;
IF td + 15 > SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
pipe.errors := pipe.errors + UsbHcdi.OutOfTDs;
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: TD buffer too small"); KernelLog.Ln; END;
RETURN FALSE;
END;
SYSTEM.PUT32(td + TdCurrentBufferP, pipe.sgList[idx].adr + offset);
curlen := PageSize - SHORT ((pipe.sgList[idx].adr + offset) MOD PageSize);
IF curlen >= restlen THEN
curlen := restlen; restlen := 0;
SYSTEM.PUT32(td + TdBufferEndP, pipe.sgList[idx].adr + offset + curlen - 1);
ELSE
restlen := restlen - curlen; offset := 0; INC(idx);
ASSERT(idx < numRanges);
temp := PageSize - ((curlen + PageSize) MOD pipe.maxPacketSize);
IF restlen > temp THEN
curlen := curlen + temp; offset := temp; restlen := restlen - temp;
SYSTEM.PUT32(td + TdBufferEndP, pipe.sgList[idx].adr + temp - 1);
IF offset = PageSize THEN INC(idx); offset := 0;
ELSE
END;
ELSE
curlen := curlen + restlen;
SYSTEM.PUT32(td + TdBufferEndP, pipe.sgList[idx].adr + restlen - 1);
restlen := 0;
END;
END;
ASSERT(curlen <= 8192);
SYSTEM.PUT32(td + TdNextTdP, td + 16);
dword := {29,30,31} + TdDelayInterrupt + TdBufferRounding ;
dword := dword + SYSTEM.VAL(SET, curlen) * TdTransferSize;
IF tdToggle THEN
dword := dword + TdDataToggleFromTd;
IF pipe.dataToggle THEN dword := dword + TdDataToggle; END;
IF (curlen DIV pipe.maxPacketSize) MOD 2 # 0 THEN
pipe.dataToggle := ~pipe.dataToggle;
END;
END;
IF direction = UsbHcdi.In THEN
INCL(dword, 20);
ELSIF direction = UsbHcdi.Out THEN
INCL(dword, 19);
END;
SYSTEM.PUT32(td + TdCommand, dword);
END;
lastTD := td;
RETURN TRUE;
END CreateTDList;
PROCEDURE InterruptHandler;
VAR s : SET;
BEGIN
IF Debug.Stats THEN INC(NnofInterrupts); END;
IF state >= UsbHcdi.Initialized THEN
s := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcInterruptStatus));
IF s # {} THEN
IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
SYSTEM.PUT32(iobase + HcInterruptStatus, s - HcIntReserved); FlushPCI;
IF s * HcIntSchedulingOverrun # {} THEN KernelLog.String("UsbOhci: HcIntSchedulingOverrun"); KernelLog.Ln; END;
IF s * HcIntResumeDetected # {} THEN KernelLog.String("UsbOhci: HcIntResumeDetected"); KernelLog.Ln; END;
IF s * HcIntUnrecoverableError # {} THEN KernelLog.String("UsbOhci: HcIntUnrecoverableError"); KernelLog.Ln; END;
IF s * HcIntFrameNumberOverflow # {} THEN END;
IF s * HcIntRootHubStatusChange # {} THEN
IF statusChangeHandler # NIL THEN statusChangeHandler(0, 0); END;
END;
IF s * HcIntOwnerShipChange # {} THEN KernelLog.String("UsbOhci: HcIntOwnerShipChange"); KernelLog.Ln; END;
IF s * HcIntWriteBackDoneHead # {} THEN
NotifyCompletionHandlers;
END;
END;
END;
END InterruptHandler;
PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
CONST
MaxLoops = 10000;
VAR
tailP, headP : SET;
td : LONGINT;
currentBufferP, bufferEndP : LONGINT;
dword, errors : SET;
cc : LONGINT;
actLen, len : LONGINT;
loop : LONGINT;
BEGIN
IF SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus)) * EdSkip # {} THEN RETURN; END;
tailP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdTailP));
headP := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdHeadP));
IF headP * EdHalted # {} THEN
td := pipe.firstTD; loop := 0; actLen := 0; errors := UsbHcdi.NoErrors;
LOOP
ASSERT(SYSTEM.VAL(SET, td) * {0..3} = {});
dword := SYSTEM.VAL(SET, SYSTEM.GET32(td + TdCommand));
cc := SYSTEM.LSH(SYSTEM.VAL(LONGINT, dword * TdConditionCode), -28);
CASE cc OF
| TdNoError :
| TdCrc : errors := errors + UsbHcdi.Crc;
| TdBitStuffing : errors := errors + UsbHcdi.BitStuff;
| TdDataToggleMismatch : errors := errors + UsbHcdi.DataToggleMismatch;
| TdStall : errors := errors + UsbHcdi.Stalled;
| TdDeviceNotResponding : errors := errors + UsbHcdi.DeviceNotResponding;
| TdPidCheckFailure : errors := errors + UsbHcdi.PidCheckFailure;
| TdUnexpectedPid : errors := errors + UsbHcdi.UnexpectedPid;
| TdDataOverrun : errors := errors + UsbHcdi.Babble;
| TdDataUnderrun : errors := errors + UsbHcdi.ShortPacket;
| TdBufferOverrun : errors := errors + UsbHcdi.Databuffer;
| TdBufferUnderrun : errors := errors + UsbHcdi.Databuffer;
ELSE
errors := errors + UsbHcdi.Internal;
END;
IF pipe.transferLen > 0 THEN
len := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, SYSTEM.GET32(td + TdCommand)) * TdTransferSize);
currentBufferP := SYSTEM.GET32(td + TdCurrentBufferP);
IF currentBufferP = 0 THEN
actLen := actLen + len;
ELSE
bufferEndP := SYSTEM.GET32(td + TdBufferEndP);
actLen := actLen + len - (bufferEndP - currentBufferP + 1);
errors := errors + UsbHcdi.ShortPacket;
END;
END;
IF errors - UsbHcdi.ShortPacket # {} THEN EXIT; END;
IF td = pipe.lastTD THEN EXIT; END;
td := td + 16;
IF loop > MaxLoops THEN EXIT; END;
INC(loop);
END;
pipe.actLen := actLen; 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;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + EdControlStatus));
SYSTEM.PUT32(pipe.qh + EdControlStatus, dword + EdSkip);
SYSTEM.PUT32(pipe.qh+ EdTailP, nullTD);
SYSTEM.PUT32(pipe.qh + EdHeadP, nullTD);
ELSIF (headP * {4..31} = tailP * {4..31}) THEN
pipe.actLen := pipe.transferLen;
pipe.status := Usbdi.Ok;
pipe.errors := errors;
ELSE
END;
END UpdatePipeStatus;
PROCEDURE HardwareReset() : BOOLEAN;
VAR timer : Kernel.Timer;
BEGIN
SYSTEM.PUT32(iobase + HcControl, SYSTEM.LSH(UsbReset, 6)); FlushPCI;
NEW(timer); timer.Sleep(UsbHcdi.RootHubResetTime);
RETURN TRUE;
END HardwareReset;
PROCEDURE SoftwareReset() : BOOLEAN;
VAR millitimer : Kernel.MilliTimer; dword : SET;
BEGIN
SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdHostControllerReset); FlushPCI;
Kernel.SetTimer(millitimer, 10);
LOOP
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCommandStatus));
IF (HcCmdHostControllerReset * dword = {}) OR Kernel.Expired(millitimer) THEN EXIT END;
END;
RETURN HcCmdHostControllerReset * dword = {};
END SoftwareReset;
PROCEDURE InitHcca(): BOOLEAN;
VAR i, k, j : LONGINT;
BEGIN
hcca := UsbHcdi.GetAlignedMemSpace(256, 4096);
tdlist := UsbHcdi.GetAlignedMemSpace(16*TdListSize, 16);
NEW(interruptED); FOR i := 0 TO 5 DO interruptED[i] := Machine.Ensure32BitAddress (SYSTEM.ADR(tdlist.data[tdlist.base]) + i*16); END;
isochronousED := interruptED[5] + 16;
controlED := isochronousED + 16;
bulkED := controlED +16;
nullTD := bulkED + 16;
FOR i := 0 TO 8 DO
tdlist.data[tdlist.base + i*4 + (EdControlStatus DIV 4)] := SYSTEM.VAL(LONGINT, EdSkip);
tdlist.data[tdlist.base + i*4 + (EdTailP DIV 4)] := nullTD;
tdlist.data[tdlist.base + i*4 + (EdHeadP DIV 4)] := nullTD;
tdlist.data[tdlist.base + i*4 + (EdNextEdP DIV 4)] := 0;
END;
FOR i := 5 TO 1 BY -1 DO
SYSTEM.PUT32(interruptED[i] + EdNextEdP, interruptED[i-1]);
END;
SYSTEM.PUT32(interruptED[0] + EdNextEdP, isochronousED);
FOR i := 0 TO 31 DO
k := 0; j := i;
LOOP
IF (SYSTEM.VAL(SET, j) * {0}) = {} THEN EXIT; END;
INC(k); j := j DIV 2;
END;
hcca.data[hcca.base + i] := interruptED[k];
END;
RETURN TRUE;
END InitHcca;
PROCEDURE Init(virtualbase, IRQ : LONGINT) : BOOLEAN;
CONST MaxOwnershipChangeRequests = 100;
VAR
dword, hcRhDescriptorA, hcRhDescriptorB, Reg_HcFmInterval : SET;
timer : Kernel.Timer;
periodicStart : LONGINT;
hcControl, temp, i : LONGINT;
ignore : BOOLEAN;
BEGIN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Starting host controller initialization..."); KernelLog.Ln; END;
iobase := virtualbase; irq := IRQ;
DMAchaining := TRUE; sgListSize := ScatterGatherListSize;
temp := SYSTEM.GET32(iobase + HcRevision);
revision:= SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, temp) * HcRevRevision);
IF revision #10H THEN
KernelLog.String("UsbOhci: Revision of OHCI Programming Interface not supported."); KernelLog.Ln;
RETURN FALSE;
END;
IF SYSTEM.VAL(SET, temp) * HcRevLegacySupport # {} THEN
legacySupport := TRUE;
END;
Reg_HcFmInterval := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcFmInterval));
hcControl := SYSTEM.GET32(iobase + HcControl);
IF (SYSTEM.VAL(SET, hcControl) * HcConInterruptRouting) # {} THEN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller initialization: SMM driver found"); KernelLog.Ln; END;
SYSTEM.PUT32(iobase + HcCommandStatus, HcCmdOwnershipChangeRequest); FlushPCI;
NEW(timer);
i := 0;
LOOP
temp := SYSTEM.GET32(iobase + HcControl);
IF SYSTEM.VAL(SET, temp) * HcConInterruptRouting = {} THEN EXIT; END;
INC(i);
IF i > MaxOwnershipChangeRequests THEN EXIT; END;
timer.Sleep(1);
END;
hcControl := temp;
IF SYSTEM.VAL(SET, temp) * HcConInterruptRouting # {} THEN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller initialization: SMM driver did not response to OwnerShipChangeRequest."); KernelLog.Ln; END;
ELSE
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller initialization: OwnerShipChangeRequest succeeded."); KernelLog.Ln; END;
END;
ELSIF SYSTEM.VAL(SET, hcControl) * HcConHcFunctionalState # {} THEN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Controller initialization: BIOS driver found"); KernelLog.Ln; END;
ELSE
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Controller initialization: Cold start..."); KernelLog.Ln; END;
END;
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Host Controller Initialization: USB Reset."); KernelLog.Ln; END;
IF ~HardwareReset() THEN RETURN FALSE; END;
IF ~SoftwareReset() THEN RETURN FALSE; END;
SYSTEM.PUT32(iobase + HcFmInterval, Reg_HcFmInterval);
IF (Reg_HcFmInterval * HcFmiFrameInterval = {}) OR (Reg_HcFmInterval * HcFmiFsLargestDataPacket = {}) THEN
IF Debug.Trace & Debug.traceInit THEN
KernelLog.String("UsbOhci: HcFmInterval values invalid. Set to defaults."); KernelLog.Ln;
END;
IF (Reg_HcFmInterval * HcFmiFrameInterval = {}) THEN
Reg_HcFmInterval := Reg_HcFmInterval + (SYSTEM.VAL(SET, HccFrameInterval) * HcFmiFrameInterval);
END;
IF (Reg_HcFmInterval * HcFmiFsLargestDataPacket = {}) THEN
Reg_HcFmInterval := Reg_HcFmInterval + (SYSTEM.LSH(SYSTEM.VAL(SET, HccFSLargestDataPacket), 16) * HcFmiFsLargestDataPacket);
END;
END;
IF (Reg_HcFmInterval * HcFmiFrameIntervalToggle = {}) THEN
Reg_HcFmInterval := Reg_HcFmInterval + HcFmiFrameIntervalToggle;
ELSE
Reg_HcFmInterval := Reg_HcFmInterval - HcFmiFrameIntervalToggle;
END;
SYSTEM.PUT32(iobase + HcFmInterval, Reg_HcFmInterval);
IF legacySupport THEN
temp := SYSTEM.GET32(iobase + HceControl);
IF SYSTEM.VAL(SET, temp) * {0} # {} THEN
SYSTEM.PUT32(iobase + HceControl, SYSTEM.VAL(SET, temp) * HceControlReserved); FlushPCI;
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Disabled legacy emulation."); KernelLog.Ln; END;
END;
ELSE
legacySupport := FALSE;
END;
hcRhDescriptorA := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcRhDescriptorA));
portCount := SYSTEM.VAL(LONGINT, hcRhDescriptorA * HcRhaNumberDownstreamPorts);
IF (portCount < 1) OR (portCount > 15) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Host Controller Initialization failed (port count error)."); KernelLog.Ln; END;
RETURN FALSE;
END;
NEW(ports, portCount);
FOR i := 0 TO portCount-1 DO ports[ i ] := iobase + HcRhPortStatus1 + i*4; END;
NEW(hubDescriptor, 8);
hubDescriptor[0] := CHR(7);
hubDescriptor[1] := CHR(29H);
hubDescriptor[2] := CHR(portCount);
IF hcRhDescriptorA * HcRhaNoPowerSwitching # {} THEN
dword := dword + {1};
ELSIF hcRhDescriptorA * HcRhaPowerSwitchingMode # {} THEN
dword := dword + {0};
hcRhDescriptorB := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcRhDescriptorB));
hcRhDescriptorB := hcRhDescriptorB + HcRhbPortPowerControlMask;
SYSTEM.PUT32(iobase + HcRhDescriptorB, hcRhDescriptorB - HcRhbDeviceRemovable); FlushPCI;
ELSE
globalPowerSwitching := TRUE;
END;
IF hcRhDescriptorA * HcRhaNoOverCurrentProtection # {} THEN
dword := dword + {4};
ELSIF hcRhDescriptorA * HcRhaOverCurrentProtectionMode # {} THEN
dword := dword + {3};
ELSE
END;
hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
hubDescriptor[4] := CHR(0);
hubDescriptor[5] := CHR(SYSTEM.VAL(LONGINT, SYSTEM.LSH(hcRhDescriptorA * HcRhaPowerOnToPowerGoodTime, -24)));
hubDescriptor[6] := CHR(0);
IF ~InitHcca() THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Initialization of HCCA failed."); KernelLog.Ln; END;
RETURN FALSE;
END;
IF Start() = FALSE THEN
ignore := SoftwareReset(); KernelLog.String("UsbOhci: Couldn't start host controller."); KernelLog.Ln; RETURN FALSE;
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcFmInterval));
IF (dword # Reg_HcFmInterval) THEN
SYSTEM.PUT32(iobase + HcFmInterval, Reg_HcFmInterval);
END;
periodicStart := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, Reg_HcFmInterval) * HcFmiFrameInterval);
periodicStart := (periodicStart * 9) DIV 10;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcPeriodicStart));
dword := dword - HcPerPeriodicStart + (SYSTEM.VAL(SET, periodicStart) * HcPerPeriodicStart);
SYSTEM.PUT32(iobase + HcPeriodicStart, dword);
RETURN TRUE;
END Init;
PROCEDURE Start():BOOLEAN;
VAR dword : SET;
BEGIN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Starting host controller..."); END;
SYSTEM.PUT32(iobase + HcHCCA, SYSTEM.ADR(hcca.data[hcca.base]));
SYSTEM.PUT32(iobase + HcControlHeadED, controlED);
SYSTEM.PUT32(iobase + HcBulkHeadED, bulkED);
SYSTEM.PUT32(iobase + HcControlCurrentED, 0);
SYSTEM.PUT32(iobase + HcBulkCurrentED, 0);
FlushPCI;
SetState(UsbHcdi.Initialized);
Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
dword := IntMasterInterruptEnable + IntHcDoneHeadWriteback + IntFrameNumberOverflow + IntUnrecoverableError + IntOwnerShipChange + IntRootHubStatusChange;
SYSTEM.PUT32(iobase + HcInterruptEnable, dword); FlushPCI;
dword := HcConPeriodicListEnable + HcConControlListEnable + HcConBulkListEnable + {7};
SYSTEM.PUT32(iobase + HcControl, dword); FlushPCI;
SetState(UsbHcdi.Operational);
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("done."); KernelLog.Ln; END;
RETURN TRUE;
END Start;
PROCEDURE FlushPCI;
VAR ignore : LONGINT;
BEGIN
ignore := SYSTEM.GET32(iobase + HcControl)
END FlushPCI;
PROCEDURE Cleanup;
BEGIN
IF state >= UsbHcdi.Initialized THEN Objects.RemoveHandler(InterruptHandler, Machine.IRQ0 + irq);END;
Cleanup^;
IF ~HardwareReset() THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Host controller reset failed."); KernelLog.Ln; END;
END;
IF ~SoftwareReset() THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbOhci: Software reset failed."); KernelLog.Ln; END;
END;
Machine.UnmapPhysical(iobase, 4096);
END Cleanup;
PROCEDURE ShowSchedule*;
BEGIN
IF Debug.Trace THEN
KernelLog.String("Host Controller Data Structures for ");
KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
HumanSchedule(SELF);
END;
END ShowSchedule;
PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
BEGIN
HumanED(pipe.qh, SELF, 4, FALSE, FALSE);
HumanTD(pipe.firstTD, pipe.lastTD, nullTD, 8);
END ShowPipe;
PROCEDURE Diag*;
VAR s : SET; temp : LONGINT;
BEGIN
IF Debug.Trace THEN
Diag^;
temp := SYSTEM.GET32(iobase+HcRevision);
KernelLog.String(" HcRevision: ");
KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, temp) * {4..7}, -4)), -2); KernelLog.String(".");
KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, temp) * {0..3}), -2);
KernelLog.String(" Legacy support: ");
IF HcRevLegacySupport * SYSTEM.VAL(SET, temp) # {} THEN
KernelLog.String("Yes");
KernelLog.String(" [HceControl: "); temp := SYSTEM.GET32(iobase+HceControl); KernelLog.Hex(temp, 8); KernelLog.String("H]");
KernelLog.String(" [HceStatus: "); temp := SYSTEM.GET32(iobase+HceStatus); KernelLog.Hex(temp, 8); KernelLog.String("H]");
KernelLog.String(" [HceInput: "); temp := SYSTEM.GET32(iobase+HceInput); KernelLog.Hex(temp, 8); KernelLog.String("H]");
KernelLog.String(" [HceOutput: "); temp := SYSTEM.GET32(iobase+HceOutput); KernelLog.Hex(temp, 8); KernelLog.String("H]");
ELSE
KernelLog.String("No");
END;
KernelLog.Ln;
KernelLog.String(" HcControl: State: ");
temp := SYSTEM.GET32(iobase + HcControl);
s := SYSTEM.VAL(SET, temp);
CASE SYSTEM.VAL(LONGINT, SYSTEM.LSH(s*HcConHcFunctionalState, -6)) OF
UsbReset : KernelLog.String("UsbReset");
| UsbResume : KernelLog.String("UsbResume");
| UsbOperational : KernelLog.String("UsbOperational");
| UsbSuspend : KernelLog.String("UsbSuspend");
ELSE
KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(s*HcConHcFunctionalState,-6)),0);
END;
KernelLog.String(" C/B-Ratio: ");
temp := SYSTEM.VAL(LONGINT, s * HcConControlBulkServiceRatio);
KernelLog.Int(temp, 0); KernelLog.String(": 1,");
KernelLog.String(" Flags: ");
IF s * HcConPeriodicListEnable # {} THEN KernelLog.String(" [PeriodicListEnabled]"); END;
IF s * HcConControlListEnable # {} THEN KernelLog.String(" [ControlListEnabled]"); END;
IF s * HcConBulkListEnable # {} THEN KernelLog.String(" [BulkListEnabled]"); END;
IF s * HcConIsochronousEnable # {} THEN KernelLog.String(" [IsochronousEnabled]"); END;
IF s * HcConInterruptRouting # {} THEN KernelLog.String(" [InterruptRouting]"); END;
IF s * HcConRemoteWakeupConnected # {} THEN KernelLog.String(" [RemoteWakeupConnected]"); END;
IF s * HcConRemoteWakeupEnable # {} THEN KernelLog.String(" [RemoteWakeupEnabled]"); END;
KernelLog.Ln;
KernelLog.String(" HcCommandStatus: SchedulingOverrungCount: ");
KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(s * HcCmdSchedulingOverrunCount,16)), 0);
KernelLog.String(", Flags: ");
temp := SYSTEM.GET32(iobase + HcCommandStatus);
s := SYSTEM.VAL(SET, temp);
IF s * HcCmdHostControllerReset # {} THEN KernelLog.String("[Reset]"); END;
IF s * HcCmdControlListFilled # {} THEN KernelLog.String("[ControlListFilled]"); END;
IF s * HcCmdBulkListFilled # {} THEN KernelLog.String("[BulkListFilled]"); END;
IF s * HcCmdOwnershipChangeRequest # {} THEN KernelLog.String("[OwnerShipRequest]"); END;
KernelLog.Ln;
temp := SYSTEM.GET32(iobase + HcInterruptEnable);
s := SYSTEM.VAL(SET, temp);
KernelLog.String(" Interrupts Enabled: ");
IF s * IntMasterInterruptEnable # {} THEN KernelLog.String("[MasterInterrupt]"); END;
IF s * IntSchedulingOverrun # {} THEN KernelLog.String("[SchedulingOverflow]"); END;
IF s * IntHcDoneHeadWriteback # {} THEN KernelLog.String("[WBDoneHead]"); END;
IF s * IntStartOfFrame # {} THEN KernelLog.String("[SOF]"); END;
IF s * IntResumeDetect # {} THEN KernelLog.String("[ResumeDetect]"); END;
IF s * IntUnrecoverableError # {} THEN KernelLog.String("[Error]"); END;
IF s * IntFrameNumberOverflow # {} THEN KernelLog.String("[FmOverflow]"); END;
IF s * IntRootHubStatusChange # {} THEN KernelLog.String("[RHStatusChange]"); END;
IF s * IntOwnerShipChange # {} THEN KernelLog.String("[OwnerShipChange]"); END;
KernelLog.Ln;
temp := SYSTEM.GET32(iobase + HcInterruptStatus);
s := SYSTEM.VAL(SET, temp);
KernelLog.String(" HcInterruptStatus: ");
IF s # {} THEN
IF s * HcIntSchedulingOverrun # {} THEN KernelLog.String("[SchedulingOverrun]"); END;
IF s * HcIntWriteBackDoneHead # {} THEN KernelLog.String("[WriteBackDoneHead]"); END;
IF s * HcIntStartOfFrame # {} THEN KernelLog.String("[StartOfFrame]"); END;
IF s * HcIntResumeDetected # {} THEN KernelLog.String("[ResumeDetected]"); END;
IF s * HcIntUnrecoverableError # {} THEN KernelLog.String("[UnrecoverableError]"); END;
IF s * HcIntFrameNumberOverflow # {} THEN KernelLog.String("[FrameNumberOverflow]"); END;
IF s * HcIntRootHubStatusChange # {} THEN KernelLog.String("[RooHubStatusChange]"); END;
IF s * HcIntOwnerShipChange # {} THEN KernelLog.String("[IntOwnerShipChange]"); END;
ELSE
KernelLog.String(" [ok]");
END;
KernelLog.Ln;
temp := SYSTEM.GET32(iobase + HcFmNumber);
KernelLog.String(" Frame: Nbr: "); KernelLog.Hex(temp, 8);
s := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcFmInterval));
KernelLog.String(" FmInterval: "); KernelLog.Int(SYSTEM.VAL(LONGINT, s * HcFmiFrameInterval), 0);
KernelLog.String(" FSMaxPacketSize: "); KernelLog.Int(SYSTEM.LSH(SYSTEM.VAL(LONGINT, s * HcFmiFsLargestDataPacket),-16), 0); KernelLog.String(" Bits, ");
KernelLog.String(" FmiToggle: ");
IF s * HcFmiFrameIntervalToggle # {} THEN KernelLog.String("Set"); ELSE KernelLog.String("Not Set"); END;
temp := SYSTEM.GET32(iobase + HcPeriodicStart); KernelLog.String(" PeriodicStart: "); KernelLog.Hex(temp, 8);
temp := SYSTEM.GET32(iobase + HcPeriodCurrentED); KernelLog.String(" PeriodicCurrentED: "); KernelLog.Hex(temp, 8);
KernelLog.Ln;
temp := SYSTEM.GET32(iobase + HcControlHeadED); KernelLog.String(" ControlHeadED: "); KernelLog.Hex(temp, 8);
temp := SYSTEM.GET32(iobase + HcControlCurrentED); KernelLog.String(" CurrentControlED: "); KernelLog.Hex(temp, 8);
temp := SYSTEM.GET32(iobase + HcBulkHeadED); KernelLog.String(" BulkHeadED: "); KernelLog.Hex(temp, 8);
temp := SYSTEM.GET32(iobase + HcBulkCurrentED); KernelLog.String(" CurrentBulkED: "); KernelLog.Hex(temp, 8);
KernelLog.Ln;
END;
END Diag;
END OpenHostController;
VAR
qhCounter : LONGINT;
PROCEDURE HumanSchedule(controller : OpenHostController);
BEGIN
IF Debug.Trace THEN
HumanED(controller.interruptED[5], controller, 4, TRUE, TRUE);
HumanED(controller.bulkED, controller, 4, TRUE, TRUE);
HumanED(controller.controlED, controller, 4, TRUE, TRUE);
qhCounter := 0;
END;
END HumanSchedule;
PROCEDURE Indent(spaces : LONGINT);
VAR i : LONGINT;
BEGIN
FOR i := 1 TO spaces DO KernelLog.Char(" "); END;
END Indent;
PROCEDURE HumanED(ed : LONGINT; controller : OpenHostController; spaces : LONGINT; showTds, showEds : BOOLEAN);
VAR dword : SET; adr, ep : LONGINT; pipe : UsbHcdi.Pipe;
BEGIN
IF Debug.Trace THEN
IF qhCounter > ShowScheduleMaxQH THEN
KernelLog.String("UsbOhci: HumanED: UsbOhci.ShowScheduleMaxQH showed... aborting."); KernelLog.Ln;
RETURN;
ELSE
INC(qhCounter);
END;
ed := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ed) * {4..31});
dword :=SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdControlStatus));
adr := SYSTEM.VAL(LONGINT, dword * EdFunctionAddress);
ep := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * EdEndpointNumber, -7));
Indent(spaces); KernelLog.String("ED at address: "); KernelLog.Hex(ed, 8); KernelLog.String("H ");
IF ed = controller.controlED THEN KernelLog.String("(ControlList Head)");
ELSIF ed = controller.bulkED THEN KernelLog.String("(BulkList Head)");
ELSIF ed = controller.isochronousED THEN KernelLog.String("(IsochronousList Head)");
ELSIF ed = controller.interruptED[0] THEN KernelLog.String("(1ms Interrupt Head)");
ELSIF ed = controller.interruptED[1] THEN KernelLog.String("(2ms Interrupt Head)");
ELSIF ed = controller.interruptED[2] THEN KernelLog.String("(4ms Interrupt Head)");
ELSIF ed = controller.interruptED[3] THEN KernelLog.String("(8ms Interrupt Head)");
ELSIF ed = controller.interruptED[4] THEN KernelLog.String("(16ms Interrupt Head)");
ELSIF ed = controller.interruptED[5] THEN KernelLog.String("(32ms Interrupt Head)");
ELSE
KernelLog.String("(USB Pipe)");
END;
KernelLog.Ln;
Indent(spaces+4);
KernelLog.String("Adr: "); KernelLog.Int(adr, 0); KernelLog.String(" Ep: "); KernelLog.Int(ep, 0);
IF pipe # NIL THEN
KernelLog.String(" Type: ");
CASE pipe.type OF
UsbHcdi.PipeControl : KernelLog.String("Control");
| UsbHcdi.PipeBulk : KernelLog.String("Bulk");
| UsbHcdi.PipeIsochronous : KernelLog.String("Isochronous");
| UsbHcdi.PipeInterrupt : KernelLog.String("Interrupt");
ELSE
KernelLog.String("UNKNOWN!!!");
END;
END;
KernelLog.String(" Dir: ");
CASE SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * EdDirection ,-11)) OF
PidIn : KernelLog.String("In");
| PidOut : KernelLog.String("Out");
ELSE
KernelLog.String("Specified in TD");
END;
KernelLog.String(" MaxPacketSize: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * EdMaximumPacketSize, -16)), 0); KernelLog.String(" Bytes");
IF pipe # NIL THEN
KernelLog.String(", LastStatus: "); UsbHcdi.ShowStatus(pipe.status);
END;
KernelLog.String(", Flags: ");
IF EdSkip * dword # {} THEN KernelLog.String("[Skip]"); END;
IF EdSpeed * dword # {} THEN KernelLog.String("[LowSpeed]"); ELSE KernelLog.String("[FullSpeed]"); END;
IF EdFormat * dword # {} THEN KernelLog.String("[Isochronous]"); END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdHeadP));
IF EdHalted * dword # {} THEN KernelLog.String("[Halted]"); END;
IF EdToggleCarry * dword # {} THEN KernelLog.String("[Toggle=DATA1]"); ELSE KernelLog.String("[Toggle=DATA0]"); END;
KernelLog.Ln;
Indent(spaces+4); KernelLog.String("HeadP: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdHeadP)) * {4..31};
IF SYSTEM.VAL(LONGINT, dword) = controller.nullTD THEN
KernelLog.String("NullTD");
ELSE
KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.String("H");
END;
KernelLog.String(", TailP: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdTailP));
IF SYSTEM.VAL(LONGINT, dword) = controller.nullTD THEN
KernelLog.String("NullTD");
ELSE
KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.String("H");
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdNextEdP));
KernelLog.String(", NextED: ");
IF dword = {} THEN
KernelLog.String("None");
ELSE
KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.String("H");
END;
KernelLog.Ln;
Indent(spaces+4); KernelLog.String("TDList: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdHeadP)) * {4..31};
IF SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdTailP)) * {4..31} = dword THEN
KernelLog.String("Empty"); KernelLog.Ln;
ELSE
KernelLog.String("Non_Empty - First TD at "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {4..31}), 8); KernelLog.Char("H"); KernelLog.Ln; KernelLog.Ln;
IF showTds THEN
KernelLog.Ln; HumanTD(SYSTEM.VAL(LONGINT, dword * {4..31}), 0, controller.nullTD, spaces + 4); KernelLog.Ln;
END;
END;
IF showEds THEN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ed + EdNextEdP)) * {4..31};
IF dword # {} THEN
HumanED(SYSTEM.VAL(LONGINT, dword), controller, 4, showTds, showEds);
END;
END;
END;
END HumanED;
PROCEDURE HumanTD(nexttd, lasttd, nulltd: LONGINT; spaces : LONGINT);
CONST MaxTDListLength = 200;
VAR val, val2 : LONGINT; dword : SET; iteration : LONGINT;
BEGIN
IF Debug.Trace THEN
iteration := 1;
LOOP
IF iteration > MaxTDListLength THEN
Indent(spaces); KernelLog.String("UsbOhci.HumandTD.MaxTDListLength reached... aborting.");
EXIT;
END;
Indent(spaces); KernelLog.String("TD at address: "); KernelLog.Hex(nexttd, 8); KernelLog.String("H"); KernelLog.Ln;
IF SYSTEM.VAL(SET, nexttd) * {0..3} # {} THEN
Indent(spaces); KernelLog.String("Error: not 16byte aligned!!");
EXIT;
ELSIF nexttd = 0 THEN
Indent(spaces); KernelLog.String("Error: Address = 0 !!!"); KernelLog.Ln;
EXIT;
ELSE
Indent(spaces+4); KernelLog.String("Condition Codes:");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(nexttd + TdCommand));
CASE SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword,-28)) OF
| TdNoError : KernelLog.String("[NoError]");
| TdCrc : KernelLog.String("[CRC]");
| TdBitStuffing : KernelLog.String("[BitStuffError]");
| TdDataToggleMismatch : KernelLog.String("[DataToggleMismatch]");
| TdStall : KernelLog.String("[STALL]");
| TdDeviceNotResponding : KernelLog.String("[DeviceNotResponding]");
| TdPidCheckFailure : KernelLog.String("[PidCheckFailure]");
| TdUnexpectedPid : KernelLog.String("[UnexpectedPid]");
| TdDataOverrun : KernelLog.String("[DataOverrun]");
| TdDataUnderrun : KernelLog.String("[DataUnderrun]");
| TdBufferOverrun : KernelLog.String("[BufferOverrun]");
| TdBufferUnderrun : KernelLog.String("[BufferUnderrun]");
| 14..15: KernelLog.String("[NotAccessed]");
ELSE
KernelLog.String("[ConditionCodesNotValid]");
END;
KernelLog.Ln;
Indent(spaces+4); KernelLog.String("Pid: ");
CASE SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * TdDirectionPid, -19)) OF
PidSetup : KernelLog.String("PidSetup");
| PidIn : KernelLog.String("PidIn");
| PidOut : KernelLog.String("PidOut");
| 3 : KernelLog.String("Reserved");
ELSE
KernelLog.String("Invalid");
END;
KernelLog.String(", Flags: ");
IF dword * TdBufferRounding # {} THEN KernelLog.String("[BufferRounding]"); END;
IF 25 IN dword THEN KernelLog.String("[ToggleFromTD]"); ELSE KernelLog.String("[ToggleFromEd]"); END;
IF 24 IN dword THEN KernelLog.String("[Toggle=DATA1]"); ELSE KernelLog.String("[Toggle=DATA0]"); END;
IF SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * TdDelayInterrupt, -21)) # 7 THEN
KernelLog.String("[IOC, MaxDelay: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * TdDelayInterrupt, -21)), 0); KernelLog.Char("]");
END;
KernelLog.String(", Errors: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * TdErrorCount, -26)), 0);
KernelLog.Ln;
Indent(spaces+4); KernelLog.String("CurBufferP: ");
val := SYSTEM.GET32(nexttd + TdCurrentBufferP); KernelLog.Hex(val, 8); KernelLog.String("H");
KernelLog.String(", BufferEnd: ");
val2 := SYSTEM.GET32(nexttd + TdBufferEndP); KernelLog.Hex(val2, 8); KernelLog.String("H");
KernelLog.String(" (");
IF val = 0 THEN KernelLog.String("0");
ELSE KernelLog.Int(val2 - val + 1, 0);
END;
KernelLog.String(" Bytes)");
val := SYSTEM.GET32(nexttd + TdNextTdP);
KernelLog.String(", NextTD: ");
IF val = nulltd THEN KernelLog.String("NullTD");
ELSE KernelLog.Hex(val, 8); KernelLog.String("H");
END;
KernelLog.Ln;
IF SYSTEM.GET32(nexttd + TdNextTdP) = nulltd THEN
Indent(spaces); KernelLog.String("NullTD (EOL)"); KernelLog.Ln; EXIT;
ELSIF nexttd = lasttd THEN
Indent(spaces); KernelLog.String("Pipe.lastTD (EOL)"); KernelLog.Ln; EXIT;
ELSIF nexttd = 0 THEN
Indent(spaces); KernelLog.String("nexttd adr is zero"); KernelLog.Ln; EXIT;
END;
KernelLog.Ln;
nexttd := nexttd + 16;
INC(iteration);
END;
END;
END;
END HumanTD;
PROCEDURE PCIFindOhci;
CONST
OhciClassCode = 0C0310H;
PCIStatusErrorMask = {24,27,28,29,30,31};
VAR
hostController : OpenHostController;
bus, device, function : LONGINT;
iobasePhys, irq : LONGINT;
iobaseVirt: SYSTEM.ADDRESS;
pciCmdReg : LONGINT;
index : LONGINT;
res: LONGINT;
BEGIN
index := 0;
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbOhci: Looking for PCI Open Host Controllers..."); KernelLog.Ln; END;
WHILE PCI.FindPCIClassCode(OhciClassCode, index, bus, device, function) = PCI.Done DO
res := PCI.ReadConfigDword(bus, device, function, PCI.CmdReg, pciCmdReg); ASSERT(res = PCI.Done);
IF SYSTEM.VAL(SET, pciCmdReg) * PCIStatusErrorMask # {} THEN
KernelLog.String("UsbOhci: PCI device is in error state."); KernelLog.Ln;
ELSIF PCI.Enable(PCI.MemorySpace + PCI.BusMaster, bus, device, function) # PCI.Done THEN
KernelLog.String("UsbOhci: Could not enable nus mastering or memory space access."); KernelLog.Ln;
ELSE
res := PCI.ReadConfigByte(bus, device, function, PCI.IntlReg, irq); ASSERT(res = PCI.Done);
res := PCI.ReadConfigDword(bus, device, function, PCI.Adr0Reg, iobasePhys); ASSERT(res = PCI.Done);
IF SYSTEM.VAL(SET, iobasePhys) * {0} # {} THEN
KernelLog.String("UsbOhci: Error: Operational Register are not memory mapped"); KernelLog.Ln;
ELSIF SYSTEM.VAL(SET, iobasePhys) * {1,2,3} # {} THEN
KernelLog.String("UsbOhci: Error: Operational Register are not correctly mapped "); KernelLog.Ln;
ELSIF irq = 0 THEN
KernelLog.String("UsbOhci: Error: Please enable interrupts for USB Host Controller."); KernelLog.Ln;
ELSE
iobasePhys := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, iobasePhys) * {12..31});
Machine.MapPhysical(iobasePhys, 4096, iobaseVirt);
NEW(hostController, bus, device, function);
IF hostController.Init(Machine.Ensure32BitAddress(iobaseVirt), irq) THEN
IF Debug.Verbose THEN
KernelLog.Enter;
KernelLog.String("UsbOhci: Initialised USB Open Host Controller at base 0"); KernelLog.Hex(iobasePhys, 8);
KernelLog.String(", Irq: "); KernelLog.Int(irq, 0);
KernelLog.Exit;
END;
UsbHcdi.RegisterHostController(hostController, Description);
ELSE
KernelLog.Enter;
KernelLog.String("UsbOhci: Cannot init USB Open Host Controller at base 0"); KernelLog.Hex(iobasePhys, 8);
KernelLog.String(", Irq: "); KernelLog.Int(irq, 0);
KernelLog.Exit;
END;
END;
END;
INC(index);
END;
END PCIFindOhci;
PROCEDURE Cleanup;
BEGIN
UsbHcdi.UnRegisterHostControllers(Description);
END Cleanup;
PROCEDURE Install*;
END Install;
BEGIN
Modules.InstallTermHandler(Cleanup);
PCIFindOhci;
END UsbOhci.
UsbOhci.Install ~ SystemTools.Free UsbOhci ~