-- File: PupRouterCool.Mesa,  Last Edit: HGM  March 19, 1981  6:33 PM
-- This section of code is mostly used when creating/destroying sockets

DIRECTORY
  Inline USING [LowHalf],
  Process USING [InitializeCondition],
  System USING [GetClockPulses],
  CommFlags USING [doDebug],
  CommUtilDefs USING [MaybeShorten],
  PupRouterDefs USING [
    routerLock, BeSurePupIsOn, GetRoutingTableEntry, RoutingTableEntry,
    routingTable, RoutingTable, firstSocket, PupRouterSocket],
  DriverDefs USING [Network, GetDeviceChain],
  PupDefs USING [],
  BufferDefs USING [QueueInitialize, QueueCleanup],
  PupTypes USING [
    fillInNetID, fillInHostID, fillInSocketID, PupAddress, PupNetID, PupSocketID,
    Pair];

PupRouterCool: MONITOR LOCKS PupRouterDefs.routerLock
  IMPORTS
    Inline, Process, System, CommUtilDefs, DriverDefs, PupRouterDefs, BufferDefs
  EXPORTS PupDefs, PupRouterDefs =PUBLIC

  BEGIN OPEN PupTypes, PupRouterDefs;

  spareSocket: CARDINAL ← 1000B;
  nextConnectionID: CARDINAL ← 1000;

  UniqueLocalPupSocketID: ENTRY PROCEDURE RETURNS [socket: PupSocketID] =
    BEGIN
    mumble: LONG CARDINAL ← System.GetClockPulses[]; -- Alto Pulses are short
    IF (spareSocket ← spareSocket + 1) = 0 THEN spareSocket ← 1000B;
    socket ← [Inline.LowHalf[mumble], spareSocket];
    END;

  UniqueLocalPupAddress: PROCEDURE [him: POINTER TO PupAddress]
    RETURNS [me: PupAddress] =
    BEGIN RETURN[GetLocalPupAddress[PupTypes.fillInSocketID, him]]; END;

  NextPupConnectionID: ENTRY PROCEDURE RETURNS [p: PupTypes.Pair] =
    BEGIN
    mumble: LONG CARDINAL ← System.GetClockPulses[]; -- Alto Pulses are short
    p.a ← Inline.LowHalf[mumble];
    p.b ← nextConnectionID ← nextConnectionID + 1;
    END;

  GetLocalPupAddress: PROCEDURE [
    local: PupSocketID, remote: POINTER TO PupAddress] RETURNS [me: PupAddress] =
    BEGIN
    destNet: PupNetID ← [0];
    rte: RoutingTableEntry;
    network: DriverDefs.Network ← NIL;
    IF remote # NIL THEN
      BEGIN OPEN PupTypes;
      IF remote.net # fillInNetID AND remote.host # fillInHostID THEN
	destNet ← remote.net;
      END;
    rte ← GetRoutingTableEntry[destNet];
    IF rte # NIL THEN network ← rte.network;
    IF network = NIL OR network.netNumber.b > 377B THEN
      network ← DriverDefs.GetDeviceChain[];
    me.net ← [network.netNumber.b];
    me.host ← [network.hostNumber];
    me.socket ←
      IF local # PupTypes.fillInSocketID THEN local ELSE UniqueLocalPupSocketID[];
    IF remote # NIL THEN
      BEGIN OPEN PupTypes;
      IF remote.net = fillInNetID THEN
	remote.net ← IF network # NIL THEN [network.netNumber.b] ELSE me.net;
      IF remote.host = fillInHostID THEN
	remote.host ← IF network # NIL THEN [network.hostNumber] ELSE me.host;
      IF remote.socket = fillInSocketID THEN remote.socket ← me.socket;
      END;
    END;

  PupRouterKnowAboutSocket: ENTRY PROCEDURE [so: PupRouterSocket] =
    BEGIN
    IF CommFlags.doDebug THEN BeSurePupIsOn[];
    Process.InitializeCondition[CommUtilDefs.MaybeShorten[@so.ready], 0];
    BufferDefs.QueueInitialize[@so.input];
    so.next ← firstSocket;
    firstSocket ← so;
    END;

  PupRouterForgetAboutSocket: ENTRY PROCEDURE [so: PupRouterSocket] =
    BEGIN
    previous: PupRouterSocket;
    IF CommFlags.doDebug THEN BeSurePupIsOn[];
    IF firstSocket = so THEN firstSocket ← so.next
    ELSE
      FOR previous ← firstSocket, previous.next UNTIL previous = NIL DO
	IF previous.next = so THEN BEGIN previous.next ← so.next; EXIT; END;
	ENDLOOP;
    BufferDefs.QueueCleanup[@so.input];
    END;

  GetRoutingTable: PROCEDURE RETURNS [RoutingTable] =
    BEGIN BeSurePupIsOn[]; RETURN[routingTable]; END;

  GetFirstPupSocket: PROCEDURE RETURNS [PupRouterSocket] =
    BEGIN BeSurePupIsOn[]; RETURN[firstSocket]; END;

  GetHopsToNetwork: PROCEDURE [net: PupTypes.PupNetID] RETURNS [CARDINAL] =
    BEGIN
    rte: RoutingTableEntry ← GetRoutingTableEntry[net];
    IF rte = NIL OR rte.network = NIL THEN RETURN[LAST[CARDINAL]];
    RETURN[rte.hop];
    END;

  FastPath: PROCEDURE [where: PupTypes.PupAddress] RETURNS [BOOLEAN] =
    BEGIN
    rte: RoutingTableEntry ← GetRoutingTableEntry[where.net];
    IF rte = NIL OR rte.hop # 0 THEN RETURN[FALSE];
    SELECT rte.network.device FROM
      local, ethernet, ethernetOne => RETURN[TRUE];
      ENDCASE => RETURN[FALSE];
    END;

  END.