-- File: LoopBackPlug.mesa,  Last Edit: BLyon  January 16, 1981  1:36 PM
-- This module is needed/used only in the funny localOnly mode.


DIRECTORY
  CommFlags USING [doStats],
  CommUtilDefs USING [GetEthernetHostNumber, CopyLong],
  StatsDefs USING [StatBump, StatIncr],
  DriverDefs USING [
    Glitch, GetUseCount, SmashDeviceChain, GetInputBuffer, AddDeviceToChain,
    Network, NetworkObject, PutOnGlobalDoneQueue, PutOnGlobalInputQueue],
  PupDefs USING [GetDoStats],
  BufferDefs,
  DriverTypes USING [localEncapsulationBytes, localEncapsulationOffset],
  PupTypes USING [PupErrorCode],
  SpecialSystem USING [HostNumber];

LoopBackPlug: MONITOR
  IMPORTS CommUtilDefs, StatsDefs, DriverDefs, PupDefs
  EXPORTS BufferDefs, DriverDefs, PupDefs
  SHARES BufferDefs, DriverTypes, SpecialSystem =
  BEGIN OPEN StatsDefs, BufferDefs, DriverDefs;

  -- EXPORTed TYPEs
  Network: PUBLIC TYPE = DriverDefs.Network;

  myNetwork: DriverDefs.NetworkObject ←
    [decapsulateBuffer: DecapsulateBuffer, encapsulatePup: EncapsulatePup,
      encapsulateOis: EncapsulateOis, sendBuffer: SendBuffer,
      forwardBuffer: ForwardBuffer, activateDriver: ActivateDriver,
      deactivateDriver: DeactivateDriver, deleteDriver: DeleteDriver,
      interrupt: DeactivateDriver,
      -- No interrupt routine
      device: local, alive: TRUE, speed: 3000, index:, buffers: 0, spare: 0,
      netNumber: [0, 0], hostNumber:, next: NIL, pupStats: PupStats, stats: NIL];

  CommPackageAlreadyActive: PUBLIC ERROR = CODE;

  SetLocalOnly: PUBLIC PROCEDURE [localOnly: BOOLEAN] =
    BEGIN
    -- NB: This won't work in the multi network case.
    IF DriverDefs.GetUseCount[] # 0 THEN
      DriverDefs.Glitch[CommPackageAlreadyActive];
    DriverDefs.SmashDeviceChain[];
    IF localOnly THEN [] ← CreateLoopBackDriver[];
    END;

  DecapsulateBuffer: PROCEDURE [b: Buffer] RETURNS [BufferDefs.BufferType] =
    BEGIN
    SELECT b.encapsulation.localType FROM
      pup => RETURN[pup];
      ois => RETURN[ois];
      ENDCASE => RETURN[rejected];
    END;

  EncapsulatePup: PROCEDURE [b: PupBuffer, destination: PupHostID] =
    BEGIN
    b.encapsulation ←
      [local[
	localSpare1:, localSpare2:, localSpare3:, localSpare4:, localSpare5:,
	localHost: b.dest.host, localType: pup]];
    b.length ← (b.pupLength + 1 + DriverTypes.localEncapsulationBytes)/2;
    END;

  EncapsulateOis: PROCEDURE [
    b: OisBuffer, destination: SpecialSystem.HostNumber] =
    BEGIN IF TRUE THEN ERROR; b.length ← (b.ois.pktLength + 3)/2; END;

  ForwardBuffer: PROCEDURE [b: Buffer] RETURNS [PupTypes.PupErrorCode] =
    BEGIN SendBuffer[b]; RETURN[noErrorPupErrorCode]; END;

  SendBuffer: ENTRY PROCEDURE [b: Buffer] =
    BEGIN
    copy: Buffer;
    b.device ← local;
    IF b.encapsulation.localHost = myNetwork.hostNumber THEN
      BEGIN -- sending to ourself, copy it over
      copy ← GetInputBuffer[];
      IF copy # NIL THEN
	BEGIN
	copy.device ← local;
	CommUtilDefs.CopyLong[
	  from: @b.encapsulation + DriverTypes.localEncapsulationOffset,
	  nwords: b.length,
	  to: @copy.encapsulation + DriverTypes.localEncapsulationOffset];
	copy.length ← b.length;
	copy.network ← LONG[@myNetwork];
	IF CommFlags.doStats THEN StatIncr[statEtherPacketsLocal];
	IF CommFlags.doStats THEN StatBump[statEtherWordsLocal, b.length];
	END
      ELSE IF CommFlags.doStats THEN StatIncr[statEtherEmptyFreeQueue];
      PutOnGlobalDoneQueue[b]; -- give this one back first
      IF copy # NIL THEN PutOnGlobalInputQueue[copy];
      RETURN
      END;
    PutOnGlobalDoneQueue[b]; -- just throw it away

    END;

  CreateLoopBackDriver: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
    BEGIN AddDeviceToChain[@myNetwork, 0]; RETURN[TRUE]; END;

  DeleteDriver: PROCEDURE = BEGIN END;

  ActivateDriver: PROCEDURE =
    BEGIN myNetwork.hostNumber ← CommUtilDefs.GetEthernetHostNumber[]; END;

  DeactivateDriver: PROCEDURE = BEGIN END;

  PupStats: PROCEDURE [PupBuffer, Network] RETURNS [BOOLEAN] =
    BEGIN RETURN[FALSE]; END;

  -- initialization

  [] ← PupDefs.GetDoStats[]; -- Get main PupPackage started

  END.