-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- GateStats.mesa, HGM, 23-Oct-85 23:19:09

DIRECTORY
  Environment USING [Byte],
  Inline USING [LowHalf],
  Process USING [Detach],
  System USING [localHostNumber, nullHostNumber],
  
  Buffer USING [ReturnBuffer],
  CpuIdle USING [GetCalibration, GetCycles],
  Driver USING [Network, GetDeviceChain],
  EthernetFormat USING [EtherStatsEntry, ethernetStatsReply, etherVersion],
  EthernetDriverFriends USING [EtherStatsInfo],
  GateControlDefs USING [pupStatsAck, pupStatsNak, pupStatsSend],
  PhoneNetFriends USING [PhoneNetInfo, StatsPtrToStats],
  Protocol1 USING [GetContext],
  PupDefs USING [
    PupBuffer, GetPupContentsBytes, PupRouterSendThis,
    SwapPupSourceAndDest, ReturnPup, PupSocket, PupSocketMake,
    SetPupContentsWords, veryLongWait],
  PupRouterDefs USING [NetworkContext],
  PupTypes USING [statSoc, fillInPupAddress],
  PupWireFormat USING [MesaToBcplLongNumber],
  SlaFormat USING [LineState, SlaStatsEntry, slaStatsReply, slaVersion];

