12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213 |
- MODULE UsbEhci; (** AUTHOR "staubesv"; PURPOSE "USB Enhanced Host Controller Driver"; *)
- (**
- * Bluebottle USB Enhanced Host Controller Driver
- * Implements the UsbHcdi host controller driver interface (HCDI)
- *
- * Usage:
- *
- * This module provides the core implementation of the EHCI driver. Finding EHCI controllers was moved to a separate
- * module to increase flexibility. If your EHCI controller is accessible with PCI, the command:
- * UsbEhciPCI.Install ~
- * will install EHCI drivers as needed. To remove them, simply uninstall Ehci modules:
- * SystemTools.Free UsbEhciPCI UsbEhci ~
- *
- * In other cases, specific modules can be written, that will instanciate drivers as needed.
- *
- * References:
- * Enhanced Host Controller Interface Specification for Universal Serial Bus, Rev. 1.0
- *
- * Notes:
- * - 64bit control data structures: This driver doesn`t support real 64bit operation. If the host controller indicates 64bit capabilities, i.e. all
- * pointers used for control data structures as qTd, QHs and buffers are 64bit memory addresses, the upper 32bit of the address are just set to zero
- * to selected the 0..4GB segment.
- *
- * TODOs:
- * - FSTN save path pointers
- * - use sparse tree for more fine granular scheduling
- * - implement isochronous transfers
- * - DataToggle would not work for Control Transfers spanning multiple TDs
- *
- * History:
- *
- * 24.11.2005 First release (staubesv)
- * 15.12.2005 Moved buffer checks to UsbHcdi.Mod (staubesv)
- * 05.01.2006 Fixed EnhancedHostController.DeleteTranfer (staubesv)
- * 10.01.2006 H/W scatter/gather support implemented, fixed bug when sending > 16KB blocks (staubesv)
- * 16.01.2006 FlushPCI added (staubesv)
- * 08.01.2006 Added ScheduleOn, more safe TD unlinked/QH removal (staubesv)
- * 01.03.2006 Fixed critical bug in CreateTDList (staubesv)
- * 02.03.2006 Implemented port indicator control (staubesv)
- * 03.04.2006 Improved interrupt sharing (staubesv)
- * 05.04.2006 Fixed BIOS-to-OS handoff (staubesv)
- * 28.06.2006 Use KernelLog.Hex instead of UsbHcdi.PrintHex (staubesv)
- * 30.06.2006 Bugfix in InitFramelist: Also reset QH structure for isochronousQH, fixed bug when removing periodic QHs (staubesv)
- * 03.06.2006 UpdatePipe removed (staubesv)
- * 04.06.2006 Allow LinkTD to automatically clear halt condition (staubesv)
- * 20.07.2006 Release HC ownership when HC driver is shutdown, introduced OverrideHcOwnership constant (staubesv)
- * 03.08.2006 Adapted to UsbHcdi, fixed control transfer > 1TD (staubesv)
- * 13.11.2006 UpdatePipeStatus: Set pipe.status to Usbdi.Stalled when a stall is detected (staubesv)
- *)
- IMPORT SYSTEM, KernelLog, Machine, Kernel, Objects, Modules, UsbHcdi, Usbdi, Debug := UsbDebug;
- CONST
- Description * = "USB Enhanced Host Controller";
- (* Some configuration stuff *)
- HcInterruptThreshold = 01H; (* Maximum rate at which the host controller will issue interrupts (in microframes, 125 microseconds) *)
- HcFrameListSize = 1024;
- ScatterGatherListSize = 4200; (* Number of entries in scatter/gather list -> limits the maximum transfer size *)
- (* How many transaction of a single queue head in the asynchronous schedule list is the host controller allowed to execute
- within a micro-frame? Valid values: [0..3] (0 disables the feature). Will only be set if the HC supports the Asynchronous Schedule Park Mode. Higher values
- should result in higher performance. *)
- HcAsyncParkModeCount = 3;
- (* Host Controller Capability Registers *)
- HcCapLength = 00H;
- HcCapHciVersion = 02H;
- HcCapSparams = 04H;
- HcCapCparams * = 08H;
- HcCapPortroute = 0CH;
- (* Host Controller Operational Registers *)
- HcUsbCmd * = 00H;
- HcUsbSts * = 04H;
- HcUsbIntr * = 08H;
- HcFrIndex * = 0CH;
- HcCtrlDsSegment * = 10H;
- HcPeriodicListBase * = 14H;
- HcAsyncListAddr * = 18H;
- HcConfigFlag * = 40H;
- HcPortSc * = 44H;
- (* HcUsbCmd register fields *)
- CmdInterruptThreshold * = {16..23};
- CmdAsyncSchedParkMode * = {11};
- CmdAsyncSchedParkCount * = {8..9};
- CmdLightHcReset * = {7}; (* Note: optional *)
- CmdAsyncAdvDoorbell * = {6};
- CmdAsyncSchedEnable * = {5};
- CmdPeriodicSchedEnable * = {4};
- CmdFrameListSize * = {2 .. 3};
- CmdHcReset * = {1};
- CmdRunStop * = {0};
- CmdReserved * = {10} + {12..15} + {24..31};
- (* HcUsbSts register fields *)
- StsAsyncSchedule * = {15};
- StsPeriodicSchedule * = {14};
- StsReclamation * = {13};
- StsHcHalted * = {12};
- (* HcUsbSts & HcUsbIntr common fields *)
- StsAsyncAdvance * = {5};
- StsHostSystemError * = {4};
- StsFrameListRollover * = {3};
- StsPortChange * = {2};
- StsUsbError * = {1};
- StsUsbInterrupt * = {0};
- (* Port Status & Control register, EHCIspec p. 26-30 *)
- 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};
- (* Queue Element Transfer Descriptor; must be 32byte aligned *)
- (* Offsets *)
- 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;
- (* Masks *)
- QtdTerminate = {0};
- QtdBufferPtr = {12..31};
- (* qTD Token *)
- QtdDataToggle = {31};
- QtdBytesToTransfer = {16..30};
- QtdIoc = {15}; (* Interrupt on complete *)
- QtdCurrentPage = {12..14};
- QtdErrorCounter = {10..11};
- QtdPidCode = {8..9};
- QtdStatus = {0..7};
- (* Isochronous Transfer Descriptor *)
- ItdNextLinkPointer = 00H;
- ItdTransaction0 = 04H;
- ItdBufferPtr0 = 024H;
- ItdBufferPtr1 = 028H;
- ItdBufferPtr2 = 02CH;
- ItdExtBufferPtr0 = 40H;
- (* ITD Transaction *)
- ItdTransactionStatus = {28..31};
- ItdTransactionLength = {16..27};
- ItdTransactionIoc = {15};
- ItdTransactionPg = {12..14};
- ItdTransactionOffset = {0..11};
- (* ITD Buffer Pointers *)
- ItdBufferPtr = {12..31};
- (* ItdBufferPtr0 *)
- ItdEndPt = {8..11};
- ItdReserved = {7};
- ItdDevAdr = {0..6};
- (* ItdBufferPtr1 *)
- ItdIn = {11};
- ItdMaxPacketSize = {0..10};
- (* ItdBufferPtr2 *)
- ItdMult = {0..1};
- (* ITD Transaction Status *)
- ItdStatus = {28..31};
- ItdActive = {31};
- ItdDataBufferError = {30};
- ItdBabbleDetected = {29};
- ItdTransactionError = {28};
- (* Queue Head *)
- (* Offsets *)
- 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;
- (* Masks *)
- (* Queue Head Horizontal Link Pointer *)
- QhTyp = {1..2};
- QhTypItd = 0;
- QhTypQh = 1;
- QhTypSitd = 2;
- QhTypFstn = 3; (* Frame span traversal node *)
- QhTerminate = {0};
- (* Queue Head Endpoint Capabilities *)
- (* Dword 1 *)
- QhNakCountReload = {28..31};
- QhControlEndpointFlag = {27};
- QhMaxPacketLen = {16..26};
- QhHeadOfReclamation = {15};
- QhDataToggleControl = {14};
- QhEndpointSpeed = {12..13};
- QhEndpointNbr = {8..11};
- QhInactivate = {7};
- QhDeviceAddress = {0..6};
- (* Dword 2 *)
- QhMultiplier = {30..31}; (* High-Bandwidth Pipe Muliplier *)
- QhPortNbr = {23..29};
- QhHubAddr = {16..22};
- QhSplitCMask = {8..15};
- QhSMask = {0..7};
- (* Periodic Frame Span Traversal Node (FSTN) *)
- (* FSTN offsets *)
- FstnNormalPathLinkPointer = 0;
- FstnBackPathLinkPointer = 4;
- (* Status fields of qTD Token *)
- TdActive = {7}; (* If set, the HC will process the qTD *)
- TdHalted = {6}; (* Caused by babble, error counter transition from one to zero or STALL handshake. Will also clear TdActive. *)
- TdDataBufferError = {5}; (* Buffer overrun or underrun *)
- TdBabbleDetected = {4}; (* Babble. Will also set TdHalted *)
- TdTransactionError = {3}; (* No valid response from device during status update (Timeout, CRC errir, PID wrong...) *)
- TdMissedMicroFrame = {2};
- TdSplitTransactionState = {1};
- TdPingState = {0};
- (* Periodic Frame Span Traversal Node *)
- FstnNormalPathLink = 00H;
- FstnBackPathLink = 04H;
- (* Packet Identifier codes *)
- PidOut = 0;
- PidIn = 1;
- PidSetup = 2;
- PageSize = 4096;
- Polling = FALSE;
- TYPE
- EnhancedHostController * = OBJECT (UsbHcdi.Hcd)
- VAR
- framelist *: UsbHcdi.AlignedMemSpace;
- pwcr * : LONGINT; (* Port Wake Capability Register; Not implemented by device if pwcr = 0 *)
- (* Information from Host Controller Capability Registers *)
- (* HCSPARAMS - Structural Parameters *)
- capDebugPortNumber : LONGINT; (* 0: n/a, other: number of debug port (0-15)*)
- capPortIndicators : BOOLEAN; (* Do the ports support port indicator control? *)
- capNbrOfCompanionHc : LONGINT; (* How many companion host controllers are present (0-15) *)
- capPortsPerCompanion : LONGINT; (* Number of ports supported per companion host controller *)
- capPortRoutingRules : BOOLEAN; (* Port routing rules *)
- capPortPowerControl : BOOLEAN; (* Does the HC support Port Power Control? *)
- capNbrOfPorts : LONGINT; (* Number of physical downstream ports implemented by this host controller *)
- (* HCCPARAMS - Capability Parameters *)
- capIsoSchedThreshold : LONGINT; (* Isochronous Schedule Threshold *)
- capAsynchSchedPark : BOOLEAN; (* Does the controller support the park feature for high-speed transfers? *)
- capProgrammableFLG : BOOLEAN; (* FALSE: use default (1024); TRUE: Frame List size is programmable *)
- cap64bit : BOOLEAN; (* 32 / 64 bit memory pointers in the data structures *)
- (* EHCI Extended Capabilities Pointer. Used in relation with USB legacy support *)
- eecp : LONGINT;
- (* The size of control data structures is dependent on whether the HC uses 32bit or 64bit address pointers as indicated
- by the cap64bit field *)
- sizeQtd, alignQtd : LONGINT;
- sizeQh, alignQh : LONGINT;
- (* HC Companion Port Route Descriptor, NIL of not available.
- If the capPortRoutingRules is TRUE, the HC provides a description of which port is routed to
- which companion HC. *)
- hcportroute : POINTER TO ARRAY OF LONGINT;
- (* queue heads *)
- isochronousQh* : LONGINT;
- interruptQh : POINTER TO ARRAY 11 OF LONGINT;
- (* this array will provide the 16byte aligned TD's for controlTD, bulkTD, isochronousTD and interruptTD[] *)
- qhlist : UsbHcdi.AlignedMemSpace;
- (* The Asynchronous Advance Doorbell interrupt is always enabled by this driver. Since the interrupt handler will
- clear the bit that were set when it was invoked, it sets hcHandshake to TRUE, so its sticky *)
- hcHandshake * : BOOLEAN;
- (* Set of all currently enabled interrupts *)
- interruptsEnabled * : SET;
- (** Enable power for the specified port *)
- 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;
- (** Disable power for the specified port *)
- 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;
- (** Enable the specified port.
- The EHCI host controllers do not explicitly support a port enable command. The port will be automatically enabled
- by the host controller after a port reset, if a high-speed capable device is attached to it *)
- 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); (* >= 10ms, USBspec *)
- SYSTEM.PUT32(ports[port], status - PscChangeMask - PscPortReset); FlushPCI;
- Wait(2+1); (* 2ms recovery interval according EHCIspec, p. 28 *)
- (* The host controller should have automatically enabled this port *)
- 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;
- (** Disable the specified port. *)
- 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;
- (** Suspend the specified port (selective suspend). *)
- 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;
- (** Resume a selectively suspended port. *)
- PROCEDURE ResumePort*(port : LONGINT) : BOOLEAN;
- VAR status : SET; timer : Kernel.Timer;
- BEGIN
- status := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- IF status * CmdRunStop = {} THEN
- (* HC must be running when resume a port. Otherwise, the device would automatically re-renter
- the suspended mode in 10 ms *)
- 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); (* EHCI p. 60 *)
- SYSTEM.PUT32(ports[port], status - PscChangeMask - PscForcePortResume);
- FlushPCI;
- END;
- RETURN SYSTEM.VAL(SET, SYSTEM.GET32(ports[port])) * PscSuspend = {};
- (* TODO: write 1 to PORTSC Force resume bit if port is suspended; first wait 10ms (EHCIp59)*)
- END ResumePort;
- (** Suspend all ports and then stop the host controller. *)
- PROCEDURE Suspend*;
- VAR dword : SET; i : LONGINT; ignore : BOOLEAN;
- BEGIN
- (* Suspend all individual ports *)
- FOR i := 0 TO portCount - 1 DO ignore := SuspendPort(i); END;
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd)); (* Stop HC *)
- SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop);
- FlushPCI;
- (* Put HC in lower device state via the PCI power management interface *)
- END Suspend;
- (** Restart the host controller and selectively resume all suspended ports. *)
- PROCEDURE Resume*() : BOOLEAN;
- VAR dword : SET; i : LONGINT; res : BOOLEAN;
- BEGIN
- (* Re-start the HC *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdRunStop);
- FlushPCI;
- (* Resume all individual ports *)
- res := TRUE;
- FOR i := 0 TO portCount - 1 DO
- IF ~ResumePort(i) THEN res := FALSE; END;
- END;
- RETURN res;
- END Resume;
- (**
- * Get the status of the specified port.
- * Registers which indicate status changes are reset by GetPortStatus.
- * Note: UsbHcdi.HighSpeed will only be correctly set when the port is enabled. The hub driver
- * takes care of this special behaviour by getting the port status again after it has enabled the port.
- * @param port Port to get the status of
- * @return Port status
- *)
- PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN) : SET;
- VAR status, s : SET;
- BEGIN
- s := SYSTEM.VAL(SET, SYSTEM.GET32(ports[port]));
- (* Clear all bits that reported a change event; the correspondig register are R/WC *)
- 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;
- (* When a device is attached to a port of the root hub, the hub driver will try to reset and enable the port.
- The EHCI HC only enables the port if the connected device is a high-speed device which is determined during
- the reset. So if a device is attached to the port, the port is not in reset and it's enabled, it is a high-speed device *)
- IF (s * PscPortEnable = {}) & (s * PscCurrentConnectStatus # {}) & (s * PscPortPower # {}) & (s * {10} # {}) THEN (* Lowspeed device connected *)
- status := status + UsbHcdi.PortStatusLowSpeed;
- ELSIF (s * PscCurrentConnectStatus # {}) & (s * PscPortReset = {}) & (s * PscPortEnable # {}) THEN
- status := status + UsbHcdi.PortStatusHighSpeed;
- END;
- RETURN status;
- END GetPortStatus;
- (** Route the specified port to a companion host controller if supported. *)
- PROCEDURE RoutePortToCompanion*(port : LONGINT);
- VAR dword : SET;
- BEGIN
- (* Assert ports are not globally routed to companion controllers *)
- 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;
- (** Indicate a port state using the port indicators *)
- 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;
- (** Return the current frame number.
- The micro-frame number is incremented each micro-frame, i.e. per 125us. There are 8 micro-frames per frame *)
- PROCEDURE GetFrameNumber*() : INTEGER;
- BEGIN
- RETURN SYSTEM.VAL(INTEGER, SYSTEM.VAL(SET, LSH(SYSTEM.GET32(iobase + HcFrIndex), -3)) * {0..10});
- END GetFrameNumber;
- (*
- * Contruct the queue head of the specified pipe.
- * Fill in the following DWORDs into pipe.qh:
- * - Queue Head Endpoint Capabilities 1
- * - Queue Head Endpoint Capabilities 2
- * - Current qTD Pointer
- * - Next qTD Pointer, Alternate Next qTD Pointer, qTD Token & all five qTD Buffer Pointers
- * The Queue Head Horizontal Link Pointer will be set by InsertPipeQH
- * @param pipe
- *)
- PROCEDURE BuildQueueHead(pipe : UsbHcdi.Pipe);
- VAR dword : SET; nakRL, multi, mmask : LONGINT;
- BEGIN
- ASSERT(pipe.maxPacketSize <= 1024); (* Maximum allowed packet size *)
- (*
- pipe.qh := Align(pipe.qh, alignQh);
- pipe.tdBase := pipe.qh + alignQh;
- *)
- (*
- IF ~CheckBoundary(pipe.qh, sizeQh) THEN
- INC(pipe.qh, sizeQh);
- INC(pipe.tdBase, sizeQh)
- END;
- *)
-
- (* Queue Head Horizontal Link Pointer is not set here *)
- (* Queue Head Endpoint Capabilities 1 *)
- nakRL := 3;
- IF pipe.type = UsbHcdi.PipeInterrupt THEN nakRL := 0; END; (* EHCIspec, p.83 *)
- (* IF (pipe.speed = UsbHcdi.HighSpeed) & ((pipe.type = UsbHcdi.PipeBulk) OR
- (pipe.type = UsbHcdi.PipeControl)) (* Control OUT only *) THEN
- nakRL := pipe.irqInterval;
- END; *)
- dword := 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 + LSH(SYSTEM.VAL(SET, pipe.maxPacketSize), 16) * QhMaxPacketLen;
- IF (pipe.speed = UsbHcdi.LowSpeed) THEN (* EPS - endpoint speed *)
- dword := dword + {12}; (* Low-speed endpoint *)
- ELSIF (pipe.speed = UsbHcdi.FullSpeed) THEN
- (* Do nothing; Full-speed endpoint *)
- ELSIF (pipe.speed = UsbHcdi.HighSpeed) THEN
- dword := dword + {13}; (* High-speed endpoint *)
- ELSE
- HALT(99);
- END;
- dword := dword + LSH(SYSTEM.VAL(SET, pipe.endpoint), 8) * QhEndpointNbr;
- dword := dword + SYSTEM.VAL(SET, pipe.address) * QhDeviceAddress;
- SYSTEM.PUT32(pipe.qh + QhEpCapabilities1, dword);
- (* Queue Head Endpoint Capabilities 2 *)
- multi := 1; (* TODO: How many transactions per frame for high-speed isochronous and interrupts transfer are allowed? *)
- dword := 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));
- (* Hub port and address for split transaction *)
- dword := dword + LSH(SYSTEM.VAL(SET, pipe.ttAddress), 16) * QhHubAddr;
- dword := dword + LSH(SYSTEM.VAL(SET, pipe.ttPort + 1), 23) * QhPortNbr;
- IF (pipe.type = UsbHcdi.PipeInterrupt) OR (pipe.type = UsbHcdi.PipeIsochronous) THEN
- (* In which micro-frames the HC should issue Complete Split tokens *)
- dword := dword + 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);
- (* Zero-out the queue head transfer overlay *)
- 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;
- (** Build a Queue Head for the specified pipe and insert it into the host controller schedule. *)
- PROCEDURE InsertQH*(pipe : UsbHcdi.Pipe) : BOOLEAN;
- VAR adr, asyncListAddr : LONGINT; dword : SET;
- BEGIN (* Only call from exclusive regions *)
- ASSERT((pipe # NIL) & (pipe.qh # 0) & (SYSTEM.VAL(SET, pipe.qh) * {0..4} = {}));
- ASSERT((pipe.maxPacketSize > 0));
- CASE pipe.type OF (* In which queue should we insert the pipe ? *)
- |UsbHcdi.PipeControl : pipe.queue := 0;
- | UsbHcdi.PipeBulk : pipe.queue := 0;
- | UsbHcdi.PipeIsochronous :
- (* TODO: Implement isochronous transfers *)
- (* Enable the periodic list if necessary *)
- 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 (* 1ms queue *)
- pipe.queue := interruptQh[0];
- ELSIF pipe.irqInterval < 4 THEN (* 2ms queue *)
- pipe.queue := interruptQh[1];
- ELSIF pipe.irqInterval < 8 THEN (* 4ms queue *)
- pipe.queue := interruptQh[2];
- ELSIF pipe.irqInterval < 16 THEN (* 8ms queue *)
- pipe.queue := interruptQh[3];
- ELSIF pipe.irqInterval < 32 THEN (* 16ms queue *)
- pipe.queue := interruptQh[4];
- ELSE
- pipe.queue := interruptQh[5]; (* 32 ms queue *)
- END;
- END;
- ELSE
- RETURN FALSE;
- END;
- BuildQueueHead(pipe);
- IF pipe.queue = 0 THEN (* Insert into the asynchronous schedule list *)
- asyncListAddr := SYSTEM.GET32(iobase + HcAsyncListAddr);
- IF asyncListAddr = 0 THEN (* Not queue heads in the list yet *)
- (* Since the address is obviously invalid, the asynchronous schedule mustn't be enabled *)
- ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * StsAsyncSchedule = {});
- SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, SYSTEM.VAL(SET, pipe.qh) * {5..31} + {1} - {2} - QhTerminate);
- (* If the asynchronous schedule is enabled, exactly one queue head MUST have the H-bit set. *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhEpCapabilities1)) + QhHeadOfReclamation;
- SYSTEM.PUT32(pipe.qh + QhEpCapabilities1, dword);
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- (* Insert the queue head into the schedule list and activate the asynchronous schedule *)
- 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} = {}); (* 32byte alignment *)
- adr := asyncListAddr;
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + QhHorizontalLinkPointer));
- SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, dword);
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- (* Insert the newly created queue head into the asynchronous schedule list. *)
- dword := SYSTEM.VAL(SET, pipe.qh) * {5..31} + {1} - {2} - QhTerminate;
- SYSTEM.PUT32(adr + QhHorizontalLinkPointer, dword);
- END;
- ELSE (* Insert into the periodic schedule list *)
- adr := SYSTEM.GET32(pipe.queue + QhHorizontalLinkPointer);
- SYSTEM.PUT32(pipe.qh + QhHorizontalLinkPointer, adr);
- SYSTEM.PUT32(pipe.queue + QhHorizontalLinkPointer, pipe.qh + SYSTEM.VAL(LONGINT, {1} - {2}));
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- Machine.FlushDCacheRange(pipe.queue, sizeQh);
- (* Enable the periodic list if necessary *)
- 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.Address(pipe.qh); KernelLog.Ln; END;
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- RETURN TRUE;
- END InsertQH;
- (* Enable/Disable the periodic or asynchronous schedule. *)
- PROCEDURE ScheduleOn(cmd : SET; on : BOOLEAN) : BOOLEAN;
- VAR dword, sts : SET; mtimer : Kernel.MilliTimer;
- BEGIN (* Caller must hold obj lock *)
- 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 = LSH(SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts)) * sts, -10)); (* HcUsbCmd & HcUsbSts in consistent state *)
- IF on THEN dword := dword + cmd; ELSE dword := dword - cmd; END;
- SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
- (* Wait until the HC reaches the desired state *)
- 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;
- (*
- * Remove a queue head data structure from the host controller's asynchronous schedule.
- * The asynchronous schedule is a circular linked list of queue heads. At least one queue heads has
- * the H-bit (Head of asynchronous schedule list) set which is used by the host controller to detect
- * empty list conditions. There are two cases when we remove a queue head:
- * 1) It is the only queue head in the list. In this case, we disabled the asynchronous schedule execution and
- * and remove the queue head then.
- * 2) There are other queue heads in the list. If the queue head to be removed is the head of the list, we
- * need to set the H-bit for another queue head.
- *
- * Precondition: TDs are already removed from the QH, QH is inactive
- *)
- PROCEDURE RemoveAsyncQH(pipe : UsbHcdi.Pipe);
- VAR start, cur, prev : LONGINT; dword : SET;
- BEGIN (* Caller must hold obj lock *)
- prev := SYSTEM.GET32(iobase + HcAsyncListAddr);
- ASSERT((prev # 0) & (SYSTEM.VAL(SET, prev) * {0..4} = {}));
- Machine.InvalidateDCacheRange(prev, sizeQh);
- prev := SYSTEM.GET32(prev + QhHorizontalLinkPointer);
- ASSERT((SYSTEM.VAL(SET, prev) * {1} # {}) & (SYSTEM.VAL(SET, prev) * QhTerminate = {})); (* Pointer references queue head *)
- prev := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, prev) * {5..31});
- Machine.InvalidateDCacheRange(prev, sizeQh);
- cur := SYSTEM.GET32(prev + QhHorizontalLinkPointer);
- ASSERT((SYSTEM.VAL(SET, cur) * {1} # {}) & (SYSTEM.VAL(SET, cur) * QhTerminate = {})); (* Pointer references queue head *)
- cur := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, cur) * {5..31});
- Machine.InvalidateDCacheRange(cur, sizeQh);
- (* prev is the address of the queue head that points to the queue head with the address cur *)
- IF cur = prev THEN (* Only one queue head in the list *)
- ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhEpCapabilities1)) * QhHeadOfReclamation # {});
- ASSERT(SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhQtdToken)) * TdActive = {});
- IF cur = pipe.qh THEN (* just disable asynchronous schedule *)
- IF ScheduleOn(CmdAsyncSchedEnable, FALSE) THEN
- SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); FlushPCI; (* Mark as invalid. *)
- 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 (* Find and remove the queue head in the list *)
- (* Search the queue head that references the queue head to be removed *)
- start := cur;
- LOOP
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
- ASSERT(dword * QhTerminate = {}); (* Circular lists don't terminate *)
- ASSERT(dword * {1} # {}); (* Pointer references queue head *)
- ASSERT(dword * {2..4} = {}); (* qTD pointers must be 32byte aligned *)
- prev := cur;
- cur := SYSTEM.VAL(LONGINT, dword * {5..31});
- IF cur = pipe.qh THEN (* QH found *) EXIT; END;
- IF cur = start THEN (* list completely searched but QH not found *) EXIT; END;
- Machine.InvalidateDCacheRange(cur, sizeQh);
- END;
- IF cur = pipe.qh THEN (* Found the queue head. prev is pointing to it *)
- (* If we remove the head of reclamation, elect a new one *)
- 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;
- (* Remove QH from asynchronous list and inforam host controller *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
- SYSTEM.PUT32(prev + QhHorizontalLinkPointer, dword);
- Machine.FlushDCacheRange(prev, sizeQh);
- ELSIF Debug.Level >= Debug.Warnings THEN Show("Failed to remove QH from asynchronous list: QH not found."); KernelLog.Ln;
- END;
- (* Before we may free the pipe ressources, we have to make sure that the HC has no cached references to the structure *)
- (* we just removed. *)
- 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;
- (*
- * Inform the host controller that we removed something from the asynchronous schedule list. This is
- * necessary since the HC could have cached a copy of the pointer to the queue head we've just removed.
- *)
- PROCEDURE HcHandshake() : BOOLEAN;
- VAR dword : SET; mtimer : Kernel.MilliTimer; result : BOOLEAN;
- BEGIN (* caller holds object lock *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbSts));
- ASSERT(dword * StsAsyncSchedule # {}); (* HC behaviour undefined if ringing doorbell while async schedule is off *)
- 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 = {}; (* The HC should have cleared the bit *)
- 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 (* caller must hold obj lock *)
- IF pipe.queue = 0 THEN RETURN; END;
- cur := pipe.queue;
- LOOP
- Machine.InvalidateDCacheRange(cur, sizeQh);
- next := SYSTEM.VAL(SET, SYSTEM.GET32(cur + QhHorizontalLinkPointer));
- IF next * {5..31} = SYSTEM.VAL(SET, pipe.qh) * {5..31} THEN (* found *)
- temp := SYSTEM.GET32(pipe.qh + QhHorizontalLinkPointer);
- SYSTEM.PUT32(cur + QhHorizontalLinkPointer, temp);
- Machine.FlushDCacheRange(cur, sizeQh);
- IF Debug.Trace & Debug.traceQueuing THEN KernelLog.String("UsbEhci: Deleted Interrupt Pipe QH."); KernelLog.Ln; END;
- NEW(timer); timer.Sleep(10); (* HC has still access to QH, wait > 1ms *)
- EXIT;
- ELSIF next * QhTerminate # {} THEN (* not found, reached end of list *)
- 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;
- (** Remove the pipe's queue head from the host controller schedule *)
- PROCEDURE RemoveQH*(pipe : UsbHcdi.Pipe);
- BEGIN (* caller must hold obj lock *)
- IF Debug.Trace & Debug.traceQueuing THEN Show("Removing QH at "); KernelLog.Hex(pipe.qh, 8); KernelLog.Ln; END;
- (* First remove all transfer descriptors from the queue head *)
- UnlinkTDsInternal(pipe);
- (* Then remove the pipe's queue head from the host controller schedule *)
- IF (pipe.type = UsbHcdi.PipeControl) OR (pipe.type = UsbHcdi.PipeBulk) THEN
- RemoveAsyncQH(pipe);
- ELSIF pipe.type = UsbHcdi.PipeInterrupt THEN
- RemovePeriodicQH(pipe);
- ELSE
- (* TODO: Isochronous transfers not yet implemented *)
- END;
- END RemoveQH;
- (** Checks whether TDs may be linked to the pipe's QH *)
- 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;
- (* Insert the TD list <td> into the queue (ED) <queue> *)
- PROCEDURE LinkTDs*(pipe : UsbHcdi.Pipe; qtd : Machine.Address32);
- VAR dword : SET;
- BEGIN {EXCLUSIVE}
- ASSERT(SYSTEM.VAL(SET, qtd) * {0..4} = {}); (* 32byte alignment *)
- (* Pipe must be inactive... *)
- 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);
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- Machine.FlushDCacheRange(qtd, sizeQtd);
- 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;
- (** Remove all transfer descriptors from the pipe's queue head *)
- PROCEDURE UnlinkTDs*(pipe : UsbHcdi.Pipe);
- BEGIN {EXCLUSIVE}
- UnlinkTDsInternal(pipe);
- END UnlinkTDs;
- (** Remove all transfer descriptors from the pipe's queue head *)
- PROCEDURE UnlinkTDsInternal(pipe : UsbHcdi.Pipe);
- VAR dword : SET; timer : Kernel.Timer; qtd : LONGINT; mtimer : Kernel.MilliTimer;
- BEGIN (* caller must hold obj lock *)
- IF pipe.firstTD = 0 THEN RETURN END; (* pipe has not yet been used *)
- (* We must inactivate all qTD of the queue head... *)
- qtd := pipe.firstTD; ASSERT(pipe.lastTD >= pipe.firstTD); (* Consistency check *)
- AssertAlignment(qtd, alignQtd);
- WHILE qtd <= pipe.lastTD DO
- Machine.InvalidateDCacheRange(qtd, sizeQtd);
- AssertAlignment(qtd, alignQtd);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken));
- SYSTEM.PUT32(qtd + QtdToken, dword - TdActive);
- qtd := qtd + sizeQtd;
- END;
- (* we should wait until the transaction overlay is also inactive *)
- Kernel.SetTimer(mtimer, 2000);
- Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
- WHILE ~Kernel.Expired(mtimer) & (dword * TdActive # {}) DO
- Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
- 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); (* > 1ms - the HC could update the QhQtdToken field *)
- SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- timer.Sleep(2); (* > 1ms *)
- 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;
- (* RETURN; *)
- END;
- SYSTEM.PUT32(pipe.qh + QhNextQtdPointer, QhTerminate);
- SYSTEM.PUT32(pipe.qh + QhAltNextQtdPointer, QhTerminate);
- SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0); (* Bits 0-4 are reserved *)
- SYSTEM.PUT32(pipe.qh + QhQtdToken, 0);
- Machine.FlushDCacheRange(pipe.qh, sizeQh);
- pipe.firstTD := 0; pipe.lastTD := 0;
- END UnlinkTDsInternal;
- (*
- * Clears the Halt bit in the pipe's queue head and removes any qTD from the pipe.
- * Note that this only makes sense if the Halt feature of the USB device is also cleared used the ClearFeature standard
- * request. This procedure here only changes the pipe's queue head.
- *)
- PROCEDURE ClearHalt(pipe : UsbHcdi.Pipe);
- VAR dword : SET;
- BEGIN
- Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
- IF dword * TdHalted # {} THEN
- SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
- (* Zero-out the queue head transfer overlay *)
- 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;
- Machine.FlushDCacheRange(pipe.qh, sizeQh)
- ELSIF Debug.Level >= Debug.Warnings THEN Show("Tried to clear a non-halted pipe."); KernelLog.Ln;
- END;
- END ClearHalt;
- (**
- * Put the specified control transfer into the host controller's schedule.
- * USB Control Transfers use a three stage protocol:
- * - stage 1: control setup transaction
- * - stage 2: optional data stage
- * - stage 3: status transaction
- * For high-speed devices, the PING protocol must be used for OUT transactions in the data stage and status stage.
- *
- *
- * @param pipe
- * @param direction Direction of the control transfer (UsbHcdi.In (device-to-host) | UsbHcdi.Out (host-to-device))
- * @param msg Control message
- * @param bufferlen Number of bytes transmitted/received in the data stage
- * @param buffer Buffer where to get/put the specified number of bytes
- *)
- PROCEDURE ScheduleControl*(pipe : UsbHcdi.Pipe; direction : LONGINT; msg : UsbHcdi.ControlMessage; bufferLen : LONGINT; VAR buffer : Usbdi.Buffer);
- VAR
- qtd : Machine.Address32;
- dword : SET;
- ranges : ARRAY ScatterGatherListSize OF Machine.Range;
- numRanges : LONGINT;
- BEGIN
- (*Machine.FlushDCacheRange(ADDRESSOF(buffer[0]), bufferLen);*)
- (* pipe.tdBase = pipe.qh + 32 in UsbHcdi *)
- (* pipe.firstTD := pipe.tdBase;
- ASSERT(pipe.firstTD = pipe.qh + alignQh);
- *)
- pipe.firstTD := pipe.tdBase - 32 + alignQh;
- AssertAlignment(pipe.firstTD, alignQtd);
- ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {}); (* qTDs must be 32byte aligned *)
- ASSERT(pipe.firstTD MOD alignQtd = 0);
- IF (pipe.speed = UsbHcdi.LowSpeed) OR (pipe.speed = UsbHcdi.FullSpeed) THEN
- IF pipe.maxRetries = 0 THEN
- (* For low-speed and full-speed devices, the value 0 is not allowed *)
- pipe.maxRetries := 3;
- END;
- END;
- (* Stage1: Control setup transaction *)
- qtd := pipe.firstTD;
- ASSERT((qtd + sizeQtd - 1 <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1])));
- AssertAlignment(qtd+sizeQtd, alignQtd);
- SYSTEM.PUT32(qtd + QtdNextQtdPointer, qtd + sizeQtd);
- SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
- dword := LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* DataToggle = FALSE; Current Page = 0; no IOC *)
- dword := dword + LSH(SYSTEM.VAL(SET, 8), 16) * QtdBytesToTransfer; (* 8byte control message *)
- dword := dword + LSH(SYSTEM.VAL(SET, PidSetup), 8) * QtdPidCode + TdActive;
- SYSTEM.PUT32(qtd + QtdToken, dword);
- Machine.TranslateVirtual(ADDRESSOF(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;
- (* The HC will access the next buffer pointer when the buffer crosses a physical page... *)
- SYSTEM.PUT32(qtd + QtdBufferPtr0, ranges[0].adr);
- IF numRanges > 1 THEN (* buffer is across page boundaries *)
- 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;
- Machine.FlushDCacheRange(qtd, sizeQtd);
- (* Setup phase always starts with dataToggle = FALSE, so now it must be TRUE *)
- pipe.dataToggle := TRUE;
- (* Stage 2: Optional data stage *)
- 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;
- Machine.FlushDCacheRange(qtd, sizeQtd);
- qtd := qtd + sizeQtd;
- AssertAlignment(qtd, alignQtd);
- IF qtd + sizeQtd - 1 > ADDRESSOF(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;
- (* stage 3: status: build status TD *)
- SYSTEM.PUT32(qtd + QtdNextQtdPointer, QtdTerminate); (* Last qTD in chain *)
- SYSTEM.PUT32(qtd + QtdAltNextQtdPointer, QtdTerminate); (* Mark Alternate Next qTD Pointer as invalid *)
- dword := QtdDataToggle + TdActive; (* dataToggle always TRUE and set ind TD in status stage; CC = not accessed *)
- IF (direction = UsbHcdi.Out) OR (bufferLen = 0) THEN
- dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
- ELSE
- dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
- IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
- dword := dword + TdPingState;
- END;
- END;
- dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter;
- IF pipe.ioc THEN dword := dword + QtdIoc; END; (* Set interrupt on completion bit *)
- 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;
- Machine.FlushDCacheRange(ADDRESSOF(msg[0]), LEN(msg));
- Machine.FlushDCacheRange(ADDRESSOF(buffer[0]), bufferLen);
- Machine.FlushDCacheRange(qtd, sizeQtd);
- pipe.lastTD := qtd;
- END ScheduleControl;
- PROCEDURE Schedule*(pipe : UsbHcdi.Pipe; bufferLen, offset: LONGINT; VAR buffer: Usbdi.Buffer);
- VAR dword : SET;
- BEGIN
- Machine.FlushDCacheRange(ADDRESSOF(buffer[offset]), bufferLen);
- SYSTEM.PUT32(pipe.qh + QhCurrentQtdPointer, 0);
- (* pipe.firstTD := pipe.tdBase;
- ASSERT(pipe.firstTD = pipe.qh + alignQh);
- *)
- pipe.firstTD := pipe.tdBase - 32 + alignQh;
-
- AssertAlignment(pipe.firstTD, alignQtd);
- ASSERT(pipe.firstTD MOD alignQtd = 0);
- ASSERT(SYSTEM.VAL(SET, pipe.firstTD) * {0..4} = {}); (* qTDs must be 32byte aligned *)
- 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 : Machine.Address32; tdToggle : BOOLEAN) : BOOLEAN;
- VAR
- restlen, curlen, temp : LONGINT;
- j, qtd, nextTd : LONGINT;
- dword : SET;
- numRanges, idx, offset : LONGINT;
- BEGIN
- ASSERT((pipe.maxRetries >= 0) & (pipe.maxRetries <= 3));
- Machine.TranslateVirtual(ADDRESSOF(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; (* offset from last qTD (must fill multiples of packetSize into qTD buffers) *)
- curlen := 0; (* amount of data that is transferred in a single qTD *)
- restlen := len; (* total amount of data to be transferred *)
- WHILE restlen > 0 DO (* build qTD chain *)
-
- qtd := qtd + sizeQtd;
- IF qtd + sizeQtd - 1 > ADDRESSOF(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;
- (* Each qTD has for buffer pointers. Each buffer is 4K. The buffer must be virtually contiguous but may be *)
- (* physically non-contiguous. The HC detects crossings of page boundaries and increments the current buffer pointer. *)
- SYSTEM.PUT32(qtd + QtdBufferPtr0, pipe.sgList[idx].adr + offset);
- curlen := PageSize - LONGINT ((pipe.sgList[idx].adr + offset) MOD PageSize);
- IF curlen > restlen THEN (* No other buffer pointers needed, fits into the first page *)
- curlen := restlen;
- END;
- ASSERT(curlen > 0);
- restlen := restlen - curlen; offset := 0;
- INC(idx);
- (* Fill in the other 4 buffer pointers *)
- FOR j := 1 TO 4 DO
- IF restlen <= 0 THEN
- SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*4, 0);
- ELSE
- IF j = 4 THEN (* last buffer available in this qTD *)
- temp := PageSize - ((curlen + PageSize) MOD pipe.maxPacketSize); (* data that fits into the last buffer (max) *)
- IF restlen > temp THEN
- (* The HC will issues USB transaction at pipe.maxPacketSize granularity. If this is not the *)
- (* last qTD of this qTD chain, curlen must be multiple of pipe.maxPacketSize. If the last qTD of this *)
- (* chain was not a multiple of pipe.maxPacketSize, the device will send more data (since we *)
- (* requested more data) and the HC thinks it's a babble. *)
- curlen := curlen + temp; restlen := restlen - temp; offset := temp;
- SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*4, pipe.sgList[idx].adr);
- IF offset = PageSize THEN INC(idx); offset := 0;
- ELSE (* In the next iteration of the outer while loop, the same page will be completed -> don't increment idx *)
- END;
- ELSE (* this is the last qTD in chains *)
- curlen := curlen + restlen; restlen := 0;
- SYSTEM.PUT32(qtd + QtdBufferPtr0 + j*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 + j*4, pipe.sgList[idx].adr);
- INC(idx);
- END;
- END;
- 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); (* Mark Alternate Next qTD Pointer as invalid *)
- ASSERT(curlen <= 5000H); (* Maximum allowed value for a single qTD: 5*4KB *)
- dword := TdActive;
- dword := dword + LSH(SYSTEM.VAL(SET, pipe.maxRetries), 10) * QtdErrorCounter; (* Current Page=0 *)
- dword := dword + LSH(SYSTEM.VAL(SET, curlen), 16) * QtdBytesToTransfer;
- IF tdToggle THEN
- IF pipe.dataToggle THEN dword := dword + QtdDataToggle; END;
- (* Calculate datatoggle value for next TD *)
- IF (curlen DIV pipe.maxPacketSize) MOD 2 # 0 THEN
- pipe.dataToggle := ~pipe.dataToggle;
- END;
- END;
- IF direction = UsbHcdi.In THEN
- dword := dword + LSH(SYSTEM.VAL(SET, PidIn), 8);
- ELSIF direction = UsbHcdi.Out THEN
- dword := dword + LSH(SYSTEM.VAL(SET, PidOut), 8);
- IF pipe.speed = UsbHcdi.HighSpeed THEN (* Do PING protocol *)
- dword := dword + TdPingState;
- END;
- END;
- SYSTEM.PUT32(qtd + QtdToken, dword);
- Machine.FlushDCacheRange(qtd, sizeQtd);
- END;
- lastTD := qtd;
- RETURN TRUE;
- END CreateTDList;
- PROCEDURE InterruptHandler;
- VAR s : SET;
- BEGIN (* Works without being exclusive *)
- 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;
- (* Reset interrupt status register (Write clear)*)
- 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
- (* TODO: If wake-up, time 20ms, poll PscSuspend... enable HC if necessary*)
- IF statusChangeHandler # NIL THEN statusChangeHandler(Usbdi.Ok, 0); END;
- END;
- IF s * (StsUsbError + StsUsbInterrupt) # {} THEN (* USB Interrupt occured: can be IOC or ShortPacketInt *)
- NotifyCompletionHandlers;
- END;
- END;
- END;
- END InterruptHandler;
- (* re-evaluate the status of the pipe's qh (endpoint descriptor) and its TD list *)
- PROCEDURE UpdatePipeStatus*(pipe : UsbHcdi.Pipe);
- CONST MaxLoops = 10000;
- VAR
- qtd : LONGINT;
- s, errors : SET;
- restLen, len : LONGINT;
- loop : LONGINT;
- BEGIN
- FlushPCI;
- (* First look up active bit in the QH tranfer overlay *)
- Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
- s := SYSTEM.VAL(SET, SYSTEM.GET32(pipe.qh + QhQtdToken));
- IF s * TdActive # {} THEN (* The HC hasn't yet executed the transaction *) RETURN; END;
- errors := UsbHcdi.NoErrors; loop := 0; restLen := 0; qtd := pipe.firstTD;
- LOOP
- AssertAlignment(qtd, alignQtd);
- Machine.InvalidateDCacheRange(qtd, sizeQtd);
- (* evaluate condition codes (CC)*)
- s := SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken)) * QtdStatus - TdPingState - TdSplitTransactionState;
- (* TODO: NOTE: TdPingState would be used as error indicator for split transactions *)
- IF s = {} THEN (* No errors occured *)
- ELSIF s * TdActive # {} THEN (* qTD is still active, no errors so far *)
- RETURN;
- ELSE (* At least one error occured *)
- 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 (* Data had to be transfered... *)
- (* The host controller decrements the Total Bytes To Transfer field according the amount of data it did
- transfer. If this field has not the value zero, the host controller did not transfer all data. If there is no
- error reported, this is a short packet condition, which can be okay. *)
- (* len bytes should have been transfered for this TD *)
- len := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, SYSTEM.GET32(qtd + QtdToken)) * QtdBytesToTransfer, -16));
- IF (len # 0) THEN (* Short packet *)
- restLen := restLen + len;
- END;
- END;
- IF qtd = pipe.lastTD THEN EXIT; END; (* End of qTD chain *)
- qtd := qtd + sizeQtd;
- INC(loop);
- IF loop > MaxLoops THEN (* Endless loop protection *)
- IF Debug.Level >= Debug.Errors THEN Show("UpdateStatus: Serious error occured."); KernelLog.Ln; END;
- EXIT;
- END;
- END; (* end loop *)
- 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;
- Machine.InvalidateDCacheRange(pipe.qh, sizeQh);
- 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 we received an ACK, do the toggle *)
- IF (pipe.status = Usbdi.Ok) OR (pipe.status = Usbdi.ShortPacket) THEN
- Machine.InvalidateDCacheRange(pipe.lastTD, sizeQtd);
- 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;
- (* Reset the host controller. Note: This will NOT assert reset on the USB downstream ports. *)
- PROCEDURE HardwareReset * () : BOOLEAN;
- CONST MaxWaits = 1000; (* Timeout in milliseconds the HC must have completed the reset command *)
- VAR dword : SET; i : LONGINT;
- BEGIN
- (* Host software mustn't reset the host controller when it's running. Stop it and ... *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- SYSTEM.PUT32(iobase + HcUsbCmd, dword - CmdRunStop); FlushPCI;
- (* ... wait until the HC is halted.*)
- 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 (* HC did not stop *) RETURN FALSE; END;
- (* Do the actual reset operation *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- SYSTEM.PUT32(iobase + HcUsbCmd, dword + CmdHcReset); FlushPCI;
- (* The host controller should clear the HCRESET bit when it has finished resetting *)
- 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;
- (* HC moves to UsbSuspend state und almost all operational registers are reset.
- Does not affect the root hub and its downstream ports *)
- PROCEDURE SoftwareReset*() : BOOLEAN;
- BEGIN
- (* TODO: Implement *)
- RETURN FALSE;
- END SoftwareReset;
- (* Initialization of the data structures of the Host Controller Communication Area *)
- PROCEDURE InitFrameList(): BOOLEAN;
- VAR fstn, i, k, j, shift : LONGINT;
- BEGIN
- (* Host controller interface data structures should not cross page-boundaries... 32 byte alignment.
- These queue heads are used as skeleton and never contain any qTDs. *)
- qhlist := UsbHcdi.GetAlignedMemSpace(2048, 4096);
- framelist := UsbHcdi.GetAlignedMemSpace(4096, 4096); (* Must be 4K aligned *)
- (* Set up QHs. 11 Interrupt QHs and the isochronousQh + fstn *)
- 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; (* Pointer not valid *)
- qhlist.data[qhlist.base + i*shift + (QhAltNextQtdPointer DIV 4)] := 1; (* Pointer not valid *)
- 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;
- (* Addresses of queue heads *)
- NEW(interruptQh);
- FOR i := 0 TO 10 DO interruptQh[i] := Machine.Ensure32BitAddress (ADDRESSOF(qhlist.data[qhlist.base]) + i*sizeQh); END;
- fstn := interruptQh[10] + sizeQh; (* Actually 8 bytes *)
- isochronousQh := fstn + sizeQh;
- FOR i := 10 TO 2 BY -1 DO
- SYSTEM.PUT32(interruptQh[i] + QhHorizontalLinkPointer, interruptQh[i-1] + LSH(QhTypQh, 1));
- END;
- (* Link restore indicator. InterruptQh[1] points to FSTN points to InterruptQh[0] *)
- SYSTEM.PUT32(interruptQh[1] + QhHorizontalLinkPointer, fstn + LSH(QhTypFstn, 1));
- SYSTEM.PUT32(fstn + FstnNormalPathLinkPointer, interruptQh[0] + LSH(QhTypQh, 1));
- SYSTEM.PUT32(fstn + FstnBackPathLinkPointer, QhTerminate); (* Indicates restore indicator *)
- (* Interrupt Qh for 1ms points to isochronousQh *)
- SYSTEM.PUT32(interruptQh[0] + QhHorizontalLinkPointer, isochronousQh + LSH(QhTypQh, 1));
- SYSTEM.PUT32(isochronousQh + QhHorizontalLinkPointer, QhTerminate);
- (* tree structure:
- interrupt[0]: 1ms interrupt[1]: 2ms interrupt[2]: 4ms interrupt[3]: 8ms interrupt[4]: 16ms interrupt[5]: 32ms *)
- (* => end of queue 10 points to 9, end of 8 points to 7 , ..., end of 1 points to 0 *)
- (* => if we start at queue 10, then we will pass all others too; if we start at 9 then we will pass all queues < 9, too etc.*)
- (* queue 0 executes 1024x, queue 1 executes 512x, queue 2 executes 256x, queue 3 executes 128x*)
- (* queue 4 executes 64x, queue 5 executes 32x, queue 6 executes 16x, queue 7 executes 8x*)
- (* queue 8 executes 4x, queue 9 executes 2x, queue 10 executes 1x *)
- (* What does the following mean? => We count the 1's (starting at lsb) until we pass a zero *)
- (* This count gives the queue number for a given slot *)
- FOR i := 0 TO 1023 DO (* i is slot number, we want to calc the queue number (k) for this slot *)
- 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});
- Machine.FlushDCacheRange(ADDRESSOF(framelist.data[framelist.base]), 4096);
- END;
- RETURN TRUE;
- END InitFrameList;
- (* Initializes the host controller and builds up the data structures of the HCCA.
- * @param iobase I/O base address (virtual, pointing to capability register at offset 0)
- * @param int Interrupt Line
- * @return TRUE if initialization succeeded, FALSE otherwise
- *)
- 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;
- (* Read in the Host Controller Capability Registers *)
- (* Get and check EHCI revision *)
- reg := SYSTEM.GET16(iobase + HcCapHciVersion);
- IF reg # 0100H THEN
- KernelLog.String("UsbEhci: Revision of EHCI Programming Interface not supported."); KernelLog.Ln;
- RETURN FALSE;
- END;
- (* Get and parse the HC structural parameters register (HCSPARAM) *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapSparams));
- capDebugPortNumber := SYSTEM.VAL(LONGINT, LSH(dword * {20..31}, -20));
- IF dword * {16} # {} THEN capPortIndicators := TRUE; ELSE capPortIndicators := FALSE; END;
- capNbrOfCompanionHc := SYSTEM.VAL(LONGINT, LSH(dword * {12..15}, -12));
- capPortsPerCompanion := SYSTEM.VAL(LONGINT, 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});
- (* Get and parse the HC capability parameters register (HCCPARAM) *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcCapCparams));
- capIsoSchedThreshold := SYSTEM.VAL(LONGINT, 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;
- (* Get the EHCI Extended Capabilities Pointer (EECP) *)
- eecp := SYSTEM.VAL(LONGINT, LSH(SYSTEM.VAL(SET, dword) * {8..15}, - 8));
- (* Get HC companion port route description (60bits, 4bits per port *)
- IF capPortRoutingRules THEN (* HC companion port route description available *)
- qword := SYSTEM.GET32(iobase + HcCapPortroute);
- qword := qword + LSH(SYSTEM.GET32(iobase + HcCapPortroute + 4), 32);
- NEW(hcportroute, 16);
- FOR i := 0 TO 15 DO
- hcportroute[i] := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, LSH(qword, -i)) * {0..3});
- END;
- END;
- (* Build the emulated hub descriptor *)
- NEW(hubDescriptor, 8);
- hubDescriptor[0] := CHR(7);
- hubDescriptor[1] := CHR(29H); (* Hub Descriptor *)
- hubDescriptor[2] := CHR(capNbrOfPorts);
- IF capPortPowerControl THEN (* If power control is available, EHCI root hubs always provide per port power control *)
- dword := dword + {0};
- ELSE (* No power switching implemented *)
- dword := dword + {1};
- END;
- dword := dword + {3}; (* EHCI root hubs always provide per port overcurrent protection *)
- IF capPortIndicators THEN (* Port indicator control support available *) dword := dword + {7}; END;
- hubDescriptor[3] := CHR(SYSTEM.VAL(LONGINT, dword));
- hubDescriptor[4] := CHR(0); (* Reserved *)
- hubDescriptor[5] := CHR(10); (* 20ms Power on to power good *)
- hubDescriptor[6] := CHR(0); (* Root hubs don't draw current from the USB *)
- (* The Host Controller Capability Registers are readonly so we don't need further access to them and set
- iobase to the base of the Host Controller Operational Registers *)
- iobase := iobase + SYSTEM.GET8(iobase + HcCapLength);
- SELF.iobase := iobase;
- (* Calculate offset from iobase of the port status/controll register for each port *)
- 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;
- (* Bluebottle does not yet support 64bit address space. Set the 4GB segment selector for the control data structures to zero. *)
- SYSTEM.PUT32(iobase + HcCtrlDsSegment, 0);
- (* Note that the control data structures must finally be 32byte aligned. Since they occupy subsequent memory location when
- associated with pipes, the value are rounded up to the next value for which value MOD 32 = 0 holds. *)
- IF cap64bit THEN
- sizeQh := 96; (* Actually: 68 Bytes *)
- sizeQtd := 64; (* Actually: 52 Bytes *)
- alignQh := 128;
- alignQtd := 64;
- ELSE
- sizeQh := 64; (* Actually: 48 Byte *)
- sizeQtd := 32;
- alignQh := 64;
- alignQtd := 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 the Asynchronous Schedule Park Mode is not available or not enabled, the host controller must not
- * execute more than one bus transaction per queue head, per traversal of the asynchronous schedule. If it
- * is enabled, the host controller may execute Asynchronous Schedule Park Mode Count transaction if the
- * endpoint belongs to a high-speed device. Results in better bus utilization. *)
- IF capAsynchSchedPark THEN
- ASSERT((HcAsyncParkModeCount >= 0) & (HcAsyncParkModeCount < 4));
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- IF HcAsyncParkModeCount = 0 THEN (* Disable Asynchronous Schedule Park Mode *)
- dword := dword - CmdAsyncSchedParkMode;
- ELSE (* Enable Asynchronous Schedule Park Mode and set its count field in USBCMD *)
- dword := dword + LSH(SYSTEM.VAL(SET, HcAsyncParkModeCount), 8) * CmdAsyncSchedParkCount;
- dword := dword + CmdAsyncSchedParkMode;
- END;
- SYSTEM.PUT32(iobase + HcUsbCmd, dword);
- END;
- dword := {};
- IF capProgrammableFLG THEN (* Size of frame list can be programmed... use constant value *)
- IF Debug.Trace & Debug.traceInit THEN
- KernelLog.String("UsbEhci: Set frame list size to "); KernelLog.Int(HcFrameListSize, 0);
- KernelLog.String(" elements."); KernelLog.Ln;
- END;
- (* TODO: Programm it *)
- END;
- (* Set interrupt threshold *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- dword := dword - {16 .. 23};
- IF ((HcInterruptThreshold#01H) & (HcInterruptThreshold#02H) & (HcInterruptThreshold#04H) & (HcInterruptThreshold#08H)
- & (HcInterruptThreshold#10H) & (HcInterruptThreshold#20H) & (HcInterruptThreshold#40H)) THEN
- (* Wrong parameter value... use default *)
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbEhci: Interrupt Threshold value invalid... using default setting."); KernelLog.Ln; END;
- dword := dword + SYSTEM.VAL(SET, LSH(08H, 16)) * {16..23};
- ELSE
- dword := dword + SYSTEM.VAL(SET, LSH(HcInterruptThreshold, 16)) * {16..23};
- END;
- SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
- (* Try to start the host controller *)
- IF Start() = FALSE THEN (* ERROR: Couldn't start the host controller. Controller was probably not correctly initialized. *)
- 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;
- (* PCI writes may be posted. A read forces posted writes to be flushed before the read transaction is proceeded. *)
- PROCEDURE FlushPCI *;
- VAR ignore : LONGINT;
- BEGIN
- ignore := SYSTEM.GET32(iobase + HcUsbSts);
- END FlushPCI;
- (* Release the HC ownership semaphore; eecp is the EHCI Extended Capability Pointer *)
- PROCEDURE ReleaseHcOwnerShip(bus, device, function, eecp : LONGINT);
- END ReleaseHcOwnerShip;
- (**
- * Inits the ULPI PHY of the controller.
- *)
- PROCEDURE InitUlpi(): BOOLEAN;
- BEGIN
- RETURN TRUE
- END InitUlpi;
- (*
- * Start the host controller.
- * This will:
- * - enable interrupts for the host controller and install a interrupt handler
- * - set the addresses for the periodic and asynchronous lists
- * - turn the host controller on
- * - route all ports to the EHCI controller
- * - power on all ports of the root hub
- *)
- PROCEDURE Start * ():BOOLEAN;
- VAR dword : SET;
- TYPE
- IRQPoller = OBJECT (* support polling i/o IRQ -- for testing *)
- VAR handler: PROCEDURE {DELEGATE}; timer: Kernel.Timer;
-
- PROCEDURE & Init* (h: PROCEDURE {DELEGATE});
- BEGIN
- handler := h;
- NEW(timer);
- END Init;
-
- BEGIN{ACTIVE}
- LOOP
- handler();
- timer.Sleep(1);
- END;
- END IRQPoller;
- VAR is: IRQPoller;
- BEGIN
- IF Debug.Trace & Debug.traceInit THEN KernelLog.String("UsbEhci: Starting host controller... "); KernelLog.Ln; END;
- (* Enable Interrupts *)
- SetState(UsbHcdi.Initialized);
-
- IF Polling THEN
- NEW(is, InterruptHandler)
- ELSE
- Objects.InstallHandler(InterruptHandler, Machine.IRQ0+irq);
- END;
- (* Clear interrupts *)
- SYSTEM.PUT32(iobase + HcUsbSts, 0);
-
- (* Enable all interrupts except the frame list rollover interrupt *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbIntr));
- interruptsEnabled := dword + {0..5} - StsFrameListRollover;
- SYSTEM.PUT32(iobase + HcUsbIntr, interruptsEnabled);
- (* Set Addresses for queue heads *)
- SYSTEM.PUT32(iobase + HcPeriodicListBase, ADDRESSOF(framelist.data[framelist.base]));
- SYSTEM.PUT32(iobase + HcAsyncListAddr, 0); (* Invalid address -> list empty *)
- (* Start controller *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- dword := dword + CmdRunStop;
- SYSTEM.PUT32(iobase + HcUsbCmd, dword); FlushPCI;
- SetState(UsbHcdi.Operational);
- (* Route all ports to this EHCI host controller *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcConfigFlag));
- dword := dword + {0};
- SYSTEM.PUT32(iobase + HcConfigFlag, dword); FlushPCI;
- RETURN InitUlpi();
- END Start;
- PROCEDURE &Default*(bus, device, function : LONGINT);
- BEGIN
- Default^(bus, device, function); (* The high-speed default pipe uses 64byte maxPacketSize0 *)
- 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;
- (* Release ownership of host controller *)
- ReleaseHcOwnerShip(bus, device, function, eecp);
- (* Unmap the HC's operational registers *)
- Machine.UnmapPhysical(iobase, 4096);
- END Cleanup;
- (** Displays the host controller's data struture on KernelLog *)
- 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;
- (* Show some information on this host controller on KernelLog *)
- PROCEDURE Diag;
- VAR dword : SET;
- BEGIN
- IF Debug.Trace THEN
- Diag^;
- (* Host Controller structural capabilities *)
- 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;
- (* Host Controller capability parameters *)
- 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;
- (* Host Controller Command Register *)
- KernelLog.String(" HC Command Register: "); KernelLog.Ln;
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(iobase + HcUsbCmd));
- KernelLog.String(" Interrupt Threshold: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {16..23}, -16)), 0);
- KernelLog.String(", Async Schedule Park Mode: ");
- IF dword * {11} # {} THEN
- KernelLog.String("Enabled ("); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {8..9}, -8)), 0); KernelLog.Char(")");
- ELSE
- KernelLog.String("Disabled");
- END;
- KernelLog.String(", Frame List Size: ");
- CASE SYSTEM.VAL(LONGINT, 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;
- (* Host Controller Status information *)
- 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 AssertAlignment(desc: ADDRESS; alignment: SIZE);
- BEGIN
- ASSERT(desc MOD alignment = 0);
- END AssertAlignment;
- PROCEDURE Indent(spaces : LONGINT);
- VAR i : LONGINT;
- BEGIN
- FOR i := 1 TO spaces DO KernelLog.Char(" "); END;
- END Indent;
- (*
- * Display a textual representation of a queue head data structure and its associated qTD.
- * @param qh Virtual memory address of queue head
- * @param firstQtd First qTD of this queue. If 0, the qTD chain will not be shown
- *)
- 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, 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, LSH(dword * QhEndpointNbr, -8)), 0);
- KernelLog.String(", Speed: "); val := SYSTEM.VAL(LONGINT, 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, LSH(dword * QhMaxPacketLen, -16)), 0);
- KernelLog.String(", NakRL: "); KernelLog.Int(SYSTEM.VAL(LONGINT, 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, LSH(dword * QhMultiplier, -30)), 0);
- KernelLog.String(", HubAddr: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhHubAddr, -16)), 0);
- KernelLog.String(", HubPort: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * QhPortNbr, -23)), 0);
- KernelLog.String(", SplitCMask: "); KernelLog.Int(SYSTEM.VAL(LONGINT, 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 (* show qTD chain *)
- 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;
- (* Get next qTD *)
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(firstQtd + QtdNextQtdPointer));
- IF dword * {1..4} # {} THEN
- KernelLog.String(" Alignment error!"); KernelLog.Ln;
- chainlen := MaxChainLen; (* abort *)
- ELSIF dword * QhTerminate # {} THEN
- KernelLog.String(" End of Chain"); KernelLog.Ln;
- chainlen := MaxChainLen; (* abort *)
- 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
- (* Regular qTDs are 32byte aligned. We allow 16byte alignment for the transfer overlay area *)
- 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, 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, 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, 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, LSH(dword * QtdCurrentPage, -12)), 0);
- IF SYSTEM.VAL(SET, qtd) * {4} # {} THEN (* Should be transfer overlay since not 32byte aligned *)
- KernelLog.String(", Nak counter: ");
- KernelLog.Int(SYSTEM.VAL(LONGINT, 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 ShowItd(adr : LONGINT);
- VAR dword : SET; i : LONGINT;
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("UsbEhci: ITD at address "); KernelLog.Hex(adr, 8); KernelLog.Ln;
- Indent(4);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr));
- KernelLog.String("Next Link Pointer: "); KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {5..31}), 8);
- IF dword * {0} = {} THEN KernelLog.String(" [VALID]"); ELSE KernelLog.String("[INVALID]"); END;
- KernelLog.String(", Typ: ");
- CASE SYSTEM.VAL(LONGINT, LSH(dword * {1..2}, -1)) OF
- 0: KernelLog.String("iTD");
- |1 : KernelLog.String("QH");
- |2 : KernelLog.String("siTD");
- |3 : KernelLog.String("FSTN");
- END;
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 24H));
- KernelLog.String(", Device Address: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..6}), 0);
- KernelLog.String(", Endpoint: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {8..11}, -7)), 0);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 28H));
- KernelLog.String(" MaxPacket: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..10}), 0);
- IF dword * {11} # {} THEN KernelLog.String(" [IN]"); ELSE KernelLog.String(" [OUT]"); END;
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 2CH));
- KernelLog.String(", MULT: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..1}), 0);
- KernelLog.Ln;
- FOR i := 0 TO 7 DO
- Indent(4);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + (i+1) * 4));
- KernelLog.String("Transaction "); KernelLog.Int(i, 0); KernelLog.String(": ");
- KernelLog.String("Status: ");
- IF dword * ItdStatus # {} THEN
- IF dword * ItdActive # {} THEN KernelLog.String("[ACTIVE]"); END;
- IF dword * ItdDataBufferError # {} THEN KernelLog.String("[DataBufferError]"); END;
- IF dword * ItdBabbleDetected # {} THEN KernelLog.String("[Babble]"); END;
- IF dword * ItdTransactionError # {} THEN KernelLog.String("[TransactionError]"); END;
- ELSE
- KernelLog.String("[Done]");
- END;
- KernelLog.String(" Length: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {16..27}, -16)), 0);
- KernelLog.String(", PG: "); KernelLog.Int(SYSTEM.VAL(LONGINT, LSH(dword * {12..14}, -12)), 0);
- KernelLog.String(", Offset: "); KernelLog.Int(SYSTEM.VAL(LONGINT, dword * {0..11}), 0);
- IF dword * {15} # {} THEN KernelLog.String(" [IOC]"); END;
- KernelLog.Ln;
- END;
- FOR i := 0 TO 6 DO
- Indent(4);
- dword := SYSTEM.VAL(SET, SYSTEM.GET32(adr + 24H + i*4));
- KernelLog.String("Buffer Pointer Page "); KernelLog.Int(i, 0); KernelLog.String(": ");
- KernelLog.Hex(SYSTEM.VAL(LONGINT, dword * {12..31}), 8);
- KernelLog.Ln;
- END;
- END;
- END ShowItd;
- *)
- 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;
- (* Called when this module is unloaded. Unregister all EnhancedHostController objects *)
- PROCEDURE Cleanup;
- BEGIN
- UsbHcdi.UnRegisterHostControllers(Description);
- END Cleanup;
- BEGIN
- Modules.InstallTermHandler(Cleanup);
- END UsbEhci.
- UsbEhci.Install ~ SystemTools.Free UsbEhci ~
|