-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- GateStats.mesa, HGM,  7-Jan-85 22:36:30

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

GateStats: PROGRAM
  IMPORTS Inline, Process, System, Buffer, PupWireFormat, PupDefs, Driver
  EXPORTS GateControlDefs =
  BEGIN OPEN PupDefs;

  soc: PupSocket;

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

  Stats: PROCEDURE =
    BEGIN
    b: PupBuffer;
    firstNetwork: Driver.Network = Driver.GetDeviceChain[];
    SetupStatsEntries[];
    soc ← PupSocketMake[
      PupTypes.statSoc, PupTypes.fillInPupAddress, veryLongWait];
    DO  -- forever
      IF (b ← soc.get[]) # NIL THEN
        BEGIN
        SELECT b.pup.pupType FROM
          GateControlDefs.pupStatsSend =>
            BEGIN
            target: CARDINAL = b.pup.pupWords[0];
            network: Driver.Network;
            IF GetPupContentsBytes[b] # 2 THEN GOTO Ignore;
            FOR network ← firstNetwork, network.next UNTIL network = NIL DO
              IF network.pupNetNumber # target THEN LOOP;
              IF network.pupStats = NIL OR network.statsLevel0 = NIL THEN GOTO Reject;
              IF network.pupStats[b, network] THEN GOTO Send;
              GOTO Reject;
              ENDLOOP;
            GOTO Ignore;
            END;
          ENDCASE => GOTO Ignore;
        EXITS
          Send =>
            BEGIN  -- pupLength setup already
            b.pup.pupType ← GateControlDefs.pupStatsAck;
            SwapPupSourceAndDest[b];
            PupRouterSendThis[b];
            END;
          Reject => BEGIN ReturnPup[b, GateControlDefs.pupStatsNak, 0]; END;
          Ignore => BEGIN Buffer.ReturnBuffer[b]; END;
        END;
      ENDLOOP;
    END;

  SetupStatsEntries: PUBLIC PROCEDURE =
    BEGIN
    network: Driver.Network;
    FOR network ← Driver.GetDeviceChain[], network.next UNTIL network = NIL DO
      SELECT network.device FROM
        ethernet, ethernetOne => network.pupStats ← EthernetStats;
	phonenet => network.pupStats ← PhoneStats;
        ENDCASE;
      ENDLOOP;
    END;

  EthernetStats: PUBLIC PROCEDURE [b: PupBuffer, network: Driver.Network]
    RETURNS [BOOLEAN] =
    BEGIN
    ese: LONG POINTER TO EthernetFormat.EtherStatsEntry;
    esi: LONG POINTER TO EthernetDriverFriends.EtherStatsInfo ← network.statsLevel0;
    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];
    SetPupContentsWords[b, 1 + SIZE[EthernetFormat.EtherStatsEntry]];
    RETURN[TRUE];
    END;


  PhoneStats: PUBLIC PROCEDURE [b: PupBuffer, network: Driver.Network]
    RETURNS [BOOLEAN] =
    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;
    CrapForINRLoophole: TYPE = MONITORED RECORD [
      clientData: LONG UNSPECIFIED,
      lineNumber: CARDINAL];
    foo: LONG POINTER TO CrapForINRLoophole = network.statsLevel0;
    stats: LONG POINTER TO PhoneNetFriends.PhoneNetInfo = foo.clientData;
    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]);
    RETURN[TRUE];
    END;

  GateStatsOn[];
  END.