-- File: PupRouterCold.mesa - last edit:
-- AOF                 30-Jul-85 15:07:51
-- HGM                 26-Nov-85  1:36:07
-- Copyright (C) 1984, 1985 by Xerox Corporation. All rights reserved. 

DIRECTORY
  Buffer USING [
    Buffer, AccessHandle, DataWordsPerPupBuffer, DestroyPool, Enqueue,
    GetBuffer, MakePool, Queue, ReturnBuffer, Type],
  CommFlags USING [doDebug],
  CommHeap USING [zone],
  CommunicationInternal USING [CommPackageGo, CommPackageOff],
  CommUtil USING [GetEthernetHostNumber],
  Driver USING [
    ChangeNumberOfInputBuffers, GetInputBuffer, Glitch, Network,
    PutOnGlobalDoneQueue, GetDeviceChain],
  Frame USING [GetReturnFrame, ReadGlobalLink],
  Heap USING [FreeNode, MakeNode],
  Process USING [
    EnableAborts, SetTimeout, MsecToTicks],
  Protocol1 USING [
    Action, MatrixRecord, EvictFamily, Family, FamilyUnit,
    RegisterFamily, AddFamilyMember],
  PupDefs USING [
    defaultNumberOfNetworks, SetPupContentsWords, PupRouterBroadcastThis],
  PupPktOps,
  Pup10MBit USING [Capsulators, Create, Destroy],
  PupRouterDefs USING [
    PupRouterIn, PupRouterOut, PupChecksums, PupErrors, inThings, outThings,
    checksum, routerIsActive, BeSurePupIsOn, Timeout, routingTable,
    RoutingTableObject, RoutingTableEntry, GetRoutingTableEntry, ContextObject, 
    dataWordsPerPup, maxHop, PupInputer, PupBroadcaster, NetworkContext],
  PupStream USING [],
  PupTypes USING [PupHostID, gatewaySoc];

