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

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  CommUtilDefs: FROM "CommUtilDefs" USING [
    AllocateHeapNode, DisableTimeout, FreeHeapNode, SetTimeout, MsecToTicks],
  PupRouterDefs: FROM "PupRouterDefs" USING [
    PupRouterIn, PupRouterOut, PupChecksums, PupErrors,
    inThings, outThings, checksum, routingCacheHead, routingCacheTail,
    pupRouterIsActive, BeSurePupIsOn, Timeout, Prober,
    InsertRoutingCacheEntry, DeleteRoutingCacheEntry, RoutingCacheEntry,
    firstSocket, dataWordsPerPup, maxHop, emptyCacheEntry,
    PupInputer, PupBroadcaster],
  DriverDefs: FROM "DriverDefs" USING [
    doDebug, giantVector,
    Network, RouterObject, SetPupRouter,
    CommPackageGo, CommPackageOff, GetDeviceChain],
  PupStream: FROM "PupStream", -- EXPORTS
  PupDefs: FROM "PupDefs" USING [],
  BufferDefs: FROM "BufferDefs" USING [DataWordsPerPupBuffer, PupBuffer];

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

routerLock: PUBLIC MONITORLOCK;
routingTableUpdateTimeout: PUBLIC CONDITION;
probeResponse: PUBLIC CONDITION;
pleaseProbe: PUBLIC CONDITION;
probeRetransmitTimeout: PUBLIC CONDITION;
probesLeftToDo: PUBLIC CARDINAL;
numberOfRoutingCacheEntries: PUBLIC CARDINAL ← 20;

pupUseCount: CARDINAL ← 0;
routerTimeoutFork: PROCESS;
routerProberFork: PROCESS;

doDebug: BOOLEAN = DriverDefs.doDebug;

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


PupPackageMake: PUBLIC ENTRY PROCEDURE =
  BEGIN
  firstNetwork: DriverDefs.Network;
  IF (pupUseCount←pupUseCount+1)>1 THEN RETURN;
  DriverDefs.CommPackageGo[];
  dataWordsPerPup ← BufferDefs.DataWordsPerPupBuffer[];
  firstNetwork ← DriverDefs.GetDeviceChain[];
  routingCacheHead ← CommUtilDefs.AllocateHeapNode[
    (numberOfRoutingCacheEntries+1 --extra for net zero--)*SIZE[RoutingCacheEntry]];
  FOR i: CARDINAL IN [0..numberOfRoutingCacheEntries+1) DO
    routingCacheTail ← routingCacheHead + i*SIZE[RoutingCacheEntry];
    routingCacheTail↑ ← [next: routingCacheTail+SIZE[RoutingCacheEntry],
      net: emptyCacheEntry, entry: [hop: maxHop+1, time: 0, route: [0], network: NIL]];
    ENDLOOP;
  routingCacheTail.next ← NIL;
  pupRouterIsActive ← TRUE;
  IF doDebug THEN DriverDefs.giantVector.pupRoutingTable ←
    DESCRIPTOR[routingCacheHead, (numberOfRoutingCacheEntries+1)*SIZE[RoutingCacheEntry]];
  -- The first network on the chain becomes network zero.
  InsertRoutingCacheEntry[[0]].entry ←
    [hop: 0, time: 0, route: [0], network: firstNetwork];
  -- Insert routing table entries for any directly-connected networks whose numbers
  -- we know a priori (this happens only in gateways).
  FOR network: DriverDefs.Network ← firstNetwork, network.next UNTIL network=NIL DO
    IF network.netNumber#0 THEN
      InsertRoutingCacheEntry[[network.netNumber]].entry ←
        [hop: 0, time: 0, route: [0], network: network];
    ENDLOOP;
  DriverDefs.SetPupRouter[@pupRouter];
  routerTimeoutFork ← FORK Timeout[];
  routerProberFork ← FORK Prober[];
  IF firstNetwork=NIL THEN RETURN;
  IF firstNetwork.device=local THEN RETURN;  -- avoid hanging
  probesLeftToDo ← 10; -- Prober will try this many times
  NOTIFY pleaseProbe; -- Kick Prober to life
  WAIT probeResponse; -- Response to probe or 10-second timeout
  END;

PupPackageDestroy: PUBLIC PROCEDURE =
  BEGIN
  IF doDebug THEN BeSurePupIsOn[];
  IF PupPackageDestroyLocked[] THEN RETURN;
  JOIN routerTimeoutFork;
  JOIN routerProberFork;
  CommUtilDefs.FreeHeapNode[routingCacheHead];
  routingCacheHead ← routingCacheTail ← NIL;
  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;
  NOTIFY probeRetransmitTimeout;
  NOTIFY pleaseProbe;
  RETURN[FALSE];
  END;

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

AddNetwork: PUBLIC PROCEDURE [network: DriverDefs.Network] =
  BEGIN
  InsertRoutingCacheEntry[[network.netNumber]].entry ←
    [hop: 0, time: 0, route: [0], network: network];
  END;

RemoveNetwork: PUBLIC PROCEDURE [network: DriverDefs.Network] =
  BEGIN
  DeleteRoutingCacheEntry[[network.netNumber]];
  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[1000]];
CommUtilDefs.SetTimeout[@probeRetransmitTimeout, CommUtilDefs.MsecToTicks[2000]];
CommUtilDefs.DisableTimeout[@pleaseProbe];
IF doDebug THEN
  BEGIN
  DriverDefs.giantVector.firstPupSocket ← @firstSocket;
  END;
END.