-- File: GateStats.mesa - last edit:
-- AOF                  4-Feb-88 12:39:20
-- HGM                 23-Oct-85 23:19:09
-- Copyright (C) 1984, 1985, 1988 by Xerox Corporation. All rights reserved. 

DIRECTORY
  Buffer USING [],
  Environment USING [Byte],
  Inline USING [LowHalf],
  Process USING [Detach],
  CpuIdle USING [GetCalibration, GetCycles],
  Driver USING [Device, GetDeviceChain],
  EthernetFormat USING [EtherStatsEntry, ethernetStatsReply, etherVersion],
  EthernetDriverFriends USING [EtherStatsInfo],
  GateControlDefs USING [pupStatsAck, pupStatsNak, pupStatsSend],
  Protocol1 USING [GetContext],
  PupDefs USING [
    PupBuffer, GetPupContentsBytes, PupRouterSendThis,
    SwapPupSourceAndDest, ReturnPup, PupSocket, PupSocketMake,
    SetPupContentsWords, veryLongWait, ReturnBuffer, Body],
  PupRouterDefs USING [NetworkContext],
  PupTypes USING [statSoc, fillInPupAddress],
  PupWireFormat USING [MesaToBcplLongNumber],
  SlaFormat USING [LineState, SlaStatsEntry, slaStatsReply, slaVersion],
  SptpOps USING [GetProtocolInfo, StatsRecord],
  SptpProtocol USING [ProtocolObject];

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

  Device: PUBLIC <<Buffer>> TYPE = Driver.Device;

  soc: PupDefs.PupSocket;

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

  Stats: PROCEDURE =
    BEGIN
    b: PupDefs.PupBuffer;
    firstNetwork: Device = Driver.GetDeviceChain[];
    soc ← PupDefs.PupSocketMake[
      PupTypes.statSoc, PupTypes.fillInPupAddress, PupDefs.veryLongWait];
    DO  -- forever
      IF (b ← soc.get[]) # NIL THEN
        BEGIN
	body: PupDefs.Body ← b.pup;
        SELECT body.pupType FROM
          GateControlDefs.pupStatsSend =>
            BEGIN
            target: CARDINAL = body.pupWords[0];
	    bytes: CARDINAL = PupDefs.GetPupContentsBytes[b];
            IF bytes < 2 THEN GOTO Ignore;
            FOR network: Device ← 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;
              body.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 PupDefs.ReturnBuffer[b]; END;
          Sent => NULL;
        END;
      ENDLOOP;
    END;

  NewPupStats: PROCEDURE [b: PupDefs.PupBuffer, network: Device] =
    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: Device] =
    BEGIN
    body: PupDefs.Body ← b.pup;
    stats: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.stats;
    cpu: LONG POINTER TO Cpu = LOOPHOLE[@body.pupWords[2]];
    esi: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo = LOOPHOLE[cpu + SIZE[Cpu]];
    body.pupWords[0] ← EthernetFormat.ethernetStatsReply;
    body.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: Device] =
    BEGIN
    body: PupDefs.Body ← b.pup;
    stats: LONG POINTER TO SptpOps.StatsRecord = network.stats;
    cpu: LONG POINTER TO Cpu = LOOPHOLE[@body.pupWords[2]];
    psi: LONG POINTER TO SptpOps.StatsRecord = LOOPHOLE[cpu + SIZE[Cpu]];
    body.pupWords[0] ← SlaFormat.slaStatsReply;
    body.pupWords[1] ← SlaFormat.slaVersion+1;
    cpu↑ ← [
      cycles: CpuIdle.GetCycles[],
      cyclesPerSecond: CpuIdle.GetCalibration[].cyclesPerSecond];
    psi↑ ← stats↑;
    PupDefs.SetPupContentsWords[
      b, 2 + SIZE[Cpu] + SIZE[SptpOps.StatsRecord]];
    END;

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

  OldEthernetStats: PUBLIC PROCEDURE [b: PupDefs.PupBuffer, network: Device] =
    BEGIN
    body: PupDefs.Body ← b.pup;
    ese: LONG POINTER TO EthernetFormat.EtherStatsEntry;
    esi: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.stats;
    body.pupWords[0] ← EthernetFormat.ethernetStatsReply;
    ese ← LOOPHOLE[@body.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: Device] =
    BEGIN
    maxSlaHost: CARDINAL = 1;
    activeLines: CARDINAL = 1;
    state: SlaFormat.LineState;
    body: PupDefs.Body ← b.pup;
    info: SptpProtocol.ProtocolObject;
    rte: LONG POINTER TO RoutingTableEntry;
    sse: LONG POINTER TO SlaFormat.SlaStatsEntry;
    RoutingTableEntry: TYPE = RECORD [hops, line: Environment.Byte];
    sizeOfRoutingTable: CARDINAL = maxSlaHost*SIZE[RoutingTableEntry];
    stats: LONG POINTER TO SptpOps.StatsRecord = network.stats;
    SptpOps.GetProtocolInfo[network, @info];
    state ← SELECT TRUE FROM
      info.state # data => state ← down,
      info.him = info.me => state ← loopedBack,
      ENDCASE => state ← up;
    body.pupWords[0] ← SlaFormat.slaStatsReply;
    body.pupWords[1] ← SlaFormat.slaVersion;
    body.pupWords[2] ← maxSlaHost;
    rte ← LOOPHOLE[@body.pupWords[3]];
    rte↑ ← [0, 0];
    body.pupWords[3 + sizeOfRoutingTable] ← activeLines - 1;
    sse ← LOOPHOLE[@body.pupWords[4 + sizeOfRoutingTable]];
    sse↑ ← [
      packetsSent: PupWireFormat.MesaToBcplLongNumber[stats.packetsSent],
      packetsRecv: PupWireFormat.MesaToBcplLongNumber[stats.pktsReceived],
      bytesSent: PupWireFormat.MesaToBcplLongNumber[stats.bytesSent],
      bytesRecv: PupWireFormat.MesaToBcplLongNumber[stats.bytesReceived],
      syncErrors: Inline.LowHalf[stats.dataLost],
      badCrc: Inline.LowHalf[stats.checksumError],
      controlError: Inline.LowHalf[stats.deviceError],
      state: state];
    body.pupLength ←
      22 + 2*(4 + sizeOfRoutingTable + activeLines*SIZE[SlaFormat.SlaStatsEntry]);
    END;

  GateStatsOn[];
  END.