-- File: PacketStreamMgr.mesa - last edit:
-- AOF                  9-Sep-87  8:27:47
-- SMA                 22-May-86 11:50:37
-- MXI                  3-Nov-87  9:56:39
-- Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. 

-- Function: The implementation module for the manager of Pilot Packet Streams.

DIRECTORY
  CommunicationInternal USING [],
  CommFlags USING [doDebug],
  CommHeap USING [zone],
  Driver USING [Glitch],
  Inline USING [LowHalf],
  NewNetworkStream USING [NegotiationParameters, TSap],
  NSTypes USING [ConnectionID, WaitTime],
  PacketStream USING [
    FailureReason, Handle, SuspendReason, ClassOfService, WaitTime,
    unknownConnID],
  NetworkStream USING [],
  NetworkStreamImpl USING [
    connection, controlObject, newStartPacketStream, packetStreamObject,
    probe, startPacketStream, stopPacketStream, xmtr],
  Router USING [AssignDestinationRelativeAddress],
  Runtime USING [GlobalFrame, UnNew],
  SppNegotiation USING [NegotiationHints],
  SppOps USING [GlobalFrameFromByteStream, SppSpyProc],
  Socket USING [infiniteWaitTime],
  SocketInternal USING [SocketHandle],
  Stream USING [Handle],
  System USING [GetClockPulses, NetworkAddress, nullNetworkAddress];

