-- File: Boss.mesa,
-- Last Edit: HGM January 7, 1981 1:37 AM
-- Last Edit: BLyon March 4, 1981 2:37 PM

DIRECTORY
BufferDefs USING [
BufferAccessHandle, defaultSystemBufferPoolSize,
defaultSystemBuffersToReserve, FreeBufferPool, MakeBufferPool,
systemAccessHandle],
StatsDefs USING [StatIncr],
CommUtilDefs USING [FriendOfCopyLong, LockCode, UnlockCode, SetDebuggingPointer],
CommFlags USING [doDebug, doStats],
DriverDefs USING [
Glitch, GiantVector, GetPupRouter, GetOisRouter,
GetWordsPerIocb, SetWordsPerIocb, CreateDefaultEthernetOneDrivers,
CreateDefaultEthernetDrivers, Network, DispatcherOff, DispatcherOn,
DispatcherImpl, BufferMgr],
OISCP USING [],
PupDefs USING [],
SpecialCommunication USING [PhysicalMedium],
NSAddress USING [NetworkNumber];

Boss: MONITOR
IMPORTS BufferDefs, StatsDefs, CommUtilDefs, DriverDefs
EXPORTS DriverDefs, OISCP, PupDefs, SpecialCommunication
SHARES BufferDefs, DriverDefs =
BEGIN OPEN CommUtilDefs, DriverDefs, NSAddress, SpecialCommunication;

-- SemiPublic things for others
firstNetwork: PUBLIC Network ← NIL;
giantVector: PUBLIC POINTER TO GiantVector ← @bigBoy; -- Debugging only
bigBoy: GiantVector;
increasedBufferUseCount, useCount: PUBLIC CARDINAL ← 0;

state: {off, ready} ← off; -- on is ready with useCount>0

IocbNotBigEnough: PUBLIC ERROR = CODE;
CommPackageNotActive: PUBLIC ERROR = CODE;
NetworkNonExistent: PUBLIC ERROR = CODE;
SystemBufferPoolConfused: PUBLIC ERROR = CODE;

GetUseCount: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[useCount]; END;

GetDoStats: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[CommFlags.doStats]; END;

GetGiantVector: PUBLIC PROCEDURE RETURNS [POINTER TO GiantVector] =
BEGIN RETURN[giantVector]; END;

-- This code is a bit delicate. There are probably many funny cases that won't work correctly. In particular, there is a race condition between adding/deleting a driver and adding/deleting a router.

AddDeviceToChain: PUBLIC PROCEDURE [network: Network, iocbSize: CARDINAL] =
BEGIN
-- Add new drivers to the end of the chain so that the normal Ethernet driver will be network zero.
tail: Network ← firstNetwork;
i: CARDINAL ← 1;
UNTIL (iocbSize MOD 4) = 0 DO iocbSize ← iocbSize + 1; ENDLOOP;
IF state = off THEN SetWordsPerIocb[MAX[GetWordsPerIocb[], iocbSize]]
ELSE IF iocbSize > GetWordsPerIocb[] THEN Glitch[IocbNotBigEnough];
IF state = ready THEN LockCode[network.interrupt];
IF useCount > 0 THEN
BEGIN
network.activateDriver[];
DriverDefs.GetPupRouter[].addNetwork[network];
DriverDefs.GetOisRouter[].addNetwork[network];
END;
IF firstNetwork = NIL THEN
BEGIN firstNetwork ← network; network.index ← 1; RETURN; END;
UNTIL tail.next = NIL DO tail ← tail.next; i ← i + 1; ENDLOOP;
tail.next ← network;
network.index ← i + 1;
END;

RemoveDeviceFromChain: PUBLIC ENTRY PROCEDURE [network: Network] =
BEGIN
tail: Network ← firstNetwork;
IF useCount > 0 THEN
BEGIN
DriverDefs.GetPupRouter[].removeNetwork[network];
DriverDefs.GetOisRouter[].removeNetwork[network];
network.deactivateDriver[];
END;
IF state = ready THEN UnlockCode[network.interrupt];
IF firstNetwork = network THEN firstNetwork ← network.next
ELSE
BEGIN
UNTIL tail.next = network DO tail ← tail.next; ENDLOOP;
tail.next ← network.next;
END;
-- network.index is not updated. It is used only to collect Gateway statistics.

END;

ChangeNumberOfInputBuffers: PUBLIC ENTRY PROCEDURE [
increaseTheBuffers: BOOLEAN] =
BEGIN
IF state # ready THEN Glitch[CommPackageNotActive];
IF increaseTheBuffers THEN
BEGIN
increasedBufferUseCount ← increasedBufferUseCount + 1;
IF increasedBufferUseCount>1 THEN RETURN;
END
ELSE
BEGIN
IF increasedBufferUseCount=0 THEN RETURN; -- donot go negative
increasedBufferUseCount ← increasedBufferUseCount - 1;
IF increasedBufferUseCount#0 THEN RETURN;
END;
FOR net: Network ← firstNetwork, net.next UNTIL net = NIL DO
IF net.changeNumberOfInputBuffers=NIL THEN LOOP;
net.changeNumberOfInputBuffers[increaseTheBuffers];
ENDLOOP;
END;

