-- File: Boss.mesa - last edit:
-- AOF                  5-Nov-87 10:14:13
-- SMA                 21-May-86 10:44:29
-- HGM                 13-Jul-85 22:51:43
-- Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved. 

DIRECTORY
  Buffer USING [DeviceType],
  Protocol1 USING [
    ProtocolFamily, FamilyIndex, Family, Matrices, MatrixRecord,
    RemoveFamilyMember],
  CommFlags USING [doStats],
  CommHeap USING [Create, Destroy],
  CommunicationInternal USING [
    MakeSystemBufferPool, DestroySystemBufferPool, DispatcherOff, DispatcherOn],
  Driver USING [Glitch, Device],
  IEEE8023 USING [CreateDefaultEthernetDrivers],
  EthernetDriverFriends USING [EtherStatsInfo],
  Process USING [SecondsToTicks, Pause],
  SpecialCommunication USING [],
  StatsOps USING [Start, Stop];

Boss: MONITOR
  IMPORTS
    CommHeap, CommunicationInternal, Driver, IEEE8023, Process, Protocol1,
    StatsOps
  EXPORTS
    CommunicationInternal, Buffer, Driver, EthernetDriverFriends, Protocol1 =
  BEGIN 

  -- SemiPublic things for others
  Device: PUBLIC TYPE = Driver.Device;
  firstDevice: PUBLIC Device ← NIL;
  increasedBufferUseCount, useCount: PUBLIC CARDINAL ← 0;

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

  CommPackageNotActive: PRIVATE <<Glitch>> ERROR = CODE;
  NetworkNonExistent: PUBLIC <<Driver>> ERROR = CODE;

  <<
  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 PROC[network: Driver.Device] =
    BEGIN
    --Add new drivers to the end of the chain so that the normal
    --Ethernet driver will be network zero.
    AddToChainEntry: ENTRY PROC[] =
      BEGIN
      tail: Driver.Device ← firstDevice;
      IF firstDevice = NIL THEN {firstDevice ← network; network.index ← 1}
      ELSE
        BEGIN
	i: CARDINAL ← 1;
        UNTIL tail.next = NIL DO tail ← tail.next; i ← i + 1; ENDLOOP;
	tail.next ← network; network.index ← i + 1;
	END;
      END;  --AddToChainEntry
    network.next ← NIL;  --make sure end of chain is NIL
    IF state = ready THEN
      BEGIN
      AddToChainEntry[];  --link it up
      network.activateDriver[];  --activate him
      IF (increasedBufferUseCount > 0) AND
        (network.changeNumberOfInputBuffers # NIL) THEN
	  network.changeNumberOfInputBuffers[TRUE];
      END;
    END;  --AddDeviceToChain

  ChangeNumberOfInputBuffers: PUBLIC ENTRY PROC[increaseTheBuffers: BOOLEAN] =
    BEGIN
    IF state # ready THEN Driver.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: Driver.Device ← firstDevice, net.next UNTIL net = NIL DO
      IF net.changeNumberOfInputBuffers = NIL THEN LOOP;
      net.changeNumberOfInputBuffers[increaseTheBuffers];
      ENDLOOP;
    END;  --ChangeNumberOfInputBuffers

  CommPackageGo: PUBLIC --ENTRY-- PROC =
    BEGIN
    UseCount: ENTRY PROC RETURNS[BOOLEAN] = INLINE
      {RETURN[(useCount ← useCount + 1) > 1]};
    extra: CARDINAL ← 0;
    network: Driver.Device;

    IF UseCount[] THEN RETURN;  --we've already been here
    state ← ready;  --assert the state

    CommHeap.Create[];  --that exports CommHeap.zone
    StatsOps.Start[];  --then (maybe) start stats

    [] ← IEEE8023.CreateDefaultEthernetDrivers[];  --default drivers

    FOR network ← firstDevice, network.next UNTIL network = NIL DO
      extra ← extra + network.buffers; ENDLOOP;
    CommunicationInternal.MakeSystemBufferPool[extra];  --buffers for drivers
    CommunicationInternal.DispatcherOn[];  --and access to drivers

    END;  --CommPackageGo

  CommPackageOff: PUBLIC --ENTRY-- PROC =
    BEGIN
    UseCount: ENTRY PROC RETURNS[BOOLEAN] = INLINE
      {RETURN[(useCount ← useCount - 1) # 0]};
    IF UseCount[] THEN RETURN;  --still more clients

    FOR network: Driver.Device ← firstDevice, network.next
      UNTIL network = NIL DO network.deleteDriver[]; ENDLOOP;

    CommunicationInternal.DispatcherOff[];  --don't allow transmits
    CommunicationInternal.DestroySystemBufferPool[];  --kill buffering package
    StatsOps.Stop[];  --than maybe stop stats
    CommHeap.Destroy[];  --and the zone
    state ← off;  --record the obvious
    END;  --CommPackageOff

  GetContext: PUBLIC ENTRY PROC[
    driver: Device, family: Protocol1.ProtocolFamily]
    RETURNS[LONG POINTER] =
    BEGIN
    --looking for intersection of driver and family
    base: LONG POINTER TO Protocol1.MatrixRecord;
    base ← @driver.matrix[0];
    THROUGH[0..LENGTH[driver.matrix]) DO
      IF base.family.name = family THEN RETURN[base.context];
      base ← base + SIZE[Protocol1.MatrixRecord]
      REPEAT FINISHED => RETURN[NIL];
      ENDLOOP;
    END;  --GetContext

  GetDeviceChain: PUBLIC PROC RETURNS [Driver.Device] =
    BEGIN
    IF state # ready THEN Driver.Glitch[CommPackageNotActive];
    RETURN[firstDevice];
    END;  --GetDeviceChain

  GetDoStats: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN[CommFlags.doStats]};

  GetEthernetStats: PUBLIC PROC[physicalOrder: CARDINAL]
   RETURNS [EthernetDriverFriends.EtherStatsInfo] =
   BEGIN
   info: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo;
   info ← GetNthDevice[physicalOrder, ethernet].stats;
   RETURN[info↑];
   END;  --GetEthernetStats

  GetNthDevice: PUBLIC ENTRY PROC[
    physicalOrder: CARDINAL, medium: Buffer.DeviceType]
    RETURNS [net: Driver.Device] =
    BEGIN
    ENABLE UNWIND => NULL;
    i: CARDINAL ← 0;
    FOR net ← firstDevice, net.next UNTIL net = NIL DO
      SELECT TRUE FROM
        (net.device # medium) => NULL;
	((i ← i + 1) = physicalOrder) => RETURN;
	ENDCASE;
      ENDLOOP;
    ERROR NetworkNonExistent;
    END;  --GetNthDevice

  GetUseCount: PUBLIC PROC RETURNS[CARDINAL] = {RETURN[useCount]};

  RemoveDeviceFromChain: PUBLIC <<Driver>> PROC[network: Driver.Device] =
    BEGIN
    RemoveFromChainEntry: ENTRY PROC[] =
      BEGIN
      IF firstDevice = network THEN firstDevice ← network.next
      ELSE FOR tail: Driver.Device ← firstDevice, tail.next
	UNTIL tail.next = network DO
	REPEAT FINISHED => tail.next ← network.next;
	ENDLOOP;
      END;  --RemoveFromChainEntry

    network.alive ← FALSE;  --that stops traffic through the driver
    RemoveFromChainEntry[];  --remove the object from the device chain

    --Remove all family members associated with this driver
    FOR fi: Protocol1.FamilyIndex IN[0..LENGTH[network.matrix]) DO
      --matrix gets reordered as family members are removed.
      context: LONG POINTER ← network.matrix[0].context;
      Protocol1.RemoveFamilyMember[network, network.matrix[0].family];
      ENDLOOP;

    network.deactivateDriver[];  --then deactivate the driver

    Process.Pause[Process.SecondsToTicks[10]];  --just wait for traffic to die
    END;  --RemoveDeviceFromChain

  SmashDeviceChain: PUBLIC PROC = {firstDevice ← NIL};
 
  END.
  
LOG

16-May-84 14:39:36  AOF  Post Klamath
19-May-86 16:26:33  SMA  New encapsulation scheme.
11-Jun-87 15:40:42  AOF  Don't delete context in RemoveDeviceFromChain.
18-Jul-87 10:29:21  AOF  Moving from EtherMAC to IEEE8023.
15-Oct-87 12:19:31  AOF  Don't hold monitor and call .deactivateDriver.
 5-Nov-87 10:13:54  AOF  Consistant check for changing # of buffers = NIL.