1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582 |
- MODULE UsbHcdi; (** AUTHOR "staubesv"; PURPOSE "USB Host Controller Driver Interface (HCDI)" *)
- (**
- * Bluebottle USB Host Controller Driver Interface
- *
- * This is the hardware abstraction layer which enables the USB Bus Driver / Hub Driver to handle all USB Host Controller
- * in a unique way.
- *
- * Overview:
- *
- * HcdInterface Interface of the hardware-specific operations to be implemented by specific USB Host Controller drivers
- * Hcd(Interface) Implementation of hardware-independent functionality common to all USB Host Controllers
- * TransferToken Generic representation of a USB data transfer. Used as interface between pipes and host controller drivers
- * Pipe Implementation of logical communication channel between client software (i.e. device driver) and a device
- * endpoint.
- *
- * USB host controllers are controlled by operational registers and the host controller communication area in system memory.
- *
- * In the general design of the USB stack, device drivers use Pipe objects to communicate with a device. The Pipe in turn calls the
- * host controller driver to schedule the USB transfer. This module implements the interface layer between a Pipe object and an
- * HCD (host controller driver) object. The main concept of this interface is the TransferToken record, which generically represents
- * transfers.
- *
- * For USB communication, a HCD presents 3 procedures:
- * - Schedule execute a transfer
- * - Cancel cancel a pending transfer
- * - UpdatePipeStatus update status for all transfers of a pipe
- *
- * 3 more procedures are used for bookkeeping related to transfers:
- * - RegisterPipe register a pipe to the host controller. A pipe cannot transfer data before being registered
- * - UnregisterPipe unregister a pipe from the host controller. A pipe cannot transfer data after being deregistered
- * - CheckPipePolicy validates the policy of an isochronous pipe. An isochronous pipe cannot transfer data before its policy being validated
- *
- * History:
- *
- * 24.11.2005 History started (staubesv)
- * 29.11.2005 Introduced Notifier object and moved corresponding code from HC drivers to Hcd (staubesv)
- * 12.12.2005 More options for HcdManager.Show, use exception handling for critical calls (staubesv)
- * 13.12.2005 Fixed critical bug in RemoveQH, some cleanup (staubesv)
- * 15.12.2005 Added pipe.CheckBuffer, removed Pipe.pid field (staubesv)
- * 06.01.2006 Check whether USB device has been disconnected in Pipe transfer routines (staubesv)
- * 09.01.2006 Added TraceControlData trace option (staubesv)
- * 11.01.2006 Removed Hcd.GetPortCount, Hcd.RestartPipe, Pipe.Restart (staubesv)
- * 12.01.2006 Implemented software scatter/gather mechanism for pipes (staubesv)
- * 12.01.2006 Implemented transfer completion notification via interrupts (staubesv)
- * 16.01.2006 Bugfix: Pipe.Clearhalt request must use endpoint address instead of endpoint number (staubesv)
- * 17.01.2006 Use less memory for the TD buffer of the default pipe (staubesv)
- * 24.01.2006 Removed offset parameter for control transfer buffer (staubesv)
- * 25.01.2006 Unified Pipe.ControlTranfer & Pipe.Transfer, introduced TransferCompletion object (staubesv)
- * 26.01.2006 Don't return failure codes in Hcd.GetPipe, just NIL if allocation fails (staubesv)
- * 01.02.2006 Removed HcdManager, use Plugins mechanis instead (staubesv)
- * 01.03.2006 Fixed broken control out transfers (staubesv)
- * 28.06.2006 Removed procedure PrintHex (use KernelLog.Hex instead),
- * fixed UnRegisterHostControllers so it only removes the Controllers with the provided description (staubesv)
- * 03.06.2006 HcdInterface.UpdatePipe removed (staubesv)
- * 03.08.2006 Introduced HcdInterface.LinkTDsAllowed procedure (staubesv)
- * 05.01.2007 Separatly trace shortpackets/other errors, each pipe has now a own thread that's used to call its completion handler (staubesv)
- * XX.04.2015 Introduced TransferToken as the interface between Pipe and HCD (tmartiel)
- * XX.06.2015 Moved all scatter-gather management to actual HCD implementations (tmartiel)
- * TODOs:
- * - nicer design of pipes (message pipe / stream pipe)
- * - move pipes in separate module?
- * - Should control transfers also use IOC completion? No.
- * - Timeout handling: Fix: Transfer could still complete when a timeout occurs
- *)
- IMPORT SYSTEM, KernelLog, Machine, Plugins, Modules, Kernel, Objects, Locks, UsbBuffers, Usbdi, Debug := UsbDebug;
- CONST
- (** Bluebottle specific hub port status bits. Not all (root) hubs do support all of the possible status bits *)
- PortStatusDevicePresent* = {0}; (** USB device is attached to the port (only visible when port is powered) *)
- PortStatusEnabled* = {1}; (** The port is enabled *)
- PortStatusLowSpeed* = {2}; (** Connected device is a low-speed device *)
- PortStatusFullSpeed* = {3}; (** Connected device is a full-speed device *)
- PortStatusHighSpeed* = {4}; (** Connected device is a high-speed device *)
- PortStatusReset* = {5}; (** The port is current resetting *)
- PortStatusError* = {6}; (** The status request failed. *)
- PortStatusConnectChange* = {7}; (** The connection status of the port has changed *)
- PortStatusSuspended* = {8}; (** The port is suspended *)
- PortStatusOverCurrent* = {9}; (** There is an overcurrent condition on this port *)
- PortStatusPowered* = {10}; (** The port is powered *)
- PortStatusEnabledChange* = {11}; (** The enabled/disabled status has changed *)
- PortStatusSuspendChange* = {12}; (** The suspended status has changed *)
- PortStatusOverCurrentChange* = {13}; (** The overcurrent status has changed *)
- PortStatusResetChange* = {14}; (** The reset status has changed *)
- PortStatusWakeOnOvercurrent* = {15}; (** Overcurrent conditions are a wake-up event *)
- PortStatusWakeOnDisconnect* = {16}; (** Device disconnection is a wake-up event *)
- PortStatusWakeOnConnect* = {17}; (** Device connection is a wake-up event *)
- PortStatusTestControl* = {18}; (** The port is in test mode *)
- PortStatusIndicatorControl* = {19}; (** The port supports control of its status indicator LEDs *)
- PortStatusPortOwner* = {20}; (** The port is owned by the high-speed controller (only EHCI HCs) *)
- (** Pipe.errors coding. Gives more detailed transfer completion status than Pipe.status *)
- NoErrors* = {}; (** No errors occured *)
- ShortPacket* = {1}; (** Device transferred less data than requested *)
- Stalled* = {2}; (** Stall condition *)
- InProgress* = {3}; (** Transfer is still in progress (timeout for blocking transfers) *)
- Nak* = {4}; (** [UHCI] Device NAKed transfer *)
- Crc* = {5}; (** [OHCI] CRC error *)
- Timeout* = {6}; (** [OHCI] USB protocol timeout *)
- CrcTimeout* = {7}; (** [UHCI] CRC or Timeout error *)
- BitStuff* = {8}; (** Bitstuffing error *)
- Databuffer* = {9}; (** Databuffer error *)
- Babble* = {10}; (** Babble: Device sent more data than requested -> serious error! *)
- UnexpectedPid* = {13}; (** [OHCI] Unexpected PID *)
- PidCheckFailure* = {15}; (** [OHCI] PID check failure *)
- DataToggleMismatch* = {16}; (** [OHCI] Datatoggle mismatch *)
- DeviceNotResponding* = {17}; (** [OHCI] Device did not respond *)
- (** Pipe.errors HCDI level errors *)
- LinkTDsFailed* = {18}; (** Could not link TDs to QH since there are already linked TDs *)
- OutOfTDs* = {19}; (** No more TDs available for transfer scheduling *)
- Internal* = {11}; (** HCDI level error *)
- TransferTooLarge* = {14}; (** HCDI level error: Transfer is too large, respectively SG list is too small *)
- Disconnected* = {12}; (** Device has been disconnected *)
- (** Coding of ORD(PipePolicy.type) field *)
- PipeControl* = 0;
- PipeIsochronous* = 1;
- PipeBulk* = 2;
- PipeInterrupt* = 3;
- (* Host controller states *)
- Undefined* = 0;
- Initialized* = 1;
- Operational* = 2;
- Suspended* = 3;
- Resuming* = 4;
- Halted* = 5;
- Shutdown* = 6;
- (* USB transfer modes *)
- LowSpeed* = 0;
- FullSpeed* = 1;
- HighSpeed* = 2;
- (** Direction field for control transfers / pipes *)
- In* = 0; (* Device-to-Host *)
- Out* = 1; (* Host-to-Device *)
- (** HcCapabilities powerSwitching, overCurrentDetection field values *)
- NotAvailable* = 0; (* MUST be 0 *)
- Global* = 1; (* MUST be 1 *)
- PerPort* = 2; (* MUST be 2 *)
- (* Port indicator colors. Don't change values! *)
- Automatic* = 0;
- Amber* = 1;
- Green* = 2;
- Off* = 3;
- (** Timing [milliseconds] *)
- PortResetTime* = 10 + 20; (* "The duration of the Resetting state is nominally 10 ms to 20 ms (10 ms is preferred)", USB2.0spec Chapter 11.5.1.5 *)
- (** After a port reset, the hub should enable the port within this time *)
- PortEnableTimeout* = 20; (* ms *)
- (* Wait for at least 100ms to allow completion of insertion process and for power at the device to become stable. USB 2.0spec Chapter 9.1.2 *)
- PortInsertionTime* = 100 + 50;
- (* SetAddress recovery interval of 2ms, USB2.0spec Chapter 9.2.6.3 *)
- AddressRecoveryTime* = 2 + 10;
- (* Minimum time the root hub must assert the reset signal on its downstream ports *)
- RootHubResetTime* = 100; (* >= 50 ms, USB2.0spec, p. 282 *)
- (* Minimum time the HC must stay in suspend state once entered *)
- MinSuspendTime* = 8; (* >= 5ms, OHCI specification p. 44 *)
- StateDisconnected* = -1; (* MUST be equal to Usb.StateDisconnected *)
- (** Max number of TDs per transfer *)
- MaxTDs* = 1024;
- TYPE
- (** Aligned memory space. Use data[base] as first entry. *)
- AlignedMemSpace* = POINTER TO RECORD
- data- : POINTER TO ARRAY OF LONGINT;
- base- : ADDRESS;
- END;
- (** Emulated hub descriptor for supporting root hub emulation *)
- HubDescriptor* = POINTER TO ARRAY OF CHAR;
- (* Used for reporting root hub status and root hub port status changes *)
- StatusChangeHandler* = PROCEDURE {DELEGATE} (status : Usbdi.Status; actLen : LONGINT);
- (* Packet for message pipe transfers *)
- ControlMessage* = (*RECORD ofs*: LONGINT; value*: POINTER TO ARRAY 8 + 32 OF CHAR; END;*) Usbdi.BufferPtr;
- TYPE
- (* The HcdInterface defines the interface that must be implemented by the Host Controller Drivers *)
- HcdInterface = OBJECT(Plugins.Plugin)
- (** Root Hub Control *)
- (* Port numbers: 0.. nbrOfPorts-1 *)
- (**
- * Enable power for the specified port.
- * It is the hub driver that is responsible for waiting the PwrOn2PwrGood time after enabling a port.
- * The actual result is depending on the kind of power switching the root hub implements:
- * - Per port: Enable power for the specified port
- * - Ganged: One call will enable power for all ports, independent on the specified port number
- * - None: The hub driver will not call this procedure
- * @param port Port to enable power for
- *)
- PROCEDURE EnablePortPower*(port : LONGINT);
- BEGIN HALT(301); END EnablePortPower;
- (**
- * Disable power for the specified port.
- * The actual result is depending on the kind of power switching the root hub implements:
- * - Per port: Disable power for the specified port
- * - Ganged: One call will disable power for all ports, independent on the specified port number
- * - None: The hub driver will not call this procedure
- * @param port Port to disable power for
- *)
- PROCEDURE DisablePortPower*(port : LONGINT);
- BEGIN HALT(301); END DisablePortPower;
- (** Reset and then enable the specified port.
- Resets the port, waits until reset is complete, enables the port and then returns. The client
- (HubDriver) is responsible for waiting after the port is enabled *)
- PROCEDURE ResetAndEnablePort*(port : LONGINT) : BOOLEAN;
- BEGIN HALT(301); RETURN FALSE; END ResetAndEnablePort; (* abstract *)
- (** Disable the specified port. *)
- PROCEDURE DisablePort*(port: LONGINT);
- BEGIN HALT(301); END DisablePort; (* abstract *)
- (** Suspend the specified port *)
- PROCEDURE SuspendPort*(port: LONGINT) : BOOLEAN;
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbHcdi: Port suspending not supported."); KernelLog.Ln; END;
- RETURN FALSE;
- END SuspendPort;
- (** Resume the specified port *)
- PROCEDURE ResumePort*(port: LONGINT) : BOOLEAN;
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbHcdi: Port resuming not supported."); KernelLog.Ln; END;
- RETURN FALSE;
- END ResumePort;
- (**
- * Get the status of the specified port.
- * @param port Port to get status from
- * @param ack Ackknowlegde status bits that are set
- * @return Port status
- *)
- PROCEDURE GetPortStatus*(port : LONGINT; ack : BOOLEAN) : SET;
- BEGIN HALT(301); RETURN {}; END GetPortStatus; (* abstract *)
- (** Indicate a port state using the port indicators *)
- PROCEDURE IndicatePort*(port, indicate : LONGINT);
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbHcdi: Port indicator control not supported."); KernelLog.Ln; END;
- END IndicatePort;
- (**
- * Does the controller has a companion for lower-speed devices?
- * Not having a companion means that lower-speed devices can be connected directly to the root hub
- * and won't use a transaction translator.
- *)
- PROCEDURE HasCompanion*(): BOOLEAN;
- BEGIN
- RETURN FALSE
- END HasCompanion;
- (** Route the specified port to a companion host controller if supported. *)
- PROCEDURE RoutePortToCompanion*(port : LONGINT);
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbHcdi: Port routing not supported."); KernelLog.Ln; END;
- END RoutePortToCompanion;
- (** Updates the status field of the USB transfer request <req> *)
- PROCEDURE UpdatePipeStatus*(pipe : Pipe);
- BEGIN HALT(301); END UpdatePipeStatus; (* abstract *)
- (** Clear halt bit if pipe is stalled. The TDs associated to the pipe will be removed from the queue *)
- PROCEDURE ClearHalt*(pipe : Pipe);
- BEGIN HALT(301); END ClearHalt; (* abstract *)
- (**
- * Schedule a transfer on this hcd.
- * Will block until the transfer is effectively handed to hardware.
- * Does not wait for transfer completion.
- *)
- PROCEDURE Schedule * (transfer: TransferToken);
- BEGIN HALT(301) (* abstract *)
- END Schedule;
- (**
- * Cancel scheduled transfer.
- * Tries to cancel a scheduled transfer. Fails if transfer is completed or active.
- *)
- PROCEDURE Cancel * (transfer: TransferToken): BOOLEAN;
- BEGIN HALT(301) (* abstract *)
- END Cancel;
- (**
- * Checks that a periodic pipe policy is allowed for this HCD. Returns FALSE if the policy
- * requires too much bandwidth, TRUE if the policy is schedulable.
- *)
- PROCEDURE CheckPipePolicy * (interval, size: LONGINT): BOOLEAN;
- BEGIN HALT(301) (* abstract *)
- END CheckPipePolicy;
- (**
- * Some HCDIs require some pipe creations to be notified to them. This procedure is called whenever a new
- * pipe is created, so that the HCDI can take the necessary actions. This is needed, e.g. for bulk and control pipes
- * on EHCI: we build one qh per pipe.
- *)
- PROCEDURE RegisterPipe * (pipe: Pipe);
- BEGIN HALT(301) (* abstract *)
- END RegisterPipe;
- (**
- * Some HCDIs require some pipe freeing to be notified to them. This procedure is called whenever a pipe is freed,
- * so that the HCDI can take necessary steps. This is needed e.g. for bulk and control pipes on EHCI, so that the
- * controller can remove the queue head.
- *)
- PROCEDURE UnregisterPipe * (pipe: Pipe);
- BEGIN HALT(301) (* abstract *)
- END UnregisterPipe;
- (** Debug interface *)
- (** Show the specified queue head / endpoint descriptor *)
- PROCEDURE ShowQH*(qh, firstTD : ADDRESS);
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("QH/TD diagnostics not implemented."); KernelLog.Ln; END;
- END ShowQH;
- (** Show the data structures associated with the specified pipe *)
- PROCEDURE ShowPipe*(pipe : Pipe);
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("Pipe diagnostics not implemented."); KernelLog.Ln; END;
- END ShowPipe;
- (** Show the host controller's scheduling data structure *)
- PROCEDURE ShowSchedule*;
- BEGIN
- IF Debug.Level >= Debug.Warnings THEN KernelLog.String("Schedule diagnostics not implemented."); KernelLog.Ln; END;
- END ShowSchedule;
- END HcdInterface;
- (** Implements hardware-independent functionality of USB Host Controllers *)
- Hcd* = OBJECT(HcdInterface)
- VAR
- (* Host controller configuration data - Consider this fields as read-only. *)
- iobase*: Machine.Address32;
- irq* : LONGINT;
- portCount* : LONGINT;
- ports* : POINTER TO ARRAY OF Machine.Address32; (* addresses of the Port Status Control registers *)
- (* PCI specific *)
- bus-, device-, function- : LONGINT;
- (* HC capabilities (consider read-only) *)
- DMAchaining* : BOOLEAN; (* Can the HC do H/W scatter/gather? *)
- sgListSize* : LONGINT; (* How many entries shall the SG list support? Only valid if DMAchaining is TRUE. *)
- isHighSpeed* : BOOLEAN; (* Does this host controller support high-speed transfer mode? *)
- (* Emulated hub device descriptor *)
- hubDescriptor* : HubDescriptor;
- (* current state of the host controller (Undefined|Initialized|Operational|Suspended|Resuming|Error) *)
- state- : LONGINT;
- (* Procedure called when the status of the root hub port changes *)
- statusChangeHandler- : StatusChangeHandler;
- (* pipes[deviceaddress][direction][endpoint], [0][0][0] is the dummy default control pipe *)
- (* control pipes (bidirectional) are stored as they had dir In (0) *)
- pipes- : ARRAY 128 OF ARRAY 2 OF ARRAY 16 OF Pipe;
- (* These pipes have an handler associated which should be called when the pipe's transfer is finished *)
- (* The Default Pipe (Address 0, ep 0) of the controller is used as list head but has no completion handler *)
- notifyPipes- : Pipe;
- (* Keeps track which UDB device addresses are in use. *)
- adrRange : ARRAY 128 OF BOOLEAN;
- (* Per USB lock *)
- buslock : LONGINT;
- (* For the Wait procedure *)
- timer : Kernel.Timer;
- (* Performance monitoring *)
- NbytesTransfered- : HUGEINT;
- (* HC statistics *)
- NnofTransfers-,
- NnofBulkTransfers-, NnofControlTransfers-, NnofInterruptTransfers-, NnofIsochronousTransfers-,
- NnofUnknownTransfers-,
- NnofInterrupts*, NnofInterruptsHandled* : LONGINT;
- (** Bus Locking *)
- (** Lock this bus *)
- PROCEDURE Acquire*;
- BEGIN {EXCLUSIVE}
- AWAIT(buslock <= 0); buslock := 1;
- END Acquire;
- (** Unlock this bus *)
- PROCEDURE Release*;
- BEGIN {EXCLUSIVE}
- DEC(buslock);
- END Release;
- (** Set the state of the HC *)
- PROCEDURE SetState*(state : LONGINT);
- BEGIN {EXCLUSIVE}
- SELF.state := state;
- END SetState;
- (** Root hub control *)
- (**
- * Return the emulated hub device descriptor.
- * To be controllable by the hub driver, root hubs emulate a USB hub devices. This procedure returns
- * the emulated hub descriptor to communicate the root hubs facilities.
- *)
- PROCEDURE GetHubDescriptor*() : HubDescriptor;
- BEGIN
- IF Debug.StrongChecks THEN ASSERT(hubDescriptor # NIL); END;
- RETURN hubDescriptor;
- END GetHubDescriptor;
- (** Set the emulated hub device descriptor. *)
- PROCEDURE SetHubDescriptor*(hd : HubDescriptor);
- BEGIN
- IF Debug.StrongChecks THEN ASSERT((hd # NIL) & (LEN(hd) >= 8)); END;
- hubDescriptor := hd;
- END SetHubDescriptor;
- (**
- * Install a handler for root hub port status changes.
- * Some host controller can report changes of the port status registers via interrupts. If this is not
- * supported, the hub driver must poll the root hub for port status changes.
- * @param handler to be called when root hub port status changes occus
- * @return TRUE, if root hub support status change notifications, FALSE otherwise.
- *)
- PROCEDURE SetStatusChangeHandler*(handler : StatusChangeHandler) : BOOLEAN;
- BEGIN
- statusChangeHandler := handler; RETURN TRUE;
- END SetStatusChangeHandler;
- (** Add an interrupt handler. The handler is called, when a IOC interrupt occurs and the pipe.status field is changed *)
- PROCEDURE AddCompletionHandler*(pipe : Pipe);
- VAR temp : Pipe;
- BEGIN {EXCLUSIVE}
- IF Debug.Trace & Debug.tracePipes THEN
- KernelLog.String("UsbHcdi: Adding completion handler for pipe (Adr: "); KernelLog.Int(pipe.address, 0);
- KernelLog.String(", ep: "); KernelLog.Int(pipe.endpoint, 0); KernelLog.String(")"); KernelLog.Ln;
- END;
- temp := notifyPipes;
- WHILE(temp.next # NIL) & (temp.next # pipe) DO temp := temp.next; END;
- IF temp.next = NIL THEN
- temp.next := pipe;
- ELSIF Debug.Level >= Debug.Warnings THEN KernelLog.String("UsbHcdi: Warning: Procedure was already registered as interrupt handler"); KernelLog.Ln;
- END;
- END AddCompletionHandler;
- (** Remove an interrupt handler. The head of this list is always the control pipe for the default address. *)
- PROCEDURE RemoveCompletionHandler*(pipe : Pipe);
- VAR temp : Pipe;
- BEGIN (* only to be called from exclusive regions !! *)
- IF Debug.Trace & Debug.tracePipes & (ADDRESSOF(pipe) > 10H) THEN
- KernelLog.String("UsbHcdi: Removing completion handler for pipe (Adr: "); KernelLog.Int(pipe.address, 0);
- KernelLog.String(", ep: "); KernelLog.Int(pipe.endpoint, 0); KernelLog.String(")"); KernelLog.Ln;
- END;
- (* Never remove the default address control pipe *)
- (*pipe.transferLock.AcquireWrite;*)
- pipe.irqActive := FALSE;
- (*pipe.transferLock.ReleaseWrite;*)
- temp := notifyPipes;
- WHILE (temp.next # NIL) & (temp.next # pipe) DO temp := temp.next; END;
- IF temp.next # NIL THEN (* delete pipe in list *)
- temp.next := temp.next.next;
- ELSIF Debug.Level >= Debug.Warnings THEN
- KernelLog.String("UsbHcdi: Warning: Could not remove interrupt handler (not found)"); KernelLog.Ln;
- END;
- END RemoveCompletionHandler;
- PROCEDURE NotifyCompletionHandlers *;
- VAR
- pipe: Pipe;
- transfer: TransferToken;
- status, actLen: LONGINT;
- irqActive: BOOLEAN;
- BEGIN (* concurrent insertion/removal of completion handlers is allowed *)
- pipe := notifyPipes.next; (* notifyPipes is the Default Pipe (address 0, ep 0), skip it *)
- WHILE pipe # NIL DO
- pipe.lock.Acquire;
- irqActive := pipe.irqActive;
- IF irqActive & (pipe.mode = Usbdi.MinCpu) THEN
- UpdatePipeStatus(pipe);
- (* For all transfers, if finished, call completion notifier *)
- transfer := pipe.transfers;
- WHILE transfer # NIL DO
- IF (transfer.status # Usbdi.InProgress) & (pipe.device.state # StateDisconnected) THEN
- IF Debug.Trace & Debug.traceIoc THEN
- KernelLog.String("UsbHcdi: Notify pipe adr: "); KernelLog.Int(pipe.address, 0);
- KernelLog.String(", ep: "); KernelLog.Int(pipe.endpoint, 0); KernelLog.String(": "); ShowErrors(pipe.errors); KernelLog.Ln;
- END;
- IF Debug.Trace & (transfer.status # Usbdi.Ok) THEN
- IF (transfer.status = Usbdi.ShortPacket) THEN
- IF Debug.traceShortPackets THEN pipe.Show(TRUE); KernelLog.Ln; END;
- ELSE
- IF Debug.traceFailed THEN pipe.Show(TRUE); KernelLog.Ln; END;
- END;
- END;
- IF Debug.PerformanceMonitoring THEN
- IF (transfer.status = Usbdi.Ok) OR (transfer.status = Usbdi.ShortPacket) THEN
- (* should be protected *)
- INC(NbytesTransfered, pipe.actLen);
- END;
- END;
- status := transfer.status;
- actLen := transfer.transfered;
- pipe.CleanupTransfer(transfer);
- IF pipe.mode = Usbdi.MinCpu THEN pipe.completion.SetDone END;
- IF pipe.completionHandlerCaller # NIL THEN pipe.completionHandlerCaller.Call(status, actLen); END;
- transfer := pipe.transfers;
- ELSE
- transfer := transfer.next;
- END;
- END;
- IF pipe.transfers = NIL THEN
- (* No transfers scheduled: deactivate irq notifications *)
- pipe.irqActive := FALSE
- END;
- END;
- pipe.lock.Release;
- pipe := pipe.next;
- END;
- END NotifyCompletionHandlers;
- (* The default pipe (adr 0, ep 0) is only used to communicate with devices as long they are not in the addressed state. *)
- PROCEDURE GetDefaultPipe*(speed, ttPort, ttAddress : LONGINT; device : Usbdi.UsbDevice) : Pipe;
- VAR pipe : Pipe;
- BEGIN {EXCLUSIVE}
- IF Debug.StrongChecks THEN ASSERT(pipes[0, 0, 0] # NIL); END; (* dummy default pipe is always present *)
- pipe := pipes[0, 0, 0];
- pipe.speed := speed;
- pipe.device := device;
- pipe.completion.device := device;
- pipe.ttPort := ttPort;
- pipe.ttAddress := ttAddress;
- IF (isHighSpeed) & (speed = HighSpeed) THEN
- pipe.maxPacketSize := 64;
- ELSE
- pipe.maxPacketSize := 8;
- END;
- (*IF ~InsertQH(pipe) THEN pipe := NIL; END;*)
- RegisterPipe(pipe);
- RETURN pipe;
- END GetDefaultPipe;
- (** USB Pipe Handling *)
- PROCEDURE GetPipe*(deviceAddress, endpointNbr : LONGINT; VAR pipe : Pipe);
- BEGIN {EXCLUSIVE}
- IF Debug.StrongChecks THEN
- ASSERT((deviceAddress > 0) & (deviceAddress < 128)); (* valid USB device address *)
- ASSERT((endpointNbr MOD 16 >= 0) & (endpointNbr MOD 16 < 16)); (* valid endpoint number *)
- ASSERT((pipe # NIL) & (pipe.direction = In) OR (pipe.direction = Out));
- END;
- IF pipes[deviceAddress, pipe.direction, endpointNbr MOD 16] # NIL THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbHcdi: GetPipe: Pipe already in use."); KernelLog.Ln; END;
- pipe := NIL; RETURN;
- END;
- (* Allocate the TD buffers *)
- (*pipe.tdBufferLen := (TDsPerPipe+1)* 16 + 128;
- NEW(pipe.tdBuffer, pipe.tdBufferLen);*)
- IF Debug.StrongChecks THEN
- (*testaddr := Machine.PhysicalAdr(ADDRESSOF(pipe.tdBuffer[0]), pipe.tdBufferLen);
- IF testaddr = Machine.NilAdr THEN
- KernelLog.String("UsbHcdi: GetPipe: Allocated buffer not physically contiguous"); KernelLog.Ln;
- pipe := NIL; RETURN;
- END;*)
- END;
- (* TD's must be 32byte aligned for EHCI, 16byte aligned for UHCI & OHCI data structures*)
- (* currently max qh size is below 128 bytes for EHCI, so a queue head will not cross page boundaries *)
- (*pipe.qh := Align(ADDRESS OF pipe.tdBuffer[0], 128);
- pipe.tdBase := pipe.qh + 32;
- ASSERT((pipe.qh >= ADDRESSOF(pipe.tdBuffer[0])) & (pipe.tdBase <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1])));*)
- RegisterPipe(pipe);
- (*IF ~InsertQH(pipe) THEN
- IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbHcdi: GetPipe: InsertQH failed."); KernelLog.Ln; END;
- pipe := NIL; RETURN;
- END;*)
- pipes[deviceAddress, pipe.direction, endpointNbr MOD 16] := pipe;
- IF Debug.Trace & Debug.tracePipes THEN
- KernelLog.String("UsbHcdi: GetPipe: ");
- CASE pipe.type OF
- |PipeControl: KernelLog.String("Control");
- |PipeBulk: KernelLog.String("Bulk");
- |PipeInterrupt: KernelLog.String("Interrupt");
- |PipeIsochronous: KernelLog.String("Isochronous");
- ELSE
- KernelLog.String("Unknown");
- END;
- IF pipe.direction = In THEN KernelLog.String(" IN Pipe, ");
- ELSIF pipe.direction = Out THEN KernelLog.String(" OUT Pipe, ");
- ELSE KernelLog.String("Unknown direction Pipe, ");
- END;
- KernelLog.String(" Adr: "); KernelLog.Int(deviceAddress, 0);
- KernelLog.String(" Ep: "); KernelLog.Int(endpointNbr, 0);
- KernelLog.String(" established"); KernelLog.Ln;
- END;
- END GetPipe;
- PROCEDURE FreePipe*(pipe : Pipe);
- BEGIN {EXCLUSIVE}
- FreePipeInternal(pipe);
- END FreePipe;
- PROCEDURE FreePipeInternal(pipe : Pipe);
- BEGIN (* only call from exclusive regions *)
- (*TRACE('FreePipeInternal');*)
- IF pipe.address = 0 THEN
- (*IF pipe.qh # 0 THEN RemoveQH(pipe); END;*)
- UnregisterPipe(pipe);
- IF Debug.Trace & Debug.tracePipes THEN KernelLog.String("UsbHcdi: Default pipe at adr 0 freed up."); KernelLog.Ln; END;
- ELSIF pipes[pipe.address, pipe.direction, pipe.endpoint MOD 16] # NIL THEN
- (* De-install interrupt handler if present *)
- IF pipe.ioc THEN RemoveCompletionHandler(pipe); END;
- (* Kill completion handler caller if present *)
- IF pipe.completionHandlerCaller # NIL THEN pipe.completionHandlerCaller.Terminate; END;
- (* Remove the pipe's queue head from the host controller's schedule *)
- (*IF pipe.qh # 0 THEN RemoveQH(pipe); END;*)
- UnregisterPipe(pipe);
- (* Never free the dummy default pipe *)
- pipes[pipe.address, pipe.direction, pipe.endpoint MOD 16] := NIL;
- IF Debug.Trace & Debug.tracePipes THEN
- KernelLog.String("UsbHcdi: FreePipe:"); KernelLog.String(" Adr: "); KernelLog.Int(pipe.address, 0);
- KernelLog.String(" Ep: "); KernelLog.Int(pipe.endpoint, 0); KernelLog.String(" freed up."); KernelLog.Ln;
- END;
- ELSE (* Pipe not known by host controller *)
- IF Debug.Level >= Debug.Warnings THEN
- KernelLog.String("UsbHcdi: FreePipe: Can't free pipe... pipe not known by host controller ADR: ");
- IF pipe # NIL THEN
- KernelLog.Int(pipe.address, 0); KernelLog.String(", ep: "); KernelLog.Int(pipe.endpoint, 0);
- KernelLog.String(", dir: "); KernelLog.Int(pipe.direction, 0);
- ELSE
- KernelLog.String("NIL");
- END;
- KernelLog.Ln;
- END;
- END;
- (*TRACE('END FreePipeInternal');*)
- END FreePipeInternal;
- (** Free all pipes of the device with the address adr *)
- PROCEDURE FreeAll*(adr : LONGINT);
- VAR i, j : LONGINT;
- BEGIN {EXCLUSIVE}
- IF Debug.StrongChecks THEN ASSERT((adr >= 0) & (adr < 128)); END;
- IF adr = 0 THEN RETURN (* Emulated hub device has no pipes *) END;
- IF state = Shutdown THEN RETURN; END; (* Since the controller has been resetted, removing QHs would trap *)
- FOR i := 0 TO 15 DO
- FOR j := 0 TO 1 DO
- IF pipes[adr, j, i] # NIL THEN
- FreePipeInternal(pipes[adr, j , i]);
- END;
- END;
- END;
- END FreeAll;
- (** Returns a unused address and marks it as used; address 0 is the default address, to
- * which unaddressed USB devices at enabled endpoints will respond. *)
- PROCEDURE GetFreeAddress*() : LONGINT;
- VAR adr : LONGINT;
- BEGIN {EXCLUSIVE}
- FOR adr := 1 TO 127 DO
- IF adrRange[adr] = FALSE THEN adrRange[adr] := TRUE; RETURN adr; END;
- END;
- RETURN 0;
- END GetFreeAddress;
- (** Marks the address <adr> as free *)
- PROCEDURE FreeAddress*(adr : LONGINT);
- BEGIN {EXCLUSIVE}
- adrRange[adr] := FALSE;
- END FreeAddress;
- (** Helper: Wait for the specified number of milliseconds *)
- PROCEDURE Wait*(ms : LONGINT);
- BEGIN
- timer.Sleep(ms);
- END Wait;
- PROCEDURE Cleanup*;
- BEGIN
- SetState(Shutdown); timer.Wakeup;
- END Cleanup;
- PROCEDURE &Default*(bus, device, function : LONGINT);
- VAR pipe : Pipe; i : LONGINT;
- BEGIN
- SELF.bus := bus; SELF.device := device; SELF.function := function;
- NEW(timer);
- FOR i := 0 TO 127 DO adrRange[i] := FALSE; END;
- NEW(pipe, 0, 0, SELF);
- pipe.type := PipeControl; pipe.direction := 0;
- (*pipe.maxPacketSize := 8; (* will be reset to 64 for EHCI HCs by hub driver *) (*! Should maybe be set in specific HCDIs *)*)
- pipe.maxRetries := 3; pipe.timeout := 5000;
- (* okay, now we allocate the TD buffers *)
- (*pipe.tdBufferLen := (TDsDefaultPipe+1) * 16 + 128; (* max alignment of 128 bytes *)
- NEW(pipe.tdBuffer, pipe.tdBufferLen);*)
- (* TD's must be 32byte aligned for EHCI data structures, 16byte for UHCI & OHCI data structures *)
- (* currently max qh size is below 128 bytes for EHCI, so a queue head will not cross page boundaries *)
- (*pipe.qh := Align(ADDRESS OF pipe.tdBuffer[0], 128);*)
- (*pipe.tdBase := pipe.qh + 32;*)
- IF Debug.StrongChecks THEN
- (*ASSERT(pipe.tdBase >= ADDRESSOF(pipe.tdBuffer[0]));
- ASSERT(pipe.tdBase <= ADDRESSOF(pipe.tdBuffer[pipe.tdBufferLen-1]));*)
- END;
- pipes[0, 0, 0] := pipe;
- notifyPipes := pipe; (* Used as list head only (don't call its completion handler) *)
- END Default;
- (** Show diagnostic of host controller *)
- PROCEDURE Diag*;
- VAR port : LONGINT; dword : SET;
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("Diagnostics of "); KernelLog.String(name);
- KernelLog.String(" ("); KernelLog.String(desc); KernelLog.String(")");
- KernelLog.Ln;
- (* PCI information *)
- KernelLog.String(" PCI bus "); KernelLog.Int(bus, 0); KernelLog.String(", device "); KernelLog.Int(device, 0);
- KernelLog.String(", function "); KernelLog.Int(function, 0);
- KernelLog.Ln;
- (* IO base address and Interrupt Line *)
- KernelLog.String(" I/O base "); KernelLog.Hex(iobase, 8); KernelLog.String("H, Irq: "); KernelLog.Int (irq, 0);
- KernelLog.String(", Int Counter: "); KernelLog.Int(NnofInterrupts, 0);
- KernelLog.Ln;
- (* Port Status *)
- KernelLog.String(" Number of ports: "); KernelLog.Int(portCount, 0); KernelLog.String(", Port status: ");
- KernelLog.Ln;
- FOR port := 0 TO portCount-1 DO
- dword := GetPortStatus(port, FALSE); KernelLog.String(" Port "); KernelLog.Int(port+1, 0); KernelLog.String(": ");
- ShowPortStatus(dword); KernelLog.Ln;
- END;
- END;
- END Diag;
- END Hcd;
- (**
- * Abstract transfer descriptor.
- * These transfer descriptors are used only for interfacing pipes and HCDIs.
- * A transfer descriptor contains all necessary info for a transfer, in a transfer and hcd independent
- * structure. This makes a clean and extensible interface between the high-level pipe and the services it
- * requires from HCDIs.
- *)
- TransferToken * = POINTER TO RECORD
- (** Pipe associated with the transfer. Provides all pipe-specific information (type, packet size, direction) and completion handler. *)
- pipe *: Pipe;
- (** Transfer buffer. *)
- buffer *: (*POINTER TO ARRAY OF CHAR*) ADDRESS;
- (** ID of the transfer. Unique among all non-completed transfers of a pipe. *)
- id *,
- (** Offset and length of data to send/receive in the transfer buffer. *)
- len *: LONGINT;
- (** Status information: error, status and total transfered byte count. *)
- errors *: SET;
- status *, transfered *: LONGINT;
- (** Control message. Used for control transfers only. *)
- message *: ControlMessage;
- (** HCD-specific data structures associated with this transfer. Used by HCDI only. Invalid after transfer completion. *)
- tds *: ARRAY MaxTDs OF ADDRESS;
- (** Chaining of transfer descriptors. *)
- next *: TransferToken;
- END;
- TYPE
- (** Used for Interrupt On Completion (IOC) transfer status notification *)
- TransferCompletion = OBJECT
- VAR
- done : BOOLEAN;
- completiontimeout : BOOLEAN;
- timer : Objects.Timer;
- device* : Usbdi.UsbDevice;
- PROCEDURE SetDone;
- BEGIN {EXCLUSIVE}
- done := TRUE;
- END SetDone;
- PROCEDURE SetCompletionTimeout;
- BEGIN {EXCLUSIVE}
- completiontimeout := TRUE;
- END SetCompletionTimeout;
- PROCEDURE AwaitDone(timeout : LONGINT) : BOOLEAN;
- VAR result : BOOLEAN;
- BEGIN {EXCLUSIVE}
- Objects.SetTimeout(timer, SELF.SetCompletionTimeout, timeout);
- done := FALSE; completiontimeout := FALSE;
- AWAIT(done OR completiontimeout OR (device.state = StateDisconnected));
- Objects.CancelTimeout(timer);
- done := FALSE; result := completiontimeout; completiontimeout := FALSE;
- RETURN ~result;
- END AwaitDone;
- PROCEDURE &New*;
- BEGIN
- NEW(timer);
- END New;
- END TransferCompletion;
- TYPE
- (* Thread that calls the completion handler of a pipe (if it has one) *)
- CompletionHandlerCaller = OBJECT
- CONST
- Size = 32;
- VAR
- completionHandler : Usbdi.CompletionHandler;
- pipe : Pipe;
- events: ARRAY Size OF RECORD status, actLen: LONGINT; END;
- status, actLen: LONGINT;
- head, len: LONGINT;
- alive, dead : BOOLEAN;
- PROCEDURE Call (status, actLen: LONGINT);
- BEGIN {EXCLUSIVE}
- AWAIT(len < Size);
- events[(head + len) MOD Size].status := status;
- events[(head + len) MOD Size].actLen := actLen;
- INC(len);
- END Call;
- PROCEDURE HandleTimeout;
- BEGIN{EXCLUSIVE}
- dead := TRUE;
- END HandleTimeout;
- PROCEDURE Terminate;
- VAR timer:Objects.Timer;
- BEGIN
- BEGIN {EXCLUSIVE} alive := FALSE; END;
- (* release monitor lock *)
- NEW(timer);
- Objects.SetTimeout(timer, HandleTimeout, 100);
- BEGIN {EXCLUSIVE} AWAIT(dead); END;
- END Terminate;
- PROCEDURE &New*(pipe : Pipe; c : Usbdi.CompletionHandler);
- BEGIN
- ASSERT(c # NIL);
- SELF.pipe := pipe;
- completionHandler := c;
- alive := TRUE; dead := FALSE;
- END New;
- BEGIN {ACTIVE}
- WHILE alive DO
- BEGIN {EXCLUSIVE}
- AWAIT((len > 0) OR ~alive);
- status := events[head MOD Size].status;
- actLen := events[head MOD Size].actLen;
- INC(head);
- DEC(len);
- END;
- IF alive THEN
- (* notify the pipe that transfer finished to get transfer results in pipe. *)
- (*pipe.CleanupTransfer(transfer);*)
- completionHandler(status, actLen)
- END;
- (*BEGIN {EXCLUSIVE}
- doCall := FALSE
- END*)
- END;
- BEGIN {EXCLUSIVE} dead := TRUE; END;
- END CompletionHandlerCaller;
- TYPE
- (**
- * USB Communication Pipe
- * USB communication happens between buffers provided by client software and device endpoints. The association between
- * a client software buffer and a device endpoint is called pipe.
- * This is the low-level implementation of a pipe which is used by the host controller drivers. The fields declared here aren't visible
- * to client software which uses the interface defined by the USB Driver Interface (Usbdi).
- *
- * Concurrency:
- * - concurrent calls on a pipe must preserve the transfer token list.
- *)
- Pipe* = OBJECT(Usbdi.Pipe)
- VAR
- (* Device endpoint *)
- address* : LONGINT; (* USB device address *)
- endpoint* : LONGINT; (* Endpoint address *)
- direction* : LONGINT; (* Endpoint direction; Usbdi.In or Usbdi.Out; not used for control transfers *)
- (* Associated host controller & USB device *)
- controller* : Hcd;
- device* : Usbdi.UsbDevice;
- (* Address of hub device that contains the transaction translator we're connected to and port of the TT *)
- (* These fields are duplicate here (also available in Usb.UsbDevice to avoid the import of Usb.Mod in UsbEhci.Mod *)
- ttPort*, ttAddress* : LONGINT;
- (* Information from endpoint descriptor *)
- type* : LONGINT; (* PipeControl, PipeBulk, PipeInterrupt or PipeIsochronous *)
- irqInterval* : LONGINT; (* Interrupt interval for PipeInterrupt (interrupt transfers) *)
- mult* : LONGINT; (* For high-speed interrupt/isochronous pipes: How many transactions per microframe (1,2 or 3) *)
- (* Pipe specific features *)
- speed* : LONGINT; (* LowSpeed, FullSpeed, HighSpeed *)
- dataToggle* : BOOLEAN; (* 1bit sequence number *)
- (* hostDelay, hubLsSetup : LONGINT; (* Delay introduced by host / hub in nanoseconds *) *)
- (** Transfer list. Manipulate only in EXCLUSIVE regions. *)
- transfers*, lastTransfer*: TransferToken;
- lock *: Locks.Lock;
- (** Next transfer ID to use. Manipulate only in EXCLUSIVE regions. *)
- nextId: LONGINT;
- (* Periodic pipe policy *)
- sampleInterval, sampleSize: LONGINT;
- (* Transfer status information *)
- status* : Usbdi.Status ; (* Status of the last tranfer from/to this endpoint *)
- errors* : SET;
- transferLen* : LONGINT; (* Length of the transfer in bytes *)
- actLen* : LONGINT; (* how many bytes did the controller send/receive *)
- (* Buffer for S/W scatter/gather support *)
- sgBuffer : Usbdi.BufferPtr;
- physBufferAdr- : ADDRESS;
- (* Buffer for H/W scatter/gather suport *)
- sgList- : POINTER TO ARRAY OF Machine.Range;
- (* Pipe parameters set by client software *)
- timeout* : LONGINT;
- (* Data structures *)
- (* For control, bulk and interrupt transfers, each pipe has an associated queue head in the host controllers schedule. *)
- (* This queue head can be found in the queue <queue>. The actual USB transfers are described as linked list of *)
- (* transfer descriptors (TD), which are linked to the pipe`s queue head. *)
- queue* : Machine.Address32;
- (*qh* : Machine.Address32;
- firstTD*, lastTD* : Machine.Address32;
- (* Per pipe buffer for transfer descriptors *)
- tdBuffer* : POINTER TO ARRAY OF CHAR;
- tdBufferLen* : LONGINT;
- tdBase* : Machine.Address32;*)
- (** Holder for controller-specific descriptors that are related to a pipe and not a transfer (e.g. queue heads in EHCI). A lock is provided for synchronization. *)
- descriptors *: POINTER TO ARRAY OF ADDRESS;
- (*descriptorLock *: Locks.Lock;*)
- (* Transfer completion handling related *)
- ioc* : BOOLEAN; (* interrupt on completion enabled/disabled *)
- completionHandlerCaller* : CompletionHandlerCaller; (* if active & ioc, the procedure interruptHandler will be called if status * ResInProgress = {} *)
- irqActive* : BOOLEAN; (* Should the InterruptHandler be called? *)
- completion- : TransferCompletion;
- (* Pipe management *)
- next* : Pipe;
- (* Control pipes only: 8 Byte message *)
- message : ControlMessage;
- PROCEDURE &New*(adr, ep : LONGINT; hcd : Hcd);
- BEGIN
- NEW(completion);
- address := adr; endpoint := ep; controller := hcd; status := Usbdi.InProgress;
- (*IF controller.DMAchaining THEN NEW(sgList, controller.sgListSize); END;*)
- (*NEW(descriptorLock);*)
- NEW(lock)
- END New;
- (* For host controllers that do not support DMA chaining, the buffers must be physically contiguous. In Bluebottle, all buffers allocated *)
- (* on the heap meet this requirement. Buffers allocated on the stack may, however, be physically non-contiguous. Fortunately, the *)
- (* stack size is limited to 128K, so these buffers won't be bigger than 128K. *)
- (* This procedure... *)
- (* - Returns TRUE and as a side effect copies the client buffer into the scatter/gather buffer for OUT transfers if the specified buffer *)
- (* cannot directly be used by the host controller *)
- (* - Returns FALSE when the client buffer meets the requirement of the host controller hardware *)
- PROCEDURE NeedSWScatterGather(direction, bufferLen, offset : LONGINT; VAR buffer : Usbdi.Buffer; VAR doCopy : BOOLEAN) : BOOLEAN;
- VAR adr : ADDRESS;
- BEGIN
- doCopy := FALSE;
- IF bufferLen = 0 THEN RETURN FALSE; END;
- adr := Machine.PhysicalAdr(UsbBuffers.GetDataAddress(buffer), bufferLen);
- IF adr = -1 THEN (* buffer is not physically contiguous *)
- IF sgBuffer = NIL THEN NEW(sgBuffer, 128*1024); END; (* 128K is stack limit *)
- physBufferAdr := UsbBuffers.GetDataAddress(sgBuffer);
- IF direction = In THEN
- doCopy := TRUE;
- ELSE
- ASSERT(bufferLen <= 128*1024); (* If the buffer is on the heap, we don't reach this code. Stack limit is 128K *)
- Copy(buffer, sgBuffer, offset, 0, bufferLen);
- END;
- RETURN TRUE;
- ELSE
- physBufferAdr := adr;
- RETURN FALSE;
- END;
- END NeedSWScatterGather;
- PROCEDURE Transfer*(bufferLen, offset : LONGINT; VAR buffer : Usbdi.Buffer) : Usbdi.Status;
- VAR
- copy : BOOLEAN;
- transfer: TransferToken;
- BEGIN
- ASSERT(type # PipeControl);
- ASSERT(LEN(buffer) >= bufferLen + offset);
- IF Debug.Trace & Debug.traceTransfers THEN ShowTransfer(bufferLen, offset); END;
- transfer := GetToken();
- transfer.pipe := SELF;
- IF ~controller.DMAchaining & NeedSWScatterGather(direction, bufferLen, 0, buffer, copy) THEN
- transfer.buffer := UsbBuffers.GetDataAddress(sgBuffer)
- ELSE
- transfer.buffer := UsbBuffers.GetDataAddress(buffer)
- END;
- transfer.len := bufferLen;
- transfer.status := Usbdi.InProgress;
- IF timeout # 0 THEN status := Usbdi.InProgress END;
- lock.Acquire;
- transfer.id := nextId; INC(nextId);
- IF transfers = NIL THEN
- transfers := transfer
- ELSE
- lastTransfer.next := transfer
- END;
- lastTransfer := transfer;
- IF (timeout = 0) OR (mode = Usbdi.MinCpu) THEN
- irqActive := TRUE
- END;
- lock.Release;
- controller.Schedule(transfer);
- IF timeout = 0 THEN
- RETURN transfer.id
- ELSE
- WaitForCompletion(transfer, buffer, copy);
- RETURN status
- END
- END Transfer;
- (** Waits for the completion of the next transfer. *)
- PROCEDURE WaitForCompletion (transfer: TransferToken; buffer: Usbdi.Buffer; copy: BOOLEAN);
- VAR
- mtimer: Kernel.MilliTimer;
- BEGIN
- ASSERT(timeout # 0);
- Kernel.SetTimer(mtimer, timeout);
- IF mode # Usbdi.MinCpu THEN
- Kernel.SetTimer(mtimer, timeout);
- lock.Acquire;
- LOOP
- IF (transfer.status # Usbdi.InProgress) OR Kernel.Expired(mtimer) OR (device.state = StateDisconnected) THEN EXIT; END;
- controller.UpdatePipeStatus(SELF);
- IF mode = Usbdi.Normal THEN Objects.Yield; END;
- END;
- (* Copy result from transfer to pipe *)
- CleanupTransfer(transfer);
- lock.Release
- ELSE
- IF ~completion.AwaitDone(timeout) THEN
- (* ignore *)
- END;
- (* Transfer cleanup is done in the interrupt handling routine. *)
- END;
- IF Debug.PerformanceMonitoring THEN
- (* access should be protected *)
- INC (controller.NbytesTransfered, actLen);
- END;
- IF copy & (status # Usbdi.InProgress) THEN (* copy data from scatter/gather buffer to client buffer *)
- Copy(sgBuffer, buffer, 0, 0, actLen);
- END;
- IF Debug.Trace & Debug.traceControlData & (type = PipeControl) THEN
- IF transfer.len > 0 THEN ShowData(transfer.buffer, transfer.len); KernelLog.Char(" "); ELSE KernelLog.String("[No Data] "); END;
- END;
- IF Debug.Trace & (Debug.traceTransfers OR Debug.traceControl) THEN ShowStatus(status); KernelLog.Ln; END;
- IF Debug.Trace & (status # Usbdi.Ok) THEN
- IF (status = Usbdi.ShortPacket) THEN
- IF Debug.traceShortPackets THEN Show(TRUE); KernelLog.Ln; END;
- ELSE
- IF Debug.traceFailed THEN Show(TRUE); KernelLog.Ln; END;
- END;
- END;
- END WaitForCompletion;
- (** For control transfers (only for Control Pipes) *)
- PROCEDURE Request*(bmRequestType : SET; bRequest, wValue, wIndex, wLength : LONGINT; VAR buffer : Usbdi.Buffer) : Usbdi. Status;
- VAR
- dir, ofs: LONGINT;
- transfer: TransferToken;
- copy: BOOLEAN;
- BEGIN
- ASSERT(type = PipeControl);
- ASSERT(LEN(buffer) >= wLength);
- ASSERT(buffer # NIL);
- transfer := GetToken();
- (*transferLock.Acquire;*)
- IF transfer.message = NIL THEN NEW(transfer.message, 8); END;
- transfer.message[0] := CHR(SYSTEM.VAL(LONGINT, bmRequestType));
- transfer.message[1] := CHR(bRequest);
- transfer.message[2] := CHR(wValue);
- transfer.message[3] := CHR(LSH(wValue, -8));
- transfer.message[4] := CHR(wIndex);
- transfer.message[5] := CHR(LSH(wIndex,-8));
- transfer.message[6] := CHR(wLength);
- transfer.message[7] := CHR(LSH(wLength, -8));
- dir := direction;
- IF bmRequestType * Usbdi.ToHost # {} THEN direction := In; ELSE direction := Out; END;
- IF Debug.Trace & Debug.traceControl THEN ShowMessage(wLength, direction, transfer.message); END;
- transfer.pipe := SELF;
- IF ~controller.DMAchaining & NeedSWScatterGather(dir, wLength, 0, buffer, copy) THEN
- transfer.buffer := UsbBuffers.GetDataAddress(sgBuffer)
- ELSE
- transfer.buffer := UsbBuffers.GetDataAddress(buffer)
- END;
- transfer.len := wLength;
- transfer.status := Usbdi.InProgress;
- lock.Acquire;
- transfer.id := nextId; INC(nextId);
- (* We do not allow transfer queuing on control pipes, therefore the transfer list shall be empty *)
- ASSERT(transfers = NIL);
- transfers := transfer;
- (*lastTransfer := transfer;*)
- lock.Release;
- controller.Schedule(transfer);
- WaitForCompletion(transfer, buffer, copy);
- direction := dir;
- RETURN status
- END Request;
- PROCEDURE ClearHalt*(): BOOLEAN;
- CONST FsEndpointHalt = 0; SrClearFeature = 1;
- VAR res : BOOLEAN;
- BEGIN
- IF Debug.Trace & Debug.tracePipes THEN
- KernelLog.String("UsbHcdi: Clearhalt Pipe"); KernelLog.String(" Adr: ");KernelLog.Int(address, 0);
- KernelLog.String(" Ep: "); KernelLog.Int(endpoint, 0); KernelLog.Ln;
- END;
- controller.ClearHalt(SELF);
- lock.Acquire;
- transfers := NIL;
- lock.Release;
- res := device.Request(Usbdi.ToDevice + Usbdi.Standard + Usbdi.Endpoint, SrClearFeature, FsEndpointHalt, endpoint, 0, Usbdi.NoData) = Usbdi.Ok;
- IF res THEN dataToggle := FALSE; END;
- RETURN res;
- END ClearHalt;
- PROCEDURE IsHalted*() : BOOLEAN;
- CONST SrGetStatus = 0; Halted = {0};
- VAR buffer : Usbdi.BufferPtr; status : SET;
- BEGIN
- IF Debug.Trace & Debug.tracePipes THEN
- KernelLog.String("UsbHcdi: Get endpoint status for Adr: "); KernelLog.Int(address, 0);
- KernelLog.String(", Ep: "); KernelLog.Int(endpoint, 0); KernelLog.Ln;
- END;
- NEW(buffer, 2);
- IF device.Request(Usbdi.ToHost + Usbdi.Standard + Usbdi.Endpoint, SrGetStatus , 0, endpoint MOD 16, 2, buffer) = Usbdi.Ok THEN
- status := SYSTEM.VAL(SET, ORD(buffer[0]) + ORD(buffer[1])*100H);
- RETURN status * Halted # {};
- END;
- RETURN FALSE;
- END IsHalted;
- PROCEDURE GetActLen*() : LONGINT;
- BEGIN
- RETURN actLen;
- END GetActLen;
- PROCEDURE SetTimeout*(timeout : LONGINT);
- BEGIN
- SELF.timeout := timeout;
- END SetTimeout;
- PROCEDURE GetStatus*(VAR len : LONGINT) : Usbdi.Status;
- BEGIN
- controller.UpdatePipeStatus(SELF); len := actLen;
- RETURN status;
- END GetStatus;
- PROCEDURE SetCompletionHandler*(handler: Usbdi.CompletionHandler);
- BEGIN
- ioc := TRUE; (* set Interrupt On Completion Bit in TD's *)
- mode := Usbdi.MinCpu;
- IF completionHandlerCaller # NIL THEN
- completionHandlerCaller.Terminate;
- END;
- NEW(completionHandlerCaller, SELF, handler);
- controller.AddCompletionHandler(SELF);
- END SetCompletionHandler;
- PROCEDURE GetLastTransactionId * (): LONGINT;
- BEGIN
- END GetLastTransactionId;
- PROCEDURE CancelTransaction * (id: LONGINT): BOOLEAN;
- VAR
- tx: TransferToken;
- BEGIN
- tx := transfers;
- WHILE (tx # NIL) & (tx.id # id) DO tx := tx.next END;
- IF (tx = NIL) OR (tx.status = Usbdi.InProgress) THEN
- RETURN FALSE
- ELSE
- RETURN controller.Cancel(tx)
- END
- END CancelTransaction;
- PROCEDURE SetPolicy * (interval, size: LONGINT): BOOLEAN;
- VAR
- res: BOOLEAN;
- BEGIN
- IF (type = PipeControl) OR (type = PipeBulk) THEN RETURN FALSE END;
- res := controller.CheckPipePolicy(interval, size);
- IF res THEN
- sampleInterval := interval;
- sampleSize := size
- END;
- RETURN res
- END SetPolicy;
- (**
- * Cleanup a finished transfer.
- * This is called when a transfer finished.
- *)
- PROCEDURE CleanupTransfer (transfer: TransferToken);
- VAR
- prev: TransferToken;
- readLocked: BOOLEAN;
- BEGIN
- (*TRACE("Cleanup acquire Tk Write");*)
- (*readLocked := transferLock.HasLock();
- IF ~readLocked THEN transferLock.Acquire END;*)
- (* update pipe status variables *)
- ASSERT(transfer # NIL);
- (*IF type = PipeIsochronous THEN TRACE(transfer, transfer.status, transfer.errors, transfer.transfered) END;*)
- status := transfer.status;
- errors := transfer.errors;
- actLen := transfer.transfered;
- (* remove transfer from list *)
- (*IF type = PipeIsochronous THEN TRACE(transfer, transfers) END;*)
- IF transfer = transfers THEN
- transfers := transfers.next
- ELSE
- prev := transfers;
- WHILE (prev # NIL) & (prev.next # NIL) & (prev.next # transfer) DO
- prev := prev.next
- END;
- ASSERT(prev # NIL);
- ASSERT(prev.next = transfer);
- IF transfers = lastTransfer THEN
- prev.next := NIL;
- lastTransfer := prev
- ELSE
- prev.next := transfer.next
- END
- END;
- (*IF ~readLocked THEN transferLock.Release END;*)
- (*TRACE("Cleanup release Tk Write");*)
- PutToken(transfer)
- END CleanupTransfer;
- (* Display textual representation of the transfer that will be executed. *)
- PROCEDURE ShowTransfer(bufferLen, offset : LONGINT);
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("UsbHcdi: ");
- CASE type OF
- PipeControl : KernelLog.String("Control Transfer???:");
- | PipeBulk : KernelLog.String("Bulk Transfer:");
- | PipeInterrupt : KernelLog.String("Interrupt Transfer:");
- | PipeIsochronous : KernelLog.String("Isochronous Transfer:");
- ELSE KernelLog.String("Unknown transfer type");
- END;
- KernelLog.String(" Adr: "); KernelLog.Int(address, 0);
- KernelLog.String(" Endpoint: "); KernelLog.Int(endpoint, 0);
- KernelLog.String(" Length: "); KernelLog.Int(bufferLen, 0); KernelLog.String(" Bytes: ");
- END;
- END ShowTransfer;
- PROCEDURE ShowMessage(bufferLen, direction : LONGINT; CONST msg : ControlMessage);
- VAR i : LONGINT;
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("UsbHcdi: Control Transfer: ");
- IF direction = In THEN KernelLog.String("IN");
- ELSIF direction = Out THEN KernelLog.String("OUT");
- ELSE KernelLog.String("ERROR");
- END;
- KernelLog.String(" Adr: "); KernelLog.Int(address, 0);
- KernelLog.String(" Endpoint: "); KernelLog.Int(endpoint, 0);
- KernelLog.String(" Length: "); KernelLog.Int(bufferLen, 0); KernelLog.String(" Bytes: ");
- KernelLog.String(" CtrlMsg: "); FOR i := 0 TO 7 DO KernelLog.Hex(ORD(msg[i]), -2); KernelLog.String(" "); END;
- END;
- END ShowMessage;
- PROCEDURE ShowData (buffer: ADDRESS; len: LONGINT);
- VAR i : LONGINT;
- BEGIN
- IF Debug.Trace THEN
- KernelLog.String("[DATA: ");
- FOR i := 0 TO len - 1 DO
- KernelLog.Hex(SYSTEM.GET8(buffer + i), -2);
- IF i < len-1 THEN KernelLog.Char(" "); END;
- END;
- KernelLog.Char("]");
- END;
- END ShowData;
- PROCEDURE Show*(detailed : BOOLEAN);
- BEGIN
- IF Debug.Trace THEN
- CASE type OF
- | PipeControl: KernelLog.String(" Control ");
- | PipeInterrupt : KernelLog.String(" Interrupt ");
- | PipeBulk : KernelLog.String(" Bulk ");
- | PipeIsochronous : KernelLog.String(" Isochronous ");
- ELSE
- KernelLog.String("Unknown("); KernelLog.Int(type, 0); KernelLog.String(") ");
- END;
- CASE direction OF
- | Out: KernelLog.String("OUT");
- | In : KernelLog.String("IN");
- ELSE
- KernelLog.String("IN/OUT");
- END;
- KernelLog.String(" Pipe:"); KernelLog.String(" Adr: "); KernelLog.Int(address, 0); KernelLog.String(" Ep: "); KernelLog.Int(endpoint, 0);
- KernelLog.String(" ");
- IF ioc THEN KernelLog.String("[IOC]"); END;
- IF completionHandlerCaller # NIL THEN KernelLog.String("[Handler]"); END;
- IF irqActive THEN KernelLog.String("[IRQ_ACTIVE]"); END;
- IF speed = LowSpeed THEN KernelLog.String("[LowSpeed]");
- ELSIF speed = FullSpeed THEN KernelLog.String("[FullSpeed]");
- ELSIF speed = HighSpeed THEN KernelLog.String("[HighSpeed]");
- ELSE KernelLog.String("[ERROR: Not speed specified]");
- END;
- KernelLog.Ln;
- (*KernelLog.String(" Queue: "); KernelLog.Hex(queue, 8); KernelLog.String("H");
- KernelLog.String(", QH: "); KernelLog.Hex(qh, 8); KernelLog.String("H");
- KernelLog.String(", firstTD: "); KernelLog.Hex(firstTD, 8); KernelLog.String("H");
- KernelLog.String(", lastTD: "); KernelLog.Hex(lastTD, 8); KernelLog.String("H");
- KernelLog.Ln;*)
- IF detailed THEN
- IF type = PipeInterrupt THEN KernelLog.String(" IRQ Interval: "); KernelLog.Int(irqInterval, 0); KernelLog.String("ms, "); ELSE KernelLog.String(" "); END;
- KernelLog.String("Timeout: "); KernelLog.Int(timeout, 0); KernelLog.String("ms ");
- KernelLog.String(", MaxPacketSize: "); KernelLog.Int(maxPacketSize, 0); KernelLog.String(" Bytes");
- KernelLog.String(", MaxRetries: "); KernelLog.Int(maxRetries, 0);
- KernelLog.String(", Mode: ");
- CASE mode OF
- |Usbdi.Normal: KernelLog.String("Normal");
- |Usbdi.MaxPerformance: KernelLog.String("MaxPerformance");
- |Usbdi.MinCpu: KernelLog.String("MinCPU");
- ELSE
- KernelLog.String("Undefined ("); KernelLog.Int(mode, 0); KernelLog.String(")");
- END;
- KernelLog.String(", Last status: "); ShowStatus(status);
- KernelLog.String(", Last errors: "); ShowErrors(errors); KernelLog.Ln;
- END;
- END;
- END Show;
- END Pipe;
- TYPE
- Registry= OBJECT(Plugins.Registry) END Registry;
- VAR
- controllerCount : LONGINT; (* Only used for name creation - does not necessary reflect the actual HC count *)
- controllers- : (*Plugins.*)Registry;
- tokenPool: TransferToken;
- (** Copy data from array to array *)
- PROCEDURE Copy(VAR from, to: Usbdi.Buffer; fofs, tofs, len: LONGINT);
- BEGIN
- IF Debug.Trace & Debug.traceCopying THEN KernelLog.String("UsbHcdi: SG: Copying "); KernelLog.Int(len, 0); KernelLog.String(" Bytes."); KernelLog.Ln; END;
- IF len > 0 THEN
- ASSERT((fofs+len <= LEN(from)) & (tofs+len <= LEN(to)));
- SYSTEM.MOVE(UsbBuffers.GetDataAddress(from) + fofs, UsbBuffers.GetDataAddress(to) + tofs, len);
- END;
- END Copy;
- (** Assign a name to the host controller and add it to the controllers registry *)
- PROCEDURE RegisterHostController*(hcd : Hcd; CONST description : Plugins.Description);
- VAR name : Plugins.Name; res : LONGINT;
- BEGIN {EXCLUSIVE}
- name := "USBHC"; name[5] := CHR(controllerCount + 48); name[6] := 0X;
- hcd.SetName(name); hcd.desc := description;
- (* Register the host controller as AosPlugin *)
- controllers.Add(hcd, res);
- IF res # Plugins.Ok THEN (* ERROR: registering the host controller failed, should not happen *)
- KernelLog.Enter; KernelLog.String("UsbHcdi: Error: Couldn't add host controller to registry."); KernelLog.Exit;
- ELSE
- INC(controllerCount);
- END;
- END RegisterHostController;
- (** Remove all controllers with the specified description from the controllers registry *)
- PROCEDURE UnRegisterHostControllers*(CONST description : Plugins.Description);
- VAR table : Plugins.Table; hcd : Hcd; i : LONGINT;
- BEGIN {EXCLUSIVE}
- controllers.GetAll(table);
- IF table # NIL THEN
- FOR i := 0 TO LEN(table)-1 DO
- hcd := table[i] (Hcd);
- IF hcd.desc = description THEN
- hcd.Cleanup;
- controllers.Remove(hcd);
- END;
- END;
- END;
- END UnRegisterHostControllers;
- PROCEDURE Align*(address: ADDRESS; size: LONGINT): ADDRESS;
- BEGIN
- RETURN address + LONGINT(-address) MOD size;
- END Align;
- (**
- * Returns an AlignMemSpace with: memspace.data[memspace.base] is the first, <alignment>-aligned element of an
- * array of LONGINTs of the size <size>; parameters in bytes; alignmet has to be a power of two
- *)
- PROCEDURE GetAlignedMemSpace*(size, alignment : LONGINT ) : AlignedMemSpace;
- VAR memspace : AlignedMemSpace; base, adr: ADDRESS; len: SIZE;
- BEGIN
- ASSERT(alignment >= 4);
- NEW(memspace);
- len := (size + alignment) DIV 4;
- NEW(memspace.data, len ); (* so we will definitly find a <alignment>-aligned memory space of the size <size> *)
- adr := ADDRESSOF(memspace.data[0]);
- (* alignment *)
- base := adr + alignment - adr MOD alignment;
- (*base := adr + LONGINT(-adr) MOD alignment;*)
- ASSERT(base MOD alignment = 0);
- memspace.base := (base - adr) DIV 4;
- ASSERT(ADDRESSOF(memspace.data[memspace.base]) = base);
- RETURN memspace;
- END GetAlignedMemSpace;
- (** Display textual representation of the USB tranfer status bits defined in Usbdi.Mod *)
- PROCEDURE ShowStatus*(status : Usbdi.Status);
- BEGIN
- IF Debug.Trace THEN
- IF status = Usbdi.Ok THEN KernelLog.String("[Ok]"); END;
- IF status = Usbdi.ShortPacket THEN KernelLog.String("[ShortPacket]"); END;
- IF status = Usbdi.Stalled THEN KernelLog.String("[Stalled]"); END;
- IF status = Usbdi.InProgress THEN KernelLog.String("[InProgress]"); END;
- IF status = Usbdi.Error THEN KernelLog.String("[Error]"); END;
- IF status = Usbdi.Disconnected THEN KernelLog.String("[Disconnected]"); END;
- END;
- END ShowStatus;
- PROCEDURE ShowErrors*(errors : SET);
- BEGIN
- IF Debug.Trace THEN
- IF errors = NoErrors THEN KernelLog.String("[NoErrors]"); END;
- IF errors * ShortPacket # {} THEN KernelLog.String("[ShortPacket]"); END;
- IF errors * Stalled # {} THEN KernelLog.String("[Stalled]"); END;
- IF errors * InProgress # {} THEN KernelLog.String("[InProgress]"); END;
- IF errors * Nak # {} THEN KernelLog.String("[Nak]"); END;
- IF errors * Crc # {} THEN KernelLog.String("[Crc]"); END;
- IF errors * Timeout # {} THEN KernelLog.String("[Timeout]"); END;
- IF errors * CrcTimeout # {} THEN KernelLog.String("[CRC/Timeout]"); END;
- IF errors * BitStuff # {} THEN KernelLog.String("[Bitstuff]"); END;
- IF errors * Databuffer # {} THEN KernelLog.String("[Databuffer]"); END;
- IF errors * Babble # {} THEN KernelLog.String("[Babble]"); END;
- IF errors * Internal # {} THEN KernelLog.String("[Internal]"); END;
- IF errors * Disconnected # {} THEN KernelLog.String("[Disconnected]"); END;
- IF errors * UnexpectedPid # {} THEN KernelLog.String("[UnexpectedPid]"); END;
- IF errors * TransferTooLarge # {} THEN KernelLog.String("[TransferTooLarge]"); END;
- IF errors * PidCheckFailure # {} THEN KernelLog.String("[PidCheckFailure]"); END;
- IF errors * DataToggleMismatch # {} THEN KernelLog.String("[DatatoggleMismatch]"); END;
- IF errors * DeviceNotResponding # {} THEN KernelLog.String("[DeviceNotResponding]"); END;
- IF errors * LinkTDsFailed # {} THEN KernelLog.String("[TDLinkError]"); END;
- IF errors * OutOfTDs # {} THEN KernelLog.String("[OutOfTDs]"); END;
- END;
- END ShowErrors;
- (** Display textual represenation of the port status bits defined in UsbHcdi.Mod *)
- PROCEDURE ShowPortStatus*(status : SET);
- BEGIN
- IF Debug.Trace THEN
- IF status * PortStatusEnabled # {} THEN KernelLog.String("[Enabled]"); ELSE KernelLog.String("[Disabled]"); END;
- IF status * PortStatusDevicePresent # {} THEN
- IF status * PortStatusLowSpeed # {} THEN KernelLog.String("[LowSpeed]");
- ELSIF status * PortStatusFullSpeed # {} THEN KernelLog.String("[FullSpeed]");
- ELSIF status * PortStatusHighSpeed # {} THEN KernelLog.String("[HighSpeed]");
- ELSE
- KernelLog.String("[ERROR:Device connected but no speed indication, port enabled?]");
- END;
- END;
- IF status * PortStatusReset # {} THEN KernelLog.String("[Reset]"); END;
- IF status * PortStatusDevicePresent # {} THEN KernelLog.String("[DevicePresent]"); END;
- IF status * PortStatusError # {} THEN KernelLog.String("[Error]"); END;
- IF status * PortStatusConnectChange # {} THEN KernelLog.String("[ConnectChange]"); END;
- IF status * PortStatusSuspended # {} THEN KernelLog.String("[Suspended]"); END;
- IF status * PortStatusOverCurrent # {} THEN KernelLog.String("[OverCurrent]"); END;
- IF status * PortStatusPowered # {} THEN KernelLog.String("[Powered]"); END;
- IF status * PortStatusEnabledChange # {} THEN KernelLog.String("[EnabledChange]"); END;
- IF status * PortStatusSuspendChange # {} THEN KernelLog.String("[SuspendChange]"); END;
- IF status * PortStatusOverCurrentChange # {} THEN KernelLog.String("[OverCurrentChange]"); END;
- IF status * PortStatusWakeOnOvercurrent # {} THEN KernelLog.String("[WakeOnOvercurrent]"); END;
- IF status * PortStatusWakeOnDisconnect # {} THEN KernelLog.String("[WakeOnDisconnect]"); END;
- IF status * PortStatusWakeOnConnect # {} THEN KernelLog.String("[WakeOnConnect]"); END;
- IF status * PortStatusTestControl # {} THEN KernelLog.String("[TestControl]"); END;
- IF status * PortStatusIndicatorControl # {} THEN KernelLog.String("[IndicatorControl]"); END;
- IF status * PortStatusPortOwner # {} THEN KernelLog.String("[PortOwner]"); END;
- END;
- END ShowPortStatus;
- PROCEDURE GetToken (): TransferToken;
- VAR
- token: TransferToken;
- i: LONGINT;
- BEGIN {EXCLUSIVE}
- IF tokenPool = NIL THEN
- NEW(token)
- ELSE
- token := tokenPool;
- tokenPool := tokenPool.next;
- (* Clear token *)
- (*Machine.Fill32(SYSTEM.VAL(ADDRESS, token), SIZEOF(TransferToken), 0)*)
- token.pipe := NIL;
- token.buffer := 0;
- token.id := 0;
- token.len := 0;
- token.errors := {};
- token.status := 0;
- token.transfered := 0;
- (*token.message.value := NIL;*)
- FOR i := 0 TO MaxTDs - 1 DO token.tds[i] := 0 END;
- token.next := NIL
- END;
- RETURN token
- END GetToken;
- PROCEDURE PutToken (token: TransferToken);
- BEGIN {EXCLUSIVE}
- token.next := tokenPool;
- tokenPool := token
- END PutToken;
- PROCEDURE Cleanup;
- BEGIN
- Plugins.main.Remove(controllers);
- END Cleanup;
- BEGIN
- Modules.InstallTermHandler(Cleanup);
- NEW(controllers, "UsbHcdi","USB host controller drivers");
- END UsbHcdi.
|