GateStats: PROGRAM
  IMPORTS
    Inline, Process, System,
    Buffer, CpuIdle, Driver, Protocol1, PhoneNetFriends, PupDefs, PupWireFormat
  EXPORTS Buffer, GateControlDefs =
  BEGIN

  Network: PUBLIC TYPE = Driver.Network;

  soc: PupDefs.PupSocket;

  GateStatsOn: PUBLIC PROCEDURE = BEGIN Process.Detach[FORK Stats[]]; END;

  Stats: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    firstNetwork: Driver.Network = Driver.GetDeviceChain[];
    soc ← PupDefs.PupSocketMake[
      PupTypes.statSoc, PupTypes.fillInPupAddress, PupDefs.veryLongWait];
    DO  -- forever
      IF (b ← soc.get[]) # NIL THEN
        BEGIN
        SELECT b.pup.pupType FROM
          GateControlDefs.pupStatsSend =>
            BEGIN
            target: CARDINAL = b.pup.pupWords[0];
	    bytes: CARDINAL = PupDefs.GetPupContentsBytes[b];
            IF bytes < 2 THEN GOTO Ignore;
            FOR network: Driver.Network ← firstNetwork, network.next UNTIL network = NIL DO
              context: PupRouterDefs.NetworkContext ← Protocol1.GetContext[network, pup];
              IF context.pupNetNumber # target THEN LOOP;
              IF network.stats = NIL THEN GOTO Reject;
	      SELECT bytes FROM
                2 => OldPupStats[b, network];
		4 => NewPupStats[b, network];
		ENDCASE => GOTO Ignore;
              b.pup.pupType ← GateControlDefs.pupStatsAck;
              PupDefs.SwapPupSourceAndDest[b];
              PupDefs.PupRouterSendThis[b];
	      GOTO Sent;
              ENDLOOP;
            GOTO Ignore;
            END;
          ENDCASE => GOTO Ignore;
        EXITS
          Reject => BEGIN PupDefs.ReturnPup[b, GateControlDefs.pupStatsNak, 0]; END;
          Ignore => BEGIN Buffer.ReturnBuffer[b]; END;
          Sent => NULL;
        END;
      ENDLOOP;
    END;

  NewPupStats: PROCEDURE [b: PupDefs.PupBuffer, network: Driver.Network] =
    BEGIN
    SELECT network.device FROM
      ethernet, ethernetOne => NewEthernetStats[b, network];
      phonenet => NewPhoneStats[b, network];
      ENDCASE => ERROR;
    END;

  Cpu: TYPE = RECORD [cycles, cyclesPerSecond: LONG CARDINAL];
  
  NewEthernetStats: PUBLIC PROCEDURE [b: PupDefs.PupBuffer, network: Driver.Network] =
    BEGIN
    stats: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.stats;
    cpu: LONG POINTER TO Cpu = LOOPHOLE[@b.pup.pupWords[2]];
    esi: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo = LOOPHOLE[cpu + SIZE[Cpu]];
    b.pup.pupWords[0] ← EthernetFormat.ethernetStatsReply;
    b.pup.pupWords[1] ← EthernetFormat.etherVersion+1;
    cpu↑ ← [
      cycles: CpuIdle.GetCycles[],
      cyclesPerSecond: CpuIdle.GetCalibration[].cyclesPerSecond];
    esi↑ ← stats↑;
    PupDefs.SetPupContentsWords[b, 2 + SIZE[Cpu] + SIZE[EthernetDriverFriends.EtherStatsInfo]];
    END;

  NewPhoneStats: PUBLIC PROCEDURE [b: PupDefs.PupBuffer, network: Driver.Network] =
    BEGIN
    stats: PhoneNetFriends.PhoneNetInfo = PhoneNetFriends.StatsPtrToStats[network.stats];
    cpu: LONG POINTER TO Cpu = LOOPHOLE[@b.pup.pupWords[2]];
    psi: LONG POINTER TO PhoneNetFriends.PhoneNetInfo = LOOPHOLE[cpu + SIZE[Cpu]];
    b.pup.pupWords[0] ← SlaFormat.slaStatsReply;
    b.pup.pupWords[1] ← SlaFormat.slaVersion+1;
    cpu↑ ← [
      cycles: CpuIdle.GetCycles[],
      cyclesPerSecond: CpuIdle.GetCalibration[].cyclesPerSecond];
    psi↑ ← stats;
    PupDefs.SetPupContentsWords[b, 2 + SIZE[Cpu] + SIZE[PhoneNetFriends.PhoneNetInfo]];
    END;

  OldPupStats: PROCEDURE [b: PupDefs.PupBuffer, network: Driver.Network] =
    BEGIN
    SELECT network.device FROM
      ethernet, ethernetOne => OldEthernetStats[b, network];
      phonenet => OldPhoneStats[b, network];
      ENDCASE => ERROR;
    END;

  OldEthernetStats: PUBLIC PROCEDURE [b: PupDefs.PupBuffer, network: Driver.Network] =
    BEGIN
    ese: LONG POINTER TO EthernetFormat.EtherStatsEntry;
    esi: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.stats;
    b.pup.pupWords[0] ← EthernetFormat.ethernetStatsReply;
    ese ← LOOPHOLE[@b.pup.pupWords[1]];
    ese↑ ← [
      version: EthernetFormat.etherVersion,
      packetsSent: PupWireFormat.MesaToBcplLongNumber[esi.packetsSent],
      badSendStatus: PupWireFormat.MesaToBcplLongNumber[esi.badSendStatus],
      overruns: PupWireFormat.MesaToBcplLongNumber[esi.overrun + esi.underrun],
      packetsRecv: PupWireFormat.MesaToBcplLongNumber[esi.packetsRecv],
      badRecvStatus: PupWireFormat.MesaToBcplLongNumber[esi.badRecvStatus],
      inputOff: PupWireFormat.MesaToBcplLongNumber[esi.packetsMissed],
      loadTable:];
    FOR i: CARDINAL IN [0..16) DO
      ese.loadTable[i] ← PupWireFormat.MesaToBcplLongNumber[esi.loadTable[i]];
      ENDLOOP;
    ese.loadTable[16] ← PupWireFormat.MesaToBcplLongNumber[esi.tooManyCollisions];
    PupDefs.SetPupContentsWords[b, 1 + SIZE[EthernetFormat.EtherStatsEntry]];
    END;


  OldPhoneStats: PUBLIC PROCEDURE [b: PupDefs.PupBuffer, network: Driver.Network] =
    BEGIN
    state: SlaFormat.LineState;
    RoutingTableEntry: TYPE = RECORD [hops, line: Environment.Byte];
    activeLines: CARDINAL = 1;
    maxSlaHost: CARDINAL = 1;
    sizeOfRoutingTable: CARDINAL = maxSlaHost*SIZE[RoutingTableEntry];
    rte: LONG POINTER TO RoutingTableEntry;
    sse: LONG POINTER TO SlaFormat.SlaStatsEntry;
    stats: PhoneNetFriends.PhoneNetInfo = PhoneNetFriends.StatsPtrToStats[network.stats];
    SELECT TRUE FROM
      stats.remoteHostNumber = System.nullHostNumber => state ← down;
      stats.remoteHostNumber = System.localHostNumber => state ← loopedBack;
      ENDCASE => state ← up;
    b.pup.pupWords[0] ← SlaFormat.slaStatsReply;
    b.pup.pupWords[1] ← SlaFormat.slaVersion;
    b.pup.pupWords[2] ← maxSlaHost;
    rte ← LOOPHOLE[@b.pup.pupWords[3]];
    rte↑ ← [0, 0];
    b.pup.pupWords[3 + sizeOfRoutingTable] ← activeLines - 1;
    sse ← LOOPHOLE[@b.pup.pupWords[4 + sizeOfRoutingTable]];
    sse↑ ← [
      packetsSent: PupWireFormat.MesaToBcplLongNumber[stats.stats[pktsSent]],
      packetsRecv: PupWireFormat.MesaToBcplLongNumber[stats.stats[pktsReceived]],
      bytesSent: PupWireFormat.MesaToBcplLongNumber[stats.stats[bytesSent]],
      bytesRecv: PupWireFormat.MesaToBcplLongNumber[stats.stats[bytesReceived]],
      syncErrors: Inline.LowHalf[stats.stats[rcvErrorNoGet] + stats.stats[rcvErrorDataLost]],
      badCrc: Inline.LowHalf[stats.stats[rcvErrorCRC]],
      controlError: Inline.LowHalf[stats.stats[rcvDeviceError]],
      state: state];
    b.pup.pupLength ←
      22 + 2*(4 + sizeOfRoutingTable + activeLines*SIZE[SlaFormat.SlaStatsEntry]);
    END;

  GateStatsOn[];
  END.