PacketStreamMgr: MONITOR
  IMPORTS
    CommHeap, Driver, Inline, pktStrmInst: NetworkStreamImpl,
    Router, Runtime, SppOps, System
  EXPORTS
    CommunicationInternal, NetworkStream,
    PacketStream, SppNegotiation, SppOps =
  BEGIN

  sppWindowSize: PUBLIC <<SppOps>> CARDINAL[0..12] ← 4;  --set default

  Instance: TYPE = LONG POINTER TO FRAME[NetworkStreamImpl];
  ConnectionID: PUBLIC TYPE = NSTypes.ConnectionID;
  infiniteWaitTime: PUBLIC PacketStream.WaitTime ← Socket.infiniteWaitTime;
  
  --LEVEL 2 PROTOCOL SPY PROC
  sppSpy: SppOps.SppSpyProc ← NIL;

  --CONNECTION PARAMETERS
  connection: PUBLIC RECORD[
    spareID: ConnectionID,
    head: ConnectionTableEntry,
    maximum, active: CARDINAL] ← [[0], NIL, 40, 0];

  instance0: Instance ← NIL;
  ConnectionTableEntry: TYPE = LONG POINTER TO ConnectionTableObject;
  ConnectionTableObject: TYPE = RECORD [
    next: ConnectionTableEntry,
    rAddr: System.NetworkAddress,
    rConnID: ConnectionID];

  --various Glitches.
  NotInConnectionTable: ERROR = CODE;

  ConnectionSuspended: PUBLIC ERROR [why: PacketStream.SuspendReason] = CODE;
  ConnectionFailed: PUBLIC SIGNAL [why: PacketStream.FailureReason] = CODE;

  ConnectionAlreadyThere: PUBLIC ENTRY PROC[
    remote: System.NetworkAddress, remoteConnID: ConnectionID]
    RETURNS [BOOLEAN] =
    BEGIN
    e: ConnectionTableEntry;
    FOR e ← connection.head, e.next UNTIL e = NIL DO
      IF (e.rAddr.host = remote.host)
        AND (e.rAddr.socket = remote.socket)
        AND (e.rConnID = remoteConnID) THEN
	RETURN[TRUE];
      ENDLOOP;
    RETURN[FALSE];
    END; --ConnectionAlreadyThere

  Destroy: PUBLIC PROC[psH: PacketStream.Handle] =
    BEGIN
    instanceN: Instance ← GlobalFrameFromPacketStream[psH];
    remote: System.NetworkAddress ← instanceN.connection.remoteAddr;
    remoteConnID: ConnectionID ← instanceN.connection.remoteID;
    IF sppSpy # NIL THEN sppSpy[LOOPHOLE[instanceN], delete];
    instanceN.stopPacketStream[];  --stop the proceses, etc
    UnnewInstance[instanceN];  --unload the frame
    RemoveConnection[remote, remoteConnID];
    END; --Destroy

  DisableChecksums: PUBLIC <<SppOps>> PROC[psH: PacketStream.Handle] =
    BEGIN
    gf: Instance = NARROW[GlobalFrameFromPacketStream[psH]];
    sH: SocketInternal.SocketHandle = LOOPHOLE[gf.connection.socket];
    gf.xmtr.checksums ← FALSE;  --that take care of local case
    sH.checksums ← FALSE;  --and that takes care of all other cases
    END;  --DisableChecksums

  EnableChecksums: PUBLIC <<SppOps>> PROC[psH: PacketStream.Handle] =
    BEGIN
    gf: Instance = NARROW[GlobalFrameFromPacketStream[psH]];
    sH: SocketInternal.SocketHandle = LOOPHOLE[gf.connection.socket];
    gf.xmtr.checksums ← TRUE;  --that take care of local case
    sH.checksums ← TRUE;  --and that takes care of all other cases
    END;  --EnableChecksums

  DisableProbes: PUBLIC <<SppOps>> PROC[psH: PacketStream.Handle] =
    {NARROW[GlobalFrameFromPacketStream[psH], Instance].probe.probing ← FALSE};

  EnableProbes: PUBLIC <<SppOps>> PROC[psH: PacketStream.Handle] =
    {NARROW[GlobalFrameFromPacketStream[psH], Instance].probe.probing ← TRUE};

  GetUniqueConnectionID: PUBLIC ENTRY PROC RETURNS [iD: ConnectionID] =
    <<
    Some day the connection IDs in use will be kept around, so that
    on wrap around an unused ID will be assigned.
    >>
    BEGIN
    iD ← connection.spareID;
    connection.spareID ← [connection.spareID + 1];
    IF connection.spareID  = PacketStream.unknownConnID THEN
      connection.spareID ← ConnectionID[1];
    END; --GetUniqueConnectionID

  GlobalFrameFromPacketStream: PUBLIC <<SppOps>> PROC[psH: PacketStream.Handle]
    RETURNS[LONG POINTER --TO FRAME[NetworkStreamImpl]--] =
    {RETURN[LOOPHOLE[Runtime.GlobalFrame[LOOPHOLE[psH.get]]]]};

  PacketStreamFromByteStream: PUBLIC <<SppOps>> PROC[sH: Stream.Handle]
    RETURNS[PacketStream.Handle] =
    BEGIN
    gf: Instance = SppOps.GlobalFrameFromByteStream[sH];
    RETURN[@gf.packetStreamObject];
    END;  --PacketStreamFromByteStream

  ByteStreamFromPacketStream: PUBLIC <<SppOps>> PROC[psH: PacketStream.Handle]
    RETURNS[Stream.Handle] =
    BEGIN
    gf: Instance = GlobalFrameFromPacketStream[psH];
    RETURN[@gf.controlObject.streamObject];
    END;  --ByteStreamFromPacketStream
  
  InsertConnection: ENTRY PROC[
    remote: System.NetworkAddress, remoteConnID: ConnectionID]
    RETURNS [BOOLEAN] =
    BEGIN
    ENABLE UNWIND => NULL;
    e: ConnectionTableEntry;
    IF connection.active >= connection.maximum THEN RETURN[FALSE];
    connection.active ← connection.active + 1;
    e ← CommHeap.zone.NEW[ConnectionTableObject ← [
      next: connection.head, rAddr: remote, rConnID: remoteConnID]];
    connection.head ← e;
    RETURN[TRUE];
    END; --InsertConnection

  InsertIntoConnectionTable: PUBLIC PROC[
    remote: System.NetworkAddress, remoteConnID: ConnectionID] =
    BEGIN
    UNTIL InsertConnection[remote, remoteConnID] DO
      SIGNAL ConnectionFailed[tooManyConnections]; ENDLOOP;
    END;  --InsertIntoConnectionTable

  Make: PUBLIC PROC[
    local, remote: System.NetworkAddress, localConnID, remoteConnID:
    ConnectionID, establishConnection: BOOLEAN, timeout:
    NSTypes.WaitTime, classOfService: PacketStream.ClassOfService]
    RETURNS [psH: PacketStream.Handle] =
    BEGIN
    GetInstance: ENTRY PROC = INLINE
      {IF instance0 # NIL THEN instanceN ← NEW pktStrmInst
      ELSE instance0 ← instanceN ← pktStrmInst};
    instanceN: Instance;
    estdRemoteConnID: ConnectionID;
    estdRemoteAddr: System.NetworkAddress;
    GetInstance[];  --get instance's global frame
    BEGIN
    ENABLE UNWIND =>
      BEGIN
      instanceN.stopPacketStream[];  --stop the protocol
      UnnewInstance[instanceN];  --unload the frame
      END;
    --If we do not have a local address we pick one with a network number that
    --is a good way to get to the destination and therefore a good way to get
    --back to us!
    IF local = System.nullNetworkAddress THEN
      local ← Router.AssignDestinationRelativeAddress[remote.net];
    IF localConnID = PacketStream.unknownConnID THEN
      localConnID ← GetUniqueConnectionID[];
    IF instanceN # instance0 THEN START instanceN;  --only copied frames
    [psH, estdRemoteAddr, estdRemoteConnID] ← instanceN.startPacketStream[
      local, remote, localConnID, remoteConnID,
      timeout, classOfService, establishConnection];
    psH.destroy ← Destroy;  --this is not instance oriented
    UNTIL InsertConnection[estdRemoteAddr, estdRemoteConnID] DO
      SIGNAL ConnectionFailed[tooManyConnections]; ENDLOOP;
    IF sppSpy # NIL THEN sppSpy[LOOPHOLE[instanceN], create];
    END;
    END; --Make
    
  NewMake: PUBLIC PROC[
    local, remote: NewNetworkStream.TSap,
    negotiationParameters: NewNetworkStream.NegotiationParameters,
    hints: SppNegotiation.NegotiationHints, timeout: NSTypes.WaitTime]
    RETURNS [psH: PacketStream.Handle] =
    BEGIN
    GetInstance: ENTRY PROC = INLINE
      {IF instance0 # NIL THEN instanceN ← NEW pktStrmInst
      ELSE instance0 ← instanceN ← pktStrmInst};
    instanceN: Instance;
    GetInstance[];  --get instance's global frame
    BEGIN
    ENABLE UNWIND =>
      BEGIN
      instanceN.stopPacketStream[];  --stop the protocol
      UnnewInstance[instanceN];  --unload the frame
      END;
    --If we do not have a local address we pick one with a network number that
    --is a good way to get to the destination and therefore a good way to get
    --back to us!
    IF local.address = System.nullNetworkAddress THEN
      local.address ← Router.AssignDestinationRelativeAddress[remote.address.net];
    IF local.connectionID = LOOPHOLE[PacketStream.unknownConnID] THEN
      local.connectionID ← GetUniqueConnectionID[];
    IF instanceN # instance0 THEN START instanceN;  --only copied frames
    [psH] ← instanceN.newStartPacketStream[
      local, remote, negotiationParameters, hints, timeout];
    psH.destroy ← Destroy;  --this is not instance oriented
    UNTIL InsertConnection[remote.address, remote.connectionID] DO
      SIGNAL ConnectionFailed[tooManyConnections]; ENDLOOP;
    IF sppSpy # NIL THEN sppSpy[LOOPHOLE[instanceN], create];
    END;
    END; --NewMake
  
  PacketStreamsGo: PUBLIC ENTRY PROC=
    BEGIN
    UNTIL connection.spareID # PacketStream.unknownConnID DO
      connection.spareID ← Inline.LowHalf[System.GetClockPulses[]];
      ENDLOOP;
    END; --PacketStreamsGo
  
  RemoveConnection: ENTRY PROC[
    remote: System.NetworkAddress, remoteConnID: ConnectionID] =
    BEGIN
    e, prev: ConnectionTableEntry;
    e ← connection.head;
    UNTIL e = NIL DO
      IF (e.rAddr.host = remote.host)
        AND (e.rAddr.socket = remote.socket)
	AND (e.rConnID = remoteConnID) THEN
	BEGIN
	IF e = connection.head THEN connection.head ← e.next
	ELSE prev.next ← e.next;
	CommHeap.zone.FREE[@e];
	connection.active ← connection.active - 1;
	RETURN;
	END;
      prev ← e;
      e ← e.next;
      ENDLOOP;
    IF CommFlags.doDebug THEN Driver.Glitch[NotInConnectionTable];
    END; --RemoveConnection

  RemoveFromConnectionTable: PUBLIC PROC[
    remote: System.NetworkAddress, remoteConnID: ConnectionID] =
    {[] ← RemoveConnection[remote, remoteConnID]};
  
  SetSppSpy: PUBLIC <<SppOps>> ENTRY PROC[spy: SppOps.SppSpyProc]
    RETURNS [oldSpy: SppOps.SppSpyProc] =
    {oldSpy ← sppSpy; sppSpy ← spy};

  SetWindow: PUBLIC <<SppOps>> PROC[window: CARDINAL[0..12]] =
    {sppWindowSize ← window};  --affects all new streams

  UnnewInstance: ENTRY PROC[nth: Instance] = INLINE
    {IF nth = instance0 THEN instance0 ← NIL ELSE Runtime.UnNew[LOOPHOLE[nth]]};

  --initialization

  START pktStrmInst;  --always start the first copy

  END. --of PacketStreamMgr module

LOG
17-May-84 11:07:07  AOF  Post Klamath
 4-Dec-85 17:35:24  AOF  Adding some hacks for probing & buffering
11-Apr-86  9:21:46  AOF  Procs to enable/disable checksums for local net
22-May-86 11:49:40  SMA  No exports to SpecialCommunication.
 9-Nov-86 11:39:30  AOF  enable/disable checksums in socket too.
 9-Nov-86 12:05:18  AOF  Getting psH and sH from the other.
 9-Sep-87  8:27:05  AOF  User proc variables to start spp instance
 3-Nov-87  9:56:51  MXI  Added NewMake for SPP negotiation