-- File: PupRouterCold.Mesa
-- Edited by Levin   9-Mar-82 10:01:12
-- Edited by Andrew Birrell June 16, 1983 9:54 am

DIRECTORY
  Process USING [SetTimeout, MsecToTicks],
  BufferDefs USING [
    AdjustBufferSize, DataWordsPerPupBuffer, PupBuffer, BufferAccessHandle,
    MakeBufferPool, FreeBufferPool],
  CommFlags USING [doDebug],
  CommUtilDefs USING [GetEthernetHostNumber],
  DriverDefs USING [
    GetGiantVector, Glitch, Network, RouterObject, SetPupRouter,
    MaybeGetFreePupBuffer, GetDeviceChain, CommPackageGo, CommPackageOff],
  GermSwap USING [switches],
  PupDefs USING [
    --defaultNumberOfNetworks,-- SetPupContentsWords, PupRouterBroadcastThis],
  PupRouterDefs USING [
    BeSurePupIsOn, checksum, dataWordsPerPup, firstSocket,
    GetRoutingTableEntry, inThings, maxHop, outThings,
    PupBroadcaster, PupChecksums, PupErrors, PupInputer, PupRouterIn, PupRouterOut,
    routerIsActive, routingTable, RoutingTable, RoutingTableEntry, RoutingTableObject,
    RoutingTableSeq, Timeout],
  PupStream USING [],
  PupTypes USING [PupNetID, gatewaySoc];