PupRouterCold: MONITOR LOCKS routerLock
  IMPORTS
    Frame, Heap, Process, Buffer, CommunicationInternal, CommUtil, Driver, 
    PupDefs, Pup10MBit, PupRouterDefs, Protocol1, CommHeap
  EXPORTS Buffer, PupStream, PupDefs, PupRouterDefs, PupPktOps = PUBLIC

  BEGIN
  OPEN PupRouterDefs;
  
  FreeQueueNotInitialized: ERROR = CODE;
  QueueSealBroken: ERROR = CODE;

  Network: PUBLIC TYPE = Driver.Network;

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

  pupUseCount: CARDINAL ← 0;
  routerTimeoutFork: PROCESS;
  pupBuffersToAllocate: CARDINAL ← 15;
  pupBuffers: PUBLIC Buffer.AccessHandle ← NIL;  -- For the entire pup package.

  doDebug: BOOLEAN = CommFlags.doDebug;

  pupProtocolFamily: PUBLIC Protocol1.FamilyUnit ← [
    name: pup, status: dead, spy: NIL, state: NIL,
    receive: PupInputer, broadcast: PupBroadcaster,
    stateChanged: PupStateChanged];

  CantChangeBuffersWhenActive: PUBLIC ERROR = CODE;

  AdjustBufferParms: PUBLIC PROC[bufferPoolSize: CARDINAL] =
    BEGIN
    IF doDebug AND pupUseCount # 0 THEN
      Driver.Glitch[CantChangeBuffersWhenActive];
    IF bufferPoolSize # 0 THEN pupBuffersToAllocate ← bufferPoolSize;
    END;
    
  GetClumpOfPupBuffers: PUBLIC ENTRY PROC[
    q: Buffer.Queue, n: CARDINAL, wait: BOOLEAN] =
    BEGIN
    ENABLE UNWIND => NULL;
    b: Buffer.Buffer;
    IF doDebug THEN
      SELECT TRUE FROM
      (pupUseCount = 0) => Driver.Glitch[FreeQueueNotInitialized];
      (q.seal # queueSeal) => Driver.Glitch[QueueSealBroken];
      ENDCASE;
    THROUGH [0..n) DO
      b ← Driver.GetInputBuffer[wait];  -- get a buffer without accounting.
      IF b = NIL THEN EXIT;
      b.type ← pup; 
      b.pup.pupType ← data;
      IF CommFlags.doDebug THEN
        b.debug ← Frame.ReadGlobalLink[Frame.GetReturnFrame[]];
      Buffer.Enqueue[q, b];
      ENDLOOP;
    END;  -- GetClumpOfPupBuffers

  PupPackageMake: PUBLIC PROC RETURNS [Protocol1.Family] =
    BEGIN
    AlreadyStarted: ENTRY PROC RETURNS[BOOLEAN] = INLINE
      {RETURN[(pupUseCount ← pupUseCount + 1) > 1]};
    ProbeToStartTable: ENTRY PROC = --INLINE
      BEGIN
      ENABLE UNWIND => NULL;
      FOR i: CARDINAL IN [0..10) DO
	gotRoutingPup: BOOLEAN ← FALSE;
	ProbeForRoutingInfo[];
	WAIT probeResponse;
	FOR i: CARDINAL IN [0..LENGTH[routing]) DO
	  IF routing[i].context # NIL AND routing[i].hop # 0 THEN
	    gotRoutingPup ← TRUE;
	  ENDLOOP;
	IF gotRoutingPup THEN EXIT;
	ENDLOOP;
      END;  --ProbeToStartTable
    driver: Network;
    matrix: Protocol1.MatrixRecord ← [
      family: @pupProtocolFamily, context: NIL,
      encapsulator: NIL, decapsulator: NIL];    
    routing: LONG DESCRIPTOR FOR ARRAY OF RoutingTableObject;
    IF AlreadyStarted[] THEN RETURN[@pupProtocolFamily];
    CommunicationInternal.CommPackageGo[];
    Driver.ChangeNumberOfInputBuffers[TRUE];
    routing ← DESCRIPTOR[  -- allocates network zero too
      Heap.MakeNode[
        z: CommHeap.zone, n: (numberOfNetworks + 1)*SIZE[RoutingTableObject]],
      numberOfNetworks + 1];
    FOR i: CARDINAL IN [0..LENGTH[routing]) DO
      routing[i] ← [net: [i], hop: maxHop + 1, time: 210, route:, context: NIL];
      ENDLOOP;
    dataWordsPerPup ← Buffer.DataWordsPerPupBuffer[];
    pupBuffers ← Buffer.MakePool[send: pupBuffersToAllocate, receive: 0];
    routingTable ← routing; routerIsActive ← TRUE;
    routerTimeoutFork ← FORK Timeout[];

    BEGIN
    OPEN context: LOOPHOLE[matrix.context, PupRouterDefs.NetworkContext];
    Protocol1.RegisterFamily[@pupProtocolFamily];  --make pup known
    FOR driver ← Driver.GetDeviceChain[], driver.next UNTIL driver = NIL DO
      IF (driver.device # ethernet) OR ~driver.alive THEN LOOP;
      matrix.context ← CommHeap.zone.NEW[PupRouterDefs.ContextObject];
      context.protocol ← NIL;
      context.pupNetNumber ← 0;
      context.network ← driver;
      context.pupHostNumber ← CommUtil.GetEthernetHostNumber[];
      [matrix.decapsulator, matrix.encapsulator] ← Pup10MBit.Capsulators[];
      Protocol1.AddFamilyMember[driver, @matrix];
      ENDLOOP;
    IF routingTable[0].context = NIL THEN routingTable[0] ← [
      net: [0], hop: 0, time: 0, route: [0], context: matrix.context];
    END;

    pupProtocolFamily.status ← alive;
    ProbeToStartTable[];
    RETURN[@pupProtocolFamily];
    END;  --PupPackageMake

  ProbeForRoutingInfo: PROC=
    BEGIN
    b: Buffer.Buffer;
    b ← Buffer.GetBuffer[pup, pupBuffers, send, smallBuffer, FALSE];
    IF b = NIL THEN RETURN;
    b.bypassZeroNet ← FALSE;
    b.pup.pupType ← gatewayRequest;
    b.pup.pupID ← [0, 0];
    b.pup.dest.socket ← b.pup.source.socket ← PupTypes.gatewaySoc;
    PupDefs.SetPupContentsWords[b, 0];
    PupDefs.PupRouterBroadcastThis[b];
    END;

  PupPackageDestroy: PUBLIC PROC=
    BEGIN
    IF doDebug THEN BeSurePupIsOn[];
    IF PupPackageDestroyLocked[] THEN RETURN;
    WaitTillDispatcherIdle[];
    JOIN routerTimeoutFork;
    Heap.FreeNode[z: CommHeap.zone, p: BASE[routingTable]];
    Buffer.DestroyPool[pupBuffers];
    Driver.ChangeNumberOfInputBuffers[FALSE];
    CommunicationInternal.CommPackageOff[];
    END;

  PupPackageDestroyLocked: ENTRY PROC RETURNS [BOOLEAN] = INLINE
    BEGIN
    IF (pupUseCount ← pupUseCount - 1) # 0 THEN RETURN[TRUE];
    pupProtocolFamily.status ← dead;  --turn him off
    Protocol1.EvictFamily[pup];  --stop dispatcher's access
    routerIsActive ← FALSE;
    NOTIFY routingTableUpdateTimeout;
    RETURN[FALSE];
    END;
    
  WaitTillDispatcherIdle: ENTRY PROC=
    BEGIN
    ENABLE UNWIND => NULL;
    hit: BOOLEAN ← FALSE;
    arrived: CONDITION;
    DispatcherCalledUs: ENTRY PROC[b: Buffer.Buffer] =
      BEGIN
      ENABLE UNWIND => NULL;
      hit ← TRUE;
      Buffer.ReturnBuffer[b];
      NOTIFY arrived;
      END;
    b: Buffer.Buffer = Buffer.GetBuffer[pup, pupBuffers, send, smallBuffer];
    Process.EnableAborts[@arrived];
    b.requeueProcedure ← DispatcherCalledUs;
    Driver.PutOnGlobalDoneQueue[b];
    UNTIL hit DO WAIT arrived; ENDLOOP;
    END;
  
  GetPupPackageUseCount: PUBLIC PROC RETURNS [CARDINAL] =
    BEGIN RETURN[pupUseCount]; END;

  AddNetworkPup: PROC[driver: Network, context: PupRouterDefs.NetworkContext] =
    BEGIN
    rte: RoutingTableEntry;
    IF context = NIL THEN RETURN;
    IF driver.device = ethernet AND context.protocol = NIL THEN
      context.protocol ← Pup10MBit.Create[driver, [context.pupHostNumber]];
    rte ← GetRoutingTableEntry[[context.pupNetNumber]];
    IF rte # NIL AND context.pupNetNumber # 0 THEN
      rte↑ ← [
        net: [context.pupNetNumber], hop: 0, time: 0,
        route: [0], context: context];
    rte ← GetRoutingTableEntry[[0]];
    IF rte.context = NIL THEN
      rte↑ ← [net: [0], hop: 0, time: 0, route: [0], context: context];
    END;  --AddNetworkPup

  RemoveNetworkPup: PROC[driver: Network, context: LONG POINTER] =
    BEGIN
    OPEN c: LOOPHOLE[context, PupRouterDefs.NetworkContext];
    probe: BOOLEAN ← FALSE;
    IF ~routerIsActive THEN RETURN; -- Try to avoid ML tangle
    FOR i: CARDINAL IN [0..LENGTH[routingTable]) DO
      IF routingTable[i].context # context THEN LOOP;
      routingTable[i] ← [
        net: [i], hop: maxHop + 1, time: 210, route:, context: NIL];
      probe ← TRUE;
      ENDLOOP;
    IF driver.device = ethernet THEN Pup10MBit.Destroy[c.protocol];
    IF probe THEN ProbeForRoutingInfo[];
    END;  --RemoveNetworkPup

  PupStateChanged: PROC[
    driver: Network, context: LONG POINTER, why: Protocol1.Action] =
    BEGIN
    SELECT why FROM
      add => AddNetworkPup[driver, context];
      remove => RemoveNetworkPup[driver, context];
      ENDCASE; 
    END;  --StateChangedPup

  -- Various junky routines that live here because this is very cold code

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

  SetBadPupProc: PUBLIC PROC[proc: PROC[Buffer.Buffer]] =
    BEGIN inThings.badChecksumProc ← proc; END;

  InspectIncomingPups: PUBLIC PROC[
    new: BOOLEAN, proc: PROC[CARDINAL, Buffer.Buffer]] =
    BEGIN inThings.showIn ← new; inThings.inShower ← proc; END;

  InspectOutgoingPups: PUBLIC PROC[
    new: BOOLEAN, proc: PROC[CARDINAL, Buffer.Buffer]] =
    BEGIN outThings.showOut ← new; outThings.outShower ← proc; END;

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

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

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

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

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

  -- initialization

  Process.EnableAborts[@routingTableUpdateTimeout];
  Process.EnableAborts[@probeResponse];
  
  START PupRouterDefs.PupRouterIn;
  START PupRouterDefs.PupRouterOut;
  START PupRouterDefs.PupChecksums;
  START PupRouterDefs.PupErrors;
  
  Process.SetTimeout[@routingTableUpdateTimeout, Process.MsecToTicks[30000]];
  Process.SetTimeout[@probeResponse, Process.MsecToTicks[500]];
  END.
  
LOG

 9-Jul-84  9:59:13   AOF   Post Klamath.