-- File: Boss.mesa,
--   Last Edit: HGM  January 7, 1981  1:37 AM
--   Last Edit: BLyon  March 4, 1981  2:37 PM

DIRECTORY
  BufferDefs USING [
    BufferAccessHandle, defaultSystemBufferPoolSize,
    defaultSystemBuffersToReserve, FreeBufferPool, MakeBufferPool,
    systemAccessHandle],
  StatsDefs USING [StatIncr],
  CommUtilDefs USING [FriendOfCopyLong, LockCode, UnlockCode, SetDebuggingPointer],
  CommFlags USING [doDebug, doStats],
  DriverDefs USING [
    Glitch, GiantVector, GetPupRouter, GetOisRouter,
    GetWordsPerIocb, SetWordsPerIocb, CreateDefaultEthernetOneDrivers,
    CreateDefaultEthernetDrivers, Network, DispatcherOff, DispatcherOn,
    DispatcherImpl, BufferMgr],
  OISCP USING [],
  PupDefs USING [],
  SpecialCommunication USING [PhysicalMedium],
  SpecialSystem USING [NetworkNumber];

Boss: MONITOR
  IMPORTS BufferDefs, StatsDefs, CommUtilDefs, DriverDefs
  EXPORTS DriverDefs, OISCP, PupDefs, SpecialCommunication
  SHARES BufferDefs, DriverDefs =
  BEGIN OPEN CommUtilDefs, DriverDefs, SpecialSystem, SpecialCommunication;

  -- SemiPublic things for others
  firstNetwork: PUBLIC Network ← NIL;
  giantVector: PUBLIC POINTER TO GiantVector ← @bigBoy; -- Debugging only
  bigBoy: GiantVector;
  increasedBufferUseCount, useCount: PUBLIC CARDINAL ← 0;

  state: {off, ready} ← off; -- on is ready with useCount>0

  IocbNotBigEnough: PUBLIC ERROR = CODE;
  CommPackageNotActive: PUBLIC ERROR = CODE;
  NetworkNonExistent: PUBLIC ERROR = CODE;
  SystemBufferPoolConfused: PUBLIC ERROR = CODE;

  GetUseCount: PUBLIC PROCEDURE RETURNS [CARDINAL] = BEGIN RETURN[useCount]; END;

  GetDoStats: PUBLIC PROCEDURE RETURNS [BOOLEAN] = BEGIN RETURN[CommFlags.doStats]; END;

  GetGiantVector: PUBLIC PROCEDURE RETURNS [POINTER TO GiantVector] =
    BEGIN RETURN[giantVector]; END;

  -- This code is a bit delicate.  There are probably many funny cases that won't work correctly.  In particular, there is a race condition between adding/deleting a driver and adding/deleting a router.

  AddDeviceToChain: PUBLIC PROCEDURE [network: Network, iocbSize: CARDINAL] =
    BEGIN
    -- Add new drivers to the end of the chain so that the normal Ethernet driver will be network zero.
    tail: Network ← firstNetwork;
    i: CARDINAL ← 1;
    UNTIL (iocbSize MOD 4) = 0 DO iocbSize ← iocbSize + 1; ENDLOOP;
    IF state = off THEN SetWordsPerIocb[MAX[GetWordsPerIocb[], iocbSize]]
    ELSE IF iocbSize > GetWordsPerIocb[] THEN Glitch[IocbNotBigEnough];
    IF state = ready THEN LockCode[network.interrupt];
    IF useCount > 0 THEN
      BEGIN
      network.activateDriver[];
      DriverDefs.GetPupRouter[].addNetwork[network];
      DriverDefs.GetOisRouter[].addNetwork[network];
      END;
    IF firstNetwork = NIL THEN
      BEGIN firstNetwork ← network; network.index ← 1; RETURN; END;
    UNTIL tail.next = NIL DO tail ← tail.next; i ← i + 1; ENDLOOP;
    tail.next ← network;
    network.index ← i + 1;
    END;

  RemoveDeviceFromChain: PUBLIC ENTRY PROCEDURE [network: Network] =
    BEGIN
    tail: Network ← firstNetwork;
    IF useCount > 0 THEN
      BEGIN
      DriverDefs.GetPupRouter[].removeNetwork[network];
      DriverDefs.GetOisRouter[].removeNetwork[network];
      network.deactivateDriver[];
      END;
    IF state = ready THEN UnlockCode[network.interrupt];
    IF firstNetwork = network THEN firstNetwork ← network.next
    ELSE
      BEGIN
      UNTIL tail.next = network DO tail ← tail.next; ENDLOOP;
      tail.next ← network.next;
      END;
    -- network.index is not updated.  It is used only to collect Gateway statistics.

    END;

  ChangeNumberOfInputBuffers: PUBLIC ENTRY PROCEDURE [
    increaseTheBuffers: BOOLEAN] =
    BEGIN
    IF state # ready THEN Glitch[CommPackageNotActive];
    IF increaseTheBuffers THEN
      BEGIN
      increasedBufferUseCount ← increasedBufferUseCount + 1;
      IF increasedBufferUseCount>1 THEN RETURN;
      END
    ELSE
      BEGIN
      IF increasedBufferUseCount=0 THEN RETURN;  -- donot go negative
      increasedBufferUseCount ← increasedBufferUseCount - 1;
      IF increasedBufferUseCount#0 THEN RETURN;
      END;
    FOR net: Network ← firstNetwork, net.next UNTIL net = NIL DO
      IF net.changeNumberOfInputBuffers=NIL THEN LOOP;
      net.changeNumberOfInputBuffers[increaseTheBuffers];
      ENDLOOP;
    END;

  GetDeviceChain: PUBLIC PROCEDURE RETURNS [Network] =
    BEGIN
    IF state # ready THEN Glitch[CommPackageNotActive];
    RETURN[firstNetwork];
    END;

  SmashDeviceChain: PUBLIC PROCEDURE = BEGIN firstNetwork ← NIL; END;

  -- The drivers may have to be told what its  network numbers are.
  -- This is certainly true if this is the first machine running on a network.
  -- Physical order is the location of the network on the network device chain .

  SetNetworkID: PUBLIC ENTRY PROCEDURE [
    physicalOrder: CARDINAL, medium: PhysicalMedium, newNetID: NetworkNumber]
    RETURNS [oldNetID: NetworkNumber] =
    BEGIN
    ENABLE UNWIND => NULL;
    net: Network;
    net ← GetNthDevice[physicalOrder, medium];
    oldNetID ← net.netNumber;
    net.netNumber ← newNetID;
    DriverDefs.GetPupRouter[].stateChanged[net];
    DriverDefs.GetOisRouter[].stateChanged[net];
    END;

  -- This procedure returns the OisNetID the Nth Network Object on the network device
  -- chain.  (where N=physicalOrder).

  GetNetworkID: PUBLIC ENTRY PROCEDURE [
    physicalOrder: CARDINAL, medium: PhysicalMedium] RETURNS [NetworkNumber] =
    BEGIN
    ENABLE UNWIND => NULL;
    RETURN[GetNthDevice[physicalOrder, medium].netNumber];
    END;

  -- This procedure find the Nth (where N=physicalOrder) network of the specified medium
  --  on the network device chain.  This assumes that we locked (protected).

  GetNthDevice: PRIVATE PROCEDURE [
    physicalOrder: CARDINAL, medium: PhysicalMedium] RETURNS [net: Network] =
    BEGIN
    i: CARDINAL ← 0;
    FOR net ← firstNetwork, net.next UNTIL net=NIL DO
      IF net.device = medium THEN IF (i ← i + 1) = physicalOrder THEN RETURN;
      ENDLOOP;
    ERROR NetworkNonExistent;
    END;

  -- This may be called at any time.  It does nothing if already ready.

  OiscpPackageReady, PupPackageReady: PUBLIC ENTRY PROCEDURE =
    BEGIN CommPackageReady[]; END;

  CommPackageReady: INTERNAL PROCEDURE =
    BEGIN
    extra: CARDINAL ← 0;
    IF useCount = 0 AND firstNetwork = NIL THEN
      BEGIN
      [] ← CreateDefaultEthernetDrivers[];
      [] ← CreateDefaultEthernetOneDrivers[];
      END;
    IF state = ready THEN RETURN;
    -- On the Alto, MakeImage forgets low memory
    IF CommFlags.doDebug THEN CommUtilDefs.SetDebuggingPointer[giantVector];
    IF CommFlags.doStats THEN LockCode[StatsDefs.StatIncr];
    LockCode[DriverDefs.DispatcherImpl];
    LockCode[DriverDefs.BufferMgr];
    LockCode[CommUtilDefs.FriendOfCopyLong];
    FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
      extra ← extra + network.buffers; ENDLOOP;
    IF BufferDefs.systemAccessHandle # NIL THEN Glitch[SystemBufferPoolConfused];
    BufferDefs.systemAccessHandle ← BufferDefs.MakeBufferPool[
      total: BufferDefs.defaultSystemBufferPoolSize + extra,
      reserve: BufferDefs.defaultSystemBuffersToReserve];
    DispatcherOn[];
    FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
      LockCode[network.interrupt]; ENDLOOP;
    state ← ready;
    END;

  CommPackageGo: PUBLIC ENTRY PROCEDURE =
    BEGIN
    -- On the Alto, MakeImage forgets low memory
    IF CommFlags.doDebug THEN CommUtilDefs.SetDebuggingPointer[giantVector];
    CommPackageReady[];
    IF (useCount ← useCount + 1) > 1 THEN RETURN;
    FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
      network.activateDriver[]; ENDLOOP;
    END;

  CommPackageOff: PUBLIC ENTRY PROCEDURE =
    BEGIN
    IF (useCount ← useCount - 1) # 0 THEN RETURN;
    FOR network: Network ← firstNetwork, network.next UNTIL network = NIL DO
      network.deactivateDriver[]; UnlockCode[network.interrupt]; ENDLOOP;
    DispatcherOff[];
    IF BufferDefs.systemAccessHandle = NIL THEN Glitch[SystemBufferPoolConfused];
    BufferDefs.FreeBufferPool[BufferDefs.systemAccessHandle];
    BufferDefs.systemAccessHandle ← NIL;
    IF CommFlags.doStats THEN UnlockCode[StatsDefs.StatIncr];
    UnlockCode[CommUtilDefs.FriendOfCopyLong];
    UnlockCode[DriverDefs.BufferMgr];
    UnlockCode[DriverDefs.DispatcherImpl];
    state ← off;
    END;


  -- initialization

  IF CommFlags.doDebug THEN
    BEGIN
    CommUtilDefs.SetDebuggingPointer[giantVector];
    giantVector.slaThings ← giantVector.prThings ← NIL;
    END;
  START DriverDefs.DispatcherImpl;
  START DriverDefs.BufferMgr;
  END.