-- File: TedCold.mesa, Last Edit:
  -- MAS  April 17, 1980  9:33 PM
  -- HGM  August 3, 1980  7:34 PM

-- Copyright  Xerox Corporation 1979, 1980

DIRECTORY
  CommUtilDefs: FROM "CommUtilDefs" USING [
    DisableTimeout, SetTimeout, MsecToTicks,
    SetPriority, AddInterruptHandler, RemoveInterruptHandler,
    GetEthernetHostNumber,
    CleanupItem, CleanupReason, AllReasons,
    AddCleanupProcedure, RemoveCleanupProcedure],
  AltoEthernetDefs: FROM "AltoEthernetDefs",
  TedDefs: FROM "TedDefs",
  DriverDefs: FROM "DriverDefs" USING [
    Glitch, GetInputBuffer, NetworkObject, AddDeviceToChain,
    PutOnGlobalDoneQueue],
  BufferDefs: FROM "BufferDefs",
  DriverTypes: FROM "DriverTypes";

TedCold: MONITOR LOCKS TedDefs.lock
  IMPORTS CommUtilDefs, DriverDefs, BufferDefs, AltoEthernetDefs, TedDefs
  EXPORTS DriverDefs, TedDefs
  SHARES BufferDefs, DriverTypes =
BEGIN OPEN BufferDefs, DriverDefs, AltoEthernetDefs, TedDefs;

lock: PUBLIC MONITORLOCK;
timer: PUBLIC CONDITION;
hardware: PUBLIC CONDITION;

cleanupItem: CommUtilDefs.CleanupItem ← [,CommUtilDefs.AllReasons,Broom];
hardProcess: PROCESS;
watcherProcess: PROCESS;

myNetwork: PUBLIC DriverDefs.NetworkObject ← [
  decapsulateBuffer: DecapsulateBuffer,
  encapsulatePup: EncapsulatePup,
  encapsulateRpp: EncapsulateRpp,
  sendBuffer: SendBuffer,
  forwardBuffer: NIL,
  activateDriver: ActivateDriver,
  deactivateDriver: DeactivateDriver,
  deleteDriver: DeleteDriver,
  interrupt: Interrupt,
  device: ethernet,
  alive: TRUE,
  speed: 3000,
  index: ,
  netNumber: ,
  hostNumber: ,
  next: NIL,
  pupStats: NIL,
  stats: NIL ];

DriverNotActive: PUBLIC ERROR = CODE;
DriverAlreadyActive: PUBLIC ERROR = CODE;
NoEthernetBoard: PUBLIC ERROR = CODE;
CantSwitchMachinesWhileEtherentDriverIsActive: PUBLIC ERROR = CODE;
CantMakImageWhileEtherentDriverIsActive: PUBLIC ERROR = CODE;


CreateDefaultEthernetDriver: PUBLIC PROCEDURE RETURNS [BOOLEAN] =
  BEGIN
  myDevice.postData ← EthernetNotPosted;
  StartIO[resetCommand];
  StartIO[resetCommand];  -- sometimes it doesn't work
  IF myDevice.postData=EthernetNotPosted THEN RETURN[FALSE];
  tedPleaseStop ← TRUE;
  myDevice.postData ← EthernetNotPosted;
  myNetwork.netNumber ← 0;
  AddDeviceToChain[@myNetwork,0];
  RETURN[TRUE];
  END;

DeleteDriver: PROCEDURE =
  BEGIN
  END;

ActivateDriver: PROCEDURE =
  BEGIN
  IF ~tedPleaseStop THEN Glitch[DriverAlreadyActive];
  tedPleaseStop ← FALSE;
  StartIO[resetCommand];
  StartIO[resetCommand];  -- sometimes it doesn't work
  IF myDevice.postData=EthernetNotPosted THEN Glitch[NoEthernetBoard];
  QueueInitialize[@outputQueue];
  currentInputBuffer ← nextInputBuffer ← currentOutputBuffer ← NIL;
  myNetwork.hostNumber ← CommUtilDefs.GetEthernetHostNumber[];
  myDevice.hostNumber ← myNetwork.hostNumber;
  myDevice.inputBuffer ← [0,NIL0];
  myDevice.outputBuffer ← [0,NIL0];
  nextInputBuffer ← GetInputBuffer[];
  currentInputBuffer ← GetInputBuffer[];
  nextInputBuffer.device ← currentInputBuffer.device ← ethernet;
  nextBufferPointer ← ShortenData[
    @nextInputBuffer.encapsulation+ethernetEncapsulationOffset];
  nextBufferPointer↑ ← 0; -- show no input in yet
  myDevice.inputControlBlock ← NIL0;
  CommUtilDefs.AddCleanupProcedure[@cleanupItem];
  CommUtilDefs.AddInterruptHandler[interruptLevel,@hardware,resetCommand];
  CommUtilDefs.SetPriority[4];
  hardProcess ← FORK Interrupt[];
  CommUtilDefs.SetPriority[1];
  -- The first interrupt will set things up.
  myDevice.interruptBit ← interruptBit;
  StartIO[resetCommand];
  watcherProcess ← FORK Watcher[];
  END;

DeactivateDriver: PROCEDURE =
  BEGIN
  IF tedPleaseStop THEN Glitch[DriverNotActive];
  tedPleaseStop ← TRUE;
  StartIO[resetCommand];  -- includes (naked)NOTIFY
  JOIN hardProcess;
  CommUtilDefs.RemoveCleanupProcedure[@cleanupItem];
  myDevice.interruptBit ← 0;
  CommUtilDefs.RemoveInterruptHandler[interruptLevel];
  StartIO[resetCommand];
  KillDriverLocked[];
  JOIN watcherProcess;
  IF currentInputBuffer#NIL THEN ReturnFreeBuffer[currentInputBuffer];
  IF nextInputBuffer#NIL THEN ReturnFreeBuffer[nextInputBuffer];
  IF currentOutputBuffer#NIL THEN PutOnGlobalDoneQueue[currentOutputBuffer];
  QueueCleanup[@outputQueue];
  myNetwork.netNumber ← 0;  -- in case we turn it on after moving to another machine
  END;

KillDriverLocked: ENTRY PROCEDURE = INLINE
  BEGIN
  NOTIFY timer;
  END;

Broom: PROCEDURE [why: CommUtilDefs.CleanupReason] =
  BEGIN
  SELECT why FROM
    Finish, Abort, OutLd => myDevice.interruptBit ← 0;
    InLd =>
      BEGIN
      IF myNetwork.hostNumber#CommUtilDefs.GetEthernetHostNumber[] THEN
        Glitch[CantSwitchMachinesWhileEtherentDriverIsActive];
      myDevice.interruptBit ← interruptBit;
      END;
    Save, Checkpoint => Glitch[CantMakImageWhileEtherentDriverIsActive];
    ENDCASE;
  StartIO[resetCommand];
  END;

--Note: This module must be the Control Module for a config that includes TedIn, TedLocked and TedOut.

-- initialization
START TedDefs.TedIn;
START TedDefs.TedLocked;
START TedDefs.TedOut;
CommUtilDefs.DisableTimeout[@hardware];
CommUtilDefs.SetTimeout[@timer,CommUtilDefs.MsecToTicks[1000]];
END.  -- TedCold