MODULE UsbEhci;
IMPORT SYSTEM, KernelLog, Machine, PCI, Kernel, Objects, Modules, UsbHcdi, Usbdi, Debug := UsbDebug;
CONST
Description = "USB Enhanced Host Controller";
HcInterruptThreshold = 01H;
HcFrameListSize = 1024;
ScatterGatherListSize = 4200;
HcAsyncParkModeCount = 3;
OverrideHcOwnership = TRUE;
HcCapLength = 00H;
HcCapHciVersion = 02H;
HcCapSparams = 04H;
HcCapCparams = 08H;
HcCapPortroute = 0CH;
HcUsbCmd = 00H;
HcUsbSts = 04H;
HcUsbIntr = 08H;
HcFrIndex = 0CH;
HcCtrlDsSegment = 10H;
HcPeriodicListBase = 14H;
HcAsyncListAddr = 18H;
HcConfigFlag = 40H;
HcPortSc = 44H;
CmdInterruptThreshold = {16..23};
CmdAsyncSchedParkMode = {11};
CmdAsyncSchedParkCount = {8..9};
CmdLightHcReset = {7};
CmdAsyncAdvDoorbell = {6};
CmdAsyncSchedEnable = {5};
CmdPeriodicSchedEnable = {4};
CmdFrameListSize = {2..3};
CmdHcReset = {1};
CmdRunStop = {0};
CmdReserved = {10} + {12..15} + {24..31};
StsAsyncSchedule = {15};
StsPeriodicSchedule = {14};
StsReclamation = {13};
StsHcHalted = {12};
StsAsyncAdvance= {5};
StsHostSystemError = {4};
StsFrameListRollover = {3};
StsPortChange = {2};
StsUsbError = {1};
StsUsbInterrupt = {0};
PscWakeOnOvercurrent = {22};
PscWakeOnDisconnect = {21};
PscWakeOnConnect = {20};
PscTestControl = {16..19};
PscIndicatorControl = {14..15};
PscPortOwner = {13};
PscPortPower = {12};
PscLineStatus = {10..11};
PscPortReset = {8};
PscSuspend = {7};
PscForcePortResume = {6};
PscOvercurrentChange = {5};
PscOvercurrentActive = {4};
PscPortEnableChange = {3};
PscPortEnable = {2};
PscConnectStatusChange = {1};
PscCurrentConnectStatus = {0};
PscReserved = {9} + {23..31};
PscChangeMask = {1, 3, 5};
QtdNextQtdPointer = 00H;
QtdAltNextQtdPointer = 04H;
QtdToken = 08H;
QtdBufferPtr0 = 0CH;
QtdBufferPtr1 = 10H;
QtdBufferPtr2 = 14H;
QtdBufferPtr3 = 18H;
QtdBufferPtr4 = 1CH;
QtdExtBufferPtr0 = 20H;
QtdExtBufferPtr1 = 24H;
QtdExtBufferPtr2 = 28H;
QtdExtBufferPtr3 = 2CH;
QtdExtBufferPtr4 = 30H;
QtdTerminate = {0};
QtdBufferPtr = {12..31};
QtdDataToggle = {31};
QtdBytesToTransfer = {16..30};
QtdIoc = {15};
QtdCurrentPage = {12..14};
QtdErrorCounter = {10..11};
QtdPidCode = {8..9};
QtdStatus = {0..7};
ItdNextLinkPointer = 00H;
ItdTransaction0 = 04H;
ItdBufferPtr0 = 024H;
ItdBufferPtr1 = 028H;
ItdBufferPtr2 = 02CH;
ItdExtBufferPtr0 = 40H;
ItdTransactionStatus = {28..31};
ItdTransactionLength = {16..27};
ItdTransactionIoc = {15};
ItdTransactionPg = {12..14};
ItdTransactionOffset = {0..11};
ItdBufferPtr = {12..31};
ItdEndPt = {8..11};
ItdReserved = {7};
ItdDevAdr = {0..6};
ItdIn = {11};
ItdMaxPacketSize = {0..10};
ItdMult = {0..1};
ItdStatus = {28..31};
ItdActive = {31};
ItdDataBufferError = {30};
ItdBabbleDetected = {29};
ItdTransactionError = {28};
QhHorizontalLinkPointer = 00H;
QhEpCapabilities1 = 04H;
QhEpCapabilities2 = 08H;
QhCurrentQtdPointer = 0CH;
QhNextQtdPointer = 10H;
QhAltNextQtdPointer = 14H;
QhQtdToken = 18H;
QhBufferPointer0 = 1CH;
QhBufferPointer1 = 20H;
QhBufferPointer2 = 24H;
QhBufferPointer3 = 28H;
QhBufferPointer4 = 2CH;
QhExtBufferPointer0 = 30H;
QhExtBufferPointer1 = 34H;
QhExtBufferPointer2 = 38H;
QhExtBufferPointer3 = 3CH;
QhExtBufferPointer4 = 40H;
QhTyp = {1..2};
QhTypItd = 0;
QhTypQh = 1;
QhTypSitd = 2;
QhTypFstn = 3;
QhTerminate = {0};
QhNakCountReload = {28..31};
QhControlEndpointFlag = {27};
QhMaxPacketLen = {16..26};
QhHeadOfReclamation = {15};
QhDataToggleControl = {14};
QhEndpointSpeed = {12..13};
QhEndpointNbr = {8..11};
QhInactivate = {7};
QhDeviceAddress = {0..6};
QhMultiplier = {30..31};
QhPortNbr = {23..29};
QhHubAddr = {16..22};
QhSplitCMask = {8..15};
QhSMask = {0..7};
FstnNormalPathLinkPointer = 0;
FstnBackPathLinkPointer = 4;
TdActive = {7};
TdHalted = {6};
TdDataBufferError = {5};
TdBabbleDetected = {4};
TdTransactionError = {3};
TdMissedMicroFrame = {2};
TdSplitTransactionState = {1};
TdPingState = {0};
FstnNormalPathLink = 00H;
FstnBackPathLink = 04H;
PidOut = 0;
PidIn = 1;
PidSetup = 2;
PageSize = 4096;
bOwnedByBios = {16};
bRequestOwnership = {24};
USBCapabilityID = 01H;
TYPE
EnhancedHostController = OBJECT (UsbHcdi.Hcd)
VAR
framelist : UsbHcdi.AlignedMemSpace;
pwcr : LONGINT;
capDebugPortNumber : LONGINT;
capPortIndicators : BOOLEAN;
capNbrOfCompanionHc : LONGINT;
capPortsPerCompanion : LONGINT;
capPortRoutingRules : BOOLEAN;
capPortPowerControl : BOOLEAN;
capNbrOfPorts : LONGINT;
capIsoSchedThreshold : LONGINT;
capAsynchSchedPark : BOOLEAN;
capProgrammableFLG : BOOLEAN;
cap64bit : BOOLEAN;
eecp : LONGINT;
sizeQtd : LONGINT;
sizeQh : LONGINT;
hcportroute : POINTER TO ARRAY OF LONGINT;
isochronousQh* : LONGINT;
interruptQh : POINTER TO ARRAY 11 OF LONGINT;
qhlist : UsbHcdi.AlignedMemSpace;
hcHandshake : BOOLEAN;
interruptsEnabled : SET;
PROCEDURE EnablePortPower*(port : LONGINT);
VAR status : SET;
BEGIN
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
SYSTEM.PUT32(ports[port], status - PscChangeMask + PscPortPower); FlushPCI;
END EnablePortPower;
PROCEDURE DisablePortPower*(port : LONGINT);
VAR status : SET;
BEGIN
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortPower); FlushPCI;
END DisablePortPower;
PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
VAR status : SET; mtimer : Kernel.MilliTimer;
BEGIN
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
SYSTEM.PUT32(ports[port], status - PscChangeMask + PscPortReset - PscPortEnable); FlushPCI;
Wait(UsbHcdi.PortResetTime);
SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortReset); FlushPCI;
Wait(2+1);
Kernel.SetTimer(mtimer, UsbHcdi.PortEnableTimeout);
REPEAT
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
UNTIL (status * PscPortEnable # {}) OR Kernel.Expired(mtimer);
RETURN status * PscPortEnable # {};
END ResetAndEnablePort;
PROCEDURE DisablePort*(port : LONGINT);
VAR status : SET;
BEGIN
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
SYSTEM.PUT32(ports[port], status - PscChangeMask- PscPortEnable);
FlushPCI;
END DisablePort;
PROCEDURE SuspendPort*(port : LONGINT) : BOOLEAN;
VAR status : SET;
BEGIN
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
IF (status * PscPortEnable # {}) & (status * PscPortOwner = {}) THEN
SYSTEM.PUT32(ports[port], status - PscChangeMask + PscSuspend);
FlushPCI;
RETURN TRUE;
END;
RETURN FALSE;
END SuspendPort;
PROCEDURE ResumePort*(port : LONGINT) : BOOLEAN;
VAR status : SET; timer : Kernel.Timer;
BEGIN
status := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
IF status * CmdRunStop = {} THEN
SYSTEM.PUT32(iobase + HcUsbCmd, status + CmdRunStop);
FlushPCI;
END;
status := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
IF (status * PscSuspend # {}) & (status * PscPortOwner = {}) THEN
SYSTEM.PUT32(ports[port], status - PscChangeMask + PscForcePortResume);
FlushPCI;
NEW(timer); timer.Sleep(20);
SYSTEM.PUT32(ports[port], status - PscChangeMask - PscForcePortResume);
FlushPCI;
END;
RETURN SYSTEM.VAL(SET, SYSTEM.GET32(ports[port])) * PscSuspend = {};
END ResumePort;
PROCEDURE Suspend*;
VAR dword : SET; i : LONGINT; ignore : BOOLEAN;
BEGIN
FOR i := 0 TO portCount - 1 DO ignore := SuspendPort(i); END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop);
FlushPCI;
END Suspend;
PROCEDURE Resume*() : BOOLEAN;
VAR dword : SET; i : LONGINT; res : BOOLEAN;
BEGIN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdRunStop);
FlushPCI;
res := TRUE;
FOR i := 0 TO portCount - 1 DO
IF ~ResumePort(i) THEN res := FALSE; END;
END;
RETURN res;
END Resume;
PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN) : SET;
VAR status, s : SET;
BEGIN
s := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
IF ack & ((s * PscChangeMask) # {}) THEN SYSTEM.PUT32(ports[port], s); END; FlushPCI;
status := {};
IF s * PscCurrentConnectStatus # {} THEN status := status + UsbHcdi.PortStatusDevicePresent; END;
IF s * PscPortEnable # {} THEN status := status + UsbHcdi.PortStatusEnabled END;
IF s * PscSuspend # {} THEN status := status + UsbHcdi.PortStatusSuspended END;
IF s * PscOvercurrentActive # {} THEN status := status + UsbHcdi.PortStatusOverCurrent END;
IF s * PscPortReset # {} THEN status := status + UsbHcdi.PortStatusReset END;
IF s * PscPortPower # {} THEN status := status + UsbHcdi.PortStatusPowered END;
IF s * PscConnectStatusChange # {} THEN status := status + UsbHcdi.PortStatusConnectChange END;
IF s * PscPortEnableChange # {} THEN status := status + UsbHcdi.PortStatusEnabledChange END;
IF s * PscOvercurrentChange # {} THEN status := status + UsbHcdi.PortStatusOverCurrentChange END;
IF s * PscTestControl # {} THEN status := status + UsbHcdi.PortStatusTestControl END;
IF s * PscIndicatorControl # {} THEN status := status + UsbHcdi.PortStatusIndicatorControl END;
IF s * PscWakeOnOvercurrent # {} THEN status := status + UsbHcdi.PortStatusWakeOnOvercurrent; END;
IF s * PscWakeOnDisconnect # {} THEN status := status + UsbHcdi.PortStatusWakeOnDisconnect; END;
IF s * PscWakeOnConnect # {} THEN status := status + UsbHcdi.PortStatusWakeOnConnect; END;
IF s * PscPortOwner # {} THEN status := status + UsbHcdi.PortStatusPortOwner; END;
IF (s * PscPortEnable = {}) & (s * PscCurrentConnectStatus # {}) & (s * PscPortPower # {}) & (s * {10} # {}) THEN
status := status + UsbHcdi.PortStatusLowSpeed;
ELSIF (s * PscCurrentConnectStatus # {}) & (s * PscPortReset = {}) & (s * PscPortEnable # {}) THEN
status := status + UsbHcdi.PortStatusHighSpeed;
END;
RETURN status;
END GetPortStatus;
PROCEDURE RoutePortToCompanion*(port : LONGINT);
VAR dword : SET;
BEGIN
ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag)) * {0} # {});
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
SYSTEM.PUT32(ports[port], dword - PscChangeMask + PscPortOwner); FlushPCI;
END RoutePortToCompanion;
PROCEDURE IndicatePort*(port, indicate : LONGINT);
VAR indicators, dword : SET;
BEGIN
IF indicate = UsbHcdi.Amber THEN indicators := {14};
ELSIF indicate = UsbHcdi.Green THEN indicators := {15};
ELSE indicators := {};
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
dword := dword - PscIndicatorControl + indicators;
SYSTEM.PUT32(ports[port], dword); FlushPCI;
END IndicatePort;
PROCEDURE GetFrameNumber*() : INTEGER;
BEGIN
RETURN SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, SYSTEM.LSH(SYSTEM.GET32(iobase + HcFrIndex), -3)) * {0..10});
END GetFrameNumber;
PROCEDURE BuildQueueHead(pipe : UsbHcdi.Pipe);
VAR dword : SET; nakRL, multi, mmask : LONGINT;
BEGIN
ASSERT(pipe.maxPacketSize <= 1024);
nakRL := 3;
IF pipe.type = UsbHcdi.PipeInterrupt THEN nakRL := 0; END;
dword := SYSTEM.LSH(SYSTEM.VAL(SET, nakRL), 28) * QhNakCountReload;
IF (pipe.speed # UsbHcdi.HighSpeed) & (pipe.type = UsbHcdi.PipeControl) THEN
dword := dword + QhControlEndpointFlag;
END;
IF pipe.type = UsbHcdi.PipeControl THEN dword := dword + QhDataToggleControl; END;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, pipe.maxPacketSize), 16) * QhMaxPacketLen;
IF (pipe.speed = UsbHcdi.LowSpeed) THEN
dword := dword + {12};
ELSIF (pipe.speed = UsbHcdi.FullSpeed) THEN
ELSIF (pipe.speed = UsbHcdi.HighSpeed) THEN
dword := dword + {13};
ELSE
HALT(99);
END;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, pipe.endpoint), 8) * QhEndpointNbr;
dword := dword + SYSTEM.VAL(SET, pipe.address) * QhDeviceAddress;
SYSTEM.PUT32(pipe.qh + QhEpCapabilities1, dword);
multi := 1;
dword := SYSTEM.LSH(SYSTEM.VAL(SET, multi), 30) * QhMultiplier;
IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
ASSERT((pipe.ttAddress # 0) & (pipe.ttPort >= 0));
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, pipe.ttAddress), 16) * QhHubAddr;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, pipe.ttPort + 1), 23) * QhPortNbr;
IF (pipe.type = UsbHcdi.PipeInterrupt) OR (pipe.type = UsbHcdi.PipeIsochronous) THEN
dword := dword + SYSTEM.LSH({2..6}, 8) * QhSplitCMask;
END;
END;
mmask := 1;
IF (pipe.type = UsbHcdi.PipeInterrupt) OR (pipe.type = UsbHcdi.PipeIsochronous) THEN
dword := dword + SYSTEM.VAL(SET, mmask) * QhSMask;
END;
SYSTEM.PUT32(pipe.qh + QhEpCapabilities2, dword);
SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer0, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer1, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer2, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer3, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer4, 0);
IF cap64bit THEN
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer0, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer1, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer2, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer3, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer4, 0);
END;
END BuildQueueHead;
PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
VAR adr, asyncListAddr : LONGINT; dword : SET;
BEGIN
ASSERT((pipe # NIL) & (pipe.qh # 0) & (SYSTEM.VAL(SET, pipe.qh) * {0..4} = {}));
ASSERT((pipe.maxPacketSize > 0));
CASE pipe.type OF
|UsbHcdi.PipeControl : pipe.queue := 0;
| UsbHcdi.PipeBulk : pipe.queue := 0;
| UsbHcdi.PipeIsochronous :
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
IF dword * CmdPeriodicSchedEnable = {} THEN
SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdPeriodicSchedEnable); FlushPCI;
END;
RETURN TRUE;
| UsbHcdi.PipeInterrupt :
BEGIN
IF pipe.irqInterval = 1 THEN
pipe.queue := interruptQh[0];
ELSIF pipe.irqInterval < 4 THEN
pipe.queue := interruptQh[1];
ELSIF pipe.irqInterval < 8 THEN
pipe.queue := interruptQh[2];
ELSIF pipe.irqInterval < 16 THEN
pipe.queue := interruptQh[3];
ELSIF pipe.irqInterval < 32 THEN
pipe.queue := interruptQh[4];
ELSE
pipe.queue := interruptQh[5];
END;
END;
ELSE
RETURN FALSE;
END;
BuildQueueHead(pipe);
IF pipe.queue = 0 THEN
asyncListAddr := SYSTEM.GET32(iobase + HcAsyncListAddr);
IF asyncListAddr = 0 THEN
ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {});
SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, SYSTEM.VAL(SET, pipe.qh) * {5..31} + {1} - {2} - QhTerminate);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhEpCapabilities1)) + QhHeadOfReclamation;
SYSTEM.PUT32(pipe.qh + QhEpCapabilities1, dword);
SYSTEM.PUT32(iobase + HcAsyncListAddr, pipe.qh); FlushPCI;
IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to enable async schedule."); KernelLog.Ln; END;
ELSE
ASSERT(SYSTEM.VAL(SET, asyncListAddr) * {0..4} = {});
adr := asyncListAddr;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + QhHorizontalLinkPointer));
SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, dword);
dword := SYSTEM.VAL(SET, pipe.qh) * {5..31} + {1} - {2} - QhTerminate;
SYSTEM.PUT32(adr + QhHorizontalLinkPointer, dword);
END;
ELSE
adr := SYSTEM.GET32(pipe.queue + QhHorizontalLinkPointer);
SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, adr);
SYSTEM.PUT32(pipe.queue + QhHorizontalLinkPointer, pipe.qh + SYSTEM.VAL(LONGINT, {1} - {2}));
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
IF dword * StsPeriodicSchedule = {} THEN
IF ~ScheduleOn(CmdPeriodicSchedEnable, TRUE) THEN
IF Debug.Level >= Debug.Errors THEN Show("Could not enable periodic schedule."); KernelLog.Ln; END;
END;
END;
END;
IF Debug.Trace & Debug.traceQueuing THEN Show("Inserted QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
RETURN TRUE;
END InsertQH;
PROCEDURE ScheduleOn(cmd : SET; on : BOOLEAN) : BOOLEAN;
VAR dword, sts : SET; mtimer : Kernel.MilliTimer;
BEGIN
ASSERT((cmd = CmdPeriodicSchedEnable) OR (cmd = CmdAsyncSchedEnable));
IF Debug.Trace & Debug.traceQueuing THEN
IF on THEN Show("Enabling"); ELSE Show("Disabling"); END;
IF cmd = CmdAsyncSchedEnable THEN KernelLog.String(" asynchronous schedule."); ELSE KernelLog.String(" periodic schedule."); END;
KernelLog.Ln;
END;
IF cmd = CmdAsyncSchedEnable THEN sts := StsAsyncSchedule; ELSE sts := StsPeriodicSchedule; END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
ASSERT(dword * cmd = SYSTEM.LSH(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts, -10));
IF on THEN dword := dword + cmd; ELSE dword := dword - cmd; END;
SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
Kernel.SetTimer(mtimer, 500);
WHILE ~Kernel.Expired(mtimer) & ((SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts # {}) # on) DO
Objects.Yield;
END;
RETURN (SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts # {}) = on;
END ScheduleOn;
PROCEDURE RemoveAsyncQH(pipe : UsbHcdi.Pipe);
VAR start, cur, prev : LONGINT; dword : SET;
BEGIN
prev := SYSTEM.GET32(iobase + HcAsyncListAddr);
ASSERT((prev # 0) & (SYSTEM.VAL(SET, prev) * {0..4} = {}));
prev := SYSTEM.GET32(prev + QhHorizontalLinkPointer);
ASSERT((SYSTEM.VAL(SET, prev) * {1} # {}) & (SYSTEM.VAL(SET, prev) * QhTerminate = {}));
prev := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, prev) * {5..31});
cur := SYSTEM.GET32(prev + QhHorizontalLinkPointer);
ASSERT((SYSTEM.VAL(SET, cur) * {1} # {}) & (SYSTEM.VAL(SET, cur) * QhTerminate = {}));
cur := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cur) * {5..31});
IF cur = prev THEN
ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhEpCapabilities1)) * QhHeadOfReclamation # {});
ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhQtdToken)) * TdActive = {});
IF cur = pipe.qh THEN
IF ScheduleOn(CmdAsyncSchedEnable, FALSE) THEN
SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); FlushPCI;
ELSIF Debug.Level >= Debug.Errors THEN Show("Could not disable async schedule."); KernelLog.Ln;
END;
ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous schedule: QH not found."); KernelLog.Ln;
END;
ELSE
start := cur;
LOOP
dword := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
ASSERT(dword * QhTerminate = {});
ASSERT(dword * {1} # {});
ASSERT(dword * {2..4} = {});
prev := cur;
cur := SYSTEM.VAL(LONGINT, dword * {5..31});
IF cur = pipe.qh THEN EXIT; END;
IF cur = start THEN EXIT; END;
END;
IF cur = pipe.qh THEN
IF SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhEpCapabilities1)) * QhHeadOfReclamation # {} THEN
IF Debug.Trace & Debug.traceQueuing THEN Show("Electing new head of reclamation."); KernelLog.Ln; END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(prev + QhEpCapabilities1));
SYSTEM.PUT32(prev + QhEpCapabilities1, dword + QhHeadOfReclamation);
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
SYSTEM.PUT32(prev + QhHorizontalLinkPointer, dword);
ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous list: QH not found."); KernelLog.Ln;
END;
IF ~HcHandshake() THEN
IF Debug.Level >= Debug.Errors THEN Show("UsbEhci: Serious error: HC handshake failed."); KernelLog.Ln; END;
END;
END;
IF Debug.Trace & Debug.traceQueuing THEN Show("Removed QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
END RemoveAsyncQH;
PROCEDURE HcHandshake() : BOOLEAN;
VAR dword : SET; mtimer : Kernel.MilliTimer; result : BOOLEAN;
BEGIN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
ASSERT(dword * StsAsyncSchedule # {});
hcHandshake := FALSE;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdAsyncAdvDoorbell); FlushPCI;
Kernel.SetTimer(mtimer, 500);
WHILE ~Kernel.Expired(mtimer) & (SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)) * CmdAsyncAdvDoorbell # {}) DO
Objects.Yield;
END;
result := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)) * CmdAsyncAdvDoorbell = {};
IF Debug.Trace & Debug.traceQueuing THEN
Show("HC handshake "); IF result THEN KernelLog.String("succeeded."); ELSE KernelLog.String("failed."); END; KernelLog.Ln;
END;
RETURN result;
END HcHandshake;
PROCEDURE RemovePeriodicQH(pipe : UsbHcdi.Pipe);
VAR timer : Kernel.Timer; cur, temp : LONGINT; next : SET;
BEGIN
IF pipe.queue = 0 THEN RETURN; END;
cur := pipe.queue;
LOOP
next := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
IF next * {5..31} = SYSTEM.VAL(SET, pipe.qh) * {5..31} THEN
temp := SYSTEM.GET32(pipe.qh + QhHorizontalLinkPointer);
SYSTEM.PUT32(cur + QhHorizontalLinkPointer, temp);
IF Debug.Trace & Debug.traceQueuing THEN KernelLog.String("UsbEhci: Deleted Interrupt Pipe QH."); KernelLog.Ln; END;
NEW(timer); timer.Sleep(10);
EXIT;
ELSIF next * QhTerminate # {} THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Could not delete interrupt QH -> QH not found."); KernelLog.Ln; END;
EXIT;
ELSE
cur := SYSTEM.VAL(LONGINT, next * {5..31});
END;
END;
IF Debug.Trace & Debug.traceQueuing THEN Show("Removed QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
END RemovePeriodicQH;
PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
BEGIN
IF Debug.Trace & Debug.traceQueuing THEN Show("Removing QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
UnlinkTDsInternal(pipe);
IF (pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeBulk) THEN
RemoveAsyncQH(pipe);
ELSIF pipe.type = UsbHcdi.PipeInterrupt THEN
RemovePeriodicQH(pipe);
ELSE
END;
END RemoveQH;
PROCEDURE LinkTDsAllowed*(pipe : UsbHcdi.Pipe) : BOOLEAN;
VAR dword : SET;
BEGIN {EXCLUSIVE}
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
IF dword * TdActive # {} THEN
IF Debug.Level >= Debug.Errors THEN Show("LinkTDs: ERROR: PIPE IS STILL ACTIVE!!!!"); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
RETURN FALSE;
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhNextQtdPointer));
IF dword * QhTerminate = {} THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: LinkTDs: Overwriten valid pointer ?!?"); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed;
RETURN FALSE;
END;
RETURN TRUE;
END LinkTDsAllowed;
PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; qtd : LONGINT);
VAR dword : SET;
BEGIN {EXCLUSIVE}
ASSERT(SYSTEM.VAL(SET, qtd) * {0..4} = {});
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
IF dword * TdHalted # {} THEN
IF Debug.Trace & Debug.tracePipes THEN Show("LinkTDs: Automatically clear halt condition"); KernelLog.Ln; END;
ClearHalt(pipe);
END;
SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, qtd);
IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {} THEN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
IF dword * CmdAsyncSchedEnable = {} THEN
IF ~ScheduleOn(CmdAsyncSchedEnable, TRUE) & (Debug.Level >= Debug.Errors) THEN Show("Failed to re-enabled async schedule."); KernelLog.Ln; END;
END;
END;
END LinkTDs;
PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
BEGIN {EXCLUSIVE}
UnlinkTDsInternal(pipe);
END UnlinkTDs;
PROCEDURE UnlinkTDsInternal*(pipe : UsbHcdi.Pipe);
VAR dword : SET; timer : Kernel.Timer; qtd : LONGINT; mtimer : Kernel.MilliTimer;
BEGIN
IF pipe.firstTD = 0 THEN RETURN END;
qtd := pipe.firstTD; ASSERT(pipe.lastTD >= pipe.firstTD);
WHILE qtd <= pipe.lastTD DO
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken));
SYSTEM.PUT32(qtd + QtdToken, dword - TdActive);
qtd := qtd + sizeQtd;
END;
Kernel.SetTimer(mtimer, 2000);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
WHILE ~Kernel.Expired(mtimer) & (dword * TdActive # {}) DO
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
Objects.Yield;
END;
IF dword * TdActive # {} THEN
IF Debug.Level >= Debug.Errors THEN Show("Transaction overlay indicates active transfer!"); KernelLog.Ln; END;
END;
NEW(timer); timer.Sleep(10);
SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
timer.Sleep(2);
IF SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken)) * TdActive # {} THEN
IF Debug.Level >= Debug.Errors THEN Show("Failed to unlink TDs from pipe:"); KernelLog.Ln; pipe.Show(TRUE); KernelLog.Ln; END;
END;
SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
pipe.firstTD := 0; pipe.lastTD := 0;
END UnlinkTDsInternal;
PROCEDURE ClearHalt(pipe : UsbHcdi.Pipe);
VAR dword : SET;
BEGIN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
IF dword * TdHalted # {} THEN
SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer0, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer1, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer2, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer3, 0);
SYSTEM.PUT32(pipe.qh + QhBufferPointer4, 0);
IF cap64bit THEN
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer0, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer1, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer2, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer3, 0);
SYSTEM.PUT32(pipe.qh + QhExtBufferPointer4, 0);
END;
ELSIF Debug.Level >= Debug.Warnings THEN Show("Tried to clear a non-halted pipe."); KernelLog.Ln;
END;
END ClearHalt;
PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
VAR
qtd : LONGINT;
dword : SET;
ranges : ARRAY ScatterGatherListSize OF Machine.Range;
numRanges : LONGINT;
BEGIN
pipe.firstTD := pipe.tdBase - 32 + sizeQh;
ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {});
IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
IF pipe.maxRetries = 0 THEN
pipe.maxRetries := 3;
END;
END;
qtd := pipe.firstTD;
ASSERT((qtd + sizeQtd - 1 <= SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1])));
SYSTEM.PUT32(qtd + QtdNextQtdPointer, qtd + sizeQtd);
SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate);
dword := SYSTEM.LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, 8), 16) * QtdBytesToTransfer;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, PidSetup), 8) * QtdPidCode + TdActive;
SYSTEM.PUT32(qtd + QtdToken, dword);
Machine.TranslateVirtual(SYSTEM.ADR(msg[0]), 8, numRanges, ranges);
IF numRanges = 0 THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: ScheduleControl: Scatter/Gather list too small."); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge; RETURN;
END;
SYSTEM.PUT32(qtd + QtdBufferPtr0, ranges[0].adr);
IF numRanges > 1 THEN
SYSTEM.PUT32(qtd + QtdBufferPtr1, ranges[1].adr)
ELSE
SYSTEM.PUT32(qtd + QtdBufferPtr1, 0);
END;
SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);
IF cap64bit THEN
SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
END;
pipe.dataToggle := TRUE;
IF bufferLen # 0 THEN
IF ~CreateTDList(pipe, direction, bufferLen, 0, buffer, qtd + sizeQtd, qtd, TRUE) THEN
pipe.status := Usbdi.Error; pipe.errors := UsbHcdi.Internal; RETURN;
END;
END;
qtd := qtd + sizeQtd;
IF qtd + sizeQtd - 1 > SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: TD buffer too small."); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs; RETURN;
END;
SYSTEM.PUT32(qtd + QtdNextQtdPointer, QtdTerminate);
SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate);
dword := QtdDataToggle + TdActive;
IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, PidIn), 8);
ELSE
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, PidOut), 8);
IF pipe.speed = UsbHcdi.HighSpeed THEN
dword := dword + TdPingState;
END;
END;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
IF pipe.ioc THEN dword := dword + QtdIoc; END;
SYSTEM.PUT32(qtd + QtdToken, dword);
SYSTEM.PUT32(qtd + QtdBufferPtr0, 0);
SYSTEM.PUT32(qtd + QtdBufferPtr1, 0);
SYSTEM.PUT32(qtd + QtdBufferPtr2, 0);
SYSTEM.PUT32(qtd + QtdBufferPtr3, 0);
SYSTEM.PUT32(qtd + QtdBufferPtr4, 0);
IF cap64bit THEN
SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
END;
pipe.lastTD := qtd;
END ScheduleControl;
PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
VAR dword : SET;
BEGIN
SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
pipe.firstTD := pipe.tdBase - 32 + sizeQh;
ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {});
IF ~CreateTDList(pipe, pipe.direction, bufferLen, offset, buffer, pipe.firstTD, pipe.lastTD, FALSE) THEN
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.LinkTDsFailed; RETURN;
END;
SYSTEM.PUT32(pipe.lastTD + QtdNextQtdPointer, QhTerminate);
IF pipe.ioc THEN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + QtdToken));
dword := dword + QtdIoc;
SYSTEM.PUT32(pipe.lastTD + QtdToken, 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, temp : LONGINT;
i, qtd : LONGINT;
dword : SET;
numRanges, idx, offset : LONGINT;
BEGIN
ASSERT((pipe.maxRetries >= 0) & (pipe.maxRetries <= 3));
Machine.TranslateVirtual(SYSTEM.ADR(buffer[ofs]), len, numRanges, pipe.sgList^);
IF numRanges = 0 THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Schedule: Scatter/Gather list too small"); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.TransferTooLarge;
RETURN FALSE;
END;
qtd := firstTD - sizeQtd;
idx := 0;
offset := 0;
curlen := 0;
restlen := len;
WHILE restlen > 0 DO
qtd := qtd + sizeQtd;
IF qtd + sizeQtd - 1 > SYSTEM.ADR(pipe.tdBuffer[pipe.tdBufferLen-1]) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: TD buffer too small"); KernelLog.Ln; END;
pipe.status := Usbdi.Error; pipe.errors := pipe.errors + UsbHcdi.OutOfTDs;
RETURN FALSE;
END;
SYSTEM.PUT32(qtd + QtdBufferPtr0, pipe.sgList[idx].adr + offset);
curlen := PageSize - SHORT ((pipe.sgList[idx].adr + offset) MOD PageSize);
IF curlen > restlen THEN
curlen := restlen;
END;
ASSERT(curlen > 0);
restlen := restlen - curlen; offset := 0;
INC(idx);
i := 1;
WHILE (i < 5) DO
IF restlen <= 0 THEN
SYSTEM.PUT32(qtd + QtdBufferPtr0 + i*4, 0);
ELSE
IF i = 4 THEN
temp := PageSize - ((curlen + PageSize) MOD pipe.maxPacketSize);
IF restlen > temp THEN
curlen := curlen + temp; restlen := restlen - temp; offset := temp;
SYSTEM.PUT32(qtd + QtdBufferPtr0 + i*4, pipe.sgList[idx].adr);
IF offset = PageSize THEN INC(idx); offset := 0;
ELSE
END;
ELSE
curlen := curlen + restlen; restlen := 0;
SYSTEM.PUT32(qtd + QtdBufferPtr0 + i*4, pipe.sgList[idx].adr);
END;
ELSE
IF restlen > PageSize THEN
curlen := curlen + PageSize; restlen := restlen - PageSize;
ELSE
curlen := curlen + restlen; restlen := 0;
END;
SYSTEM.PUT32(qtd + QtdBufferPtr0 + i*4, pipe.sgList[idx].adr);
INC(idx);
END;
END;
INC(i);
END;
IF cap64bit THEN
SYSTEM.PUT32(qtd + QtdExtBufferPtr0, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr1, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr2, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr3, 0);
SYSTEM.PUT32(qtd + QtdExtBufferPtr4, 0);
END;
SYSTEM.PUT32(qtd + QtdNextQtdPointer, qtd + sizeQtd);
SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate);
ASSERT(curlen <= 5000H);
dword := TdActive;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, curlen), 16) * QtdBytesToTransfer;
IF tdToggle THEN
IF pipe.dataToggle THEN dword := dword + QtdDataToggle; END;
IF (curlen DIV pipe.maxPacketSize) MOD 2 # 0 THEN
pipe.dataToggle := ~pipe.dataToggle;
END;
END;
IF direction = UsbHcdi.In THEN
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, PidIn), 8);
ELSIF direction = UsbHcdi.Out THEN
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, PidOut), 8);
IF pipe.speed = UsbHcdi.HighSpeed THEN
dword := dword + TdPingState;
END;
END;
SYSTEM.PUT32(qtd + QtdToken, dword);
END;
lastTD := qtd;
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 + HcUsbSts)) * interruptsEnabled;
IF s # {} THEN
IF Debug.Stats THEN INC(NnofInterruptsHandled); END;
IF Debug.Trace & Debug.traceInterrupts THEN
Show("Interrupt: "); ShowInterrupts(s); KernelLog.Ln;
END;
SYSTEM.PUT32(iobase + HcUsbSts, s * {0..5}); FlushPCI;
IF s * StsAsyncAdvance # {} THEN hcHandshake := TRUE; END;
IF s * StsHostSystemError # {} THEN
Show("Serious error. Please restart the EHCI driver:");
IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsHcHalted # {} THEN
KernelLog.String(" [HC halted]");
SetState(UsbHcdi.Halted);
END;
KernelLog.Ln;
END;
IF s * StsFrameListRollover # {} THEN END;
IF s * StsPortChange # {} THEN
IF statusChangeHandler # NIL THEN statusChangeHandler(Usbdi.Ok, 0); END;
END;
IF s * (StsUsbError + StsUsbInterrupt) # {} THEN
NotifyCompletionHandlers;
END;
END;
END;
END InterruptHandler;
PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
CONST MaxLoops = 10000;
VAR
qtd : LONGINT;
s, errors : SET;
restLen, len : LONGINT;
loop : LONGINT;
BEGIN
FlushPCI;
s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
IF s * TdActive # {} THEN RETURN; END;
errors := UsbHcdi.NoErrors; loop := 0; restLen := 0; qtd := pipe.firstTD;
LOOP
s := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken)) * QtdStatus - TdPingState - TdSplitTransactionState;
IF s = {} THEN
ELSIF s * TdActive # {} THEN
RETURN;
ELSE
IF s * TdHalted # {} THEN errors := errors + UsbHcdi.Stalled; END;
IF s * TdDataBufferError # {} THEN errors := errors + UsbHcdi.Databuffer; END;
IF s * TdBabbleDetected # {} THEN errors := errors + UsbHcdi.Babble; END;
IF s * TdTransactionError # {} THEN errors := errors + UsbHcdi.CrcTimeout; END;
IF s * TdMissedMicroFrame # {} THEN errors := errors + UsbHcdi.Internal; END;
EXIT;
END;
IF pipe.transferLen > 0 THEN
len := SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken)) * QtdBytesToTransfer, -16));
IF (len # 0) THEN
restLen := restLen + len;
END;
END;
IF qtd = pipe.lastTD THEN EXIT; END;
qtd := qtd + sizeQtd;
INC(loop);
IF loop > MaxLoops THEN
IF Debug.Level >= Debug.Errors THEN Show("UpdateStatus: Serious error occured."); KernelLog.Ln; END;
EXIT;
END;
END;
pipe.errors := errors;
IF errors = UsbHcdi.NoErrors THEN
IF restLen = 0 THEN
pipe.actLen := pipe.transferLen;
pipe.status := Usbdi.Ok;
ELSE
pipe.actLen := pipe.transferLen - restLen;
pipe.status := Usbdi.ShortPacket;
pipe.errors := pipe.errors + UsbHcdi.ShortPacket;
END;
ELSE
pipe.actLen := pipe.transferLen - restLen;
IF errors * UsbHcdi.Stalled # {} THEN
pipe.status := Usbdi.Stalled;
ELSE
pipe.status := Usbdi.Error;
END;
END;
s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
IF s * TdHalted # {} THEN
ClearHalt(pipe);
END;
IF (pipe.type = UsbHcdi.PipeBulk) OR (pipe.type = UsbHcdi.PipeInterrupt) THEN
IF (pipe.status = Usbdi.Ok) OR (pipe.status = Usbdi.ShortPacket) THEN
s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.lastTD + QtdToken));
IF s * QtdDataToggle # {} THEN pipe.dataToggle := TRUE; ELSE pipe.dataToggle := FALSE; END;
END;
END;
END UpdatePipeStatus;
PROCEDURE HardwareReset() : BOOLEAN;
CONST MaxWaits = 1000;
VAR dword : SET; i : LONGINT;
BEGIN
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop); FlushPCI;
i := 1; dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
WHILE (dword * StsHcHalted = {}) & (i <= MaxWaits) DO
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
INC(i); Wait(1);
END;
IF dword * StsHcHalted = {} THEN RETURN FALSE; END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdHcReset); FlushPCI;
FOR i := 1 TO MaxWaits DO
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
IF dword * CmdHcReset = {} THEN
RETURN TRUE;
END;
Wait(1);
END;
RETURN FALSE;
END HardwareReset;
PROCEDURE SoftwareReset*() : BOOLEAN;
BEGIN
RETURN FALSE;
END SoftwareReset;
PROCEDURE InitFrameList(): BOOLEAN;
VAR fstn, i, k, j, shift : LONGINT;
BEGIN
qhlist := UsbHcdi.GetAlignedMemSpace(2048, 4096);
framelist := UsbHcdi.GetAlignedMemSpace(4096, 4096);
shift := sizeQh DIV 4; ASSERT(sizeQh MOD 4 = 0);
FOR i := 0 TO 12 DO
qhlist.data[qhlist.base + i*shift + (QhEpCapabilities1 DIV 4)] := 0;
qhlist.data[qhlist.base + i*shift + (QhEpCapabilities2 DIV 4)] := 0;
qhlist.data[qhlist.base + i*shift + (QhQtdToken DIV 4)] := 0;
qhlist.data[qhlist.base + i*shift + (QhCurrentQtdPointer DIV 4)] := 0;
qhlist.data[qhlist.base + i*shift + (QhNextQtdPointer DIV 4)] := 1;
qhlist.data[qhlist.base + i*shift + (QhAltNextQtdPointer DIV 4)] := 1;
FOR j := 0 TO 4 DO
qhlist.data[qhlist.base + i*shift + (QhBufferPointer0 DIV 4) + j] := 0;
END;
IF cap64bit THEN
FOR j := 0 TO 4 DO
qhlist.data[qhlist.base + i*shift + (QhExtBufferPointer0 DIV 4) + j] := 0;
END;
END;
END;
NEW(interruptQh);
FOR i := 0 TO 10 DO interruptQh[i] := Machine.Ensure32BitAddress (SYSTEM.ADR(qhlist.data[qhlist.base]) + i*sizeQh); END;
fstn := interruptQh[10] + sizeQh;
isochronousQh := fstn + sizeQh;
FOR i := 10 TO 2 BY -1 DO
SYSTEM.PUT32(interruptQh[i] + QhHorizontalLinkPointer, interruptQh[i-1] + SYSTEM.LSH(QhTypQh, 1));
END;
SYSTEM.PUT32(interruptQh[1] + QhHorizontalLinkPointer, fstn + SYSTEM.LSH(QhTypFstn, 1));
SYSTEM.PUT32(fstn + FstnNormalPathLinkPointer, interruptQh[0] + SYSTEM.LSH(QhTypQh, 1));
SYSTEM.PUT32(fstn + FstnBackPathLinkPointer, QhTerminate);
SYSTEM.PUT32(interruptQh[0] + QhHorizontalLinkPointer, isochronousQh + SYSTEM.LSH(QhTypQh, 1));
SYSTEM.PUT32(isochronousQh + QhHorizontalLinkPointer, QhTerminate);
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.data[framelist.base + i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, interruptQh[k]) + {1});
END;
RETURN TRUE;
END InitFrameList;
PROCEDURE Init(iobase , irq : LONGINT) : BOOLEAN;
VAR
reg : LONGINT;
dword : SET; qword : HUGEINT;
ignore : BOOLEAN;
i : LONGINT;
BEGIN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller initialization..."); KernelLog.Ln; END;
SELF.iobase := iobase; SELF.irq := irq;
isHighSpeed := TRUE; DMAchaining := TRUE; sgListSize := ScatterGatherListSize;
reg :=SYSTEM.GET16(iobase + HcCapHciVersion);
IF reg # 0100H THEN
KernelLog.String("UsbEhci: Revision of EHCI Programming Interface not supported."); KernelLog.Ln;
RETURN FALSE;
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapSparams));
capDebugPortNumber := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {20..31}, -20));
IF dword * {16} # {} THEN capPortIndicators := TRUE; ELSE capPortIndicators := FALSE; END;
capNbrOfCompanionHc := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {12..15}, -12));
capPortsPerCompanion := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {8..11}, -8));
IF dword * {7} # {} THEN capPortRoutingRules := TRUE; ELSE capPortRoutingRules := FALSE; END;
IF dword * {4} # {} THEN capPortPowerControl := TRUE; ELSE capPortPowerControl := FALSE; END;
capNbrOfPorts := SYSTEM.VAL(LONGINT, dword * {0..3});
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapCparams));
capIsoSchedThreshold := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {4..7}, -4));
IF dword * {2} # {} THEN capAsynchSchedPark := TRUE; ELSE capAsynchSchedPark := FALSE; END;
IF dword * {1} # {} THEN capProgrammableFLG := TRUE; ELSE capProgrammableFLG := FALSE; END;
IF dword * {0} # {} THEN cap64bit := TRUE; ELSE cap64bit := FALSE; END;
eecp := SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, dword) * {8..15}, - 8));
IF capPortRoutingRules THEN
qword := SYSTEM.GET32(iobase + HcCapPortroute);
qword := qword + SYSTEM.LSH(SYSTEM.GET32(iobase + HcCapPortroute + 4), 32);
NEW(hcportroute, 16);
FOR i := 0 TO 15 DO
hcportroute[i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, SYSTEM.LSH(qword, -i)) * {0..3});
END;
END;
NEW(hubDescriptor, 8);
hubDescriptor[0] := CHR(7);
hubDescriptor[1] := CHR(29H);
hubDescriptor[2] := CHR(capNbrOfPorts);
IF capPortPowerControl THEN
dword := dword + {0};
ELSE
dword := dword + {1};
END;
dword := dword + {3};
IF capPortIndicators THEN dword := dword + {7}; END;
hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
hubDescriptor[4] := CHR(0);
hubDescriptor[5] := CHR(10);
hubDescriptor[6] := CHR(0);
iobase := iobase + SYSTEM.GET8(iobase + HcCapLength);
SELF.iobase := iobase;
portCount := capNbrOfPorts;
NEW(ports, portCount);
FOR i := 0 TO portCount - 1 DO ports[i] := iobase + HcPortSc + i*4; END;
IF ~HardwareReset() THEN RETURN FALSE; END;
SYSTEM.PUT32(iobase + HcCtrlDsSegment, 0);
IF cap64bit THEN
sizeQh := 96;
sizeQtd := 64;
ELSE
sizeQh := 64;
sizeQtd := 32;
END;
IF ~InitFrameList() THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Initialization of HCCA failed."); KernelLog.Ln; END;
RETURN FALSE;
END;
IF capAsynchSchedPark THEN
ASSERT((HcAsyncParkModeCount >= 0) & (HcAsyncParkModeCount < 4));
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
IF HcAsyncParkModeCount = 0 THEN
dword := dword - CmdAsyncSchedParkMode;
ELSE
dword := dword + SYSTEM.LSH(SYSTEM.VAL(SET, HcAsyncParkModeCount), 8) * CmdAsyncSchedParkCount;
dword := dword + CmdAsyncSchedParkMode;
END;
SYSTEM.PUT32(iobase + HcUsbCmd, dword);
END;
dword := {};
IF capProgrammableFLG THEN
IF Debug.Trace & Debug.traceInit THEN
KernelLog.String("UsbEhci: Set frame list size to "); KernelLog.Int(HcFrameListSize, 0);
KernelLog.String(" elements."); KernelLog.Ln;
END;
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
IF ((HcInterruptThreshold#01H) & (HcInterruptThreshold#02H) & (HcInterruptThreshold#04H) & (HcInterruptThreshold#08H)
& (HcInterruptThreshold#10H) & (HcInterruptThreshold#20H) & (HcInterruptThreshold#40H)) THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Interrupt Threshold value invalid... using default setting."); KernelLog.Ln; END;
dword := dword + SYSTEM.VAL(SET, SYSTEM.LSH(08H, 16)) * {16..23};
ELSE
dword := dword + SYSTEM.VAL(SET, SYSTEM.LSH(HcInterruptThreshold, 16)) * {16..23};
END;
SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
IF Start() = FALSE THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: Couldn't start host controller."); KernelLog.Ln; END;
ignore := HardwareReset();
RETURN FALSE;
END;
RETURN TRUE;
END Init;
PROCEDURE FlushPCI;
VAR ignore : LONGINT;
BEGIN
ignore := SYSTEM.GET32(iobase + HcUsbSts);
END FlushPCI;
PROCEDURE Start():BOOLEAN;
VAR dword : SET;
BEGIN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller... "); KernelLog.Ln; END;
SetState(UsbHcdi.Initialized);
Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
interruptsEnabled := dword + {0..5} - StsFrameListRollover;
SYSTEM.PUT32(iobase + HcUsbIntr, interruptsEnabled);
SYSTEM.PUT32(iobase + HcPeriodicListBase, SYSTEM.ADR(framelist.data[framelist.base]));
SYSTEM.PUT32(iobase + HcAsyncListAddr, 0);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
dword := dword + CmdRunStop;
SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
SetState(UsbHcdi.Operational);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag));
dword := dword + {0};
SYSTEM.PUT32(iobase + HcConfigFlag, dword); FlushPCI;
RETURN TRUE;
END Start;
PROCEDURE &Default*(bus, device, function : LONGINT);
BEGIN
Default^(bus, device, function);
pipes[0, 0, 0].maxPacketSize := 64;
END Default;
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 Show("Host controller reset failed."); KernelLog.Ln; END;
END;
ReleaseHcOwnerShip(bus, device, function, eecp);
Machine.UnmapPhysical(iobase, 4096);
END Cleanup;
PROCEDURE ShowSchedule*;
CONST MaxIterations =21;
VAR dword : SET; first, cur : LONGINT; i, ms : LONGINT;
BEGIN
IF Debug.Trace THEN
KernelLog.String("Host Controller Data Structures for ");
KernelLog.String(name);
KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("):"); KernelLog.Ln;
KernelLog.String("Periodic Schedule: "); KernelLog.Ln;
IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsPeriodicSchedule = {} THEN
KernelLog.String("Periodic schedule is disabled."); KernelLog.Ln;
END;
KernelLog.String("*** Isochronous schedule: "); KernelLog.Ln;
ShowQueueHead(isochronousQh, 0, cap64bit);
ms := 1;
FOR i := 0 TO 10 DO
KernelLog.String("*** "); KernelLog.Int(ms, 0); KernelLog.String(" ms schedule:"); KernelLog.Ln;
ShowQueueHead(interruptQh[i], 0, cap64bit);
dword := SYSTEM.VAL(SET, SYSTEM.GET32(interruptQh[i] + QhHorizontalLinkPointer));
cur := 0;
LOOP
IF dword * QhTerminate # {} THEN EXIT; END;
IF cur > MaxIterations THEN
KernelLog.String("Maximum allowed iterations reached."); KernelLog.Ln;
EXIT;
END;
INC(cur);
IF i > 0 THEN
IF SYSTEM.VAL(LONGINT, dword * {5..31}) = interruptQh[i-1] THEN
dword := dword + QhTerminate;
ELSE
dword := dword * {5..31};
IF SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer)) * QhTerminate # {} THEN
ShowQueueHead(SYSTEM.VAL(LONGINT, dword), 0, cap64bit);
ELSE
ShowQueueHead(SYSTEM.VAL(LONGINT, dword), SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer), cap64bit);
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhHorizontalLinkPointer));
END;
ELSIF SYSTEM.VAL(LONGINT, dword * {5..31}) = isochronousQh THEN
dword := dword + QhTerminate;
ELSE
dword := dword * {5..31};
IF SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer)) * QhTerminate # {} THEN
ShowQueueHead(SYSTEM.VAL(LONGINT, dword), 0, cap64bit);
ELSE
ShowQueueHead(SYSTEM.VAL(LONGINT, dword), SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhNextQtdPointer), cap64bit);
END;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.VAL(LONGINT, dword) + QhHorizontalLinkPointer));
END;
END;
ms := ms * 2;
END;
KernelLog.String("*** Asynchronous list: "); KernelLog.Ln;
IF SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule # {} THEN
first := SYSTEM.GET32(iobase + HcAsyncListAddr);
IF (SYSTEM.VAL(SET, first) * {0..4} = {}) & (first # 0) THEN
first := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, first) * {5..31});
ShowQueueHead(first, 0, cap64bit);
i := 0; cur := first;
LOOP
cur := SYSTEM.GET32(cur + QhHorizontalLinkPointer);
IF (SYSTEM.VAL(SET, cur) * {2..4} # {}) OR (SYSTEM.VAL(SET, cur) * {1} = {}) THEN
KernelLog.String("Error: Queue head horizontal link pointer is invalid."); KernelLog.Ln;
EXIT;
END;
cur := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cur) * {5..31});
IF (cur = first) OR (i >= MaxIterations) THEN EXIT END;
ShowQueueHead(cur, 0, cap64bit);
INC(i);
END;
IF i >= MaxIterations THEN
KernelLog.String("MaxIterations reached. Aborting..."); KernelLog.Ln;
END;
ELSE KernelLog.String("Error: Asynchronous Schedule List address is invalid.");
END;
ELSE KernelLog.String("Asynchronous Schedule is disabled.");
END;
KernelLog.Ln;
END;
END ShowSchedule;
PROCEDURE ShowPipe*(pipe : UsbHcdi.Pipe);
BEGIN
ShowQueueHead(pipe.qh, pipe.firstTD, cap64bit);
END ShowPipe;
PROCEDURE Diag;
VAR dword : SET;
BEGIN
IF Debug.Trace THEN
Diag^;
KernelLog.String(" HC Structural Parameters: "); KernelLog.Ln;
KernelLog.String(" Nbr of ports: "); KernelLog.Int(capNbrOfPorts, 0);
KernelLog.String(", Debug port: ");
IF capDebugPortNumber # 0 THEN KernelLog.Int(capDebugPortNumber, 0); ELSE KernelLog.String("n/a"); END;
KernelLog.Ln;
KernelLog.String(" Per port power control: ");
IF capPortPowerControl THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.String(", Port indicator control: ");
IF capPortIndicators THEN KernelLog.String("Available"); ELSE KernelLog.String("n/a"); END;
KernelLog.Ln;
KernelLog.String(" Nbr of companion HCs: "); KernelLog.Int(capNbrOfCompanionHc, 0);
KernelLog.String(", Ports per companion: "); KernelLog.Int(capPortsPerCompanion, 0);
KernelLog.String(", Port routing rules: ");
IF capPortRoutingRules THEN KernelLog.String("Available"); ELSE KernelLog.String("n/a"); END;
KernelLog.Ln;
KernelLog.String(" HC Capablilty Parameters:"); KernelLog.Ln;
KernelLog.String(" 64bit Data Structures: "); IF cap64bit THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.String(", Async Schedule Park Mode support: "); IF capAsynchSchedPark THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.Ln;
KernelLog.String(" Programmable Frame List Size: "); IF capAsynchSchedPark THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.String(", Isochronous Scheduling Threshold: "); KernelLog.Int(capIsoSchedThreshold, 0);
KernelLog.Ln;
KernelLog.String(" HC Command Register: "); KernelLog.Ln;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
KernelLog.String(" Interrupt Threshold: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {16..23}, -16)), 0);
KernelLog.String(", Async Schedule Park Mode: ");
IF dword * {11} # {} THEN
KernelLog.String("Enabled ("); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {8..9}, -8)), 0); KernelLog.Char(")");
ELSE
KernelLog.String("Disabled");
END;
KernelLog.String(", Frame List Size: ");
CASE SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * {2..3}, -2)) OF
0: KernelLog.String("1024");
|1: KernelLog.String("512");
|2: KernelLog.String("256");
|3: KernelLog.String("Reserved");
END;
KernelLog.Ln;
KernelLog.String(" HC Status Register:"); KernelLog.Ln;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
KernelLog.String(" Asynchronous Schedule: ");
IF dword * StsAsyncSchedule # {} THEN KernelLog.String("Enabled"); ELSE KernelLog.String("Disabled"); END;
KernelLog.String(", Periodic Schedule: ");
IF dword * StsPeriodicSchedule # {} THEN KernelLog.String("Enabled"); ELSE KernelLog.String("Disabled"); END;
KernelLog.String(" ");
IF dword * StsReclamation # {} THEN KernelLog.String("[Reclamation]"); END;
IF dword * StsHcHalted # {} THEN KernelLog.String("[HcHalted]"); END;
KernelLog.Ln;
KernelLog.String(" Interrupt Status: "); ShowInterrupts(dword); KernelLog.Ln;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
KernelLog.String(" Interrupts Enabled: "); ShowInterrupts(dword); KernelLog.Ln;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
KernelLog.String(" HC operation: ");
IF dword * {0} # {} THEN KernelLog.String("Running"); ELSE KernelLog.String("Stopped"); END;
KernelLog.Ln;
END;
END Diag;
PROCEDURE Show(txt : ARRAY OF CHAR);
BEGIN
KernelLog.String("UsbEhci: "); KernelLog.String(name); KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String("): ");
KernelLog.String(txt);
END Show;
END EnhancedHostController;
PROCEDURE Indent(spaces : LONGINT);
VAR i : LONGINT;
BEGIN
FOR i := 1 TO spaces DO KernelLog.Char(" "); END;
END Indent;
PROCEDURE ShowQueueHead(qh, firstQtd : LONGINT; cap64bit : BOOLEAN);
CONST MaxChainLen = 32;
VAR
dword : SET;
val, chainlen : LONGINT;
PROCEDURE ShowQhTyp(qh : LONGINT);
BEGIN
IF Debug.Trace THEN
val := SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, qh) * QhTyp, -1));
IF val = 0 THEN KernelLog.String("Isochronous Tranfers Desriptor");
ELSIF val = 1 THEN KernelLog.String("Queue Head");
ELSIF val = 2 THEN KernelLog.String("Split Transaction Isochronous Transfer Descriptor");
ELSIF val = 3 THEN KernelLog.String("Frame Span Traversal Node");
END;
END;
END ShowQhTyp;
BEGIN
IF Debug.Trace THEN
KernelLog.String("EHCI data structure at "); KernelLog.Hex(qh, 8); KernelLog.String(": ");
ShowQhTyp(qh); KernelLog.String(" ");
IF qh = 0 THEN KernelLog.String("Error: QH pointer = 0"); KernelLog.Ln;RETURN;
ELSIF SYSTEM.VAL(SET, qh) * {0..4} # {} THEN KernelLog.String("Error: Not aligned"); KernelLog.Ln; RETURN;
END;
KernelLog.Ln;
KernelLog.String(" Endpoint Capabilities 1: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhEpCapabilities1));
KernelLog.String(" DeviceAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * QhDeviceAddress), 0);
KernelLog.String(", Endpoint: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhEndpointNbr, -8)), 0);
KernelLog.String(", Speed: "); val := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhEndpointSpeed, -12));
IF val = 0 THEN KernelLog.String("FullSpeed");
ELSIF val = 1 THEN KernelLog.String("LowSpeed");
ELSIF val = 2 THEN KernelLog.String("HighSpeed");
ELSE KernelLog.String("ERROR: Not set correctly");
END;
KernelLog.String(", MaxPacketSize: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhMaxPacketLen, -16)), 0);
KernelLog.String(", NakRL: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhNakCountReload, -28)), 0);
KernelLog.String(", Flags: ");
IF dword * QhControlEndpointFlag # {} THEN KernelLog.String("[ControlEp]"); END;
IF dword * QhDataToggleControl # {} THEN KernelLog.String("[DataToggleControl]"); END;
IF dword * QhHeadOfReclamation # {} THEN KernelLog.String("[Head]"); END;
IF dword * QhInactivate # {} THEN KernelLog.String("[Inactivate]"); END;
KernelLog.Ln;
KernelLog.String(" Endpoint Capabilities 2: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhEpCapabilities2));
KernelLog.String("Mult: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhMultiplier, -30)), 0);
KernelLog.String(", HubAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhHubAddr, -16)), 0);
KernelLog.String(", HubPort: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhPortNbr, -23)), 0);
KernelLog.String(", SplitCMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QhSplitCMask, -8)), 0);
KernelLog.String(", QhSMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * QhSMask), 0);
KernelLog.Ln;
KernelLog.String(" Queue Head Horizontal Link Pointer: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhHorizontalLinkPointer));
IF dword * QhTerminate # {} THEN
KernelLog.String("Invalid ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String("H)");
ELSE
KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {5..31}), 8);
KernelLog.String(" ("); ShowQhTyp(SYSTEM.VAL(LONGINT, dword)); KernelLog.String(")");
END;
KernelLog.Ln;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qh + QhCurrentQtdPointer));
KernelLog.String(" Current qTD Pointer: "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8);
KernelLog.String(", Next qTD Pointer: "); KernelLog.Hex(SYSTEM.GET32(qh + QhNextQtdPointer), 8);
KernelLog.Ln;
KernelLog.String(" Transfer overlay: "); KernelLog.Ln;
ShowQtd(qh+16, 8, cap64bit, TRUE); KernelLog.Ln;
IF firstQtd # 0 THEN
KernelLog.String(" qTD chain:"); KernelLog.Ln;
IF SYSTEM.VAL(SET, firstQtd) * {0..4} # {} THEN
KernelLog.String(" qTD Pointer not 32byte aligned!"); KernelLog.Ln;
ELSE
chainlen := 0;
WHILE(SYSTEM.VAL(SET, firstQtd) * QhTerminate = {}) & (chainlen < MaxChainLen) DO
INC(chainlen);
ShowQtd(firstQtd, 8, cap64bit, FALSE); KernelLog.Ln;
dword := SYSTEM.VAL(SET, SYSTEM.GET32(firstQtd + QtdNextQtdPointer));
IF dword * {1..4} # {} THEN
KernelLog.String(" Alignment error!"); KernelLog.Ln;
chainlen := MaxChainLen;
ELSIF dword * QhTerminate # {} THEN
KernelLog.String(" End of Chain"); KernelLog.Ln;
chainlen := MaxChainLen;
ELSE
firstQtd := SYSTEM.VAL(LONGINT, dword * {5..31});
END;
END;
END;
END;
END;
END ShowQueueHead;
PROCEDURE ShowQtd(qtd, spaces : LONGINT; cap64bit, overlay : BOOLEAN);
VAR i, val : LONGINT; dword : SET;
BEGIN
IF Debug.Trace THEN
Indent(spaces);
KernelLog.String("qTD at "); KernelLog.Hex(qtd, 8); KernelLog.String(": ");
IF SYSTEM.VAL(SET, qtd) * {0..3} # {} THEN
KernelLog.String("Not 16byte aligned... aborting."); KernelLog.Ln; RETURN;
ELSIF qtd = 0 THEN
KernelLog.String("Address = 0?"); KernelLog.Ln; RETURN;
END;
KernelLog.Ln;
Indent(spaces+ 4);
KernelLog.String("qTD Token: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken));
KernelLog.String("Pid: ");
val := SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QtdPidCode, -8));
IF val = PidSetup THEN KernelLog.String("SETUP");
ELSIF val = PidIn THEN KernelLog.String("IN");
ELSIF val = PidOut THEN KernelLog.String("OUT");
ELSE KernelLog.String("PID ERROR!");
END;
KernelLog.String(", DataToggle: ");
IF dword * QtdDataToggle # {} THEN KernelLog.String("DATA1"); ELSE KernelLog.String("DATA0"); END;
KernelLog.String(", Bytes to transfer: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QtdBytesToTransfer, -16)), 0);
KernelLog.String(", IOC: "); IF dword * QtdIoc # {} THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.String(", CERR: "); KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QtdErrorCounter, -10)) ,0);
KernelLog.Ln;
Indent(spaces + 4);
KernelLog.String("qTD Token Status: ");
dword := dword * QtdStatus;
IF dword * TdActive # {} THEN KernelLog.String("[Active]"); END;
IF dword * TdHalted # {} THEN KernelLog.String("[Halted]"); END;
IF dword * TdDataBufferError # {} THEN KernelLog.String("[DataBufferError]"); END;
IF dword * TdBabbleDetected # {} THEN KernelLog.String("[BabbleDetected]"); END;
IF dword * TdTransactionError # {} THEN KernelLog.String("[TransactionError]"); END;
IF dword * TdMissedMicroFrame # {} THEN KernelLog.String("[MissedMicroFrame]"); END;
IF dword * TdSplitTransactionState # {} THEN KernelLog.String("[SplitTransactionState]"); END;
IF dword * TdPingState # {} THEN KernelLog.String("[PingState]"); END;
KernelLog.Ln;
Indent(spaces + 4); KernelLog.String("Buffer information:");KernelLog.Ln;
Indent(spaces + 8); KernelLog.String("Current Buffer: ");
KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(dword * QtdCurrentPage, -12)), 0);
IF SYSTEM.VAL(SET, qtd) * {4} # {} THEN
KernelLog.String(", Nak counter: ");
KernelLog.Int(SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdAltNextQtdPointer)) * {1..3}, -1)), 0);
END;
KernelLog.Ln;
FOR i := 0 TO 4 DO
val := SYSTEM.GET32(qtd + QtdBufferPtr0 + i*4);
Indent(spaces + 8);
KernelLog.String("Buffer Pointer "); KernelLog.Int(i, 0); KernelLog.String(": "); KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {12..31}), 8);
val := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, val) * {0..11});
IF i = 0 THEN
KernelLog.String(" Current Offset: "); KernelLog.Hex(val, 8);
ELSIF overlay & (i = 1) THEN
KernelLog.String(" C-prog-mask: "); KernelLog.Hex(val, 8);
ELSIF overlay & (i = 2) THEN
KernelLog.String(" S-Bytes / Frametag: "); KernelLog.Hex(val, 8);
END;
KernelLog.Ln;
END;
IF cap64bit THEN
FOR i := 0 TO 4 DO
val := SYSTEM.GET32(qtd + QtdExtBufferPtr0 + i*4);
Indent(spaces + 8);
KernelLog.String(" ExtBufferPointer"); KernelLog.Int(i, 0); KernelLog.String(": "); KernelLog.Hex(val, 8); KernelLog.Ln;
END;
END;
Indent(spaces + 4); KernelLog.String("Alternate Next qTD Pointer: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdAltNextQtdPointer));
IF dword * QhTerminate # {} THEN
KernelLog.String("Invalid ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
ELSIF dword * {1..3} # {} THEN
KernelLog.String("Alignment Error ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
ELSE
KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8);
END;
KernelLog.Ln;
Indent(spaces + 4);
KernelLog.String("Next qTD Pointer: ");
dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdNextQtdPointer));
IF dword * QhTerminate # {} THEN
KernelLog.String("Invalid ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
qtd := 0;
ELSIF dword * {1..3} # {} THEN
KernelLog.String("Alignment Error ("); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword), 8); KernelLog.String(")");
qtd := 0;
ELSE
KernelLog.Hex(SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, dword) * {5..31}), 8);
qtd := SYSTEM.VAL(LONGINT, dword * {5..31});
END;
KernelLog.Ln;
END;
END ShowQtd;
PROCEDURE ShowInterrupts(s : SET);
BEGIN
IF Debug.Trace THEN
IF s * StsAsyncAdvance # {} THEN KernelLog.String("[AsyncAdvance]"); END;
IF s * StsHostSystemError # {} THEN KernelLog.String("[HostSystemError]"); END;
IF s * StsFrameListRollover # {} THEN KernelLog.String("[FrameListRollover]"); END;
IF s * StsPortChange # {} THEN KernelLog.String("[PortChange]"); END;
IF s * StsUsbError # {} THEN KernelLog.String("[UsbError]"); END;
IF s * StsUsbInterrupt # {} THEN KernelLog.String("[UsbInterrupt]"); END;
END;
END ShowInterrupts;
PROCEDURE GetHcOwnerShip(bus, device, function: LONGINT; iobase : SYSTEM.ADDRESS) : BOOLEAN;
CONST
MaxWaits = 1000;
VAR
timer : Kernel.Timer;
usblegsup : SET;
eecp, reg, res, waits : LONGINT;
hcOwnedByOS : BOOLEAN;
BEGIN
reg := SYSTEM.GET32(iobase + HcCapCparams);
eecp := SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, reg) * {8..15}, - 8));
IF eecp > 40H THEN
IF Debug.Trace & Debug.traceInit THEN
KernelLog.String("UsbEhci: EHCI Extended Capabilities at offset "); KernelLog.Hex(eecp, -2); KernelLog.Char("H"); KernelLog.Ln;
END;
res := PCI.ReadConfigDword(bus, device, function, eecp, reg);
IF (res = PCI.Done) & (reg MOD 256 = USBCapabilityID) THEN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Legacy support capability found."); KernelLog.Ln; END;
usblegsup := SYSTEM.VAL(SET, reg);
IF usblegsup * bOwnedByBios # {} THEN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Request ownership of host controller... "); END;
IF usblegsup * bRequestOwnership # {} THEN
KernelLog.Enter; KernelLog.String("UsbEhci: Warning: controller already owns HC."); KernelLog.Exit;
res := PCI.WriteConfigByte(bus, device, function, eecp + 3, SYSTEM.VAL(LONGINT, SYSTEM.LSH(usblegsup - bRequestOwnership, -24)));
ASSERT(res = PCI.Done);
END;
res := PCI.WriteConfigByte(bus, device, function, eecp + 3, SYSTEM.VAL(LONGINT, SYSTEM.LSH(usblegsup + bRequestOwnership, -24)));
hcOwnedByOS := FALSE; waits := 0; NEW(timer);
WHILE ~hcOwnedByOS & (res = PCI.Done) & (waits < MaxWaits) DO
res := PCI.ReadConfigDword(bus, device, function, eecp, reg);
usblegsup := SYSTEM.VAL(SET, reg);
IF (usblegsup * bRequestOwnership # {}) & (usblegsup * bOwnedByBios = {}) THEN
hcOwnedByOS := TRUE;
ELSE
INC(waits, 1); timer.Sleep(1);
END;
END;
IF ~hcOwnedByOS THEN
KernelLog.Enter; KernelLog.String("UsbEhci: Pre-OS to system software handoff failed."); KernelLog.Exit;
IF OverrideHcOwnership THEN
KernelLog.Enter; KernelLog.String("UsbEhci: Override Pre-OS... take over HC!"); KernelLog.Exit;
RETURN TRUE;
ELSE
RETURN FALSE;
END;
END;
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("done."); KernelLog.Ln; END;
END;
ELSE
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbEhci: PCI error: Couldn't get USB Legacy Support register."); KernelLog.Ln; END;
RETURN FALSE;
END;
END;
RETURN TRUE;
END GetHcOwnerShip;
PROCEDURE ReleaseHcOwnerShip(bus, device, function, eecp : LONGINT);
VAR register, res : LONGINT; usblegsup : SET;
BEGIN
IF (eecp > 40H) THEN
res := PCI.ReadConfigDword(bus, device, function, eecp, register);
ASSERT(res = PCI.Done);
IF (register MOD 256 = USBCapabilityID) THEN
usblegsup := SYSTEM.VAL(SET, register);
IF usblegsup * bRequestOwnership # {} THEN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Release HC ownership semaphore... "); END;
res := PCI.WriteConfigByte(bus, device, function, eecp + 3, SYSTEM.VAL(LONGINT, SYSTEM.LSH(usblegsup - bRequestOwnership, -24)));
ASSERT(res = PCI.Done);
END;
END;
END;
END ReleaseHcOwnerShip;
PROCEDURE PCIFindEhci;
CONST
EhciClassCode = 0C0320H;
PCIStatusErrorMask = {24,27,28,29,30,31};
VAR
hostController : EnhancedHostController;
bus, device, function : LONGINT;
iobasePhys, irq : LONGINT;
iobaseVirt: SYSTEM.ADDRESS;
pciCmdStsReg, sbrn : LONGINT;
index : LONGINT;
res: LONGINT;
BEGIN
IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Looking for PCI Enhanced USB Host Controllers..."); KernelLog.Ln; END;
index := 0;
WHILE PCI.FindPCIClassCode(EhciClassCode, index, bus, device, function) = PCI.Done DO
res := PCI.ReadConfigDword(bus, device, function, PCI.CmdReg, pciCmdStsReg); ASSERT(res = PCI.Done);
IF SYSTEM.VAL(SET, pciCmdStsReg) * PCIStatusErrorMask # {} THEN
IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Warning: PCI device is in error ."); KernelLog.Ln; END;
END;
IF PCI.Enable(PCI.MemorySpace + PCI.BusMaster, bus, device, function) = PCI.Done THEN
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("UsbEhci: Error: Operational Register are not memory mapped"); KernelLog.Ln;
ELSIF SYSTEM.VAL(SET, iobasePhys) * {1,2,3} # {} THEN
KernelLog.String("UsbEhci: Error: Operational Register are not correctly mapped "); KernelLog.Ln;
ELSIF irq = 0 THEN
KernelLog.String("UsbEhci: Error: Please enable interrupts for all USB Host Controllers."); KernelLog.Ln;
ELSE
iobasePhys := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, iobasePhys) * {4..31});
res := PCI.ReadConfigDword(bus, device, function, 60H, sbrn); ASSERT(res = PCI.Done);
IF (sbrn MOD 256) # 20H THEN
KernelLog.Enter; KernelLog.String("UsbEhci: Error: Serial bus release number not supported."); KernelLog.Exit;
ELSE
Machine.MapPhysical(iobasePhys, 4096, iobaseVirt);
IF GetHcOwnerShip(bus, device, function, iobaseVirt) THEN
NEW(hostController, bus, device, function);
IF hostController.Init(Machine.Ensure32BitAddress (iobaseVirt), irq) THEN
IF Debug.Verbose THEN
KernelLog.Enter;
KernelLog.String("UsbEhci: Initialised USB Enhanced Host Controller at base 0");
KernelLog.Hex(iobasePhys, 8); KernelLog.String(", Irq: "); KernelLog.Int(irq, 0);
KernelLog.Exit;
END;
hostController.pwcr := SYSTEM.LSH(sbrn, -16);
UsbHcdi.RegisterHostController(hostController, Description);
ELSE
KernelLog.Enter;
KernelLog.String("UsbEhci: Cannot init USB Enhanced Host Controller at base 0");
KernelLog.Hex(iobasePhys, 8); KernelLog.String(", Irq: "); KernelLog.Int(irq, 0);
KernelLog.Exit;
END;
ELSE
KernelLog.Enter; KernelLog.String("UsbEhci: Couldn't get ownership of host controller."); KernelLog.Exit;
END;
END;
END;
ELSE
KernelLog.Enter; KernelLog.String("UsbEhci: Could not enable bus mastering or memory space access."); KernelLog.Exit;
END;
INC(index);
END;
END PCIFindEhci;
PROCEDURE Cleanup;
BEGIN
UsbHcdi.UnRegisterHostControllers(Description);
END Cleanup;
PROCEDURE Install*;
END Install;
BEGIN
Modules.InstallTermHandler(Cleanup);
PCIFindEhci;
END UsbEhci.
UsbEhci.Install ~ SystemTools.Free UsbEhci ~