-- File: PupRouterCold.Mesa,  Last Edit:
  -- MAS  Apr 18, 1980 6:13 PM
  -- HGM  January 5, 1980  3:17 PM

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  CommUtilDefs: FROM "CommUtilDefs" USING [
    AllocateHeapNode, FreeHeapNode, SetTimeout, MsecToTicks],
  PupRouterDefs: FROM "PupRouterDefs" USING [
    PupRouterIn, PupRouterOut, PupChecksums, PupErrors,
    inThings, outThings, checksum, GetPupRoutingTable,
    pupRouterIsActive, BeSurePupIsOn, Timeout,
    pupRoutingTable, pupRoutingTableOut, PupRoutingTableEntry,
    firstSocket, dataWordsPerPup, maxHop,
    PupInputer, PupBroadcaster],
  DriverDefs: FROM "DriverDefs" USING [
    doDebug, giantVector,
    Network, RouterObject, SetPupRouter,
    CommPackageGo, CommPackageOff, GetDeviceChain],
  PupStream: FROM "PupStream", -- EXPORTS
  PupDefs: FROM "PupDefs" USING [
    defaultNumberOfNetworks,
    GetFreePupBuffer, SetPupContentsWords, PupRouterBroadcastThis],
  BufferDefs: FROM "BufferDefs" USING [DataWordsPerPupBuffer, PupBuffer],
  PupTypes: FROM "PupTypes" USING [gatewaySoc];

PupRouterCold: MONITOR LOCKS routerLock
  IMPORTS CommUtilDefs, PupRouterDefs, DriverDefs, PupDefs, BufferDefs
  EXPORTS PupStream, PupDefs, PupRouterDefs
  SHARES BufferDefs =
PUBLIC BEGIN OPEN PupRouterDefs;

routerLock: PUBLIC MONITORLOCK;
routingTableUpdateTimeout: PUBLIC CONDITION;
probeResponse: PUBLIC CONDITION;
numberOfNetworks: PUBLIC CARDINAL ← PupDefs.defaultNumberOfNetworks;

pupUseCount: CARDINAL ← 0;
routerTimeoutFork: PROCESS;

doDebug: BOOLEAN = DriverDefs.doDebug;

pupRouter: DriverDefs.RouterObject ← [
  input: LOOPHOLE[PupInputer],
  broadcast: LOOPHOLE[PupBroadcaster],
  addNetwork: AddNetwork,
  removeNetwork: RemoveNetwork ];


PupPackageMake: PUBLIC ENTRY PROCEDURE =
  BEGIN
  b: BufferDefs.PupBuffer;
  i: CARDINAL;
  routing: DESCRIPTOR FOR ARRAY OF PupRoutingTableEntry;
  network, firstNetwork: DriverDefs.Network;
  IF (pupUseCount←pupUseCount+1)>1 THEN RETURN;
  DriverDefs.CommPackageGo[];
  dataWordsPerPup ← BufferDefs.DataWordsPerPupBuffer[];
  network ← firstNetwork ← DriverDefs.GetDeviceChain[];
  routing ← DESCRIPTOR [  -- allocates network zero too
    CommUtilDefs.AllocateHeapNode[(numberOfNetworks+1)*SIZE[PupRoutingTableEntry]],
    numberOfNetworks+1];
  pupRoutingTable ← pupRoutingTableOut ← routing;
  pupRouterIsActive ← TRUE;
  IF doDebug THEN DriverDefs.giantVector.pupRoutingTable ← routing;
  FOR i IN [0..LENGTH[routing]) DO
    routing[i] ← [hop: maxHop+1, time: 210, route: , network: NIL];
    ENDLOOP;
  -- The first network on the chain becomes network zero.
  routing[0] ← [hop: 0, time: 0, route: [0], network: network];
  UNTIL network=NIL DO
    IF network.netNumber#0 AND network.netNumber<LENGTH[pupRoutingTable] THEN
      pupRoutingTable[network.netNumber] ← [hop: 0, time: 0, route: [0], network: network];
    network ← network.next;
    ENDLOOP;
  DriverDefs.SetPupRouter[@pupRouter];
  routerTimeoutFork ← FORK Timeout[];
  IF firstNetwork=NIL THEN RETURN;
  IF firstNetwork.device=local THEN RETURN;  -- avoid hanging
