MODULE UsbHubDriver;
IMPORT SYSTEM, KernelLog, Usb, UsbHcdi, Usbdi, Kernel, Modules, Debug := UsbDebug;
CONST
Name = "UsbHub";
Description = "USB Hub Driver";
AllowSuspend = FALSE;
PollingInterval = 200;
PsCurrentConnectStatus = {0};
PsPortEnabled = {1};
PsSuspend = {2};
PsOverCurrent = {3};
PsReset = {4};
PsPortPower = {8};
PsLowSpeed = {9};
PsHighSpeed = {10};
PsPortTestMode = {11};
PsPortIndicators = {12};
PsConnectStatusChange = {16};
PsPortEnabledChange = {17};
PsSuspendChange = {18};
PsOvercurrentChange = {19};
PsResetChange = {20};
PsChangeMask = {16..20};
HsLocalPowerLost = {0};
HsOvercurrent = {1};
HsLocalPowerSourceChange = {16};
HsOvercurrentChange = {17};
GetStatus = 0;
ClearFeature = 1;
SetFeature = 3;
GetDescriptor = 6;
SetDescriptor = 7;
ClearTtBuffer = 8;
ResetTt = 9;
GetTtState = 10;
StopTt = 11;
HubLocalPowerChange = 0;
HubOverCurrentChange = 1;
PortConnection = 0;
PortEnable = 1;
PortSuspend = 2;
PortOverCurrent = 3;
PortReset = 4;
PortPower = 8;
PortLowSpeed = 9;
PortConnectionChange = 16;
PortEnableChange = 17;
PortSuspendChange = 18;
PortOverCurrentChange = 19;
PortResetChange = 20;
PortTest = 21;
PortIndicator =22;
DescriptorHub = 29H;
DescriptorDevice = 1;
NotAvailable = UsbHcdi.NotAvailable;
Global = UsbHcdi.Global;
PerPort = UsbHcdi.PerPort;
ToDevice = Usbdi.ToDevice;
ToHost = Usbdi.ToHost;
Class = Usbdi.Class;
Device = Usbdi.Device;
Other = Usbdi.Other;
AllPorts = -1;
DeviceAttached = 0;
DeviceRemoved = 1;
StatusPipeMaxRetries = 5;
TYPE
HubInterface = OBJECT(Usbdi.Driver)
VAR
PROCEDURE GetHubDescriptor(type, index, length : LONGINT; VAR buffer : Usbdi.Buffer) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END GetHubDescriptor;
PROCEDURE SetHubDescriptor(type, index : LONGINT; buffer : Usbdi.Buffer) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END SetHubDescriptor;
PROCEDURE ClearHubFeature(feature : LONGINT) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END ClearHubFeature;
PROCEDURE SetHubFeature(feature : LONGINT) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END SetHubFeature;
PROCEDURE ClearPortFeature(feature, port, selector : LONGINT) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END ClearPortFeature;
PROCEDURE SetPortFeature(feature, port, selector : LONGINT) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END SetPortFeature;
PROCEDURE GetHubStatus(VAR hubstatus : SET) : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END GetHubStatus;
PROCEDURE GetPortStatus(port : LONGINT; ack : BOOLEAN) : SET;
BEGIN HALT(301); RETURN {}; END GetPortStatus;
PROCEDURE ClearTTBuffer(dev : Usb.UsbDevice; endpoint, port : LONGINT) : BOOLEAN;
BEGIN
RETURN FALSE;
END ClearTTBuffer;
PROCEDURE GetTTState(flags, port, len : LONGINT; VAR buffer : Usbdi.Buffer) : BOOLEAN;
BEGIN
RETURN FALSE;
END GetTTState;
PROCEDURE ResetTT(port : LONGINT) : BOOLEAN;
BEGIN
RETURN FALSE;
END ResetTT;
PROCEDURE StopTT(port : LONGINT) : BOOLEAN;
BEGIN
RETURN FALSE;
END StopTT;
PROCEDURE Initialize() : BOOLEAN;
BEGIN HALT(301); RETURN FALSE; END Initialize;
END HubInterface;
TYPE
HubDriver = OBJECT (HubInterface)
VAR
hub : Usb.UsbDevice;
nbrOfPorts : LONGINT;
pwrOn2pwrGood : LONGINT;
powerSwitching : LONGINT;
isCompound : BOOLEAN;
ocProtection : LONGINT;
thinkTime : LONGINT;
portIndicators : BOOLEAN;
ctrlCurrent : LONGINT;
deviceRemovable : POINTER TO ARRAY OF BOOLEAN;
timer : Kernel.Timer;
PROCEDURE EnablePortPower(port : LONGINT) : BOOLEAN;
VAR i : LONGINT;
BEGIN
IF port = AllPorts THEN
IF Debug.Trace & Debug.traceHubRequests THEN Show("Enable power on all ports"); KernelLog.Ln; END;
FOR i := 0 TO nbrOfPorts-1 DO
IF ~SetPortFeature(PortPower, i, 0) THEN
IF Debug.Level >= Debug.Errors THEN Show("Could not enable power on port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
RETURN FALSE;
END;
END;
Wait(pwrOn2pwrGood);
RETURN TRUE;
ELSE
IF Debug.Trace & Debug.traceHubRequests THEN Show("Enable power on port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
IF SetPortFeature(PortPower, port, 0) THEN
Wait(pwrOn2pwrGood);
RETURN TRUE;
END;
END;
RETURN FALSE;
END EnablePortPower;
PROCEDURE DisablePortPower(port : LONGINT) : BOOLEAN;
BEGIN
IF Debug.Trace & Debug.traceHubRequests THEN Show("Disable power on port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
RETURN ClearPortFeature(PortPower, port, 0);
END DisablePortPower;
PROCEDURE ResetAndEnablePort(port :LONGINT) : BOOLEAN;
VAR status : SET; timer : Kernel.Timer;
BEGIN
IF Debug.Trace & Debug.traceHubRequests THEN Show("Enable port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
IF SetPortFeature(PortReset, port, 0) THEN
NEW(timer); timer.Sleep(100);
status := GetPortStatus(port, FALSE);
IF status * UsbHcdi.PortStatusError # {} THEN
IF Debug.Level >= Debug.Errors THEN Show("Cannot get port status after enabling."); KernelLog.Ln; END;
RETURN FALSE;
ELSIF status * UsbHcdi.PortStatusReset # {} THEN
IF Debug.Level >= Debug.Errors THEN Show("Port still in reset (after 50ms!)"); KernelLog.Ln; END;
RETURN FALSE;
ELSIF status * UsbHcdi.PortStatusEnabled = {} THEN
IF Debug.Level >= Debug.Errors THEN Show("Could not enable port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
RETURN FALSE;
END;
ELSE
RETURN FALSE;
END;
RETURN TRUE;
END ResetAndEnablePort;
PROCEDURE DisablePort(port : LONGINT) : BOOLEAN;
BEGIN
IF Debug.Trace & Debug.traceHubRequests THEN Show("Disable port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
RETURN ClearPortFeature(PortEnable, port, 0);
END DisablePort;
PROCEDURE SuspendPort(port : LONGINT) : BOOLEAN;
BEGIN
IF Debug.Trace & (Debug.traceHubRequests OR Debug.traceSuspend) THEN Show("Suspend port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
IF SetPortFeature(PortSuspend, port, 0) THEN
hub.deviceAtPort[port].SetState(Usb.StateSuspended);
RETURN TRUE;
ELSIF Debug.Level >= Debug.Errors THEN Show("Failed to suspend port"); KernelLog.Int(port+1, 0); KernelLog.Ln;
END;
RETURN FALSE;
END SuspendPort;
PROCEDURE ResumePort(port : LONGINT) : BOOLEAN;
BEGIN
IF Debug.Trace & (Debug.traceHubRequests OR Debug.traceSuspend) THEN Show("Resume port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
IF ClearPortFeature(PortSuspend, port, 0) THEN
hub.deviceAtPort[port].SetState(Usb.StateConfigured);
RETURN TRUE;
ELSIF Debug.Level >= Debug.Errors THEN Show("Failed to resume port "); KernelLog.Int(port+1, 0); KernelLog.Ln;
END;
RETURN FALSE;
END ResumePort;
PROCEDURE DriversInstalled(dev : Usb.UsbDevice) : BOOLEAN;
VAR intf : Usb.InterfaceDescriptor; i : LONGINT;
BEGIN
FOR i := 0 TO LEN(dev.actConfiguration.interfaces)-1 DO
intf := dev.actConfiguration.interfaces[i] (Usb.InterfaceDescriptor);
IF intf.driver # NIL THEN RETURN TRUE; END;
END;
RETURN FALSE;
END DriversInstalled;
PROCEDURE Indicate(port, ledstatus : LONGINT);
BEGIN
IF Debug.StrongChecks THEN
ASSERT((ledstatus = UsbHcdi.Automatic) OR (ledstatus =UsbHcdi. Green) OR (ledstatus = UsbHcdi.Amber) OR (ledstatus = UsbHcdi.Off));
END;
IF portIndicators THEN
IF Debug.Trace & Debug.traceHubRequests THEN
Show("Set port indicator of port "); KernelLog.Int(port+1, 0); KernelLog.String(" to "); KernelLog.Int(ledstatus, 0); KernelLog.Ln;
END;
IF SetPortFeature(PortIndicator, port, ledstatus) THEN
ELSIF Debug.Level >= Debug.Errors THEN Show("Could not control port indicator."); KernelLog.Ln;
END;
END;
END Indicate;
PROCEDURE AvailableCurrent() : LONGINT;
VAR hubstatus : SET; current : LONGINT;
BEGIN
current := 0;
IF GetHubStatus(hubstatus) THEN
IF hubstatus * HsLocalPowerLost = {} THEN
ELSE
END;
ELSE
END;
RETURN current;
END AvailableCurrent;
PROCEDURE HandleHubStatusChange;
VAR hubstatus : SET; ignore : BOOLEAN;
BEGIN
IF Debug.Trace & Debug.traceConnects THEN Show("Handling hub status change."); KernelLog.Ln; END;
IF GetHubStatus(hubstatus) THEN
IF hubstatus * HsLocalPowerLost # {} THEN
IF Debug.Level >= Debug.Default THEN Show("Hub hast lost power supplier"); KernelLog.Ln; END;
END;
IF hubstatus * HsOvercurrent # {} THEN
IF Debug.Level >= Debug.Default THEN Show("Hub reports overcurrent condition"); KernelLog.Ln END;
END;
IF hubstatus * HsLocalPowerSourceChange # {} THEN
ignore := ClearHubFeature(HubLocalPowerChange);
END;
IF hubstatus * HsOvercurrentChange # {} THEN
ignore := ClearHubFeature(HubOverCurrentChange);
END;
ELSIF Debug.Level >= Debug.Errors THEN Show("Hub status change but could not get hub status."); KernelLog.Ln;
END;
END HandleHubStatusChange;
PROCEDURE LookForDevices;
VAR i : LONGINT; trap : BOOLEAN;
BEGIN
IF nbrOfPorts > 0 THEN
FOR i := 0 TO nbrOfPorts-1 DO
HandlePortStatusChange(i);
END;
END;
FINALLY
IF trap & (Debug.Level >= Debug.Warnings) THEN KernelLog.String("UsbHubDriver: TRAP catched."); KernelLog.Ln; END;
END LookForDevices;
PROCEDURE RemoveDeviceFromPort(port : LONGINT);
BEGIN
IF hub.deviceAtPort[port] # NIL THEN
hub.deviceAtPort[port].SetState(Usb.StateDisconnected);
hub.deviceAtPort[port].Remove;
hub.deviceAtPort[port] := NIL;
END;
END RemoveDeviceFromPort;
PROCEDURE HandlePortStatusChange(port : LONGINT);
CONST MaxPortStatusErrors = 10;
VAR dev : Usb.UsbDevice; status : SET; i : LONGINT; res : BOOLEAN;
BEGIN
IF Debug.Trace & Debug.traceHubRequests THEN Show("Handling port status change for port "); KernelLog.Int(port + 1, 0); KernelLog.Ln; END;
status := GetPortStatus(port, TRUE);
IF status * UsbHcdi.PortStatusError # {} THEN
INC(hub.portErrors[port]);
IF hub.portErrors[port] >= MaxPortStatusErrors THEN
IF Debug.Level >= Debug.Errors THEN Show("Error: Could not get status of port "); KernelLog.Int(port + 1, 0); KernelLog.Ln; END;
RemoveDeviceFromPort(port);
Indicate(port, UsbHcdi.Amber);
END;
RETURN;
ELSE
hub.portErrors[port] := 0;
END;
IF status * UsbHcdi.PortStatusOverCurrent # {} THEN
IF Debug.Level >= Debug.Default THEN Show("Warning: Overcurrent detected on port "); KernelLog.Int(port + 1, 0); KernelLog.Ln; END;
END;
IF status * UsbHcdi.PortStatusConnectChange # {} THEN
IF status * UsbHcdi.PortStatusDevicePresent # {} THEN
IF Debug.Trace & Debug.traceConnects THEN Show("Looking at device at port "); KernelLog.Int(port + 1, 0); KernelLog.Ln; END;
IF hub.deviceAtPort[port] # NIL THEN
IF Debug.Level >= Debug.Warnings THEN Show("Device already present. Remove it."); KernelLog.Ln; END;
RemoveDeviceFromPort(port);
END;
IF ~hub.portPermanentDisabled[port] THEN
Wait(UsbHcdi.PortInsertionTime);
hub.controller.Acquire;
res := ResetAndEnablePort(port);
IF res THEN
i := 0;
LOOP
dev := GetAddressedDevice(port);
IF dev # NIL THEN EXIT; END;
IF Debug.Trace & Debug.traceConnects THEN Show("Retrying to connect device."); KernelLog.Ln;END;
res := ResetAndEnablePort(port);
Wait(100 + i * 50);
INC(i);
IF i >=4 THEN EXIT END;
END;
IF dev = NIL THEN
res := DisablePort(port);
hub.controller.Release;
status := GetPortStatus(port, FALSE);
IF status * UsbHcdi.PortStatusDevicePresent = {} THEN
Indicate(port, UsbHcdi.Off);
ELSE
IF Debug.Level >= Debug.Default THEN
Show("Cannot access device. Permanently disabled port "); KernelLog.Int(port+1, 0);
KernelLog.String(". Replug connector of device!"); KernelLog.Ln;
END;
hub.portPermanentDisabled[port] := TRUE;
Indicate(port, UsbHcdi.Amber);
END;
ELSE
hub.controller.Release;
IF InquiryDevice(dev) THEN
dev.Register(hub, port);
IF Debug.Verbose THEN ShowDevice(DeviceAttached, port+1, dev); END;
Usb.drivers.ProbeDevice(dev);
IF ~DriversInstalled(dev) THEN
END;
Indicate(port, UsbHcdi.Green);
ELSE
IF Debug.Level >= Debug.Default THEN Show("Failed to inquiry addressed device at port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
IF ~DisablePort(port) THEN END;
Indicate(port, UsbHcdi.Amber);
END;
END;
ELSE
hub.controller.Release;
IF (hub.parent = hub) & hub.controller.isHighSpeed THEN
status := GetPortStatus(port, FALSE);
IF (status * UsbHcdi.PortStatusEnabled = {}) & (status * UsbHcdi.PortStatusDevicePresent # {}) THEN
hub.controller.RoutePortToCompanion(port);
END;
ELSE
IF Debug.Level >= Debug.Default THEN Show("Could not enable port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
hub.portPermanentDisabled[port] := TRUE;
Indicate(port, UsbHcdi.Amber);
END;
END;
ELSE
IF Debug.Level >= Debug.Default THEN Show("Device connected to permanently disabled port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
END;
ELSE
IF hub.deviceAtPort[port] # NIL THEN
IF Debug.Verbose THEN ShowDevice(DeviceRemoved, port, hub.deviceAtPort[port]); END;
RemoveDeviceFromPort(port);
END;
res := DisablePort(port);
hub.portPermanentDisabled[port] := FALSE;
hub.portErrors[port] := 0;
Indicate(port, UsbHcdi.Off);
END;
END;
status := GetPortStatus(port, FALSE);
IF status * UsbHcdi.PortStatusDevicePresent = {} THEN
IF hub.deviceAtPort[port] # NIL THEN
IF Debug.Level >= Debug.Warnings THEN Show("Port indicates no device present, but USB driver has one."); KernelLog.Ln; END;
RemoveDeviceFromPort(port);
hub.portPermanentDisabled[port] := FALSE;
END;
END;
IF status * UsbHcdi.PortStatusEnabled # {} THEN
IF hub.deviceAtPort[port] = NIL THEN
IF Debug.Level >= Debug.Warnings THEN Show("Port was enabled, but USB software did not know it!"); KernelLog.Ln; END;
RemoveDeviceFromPort(port);
res := DisablePort(port);
Indicate(port, UsbHcdi.Off);
END;
END;
END HandlePortStatusChange;
PROCEDURE GetTransactionTranslator(device : Usb.UsbDevice) : BOOLEAN;
VAR dev : Usb.UsbDevice;
BEGIN
dev := device;
IF dev.controller.isHighSpeed & (dev.speed # Usb.HighSpeed) THEN
WHILE (dev.parent # NIL) & (dev.parent.speed # Usb.HighSpeed) DO dev := dev.parent; END;
IF dev # NIL THEN
device.ttAddress := dev.parent.address; device.ttPort := dev.port;
IF Debug.Trace & Debug.traceConnects THEN
Show("TT Address: "); KernelLog.Int(device.ttAddress, 0); KernelLog.String(", TT Port: "); KernelLog.Int(device.ttPort, 0); KernelLog.Ln;
END;
RETURN TRUE;
ELSE
IF Debug.Level >= Debug.Errors THEN Show("Could not find transaction translator."); KernelLog.Ln; END;
RETURN FALSE;
END;
ELSE
device.ttAddress := 0; device.ttPort := 0;
RETURN TRUE;
END;
END GetTransactionTranslator;
PROCEDURE GetAddressedDevice(port : LONGINT) : Usb.UsbDevice;
VAR
dev : Usb.UsbDevice; defaultpipe : UsbHcdi.Pipe;
descriptor : Usb.DeviceDescriptor;
adr : LONGINT;
status : SET;
BEGIN
IF Debug.Trace & Debug.traceConnects THEN Show("Assign address to device at port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
status := GetPortStatus(port, FALSE);
IF status * UsbHcdi.PortStatusError # {} THEN
IF Debug.Level >= Debug.Errors THEN Show("GetAddressedDevice: Cannot get status of port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
RETURN NIL;
ELSIF status * UsbHcdi.PortStatusDevicePresent = {} THEN
IF Debug.Level >= Debug.Errors THEN Show("GetAddressedDevice: Device no more present ??"); KernelLog.Ln; END;
RETURN NIL;
END;
NEW(dev); NEW(descriptor);
dev.descriptor := descriptor;
dev.address := 0;
dev.controller := hub.controller;
dev.parent := hub;
dev.port := port;
dev.SetState(Usb.StateDefault);
IF status * UsbHcdi.PortStatusLowSpeed # {} THEN
dev.speed := UsbHcdi.LowSpeed;
ELSIF status * UsbHcdi.PortStatusFullSpeed # {} THEN
dev.speed := UsbHcdi.FullSpeed;
ELSIF status * UsbHcdi.PortStatusHighSpeed # {} THEN
dev.speed := UsbHcdi.HighSpeed;
ELSE
IF Debug.Level >= Debug.Errors THEN Show("Device speed error"); KernelLog.Ln; END;
RETURN NIL;
END;
IF ~GetTransactionTranslator(dev) THEN
RETURN NIL;
END;
defaultpipe := hub.controller.GetDefaultPipe(dev.speed, dev.ttPort, dev.ttAddress, dev);
IF defaultpipe = NIL THEN
IF Debug.Level >= Debug.Errors THEN Show("Couldn't get default pipe."); KernelLog.Ln; END;
RETURN NIL;
END;
adr := hub.controller.GetFreeAddress();
IF adr = 0 THEN
KernelLog.String("Usb: Cannot configure device: No free device addresses. "); KernelLog.Ln;
dev.FreePipe(defaultpipe);
RETURN NIL;
END;
dev.defaultpipe := defaultpipe;
IF ~dev.SetAddress(adr) THEN
dev.FreePipe(dev.defaultpipe);
hub.controller.FreeAll(adr);
hub.controller.FreeAddress(adr);
IF Debug.Level >= Debug.Warnings THEN Show("Address Setup failed."); KernelLog.Ln;END;
RETURN NIL;
END;
Wait(UsbHcdi.AddressRecoveryTime);
dev.SetState(Usb.StateAddress);
dev.FreePipe(dev.defaultpipe);
RETURN dev;
END GetAddressedDevice;
PROCEDURE InquiryDevice(dev : Usb.UsbDevice) : BOOLEAN;
VAR
defaultpipe, tempPipe : UsbHcdi.Pipe;
buffer : Usbdi.BufferPtr;
BEGIN
NEW(defaultpipe, dev.address, 0, dev.controller);
dev.defaultpipe := defaultpipe;
dev.defaultpipe.device := dev;
dev.defaultpipe.completion.device := dev;
dev.defaultpipe.address := dev.address;
dev.defaultpipe.maxRetries := 3;
dev.defaultpipe.type := UsbHcdi.PipeControl;
dev.defaultpipe.maxPacketSize := 8;
dev.defaultpipe.speed := dev.speed;
dev.defaultpipe.timeout := Usb.DefaultTimeout;
IF GetTransactionTranslator(dev) THEN
dev.defaultpipe.ttAddress := dev.ttAddress;
dev.defaultpipe.ttPort := dev.ttPort;
ELSE
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
RETURN FALSE;
END;
hub.controller.GetPipe(dev.address, 0, dev.defaultpipe);
IF dev.defaultpipe = NIL THEN
IF Debug.Level >= Debug.Errors THEN Show("InquiryDevice: Could not register the default control pipe"); KernelLog.Ln; END;
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
RETURN FALSE;
END;
NEW(buffer, 8);
IF ~dev.GetDescriptor(DescriptorDevice, 0, 0, 8, buffer^) THEN
IF Debug.Level >= Debug.Errors THEN Show("InquiryDevice: Read first 8 bytes of device descriptor failed."); KernelLog.Ln; END;
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
RETURN FALSE;
END;
dev.defaultpipe.maxPacketSize := ORD(buffer[7]);
tempPipe := dev.defaultpipe;
dev.FreePipe(dev.defaultpipe);
tempPipe.device := dev;
hub.controller.GetPipe(dev.address, 0, tempPipe);
IF tempPipe = NIL THEN
IF Debug.Level >= Debug.Errors THEN Show("InquiryDevice: Could not register the default control pipe"); KernelLog.Ln; END;
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
RETURN FALSE;
END;
dev.defaultpipe := tempPipe;
dev.defaultpipe.completion.device := dev;
IF ~dev.GetDeviceDescriptor() OR ~dev.GetConfigurations()THEN
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
IF Debug.Level >= Debug.Errors THEN Show("Parsing descriptors failed."); KernelLog.Ln; END;
RETURN FALSE;
END;
IF dev.descriptor.bcdUSB >= 0200H THEN
IF Debug.Trace & Debug.traceConnects THEN Show("Get device qualifier."); KernelLog.Ln; END;
IF ~dev.GetDeviceQualifier() THEN
IF Debug.Level >= Debug.Errors THEN Show("Couldn't get device qualifier."); KernelLog.Ln; END;
ELSIF dev.GetOtherSpeedConfigurations() THEN
IF ~dev.controller.isHighSpeed THEN
KernelLog.String("UsbHubDriver: Warning: Connected high-speed capable device to low-/full-speed controller."); KernelLog.Ln;
END;
ELSE
IF Debug.Level >= Debug.Errors THEN Show("Couldn't get other speed configurations"); KernelLog.Ln; END;
END;
END;
IF ~ValidTopology(dev, hub) THEN
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
Show("Topology constraints violated. Cannot configure device."); KernelLog.Ln;
RETURN FALSE;
END;
IF ~EnoughPower(dev, hub) THEN
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
Show("Not enough power available. Cannot configure device."); KernelLog.Ln;
RETURN FALSE;
END;
IF ~dev.SetConfiguration(0) THEN
hub.controller.FreeAll(dev.address);
hub.controller.FreeAddress(dev.address);
IF Debug.Level >= Debug.Errors THEN Show("Could not set configuration"); KernelLog.Ln; END;
RETURN FALSE;
END;
dev.SetState(Usb.StateConfigured);
Usb.GetStrings(dev);
RETURN TRUE;
END InquiryDevice;
PROCEDURE ParseHubDescriptor(buffer : ARRAY OF CHAR) : BOOLEAN;
VAR i : LONGINT;
BEGIN
IF (LEN(buffer) < 2) OR (ORD(buffer[0]) < 7) OR (ORD(buffer[1]) # DescriptorHub) THEN RETURN FALSE; END;
nbrOfPorts := ORD(buffer[2]);
i := SYSTEM.VAL(LONGINT, SYSTEM.VAL(SET, ORD(buffer[3])) * {0..1});
CASE i OF
0 : powerSwitching := Global;
|1 : powerSwitching := PerPort;
ELSE
powerSwitching := NotAvailable;
END;
IF SYSTEM.VAL(SET, ORD(buffer[3])) * {2} # {} THEN isCompound := TRUE; END;
IF SYSTEM.VAL(SET, ORD(buffer[3])) * {7} # {} THEN portIndicators := TRUE; END;
i := SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, ORD(buffer[3])) * {3..4}, -3));
CASE i OF
0 : ocProtection := Global;
|1 : ocProtection := PerPort;
ELSE
ocProtection := NotAvailable;
END;
thinkTime := SYSTEM.VAL(LONGINT, SYSTEM.LSH(SYSTEM.VAL(SET, ORD(buffer[3])) * {3..4}, -3));
pwrOn2pwrGood := ORD(buffer[5]) * 2;
ctrlCurrent := ORD(buffer[6]);
RETURN TRUE;
END ParseHubDescriptor;
PROCEDURE Connect() : BOOLEAN;
VAR buffer : Usbdi.BufferPtr; len : LONGINT;
BEGIN
hub := device (Usb.UsbDevice);
NEW(buffer, 2);
IF ~GetHubDescriptor(DescriptorHub, 0, 2, buffer^) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbHubDriver: Could not get first two bytes of hub descriptor."); KernelLog.Ln; END;
RETURN FALSE;
END;
len := ORD(buffer[0]); NEW(buffer, len);
IF ~GetHubDescriptor(DescriptorHub, 0, SYSTEM.VAL(LONGINT, len), buffer^) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbHubDriver: Could not get hub descriptor."); KernelLog.Ln; END;
RETURN FALSE;
END;
IF ~ParseHubDescriptor(buffer^) THEN
IF Debug.Level >= Debug.Errors THEN KernelLog.String("UsbHubDriver: Failed to parse hub descriptor."); KernelLog.Ln; END;
RETURN FALSE;
END;
hub.hubFlag := TRUE;
hub.nbrOfPorts := nbrOfPorts;
NEW(hub.deviceAtPort, nbrOfPorts);
NEW(hub.portPermanentDisabled, nbrOfPorts);
NEW(hub.portErrors, nbrOfPorts);
IF Debug.Trace & Debug.traceInfo THEN ShowInfo; END;
IF ~EnablePortPower(AllPorts) THEN
IF Debug.Level >= Debug.Errors THEN Show("Error: Could not enable port power"); KernelLog.Ln; END;
RETURN FALSE;
END;
IF Debug.Verbose THEN Show(""); KernelLog.Int(nbrOfPorts, 0); KernelLog.String(" ports detected."); KernelLog.Ln; END;
RETURN Initialize();
END Connect;
PROCEDURE ValidTopology(dev, parent : Usb.UsbDevice) : BOOLEAN;
VAR segments : LONGINT; temp : Usb.UsbDevice;
BEGIN
IF dev.hubFlag THEN
temp := dev;
WHILE temp.parent # dev DO
INC(segments);
temp := temp.parent;
END;
IF segments > 6 THEN
Show("Bus topology constraint not met: maximum of 6 cable segment between device and host."); KernelLog.Ln;
RETURN FALSE;
END;
END;
RETURN TRUE;
END ValidTopology;
PROCEDURE EnoughPower(dev, parent : Usb.UsbDevice) : BOOLEAN;
VAR status : SET;
BEGIN
IF dev.GetStatus(Device, 0, status) THEN
IF status * Usb.SelfPowered # {} THEN
IF Debug.Trace & Debug.traceConnects THEN Show(""); dev.ShowName; KernelLog.String(" is self-powered."); KernelLog.Ln; END;
ELSE
IF Debug.Trace & Debug.traceConnects THEN Show(""); dev.ShowName; KernelLog.String(" is bus-powered."); KernelLog.Ln; END;
END;
ELSE
IF Debug.Level >= Debug.Errors THEN Show("GetStatus request failed."); KernelLog.Ln; END;
END;
RETURN TRUE;
END EnoughPower;
PROCEDURE Disconnect;
BEGIN
IF Debug.Verbose THEN Show(" disconnected."); KernelLog.Ln;END;
END Disconnect;
PROCEDURE Wait(ms : LONGINT);
BEGIN {EXCLUSIVE}
timer.Sleep(ms)
END Wait;
PROCEDURE &New*;
BEGIN
NEW(timer);
END New;
PROCEDURE ShowDevice(mode, port : LONGINT; dev : Usb.UsbDevice);
BEGIN
IF Debug.StrongChecks THEN ASSERT((dev # NIL) & ((mode = DeviceAttached) OR (mode = DeviceRemoved))); END;
KernelLog.String("UsbHubDriver: "); dev.ShowName;
IF mode = DeviceAttached THEN
KernelLog.String(" attached to "); KernelLog.String(hub.controller.name); KernelLog.String(" port ");
KernelLog.Int(port, 0); KernelLog.String("."); KernelLog.Ln;
ELSE
KernelLog.String(" has been detached."); KernelLog.Ln;
END;
END ShowDevice;
PROCEDURE ShowInfo;
VAR i : LONGINT;
BEGIN
IF Debug.Trace THEN
Show(" Capabilities:"); KernelLog.Ln;
KernelLog.String(" Compound device: "); IF isCompound THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.String(", Port indicator control: "); IF portIndicators THEN KernelLog.String("Yes"); ELSE KernelLog.String("No"); END;
KernelLog.String(", Power switching support: ");
IF powerSwitching = NotAvailable THEN KernelLog.String("n/a");
ELSIF powerSwitching = Global THEN KernelLog.String("Global");
ELSIF powerSwitching = PerPort THEN KernelLog.String("Per port");
ELSE KernelLog.String("Error: "); KernelLog.Int(powerSwitching, 0);
END;
KernelLog.String(", Overcurrent protection: ");
IF ocProtection = NotAvailable THEN KernelLog.String("n/a");
ELSIF ocProtection = Global THEN KernelLog.String("Global");
ELSIF ocProtection = PerPort THEN KernelLog.String("Per port");
ELSE KernelLog.String("Error: "); KernelLog.Int(ocProtection, 0);
END;
KernelLog.Ln;
KernelLog.String(" Power On 2 Power Good: "); KernelLog.Int(pwrOn2pwrGood, 0); KernelLog.String(" ms");
KernelLog.String(", Control logic current: "); KernelLog.Int(ctrlCurrent, 0); KernelLog.String(" mA");
KernelLog.String(", Think time: "); KernelLog.Int(thinkTime, 0); KernelLog.String(" ms"); KernelLog.Ln;
KernelLog.String(" Number of downstream ports: "); KernelLog.Int(nbrOfPorts, 0); KernelLog.Ln;
FOR i := 0 TO nbrOfPorts-1 DO
KernelLog.String(" Port "); KernelLog.Int(i, 0); KernelLog.String(": ");
IF (deviceRemovable # NIL) & deviceRemovable[i] THEN KernelLog.String("[Removable]"); END;
UsbHcdi.ShowPortStatus(GetPortStatus(i, FALSE));
KernelLog.Ln;
END;
KernelLog.Ln;
END;
END ShowInfo;
PROCEDURE Show(CONST text : ARRAY OF CHAR);
BEGIN
KernelLog.String("UsbHubDriver: Hub "); hub.ShowName;
KernelLog.String(" attached to "); KernelLog.String(hub.controller.name); KernelLog.String(" port ");
KernelLog.Int(hub.port + 1, 0); KernelLog.String(": "); KernelLog.String(text);
END Show;
END HubDriver;
TYPE
UsbHubDriver = OBJECT(HubDriver)
VAR
statusPipe : Usbdi.Pipe;
statusBuffer : Usbdi.BufferPtr;
statusPipeRetries : LONGINT;
PROCEDURE GetHubDescriptor(type, index, length : LONGINT; VAR buffer : Usbdi.Buffer) : BOOLEAN;
BEGIN
ASSERT(length >= 2);
RETURN (hub.defaultpipe.Request(ToHost + Class + Device, GetDescriptor, index + type*100H, 0, length, buffer) = Usbdi.Ok) &
(ORD(buffer[1]) = type);
END GetHubDescriptor;
PROCEDURE SetHubDescriptor(type, index : LONGINT; buffer : Usbdi.Buffer) : BOOLEAN;
BEGIN
ASSERT((LEN(buffer) >= 2) & (ORD(buffer[0]) = LEN(buffer)) & (ORD(buffer[1]) = type));
RETURN hub.defaultpipe.Request(ToDevice + Class + Device, SetDescriptor, index + type*100H, 0, LEN(buffer), buffer) = Usbdi.Ok;
END SetHubDescriptor;
PROCEDURE ClearHubFeature(feature : LONGINT) : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN ASSERT((feature = HubLocalPowerChange) OR (feature = HubOverCurrentChange)); END;
IF Debug.Trace & Debug.traceHubRequests THEN Show("Clear hub feature "); KernelLog.Int(feature, 0); KernelLog.Ln; END;
RETURN hub.defaultpipe.Request(ToDevice + Class + Device, ClearFeature, feature, 0, 0, Usbdi.NoData) = Usbdi.Ok;
END ClearHubFeature;
PROCEDURE SetHubFeature(feature : LONGINT) : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN ASSERT((feature = HubLocalPowerChange) OR (feature = HubOverCurrentChange)); END;
IF Debug.Trace & Debug.traceHubRequests THEN Show("Set hub feature "); KernelLog.Int(feature, 0); KernelLog.Ln; END;
RETURN hub.defaultpipe.Request(ToDevice + Class + Device, SetFeature, feature, 0, 0, Usbdi.NoData) = Usbdi.Ok;
END SetHubFeature;
PROCEDURE ClearPortFeature(feature, port, selector : LONGINT) : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN
ASSERT(((feature # PortTest) & (feature # PortIndicator)) OR (selector = 0));
ASSERT((feature > 0) & (feature <= 22) & (feature # PortConnection));
END;
IF Debug.Trace & Debug.traceHubRequests THEN Show("Port "); KernelLog.Int(port + 1, 0); KernelLog.String(": Clear feature "); KernelLog.Int(feature, 0); KernelLog.Ln; END;
RETURN hub.defaultpipe.Request(ToDevice + Class + Other, ClearFeature, feature, (port + 1) + SYSTEM.LSH(selector, 8), 0, Usbdi.NoData) = Usbdi.Ok;
END ClearPortFeature;
PROCEDURE SetPortFeature(feature, port, selector : LONGINT) : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN
ASSERT(((feature = PortTest) OR (feature = PortIndicator)) OR (selector = 0));
ASSERT((feature > 0) & (feature <= 22) & (feature # PortConnection));
END;
IF Debug.Trace & Debug.traceHubRequests THEN Show("Port "); KernelLog.Int(port + 1, 0); KernelLog.String(": Set feature "); KernelLog.Int(feature, 0); KernelLog.Ln; END;
RETURN hub.defaultpipe.Request(ToDevice + Class + Other, SetFeature, feature, (port + 1) + SYSTEM.LSH(selector, 8), 0, Usbdi.NoData) = Usbdi.Ok;
END SetPortFeature;
PROCEDURE GetHubStatus(VAR hubstatus : SET) : BOOLEAN;
VAR buffer : Usbdi.BufferPtr;
BEGIN
IF Debug.Trace & Debug.traceHubRequests THEN Show("Get Hub Status."); KernelLog.Ln; END;
NEW(buffer, 4);
IF hub.defaultpipe.Request(ToHost + Class + Device, GetStatus, 0, 0, 4, buffer^) = Usbdi.Ok THEN
hubstatus := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.ADR(buffer[0])));
RETURN TRUE;
END;
RETURN FALSE;
END GetHubStatus;
PROCEDURE GetPortStatus(port : LONGINT; ack : BOOLEAN) : SET;
VAR buffer : Usbdi.BufferPtr; s, portstatus : SET;
BEGIN
IF Debug.StrongChecks THEN ASSERT(port >= 0); END;
IF Debug.Trace & Debug.traceHubRequests THEN
Show("Get port status of port "); KernelLog.Int(port + 1, 0); IF ack THEN KernelLog.String(" (ACK)"); END; KernelLog.Ln;
END;
NEW(buffer, 4);
IF hub.defaultpipe.Request(ToHost + Class + Other, GetStatus, 0, port+1, 4, buffer^) = Usbdi.Ok THEN
s := SYSTEM.VAL(SET, SYSTEM.GET32(SYSTEM.ADR(buffer[0])));
IF ack & (s * PsChangeMask # {}) THEN
IF s * PsConnectStatusChange # {} THEN
IF ~ClearPortFeature(PortConnectionChange, port, 0) THEN
RETURN UsbHcdi.PortStatusError;
END;
END;
IF s * PsPortEnabledChange # {} THEN
IF ~ClearPortFeature(PortEnableChange, port, 0) THEN
RETURN UsbHcdi.PortStatusError;
END;
END;
IF s * PsSuspendChange # {} THEN
IF ~ClearPortFeature(PortSuspendChange, port, 0) THEN
RETURN UsbHcdi.PortStatusError;
END;
END;
IF s * PsOvercurrentChange # {} THEN
IF ~ClearPortFeature(PortOverCurrentChange, port, 0) THEN
RETURN UsbHcdi.PortStatusError;
END;
END;
IF s * PsResetChange # {} THEN
IF ~ClearPortFeature(PortResetChange, port, 0) THEN
RETURN UsbHcdi.PortStatusError;
END;
END;
END;
IF s * PsCurrentConnectStatus # {} THEN
portstatus := portstatus + UsbHcdi.PortStatusDevicePresent;
IF s * PsPortEnabled # {} THEN
portstatus := portstatus + UsbHcdi.PortStatusEnabled;
IF s * PsLowSpeed # {} THEN
portstatus := portstatus + UsbHcdi.PortStatusLowSpeed;
ELSIF s * PsHighSpeed # {} THEN
portstatus := portstatus + UsbHcdi.PortStatusHighSpeed;
ELSE
portstatus := portstatus + UsbHcdi.PortStatusFullSpeed;
END;
END;
END;
IF s * PsSuspend # {} THEN portstatus := portstatus + UsbHcdi.PortStatusSuspended; END;
IF s * PsOverCurrent # {} THEN portstatus := portstatus + UsbHcdi.PortStatusOverCurrent; END;
IF s * PsReset # {} THEN portstatus := portstatus + UsbHcdi.PortStatusReset; END;
IF s * PsPortPower # {} THEN portstatus := portstatus + UsbHcdi.PortStatusPowered; END;
IF s * PsPortTestMode # {} THEN portstatus := portstatus + UsbHcdi.PortStatusTestControl; END;
IF s * PsPortIndicators # {} THEN portstatus := portstatus + UsbHcdi.PortStatusIndicatorControl; END;
IF s * PsConnectStatusChange # {} THEN portstatus := portstatus + UsbHcdi.PortStatusConnectChange; END;
IF s * PsPortEnabledChange # {} THEN portstatus := portstatus + UsbHcdi.PortStatusEnabledChange; END;
IF s * PsSuspendChange # {} THEN portstatus := portstatus + UsbHcdi.PortStatusSuspendChange; END;
IF s * PsOvercurrentChange # {} THEN portstatus := portstatus + UsbHcdi.PortStatusOverCurrentChange; END;
IF s * PsResetChange # {} THEN portstatus := portstatus + UsbHcdi.PortStatusResetChange; END;
IF Debug.Trace & Debug.traceHubRequests THEN
Show("Status of port "); KernelLog.Int(port + 1, 0); UsbHcdi.ShowPortStatus(portstatus); KernelLog.Ln;
END;
RETURN portstatus;
ELSE
IF Debug.Level >= Debug.Errors THEN Show("Can't get port status of port "); KernelLog.Int(port+1, 0); KernelLog.Ln; END;
RETURN UsbHcdi.PortStatusError;
END;
END GetPortStatus;
PROCEDURE HandleStatusChange(status : Usbdi.Status; actLen : LONGINT);
VAR ignore : Usbdi.Status; i, port : LONGINT;
BEGIN
IF Debug.Trace & Debug.traceConnects THEN
Show("Hub reports status change: "); FOR i := 0 TO LEN(statusBuffer)-1 DO KernelLog.Hex(ORD(statusBuffer[i]), -2); END; KernelLog.Ln;
END;
IF (status = Usbdi.Ok) OR ((status = Usbdi.ShortPacket) & (actLen > 0)) THEN
IF SYSTEM.VAL(SET, statusBuffer[0]) * {0} # {} THEN
IF Debug.Trace & Debug.traceConnects THEN Show("Hub status changed."); END;
statusBuffer[0] := SYSTEM.VAL(CHAR, SYSTEM.VAL(SET, statusBuffer[0]) - {0});
HandleHubStatusChange;
END;
FOR i := 0 TO actLen-1 DO
FOR port := 0 TO 7 DO
IF SYSTEM.VAL(SET, statusBuffer[i]) * {port} # {} THEN
HandlePortStatusChange(port + i * 8 - 1);
END;
END;
END;
ignore := statusPipe.Transfer(statusPipe.maxPacketSize, 0, statusBuffer^);
statusPipeRetries := 0;
ELSE
IF statusPipeRetries > StatusPipeMaxRetries THEN
IF Debug.Level >= Debug.Errors THEN Show("Status pipe error "); UsbHcdi.ShowStatus(status); KernelLog.Ln; END;
RETURN;
END;
IF (status = Usbdi.Stalled) THEN
IF ~statusPipe.ClearHalt() THEN
IF Debug.Level >= Debug.Errors THEN Show("Could not recover from status pipe error."); KernelLog.Ln; END;
RETURN;
END;
ELSIF (status = Usbdi.Disconnected) THEN
RETURN;
END;
ignore := statusPipe.Transfer(statusPipe.maxPacketSize, 0, statusBuffer^);
INC(statusPipeRetries);
END;
END HandleStatusChange;
PROCEDURE Initialize() : BOOLEAN;
VAR endpoint : Usbdi.EndpointDescriptor; ignore : Usbdi.Status;
BEGIN
endpoint := hub.actConfiguration.interfaces[0].endpoints[0];
ASSERT(endpoint.type = Usbdi.InterruptIn);
statusPipe := hub.GetPipe(endpoint.bEndpointAddress);
IF statusPipe = NIL THEN
IF Debug.Level >= Debug.Errors THEN Show("Could not establish status pipe."); KernelLog.Ln; END;
RETURN FALSE;
END;
NEW(statusBuffer, statusPipe.maxPacketSize);
statusPipe.SetTimeout(0);
statusPipe.SetCompletionHandler(HandleStatusChange);
ignore := statusPipe.Transfer(statusPipe.maxPacketSize, 0, statusBuffer^);
RETURN TRUE;
END Initialize;
PROCEDURE ClearTTBuffer(dev : Usb.UsbDevice; endpoint, port : LONGINT) : BOOLEAN;
VAR intf : Usb.InterfaceDescriptor; endp : Usb.EndpointDescriptor; wValue : SET; i, e : LONGINT;
BEGIN
IF Debug.StrongChecks THEN ASSERT((dev.speed # Usb.HighSpeed) & (dev.parent.speed = Usb.HighSpeed)); END;
LOOP
IF i > dev.actConfiguration.bNumInterfaces-1 THEN EXIT END;
intf := dev.actConfiguration.interfaces[i] (Usb.InterfaceDescriptor);
FOR e := 0 TO LEN(intf.endpoints)-1 DO
IF intf.endpoints[e].bEndpointAddress = endpoint THEN
endp := intf.endpoints[e] (Usb.EndpointDescriptor);
END;
END;
IF endp # NIL THEN EXIT END;
INC(i);
END;
IF endp = NIL THEN RETURN FALSE END;
IF (endp.bmAttributes * {0,1} # {}) OR (endp.bmAttributes * {0,1} # {1}) THEN
IF Debug.Level >= Debug.Warnings THEN Show("ClearTTBuffer error: Only allowed for non-periodic endpoints"); KernelLog.Ln; END;
RETURN FALSE;
END;
wValue := SYSTEM.VAL(SET, endp.bEndpointAddress) * {0..3} + SYSTEM.LSH(SYSTEM.VAL(SET, dev.address), 4) * {4..10};
wValue := wValue + SYSTEM.LSH(endp.bmAttributes ,11) * {11..12} + SYSTEM.LSH(SYSTEM.VAL(SET, endp.bEndpointAddress) * {7}, 8);
RETURN hub.defaultpipe.Request(ToDevice + Class + Other, ClearTtBuffer, SYSTEM.VAL(LONGINT, wValue), (port + 1), 0, Usbdi.NoData) = Usbdi.Ok;
END ClearTTBuffer;
PROCEDURE GetTTState(flags, port, len : LONGINT; VAR buffer : Usbdi.Buffer) : BOOLEAN;
BEGIN
RETURN hub.defaultpipe.Request(ToDevice + Class + Other, GetTtState, flags, (port + 1), len, buffer) = Usbdi.Ok;
END GetTTState;
PROCEDURE ResetTT(port : LONGINT) : BOOLEAN;
BEGIN
RETURN hub.defaultpipe.Request(ToDevice + Class + Other, ResetTt, 0, (port + 1), 0, Usbdi.NoData) = Usbdi.Ok;
END ResetTT;
PROCEDURE StopTT(port : LONGINT) : BOOLEAN;
BEGIN
RETURN hub.defaultpipe.Request(ToDevice + Class + Other, StopTt, 0, (port + 1), 0, Usbdi.NoData) = Usbdi.Ok;
END StopTT;
END UsbHubDriver;
TYPE
RootHubDriver = OBJECT (HubDriver)
VAR
next: RootHubDriver;
timerRH : Kernel.Timer;
alive, dead, statusChange : BOOLEAN;
pollingInterval : LONGINT;
initialized : BOOLEAN;
PROCEDURE GetHubDescriptor(type, index, length : LONGINT; VAR buffer : Usbdi.Buffer) : BOOLEAN;
VAR i : LONGINT; hd : UsbHcdi.HubDescriptor;
BEGIN
IF Debug.StrongChecks THEN ASSERT(LEN(buffer) <= length); END;
hd := device(Usb.UsbDevice).controller.GetHubDescriptor();
IF hd = NIL THEN RETURN FALSE END;
IF length > LEN(hd) THEN length := LEN(hd); END;
FOR i := 0 TO length-1 DO buffer[i] := hd[i]; END;
RETURN TRUE;
END GetHubDescriptor;
PROCEDURE SetHubDescriptor(type, index : LONGINT; buffer : Usbdi.Buffer) : BOOLEAN;
VAR i : LONGINT; hd : UsbHcdi.HubDescriptor;
BEGIN
IF Debug.StrongChecks THEN ASSERT((LEN(buffer)>=8) & (ORD(buffer[0])=LEN(buffer)) & (ORD(buffer[1])=type)); END;
NEW(hd, LEN(buffer));
FOR i := 0 TO LEN(buffer)-1 DO hd[i] := buffer[i]; END;
device(Usb.UsbDevice).controller.SetHubDescriptor(hd);
RETURN TRUE;
END SetHubDescriptor;
PROCEDURE ClearHubFeature(feature : LONGINT) : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN ASSERT((feature = HubLocalPowerChange) OR (feature = HubOverCurrentChange)); END;
RETURN TRUE;
END ClearHubFeature;
PROCEDURE SetHubFeature(feature : LONGINT) : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN ASSERT((feature = HubLocalPowerChange) OR (feature = HubOverCurrentChange)); END;
RETURN TRUE;
END SetHubFeature;
PROCEDURE ClearPortFeature(feature, port, selector : LONGINT) : BOOLEAN;
VAR res : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN
ASSERT((port >= 0) & (port < nbrOfPorts));
ASSERT(((feature # PortTest) & (feature # PortIndicator)) OR (selector = 0));
ASSERT((feature > 0) & (feature <= 22) & (feature # PortConnection));
END;
IF Debug.Trace & Debug.traceHubRequests THEN Show("Port "); KernelLog.Int(port + 1, 0); KernelLog.String(": Clear feature "); KernelLog.Int(feature, 0); KernelLog.Ln; END;
CASE feature OF
PortEnable : hub.controller.DisablePort(port); res := TRUE;
| PortSuspend : res := hub.controller.ResumePort(port);
| PortPower: hub.controller.DisablePortPower(port); res := TRUE;
| PortIndicator: hub.controller.IndicatePort(port, selector); res := TRUE;
| PortConnectionChange:
| PortResetChange:
| PortEnableChange:
| PortSuspendChange:
| PortOverCurrentChange:
ELSE
IF Debug.Level >= Debug.Warnings THEN Show("Clearing of Feature "); KernelLog.Int(feature, 0); KernelLog.String(" not supported"); KernelLog.Ln; END;
END;
RETURN res;
END ClearPortFeature;
PROCEDURE SetPortFeature(feature, port, selector : LONGINT) : BOOLEAN;
VAR res : BOOLEAN;
BEGIN
IF Debug.StrongChecks THEN
ASSERT((port >= 0) & (port < nbrOfPorts));
ASSERT(((feature = PortTest) OR (feature = PortIndicator)) OR (selector = 0));
ASSERT((feature > 0) & (feature <= 22) & (feature # PortConnection));
END;
IF Debug.Trace & Debug.traceHubRequests THEN Show("Port "); KernelLog.Int(port + 1, 0); KernelLog.String(": Set feature "); KernelLog.Int(feature, 0); KernelLog.Ln; END;
CASE feature OF
PortEnable : res := hub.controller.ResetAndEnablePort(port);
| PortSuspend : res := hub.controller.SuspendPort(port);
| PortPower: hub.controller.EnablePortPower(port); res := TRUE;
| PortReset: res := hub.controller.ResetAndEnablePort(port);
| PortTest:
| PortIndicator: hub.controller.IndicatePort(port, selector); res := TRUE;
| PortConnectionChange:
| PortResetChange:
| PortEnableChange:
| PortSuspendChange:
| PortOverCurrentChange:
ELSE
IF Debug.Level >= Debug.Warnings THEN Show("Request not supported"); KernelLog.Ln; END;
END;
RETURN res;
END SetPortFeature;
PROCEDURE GetHubStatus(VAR hubstatus : SET) : BOOLEAN;
BEGIN
hubstatus := {};
RETURN TRUE;
END GetHubStatus;
PROCEDURE GetPortStatus(port : LONGINT; ack : BOOLEAN) : SET;
BEGIN
IF Debug.StrongChecks THEN ASSERT((port >= 0) & (port < nbrOfPorts)); END;
RETURN hub.controller.GetPortStatus(port, ack);
END GetPortStatus;
PROCEDURE HandleStatusChange(status : Usbdi.Status; actLen : LONGINT);
BEGIN {EXCLUSIVE}
statusChange := TRUE;
END HandleStatusChange;
PROCEDURE AvailableCurrent() : LONGINT;
BEGIN
RETURN 500;
END AvailableCurrent;
PROCEDURE Terminate; BEGIN {EXCLUSIVE} alive:=FALSE; timerRH.Wakeup; END Terminate;
PROCEDURE SetDead; BEGIN {EXCLUSIVE} dead := TRUE; END SetDead;
PROCEDURE AwaitDead; BEGIN {EXCLUSIVE} AWAIT(dead); END AwaitDead;
PROCEDURE Initialize() : BOOLEAN;
BEGIN
IF hub.controller.SetStatusChangeHandler(HandleStatusChange) THEN
pollingInterval := 0;
END;
BEGIN {EXCLUSIVE} initialized := TRUE; END;
RETURN TRUE;
END Initialize;
PROCEDURE Show(CONST text : ARRAY OF CHAR);
BEGIN
KernelLog.String("UsbHubDriver: Root Hub "); hub.ShowName; KernelLog.String(": "); KernelLog.String(text);
END Show;
PROCEDURE Disconnect;
BEGIN
Terminate; AwaitDead;
IF Debug.Verbose THEN Show("Disconnected."); KernelLog.Ln; END;
END Disconnect;
PROCEDURE &New*;
BEGIN
New^; NEW(timerRH);
alive := TRUE; dead := FALSE; initialized := FALSE;
pollingInterval := PollingInterval;
END New;
BEGIN {ACTIVE}
(* Root hubs use a different way to communicate root hub port status changes. Either, they cannot *)
(* report there changes at all and must be polled (e.g. UHCI host controllers), or they use interrupt driven *)
(* global status change notification (e.g. OHCI and EHCI host controllers). *)
BEGIN {EXCLUSIVE} AWAIT(initialized OR ~alive); END;
WHILE alive DO
(* The first time we poll the bus (force bus enumeration) *)
LookForDevices;
IF pollingInterval = 0 THEN (* Use interrupt handler port status change notification *)
BEGIN {EXCLUSIVE}
AWAIT((alive = FALSE) OR (statusChange = TRUE));
statusChange := FALSE;
END;
ELSE (* Use polling *)
timerRH.Sleep(pollingInterval);
END;
END;
SetDead;
END RootHubDriver;
VAR
rootHubs : RootHubDriver;
PROCEDURE Probe(dev : Usbdi.UsbDevice; id : Usbdi.InterfaceDescriptor) : Usbdi.Driver;
VAR hubDriver : UsbHubDriver; rootHubDriver : RootHubDriver;
BEGIN
IF dev.descriptor.bNumConfigurations # 1 THEN RETURN NIL; END;
IF dev.configurations[0].bNumInterfaces # 1 THEN RETURN NIL; END;
IF id.bInterfaceClass # 9 THEN RETURN NIL; END;
IF id.bInterfaceSubClass # 0 THEN RETURN NIL; END;
IF id.bNumEndpoints # 1 THEN RETURN NIL; END;
IF dev(Usb.UsbDevice).parent = dev THEN
NEW(rootHubDriver);
rootHubDriver.next := rootHubs; rootHubs := rootHubDriver;
RETURN rootHubDriver;
ELSE
NEW(hubDriver);
RETURN hubDriver;
END;
END Probe;
PROCEDURE Cleanup;
VAR rh : RootHubDriver;
BEGIN
rh := rootHubs;
WHILE(rh # NIL) DO
IF Debug.Verbose THEN rh.Show("Shutting down... "); KernelLog.Ln; END;
rh.Terminate; rh.AwaitDead;
rh := rh.next;
END;
Usbdi.drivers.Remove(Name);
IF Debug.Verbose THEN KernelLog.Enter; KernelLog.String("UsbHubDriver: Removed hub driver."); KernelLog.Exit; END;
END Cleanup;
PROCEDURE Install*;
END Install;
BEGIN
Modules.InstallTermHandler(Cleanup);
Usbdi.drivers.Add(Probe, Name, Description, 10);
END UsbHubDriver.
UsbHubDriver.Install ~ SystemTools.Free UsbHubDriver ~