PupRouterCold: MONITOR LOCKS routerLock
  IMPORTS
    GermSwap, BufferDefs, CommUtilDefs, DriverDefs, Process, PupDefs, PupRouterDefs
  EXPORTS PupStream, PupDefs, PupRouterDefs
  SHARES BufferDefs = PUBLIC

  BEGIN OPEN PupRouterDefs;

  Network: TYPE = DriverDefs.Network;

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

  pupUseCount: CARDINAL ← 0;
  routerTimeoutFork: PROCESS;
  pupBuffersToAllocate: CARDINAL ← 15;
  pupBuffers: BufferDefs.BufferAccessHandle ← NIL;

  doDebug: BOOLEAN = CommFlags.doDebug;

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

  CantChangeBuffersWhenActive: PUBLIC ERROR = CODE;

  AdjustBufferParms: PUBLIC SAFE PROCEDURE [bufferPoolSize, bufferSize: CARDINAL] =
    TRUSTED BEGIN
    IF doDebug AND pupUseCount # 0 THEN
      DriverDefs.Glitch[CantChangeBuffersWhenActive];
    IF bufferPoolSize # 0 THEN pupBuffersToAllocate ← bufferPoolSize;
    IF bufferSize # 0 THEN BufferDefs.AdjustBufferSize[bufferSize];
    END;

  PupPackageMake: PUBLIC ENTRY SAFE PROCEDURE = TRUSTED
    BEGIN
    routing: RoutingTable;
    firstNetwork: Network;
    IF (pupUseCount ← pupUseCount + 1) > 1 THEN RETURN;
    DriverDefs.CommPackageGo[];
    pupBuffers ← BufferDefs.MakeBufferPool[
      total: pupBuffersToAllocate, reserve: 0];
    dataWordsPerPup ← BufferDefs.DataWordsPerPupBuffer[];
    firstNetwork ← DriverDefs.GetDeviceChain[];
    routing ← NEW[RoutingTableSeq[numberOfNetworks + 1]]; -- allocates network zero too
    routingTable ← routing;
    routerIsActive ← TRUE;
    -- IF doDebug THEN DriverDefs.GetGiantVector[].pupRoutingTable ← routing;
    FOR i: CARDINAL IN [0..routing.length) DO
      routing[i] ← [net: [i], hop: maxHop + 1, time: 210, route:, network: NIL];
      ENDLOOP;
    -- The first network on the chain becomes network zero.
    FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
      IF network.hostNumber=0 THEN
        BEGIN -- Probably a DLion (or something else without an EthernetOne)
        network.hostNumber ← CommUtilDefs.GetEthernetHostNumber[];
        END;
      AddNetwork[network];
      ENDLOOP;
    DriverDefs.SetPupRouter[@router];
    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.
    -- Unfortunately, it is more complicated that than for the DLions since OISCP sets up the network number and the answer to the first probe may get lost if the Gateway doesn't have our entry in it 8to48 cache in the driver.
    FOR i: CARDINAL IN [0..10) DO
      gotRoutingPup: BOOLEAN ← FALSE;
      ProbeForRoutingInfo[];
      WAIT probeResponse;
      FOR i: CARDINAL IN [0..routing.length) DO
        IF routing[i].network # NIL AND routing[i].hop # 0 THEN gotRoutingPup ← TRUE;
        ENDLOOP;
      IF gotRoutingPup THEN EXIT;
      ENDLOOP;
    END;

  ProbeForRoutingInfo: PROCEDURE =
    BEGIN
    b: BufferDefs.PupBuffer;
    b ← DriverDefs.MaybeGetFreePupBuffer[];
    IF b = NIL THEN RETURN;
    b.bypassZeroNet ← FALSE;
    b.pupType ← gatewayRequest;
    b.pupID ← [0, 0];
    b.dest.socket ← b.source.socket ← PupTypes.gatewaySoc;
    PupDefs.SetPupContentsWords[b, 0];
    PupDefs.PupRouterBroadcastThis[b];
    END;

  PupPackageDestroy: PUBLIC SAFE PROCEDURE = TRUSTED
    BEGIN
    IF doDebug THEN BeSurePupIsOn[];
    IF PupPackageDestroyLocked[] THEN RETURN;
    JOIN routerTimeoutFork;
    IF doDebug THEN
      DriverDefs.GetGiantVector[].pupRoutingTable ← DESCRIPTOR[NIL, 0];
    BufferDefs.FreeBufferPool[pupBuffers];
    DriverDefs.CommPackageOff[];
    END;

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

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

  AddNetwork: PROCEDURE [network: Network] =
    BEGIN
    net: PupTypes.PupNetID ← [network.netNumber.b];
    rte: RoutingTableEntry;
    IF ~network.alive THEN RETURN;
    IF routingTable[0].network = NIL THEN
      routingTable[0] ← [net: [0], hop: 0, time: 0, route: [0], network: network];
    rte ← GetRoutingTableEntry[net];
    IF rte # NIL THEN
      rte↑ ← [net: net, hop: 0, time: 0, route: [0], network: network];
    END;

  RemoveNetwork: PROCEDURE [network: Network] =
    BEGIN
    probe: BOOLEAN ← FALSE;
    FOR i: CARDINAL IN [0..routingTable.length) DO
      IF routingTable[i].network # network THEN LOOP;
      routingTable[i] ←
	[net: [i], hop: maxHop + 1, time: 210, route:, network: NIL];
      probe ← TRUE;
      ENDLOOP;
    IF probe THEN ProbeForRoutingInfo[];
    END;

  StateChanged: PROCEDURE [network: Network] =
    BEGIN RemoveNetwork[network]; AddNetwork[network]; 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 SAFE PROCEDURE [
    on, seeBroadcast: BOOLEAN,
    proc: PROCEDURE [BufferDefs.PupBuffer] RETURNS [error: BOOLEAN]] = TRUSTED
    BEGIN
    inThings.watcherIsWatching ← on;
    inThings.watcherSeesBroadcast ← seeBroadcast;
    inThings.watcherCallsThis ← proc;
    END;

  UseNullChecksumMicrocode: PUBLIC PROCEDURE =
    BEGIN PupRouterDefs.checksum ← none; END;

  UseSoftwareChecksumMicrocode: PUBLIC PROCEDURE =
    BEGIN PupRouterDefs.checksum ← software; END;

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

  UsePrincOpsChecksumMicrocode: PUBLIC PROCEDURE =
    BEGIN PupRouterDefs.checksum ← princOps; END;

  -- initialization

  START PupRouterDefs.PupRouterIn;
  START PupRouterDefs.PupRouterOut;
  START PupRouterDefs.PupChecksums;
  START PupRouterDefs.PupErrors;
  Process.SetTimeout[@routingTableUpdateTimeout, Process.MsecToTicks[30000]];
  Process.SetTimeout[@probeResponse, Process.MsecToTicks[500]];
  IF doDebug THEN
    BEGIN DriverDefs.GetGiantVector[].firstPupSocket ← @firstSocket; END;
  IF NOT GermSwap.switches[c] THEN PupPackageMake[];
  
  END.