-- If we don't know which network we are connected to, probe for a routing
-- info packet, but don't wait forever in case all of the Gateways are down.
-- Everything should work (as far as we are concerned) if myNet is 0.  This
-- happens when all of the gateways are down.  If a client program really
-- needs to know its network number, it will have to take care of itself.
-- Be sure to go around the loop at least once or we won't get an initial
-- routing table if we already know our network number because it came from
-- the switches.
  FOR i IN [0..10) DO
    b ← PupDefs.GetFreePupBuffer[];
    b.bypassZeroNet ← FALSE;
    b.pupType ← gatewayRequest;
    b.pupID ← [0,i];
    b.dest.socket ← b.source.socket ← PupTypes.gatewaySoc;
    PupDefs.SetPupContentsWords[b,0];
    PupDefs.PupRouterBroadcastThis[b];
    WAIT probeResponse;
    IF firstNetwork.netNumber#0 THEN EXIT;
    ENDLOOP;
  END;

PupPackageDestroy: PUBLIC PROCEDURE =
  BEGIN
  IF doDebug THEN BeSurePupIsOn[];
  IF PupPackageDestroyLocked[] THEN RETURN;
  JOIN routerTimeoutFork;
  CommUtilDefs.FreeHeapNode[BASE[pupRoutingTable]];
  IF doDebug THEN DriverDefs.giantVector.pupRoutingTable ← DESCRIPTOR[NIL,0];
  DriverDefs.CommPackageOff[];
  END;

PupPackageDestroyLocked: ENTRY PROCEDURE RETURNS [BOOLEAN] = INLINE
  BEGIN
  IF (pupUseCount←pupUseCount-1)#0 THEN RETURN[TRUE];
  DriverDefs.SetPupRouter[NIL];
  pupRouterIsActive ← FALSE;
  NOTIFY routingTableUpdateTimeout;
  RETURN[FALSE];
  END;

GetPupPackageUseCount: PUBLIC PROCEDURE RETURNS [CARDINAL] =
  BEGIN
  RETURN[pupUseCount];
  END;

AddNetwork: PUBLIC PROCEDURE [network: DriverDefs.Network] =
  BEGIN
  routing: DESCRIPTOR FOR ARRAY OF PupRoutingTableEntry ← GetPupRoutingTable[];
  net: CARDINAL ← network.netNumber;
  IF net IN [0..LENGTH[routing]] THEN
    routing[net] ← [hop: 0, time: 0, route: [0], network: network];
  END;

RemoveNetwork: PUBLIC PROCEDURE [network: DriverDefs.Network] =
  BEGIN
  routing: DESCRIPTOR FOR ARRAY OF PupRoutingTableEntry ← GetPupRoutingTable[];
  net: CARDINAL ← network.netNumber;
  IF net IN [0..LENGTH[routing]] THEN
    routing[net] ← [hop: 0, time: 0, route: [0], network: NIL];
  END;

-- Various junky routines that live here because this is very cold code

SetPupStormy: PUBLIC PROCEDURE [new: BOOLEAN] =
  BEGIN
  outThings.outStormy ← inThings.inStormy ← new;
  END;

SetBadPupProc: PUBLIC PROCEDURE [proc: PROCEDURE[BufferDefs.PupBuffer]] =
  BEGIN
  inThings.badChecksumProc ← proc;
  END;

InspectIncomingPups: PUBLIC PROCEDURE [
    new: BOOLEAN, proc: PROCEDURE[CARDINAL,BufferDefs.PupBuffer]] =
  BEGIN
  inThings.showIn ← new;
  inThings.inShower ← proc;
  END;

InspectOutgoingPups: PUBLIC PROCEDURE [
    new: BOOLEAN, proc: PROCEDURE[CARDINAL,BufferDefs.PupBuffer]] =
  BEGIN
  outThings.showOut ← new;
  outThings.outShower ← proc;
  END;

InspectStrayPups: PUBLIC PROCEDURE [
    on, seeBroadcast: BOOLEAN,
    proc: PROCEDURE[BufferDefs.PupBuffer] RETURNS [error: BOOLEAN]] =
  BEGIN
  inThings.watcherIsWatching ← on;
  inThings.watcherSeesBroadcast ← seeBroadcast;
  inThings.watcherCallsThis ← proc;
  END;

SetPupCheckit: PUBLIC PROCEDURE [new: BOOLEAN] =
  BEGIN
  PupRouterDefs.checksum ← IF new THEN software ELSE none;
  END;

UseAltoChecksumMicrocode: PUBLIC PROCEDURE =
  BEGIN
  PupRouterDefs.checksum ← alto;
  END;

-- initialization
START PupRouterDefs.PupRouterIn;
START PupRouterDefs.PupRouterOut;
START PupRouterDefs.PupChecksums;
START PupRouterDefs.PupErrors;
CommUtilDefs.SetTimeout[@routingTableUpdateTimeout,CommUtilDefs.MsecToTicks[30000]];
CommUtilDefs.SetTimeout[@probeResponse,CommUtilDefs.MsecToTicks[500]];
IF doDebug THEN
  BEGIN
  DriverDefs.giantVector.firstPupSocket ← @firstSocket;
  END;
END.