GetDeviceChain: PUBLIC PROCEDURE RETURNS [Network] =
BEGIN
IF state # ready THEN Glitch[CommPackageNotActive];
RETURN[firstNetwork];
END;

SmashDeviceChain: PUBLIC PROCEDURE = BEGIN firstNetwork ← NIL; END;

-- The drivers may have to be told what its network numbers are.
-- This is certainly true if this is the first machine running on a network.
-- Physical order is the location of the network on the network device chain .

SetNetworkID: PUBLIC ENTRY PROCEDURE [
physicalOrder: CARDINAL, medium: PhysicalMedium, newNetID: NetworkNumber]
RETURNS [oldNetID: NetworkNumber] =
BEGIN
ENABLE UNWIND => NULL;
net: Network;
net ← GetNthDevice[physicalOrder, medium];
oldNetID ← net.netNumber;
net.netNumber ← newNetID;
DriverDefs.GetPupRouter[].stateChanged[net];
DriverDefs.GetOisRouter[].stateChanged[net];
END;

-- This procedure returns the OisNetID the Nth Network Object on the network device
-- chain. (where N=physicalOrder).

GetNetworkID: PUBLIC ENTRY PROCEDURE [
physicalOrder: CARDINAL, medium: PhysicalMedium] RETURNS [NetworkNumber] =
BEGIN
ENABLE UNWIND => NULL;
RETURN[GetNthDevice[physicalOrder, medium].netNumber];
END;

-- This procedure find the Nth (where N=physicalOrder) network of the specified medium
-- on the network device chain. This assumes that we locked (protected).

GetNthDevice: PRIVATE PROCEDURE [
physicalOrder: CARDINAL, medium: PhysicalMedium] RETURNS [net: Network] =
BEGIN
i: CARDINAL ← 0;
FOR net ← firstNetwork, net.next UNTIL net=NIL DO
IF net.device = medium THEN IF (i ← i + 1) = physicalOrder THEN RETURN;
ENDLOOP;
ERROR NetworkNonExistent;
END;

-- This may be called at any time. It does nothing if already ready.

OiscpPackageReady, PupPackageReady: PUBLIC ENTRY PROCEDURE =
BEGIN CommPackageReady[]; END;

CommPackageReady: INTERNAL PROCEDURE =
BEGIN
extra: CARDINAL ← 0;
IF useCount = 0 AND firstNetwork = NIL THEN
BEGIN
[] ← CreateDefaultEthernetDrivers[];
[] ← CreateDefaultEthernetOneDrivers[];
END;
IF state = ready THEN RETURN;
-- On the Alto, MakeImage forgets low memory
IF CommFlags.doDebug THEN CommUtilDefs.SetDebuggingPointer[giantVector];
IF CommFlags.doStats THEN LockCode[StatsDefs.StatIncr];
LockCode[DriverDefs.DispatcherImpl];
LockCode[DriverDefs.BufferMgr];
LockCode[CommUtilDefs.FriendOfCopyLong];
FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
extra ← extra + network.buffers; ENDLOOP;
IF BufferDefs.systemAccessHandle # NIL THEN Glitch[SystemBufferPoolConfused];
BufferDefs.systemAccessHandle ← BufferDefs.MakeBufferPool[
total: BufferDefs.defaultSystemBufferPoolSize + extra,
reserve: BufferDefs.defaultSystemBuffersToReserve];
DispatcherOn[];
FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
LockCode[network.interrupt]; ENDLOOP;
state ← ready;
END;

CommPackageGo: PUBLIC ENTRY PROCEDURE =
BEGIN
-- On the Alto, MakeImage forgets low memory
IF CommFlags.doDebug THEN CommUtilDefs.SetDebuggingPointer[giantVector];
CommPackageReady[];
IF (useCount ← useCount + 1) > 1 THEN RETURN;
FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
network.activateDriver[]; ENDLOOP;
END;

CommPackageOff: PUBLIC ENTRY PROCEDURE =
BEGIN
IF (useCount ← useCount - 1) # 0 THEN RETURN;
FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
network.deactivateDriver[]; UnlockCode[network.interrupt]; ENDLOOP;
DispatcherOff[];
IF BufferDefs.systemAccessHandle = NIL THEN Glitch[SystemBufferPoolConfused];
BufferDefs.FreeBufferPool[BufferDefs.systemAccessHandle];
BufferDefs.systemAccessHandle ← NIL;
IF CommFlags.doStats THEN UnlockCode[StatsDefs.StatIncr];
UnlockCode[CommUtilDefs.FriendOfCopyLong];
UnlockCode[DriverDefs.BufferMgr];
UnlockCode[DriverDefs.DispatcherImpl];
state ← off;
END;


-- initialization

IF CommFlags.doDebug THEN
BEGIN
CommUtilDefs.SetDebuggingPointer[giantVector];
giantVector.slaThings ← giantVector.prThings ← NIL;
END;
START DriverDefs.DispatcherImpl;
START DriverDefs.BufferMgr;
END.