-- Copyright (C) 1984, 1985  by Xerox Corporation. All rights reserved. 
-- PhoneAdoptionPup.mesa, HGM, 26-Oct-85 23:21:47
-- From PhoneAdoptionImpl.mesa - (last edited by LSK 28-Jun-85 14:56:18)

DIRECTORY
  Buffer USING [Buffer, Type],
  CommFlags USING [doDebug, driverStats],
  CommHeap USING [zone],
  CommUtil USING [GetEthernetHostNumber],
  Driver USING [Network],
  Environment USING [bytesPerWord],
  ForwarderDefs USING [DoErrorPup],
  PhoneAdoption USING [AdoptForNS],
  PhoneCreate USING [],
  PhoneNet USING [Initialize],
  PhoneNetInternal USING [
    DataPacketReceived, PacketType, PhoneNetEncap, State, Version1,
    phoneEncapsulationOffsetVer1, phoneEncapsulationBytesVer1,
    dataOffsetWords,
    dataEncapsulationBytes, FindLine,
    RegisterCongestionProc, CongestionProc],
  Protocol1 USING [
    AddFamilyMember, Family,
    MatrixRecord, DecapsulatorProc, EncapsulatorProc],
  PupDefs USING [PupPackageMake],
  PupTypes USING [],
  PupRouterDefs USING [ContextObject],
  RS232C USING [ChannelHandle, CommParamObject, Create],
  RS232CEnvironment USING [],
  System USING [NetworkNumber, nullNetworkNumber];

PhoneAdoptionPup: MONITOR 
  IMPORTS
    CommHeap, CommUtil, ForwarderDefs, PhoneAdoption, PhoneNet,
    PhoneNetInternal, Protocol1, PupDefs, RS232C
  EXPORTS Buffer, PhoneCreate =
  BEGIN
  
  Network: PUBLIC TYPE = Driver.Network;
    
  CreateSimplePhoneNet: PUBLIC PROC [lineNumber: CARDINAL, pupNet: CARDINAL, nsNet: System.NetworkNumber] = {
    parms: RS232C.CommParamObject ← [full, bitSynchronous, bps9600, directConn[]];
    chan: RS232C.ChannelHandle ← RS232C.Create[lineNumber, @parms, preemptAlways, preemptNever];
    PhoneNet.Initialize [
      lineNumber: lineNumber,
      channel: chan,
      commParams: @parms,
      negotiationMode: active,
      hardwareStatsAvailable: TRUE,
      clientData: 0,
      ourEntityClass: internetworkRouter];
    PhoneAdoption.AdoptForNS [lineNumber, FALSE, System.nullNetworkNumber];
    AdoptForPup[lineNumber, pupNet ];
    };
  
  AdoptForPup: PROC [lineNumber: CARDINAL, pupNetNumber: CARDINAL] =
    BEGIN
    pupHostNumber: CARDINAL ← CommUtil.GetEthernetHostNumber[];
    driver: Driver.Network ← @PhoneNetInternal.FindLine[lineNumber].state.phoneNetObject;
    family: Protocol1.Family ← PupDefs.PupPackageMake[];
    matrix: Protocol1.MatrixRecord ← [  --AddFamilyMember copies fields
      family: family, context: ,
      encapsulator: EncapsulatePupPhonenet,
      decapsulator: DecapsulatePupPhonenet];
    matrix.context ← CommHeap.zone.NEW[PupRouterDefs.ContextObject ← [
      protocol: NIL, network: driver,
      pupNetNumber: pupNetNumber, pupHostNumber: pupHostNumber]];
    Protocol1.AddFamilyMember[driver, @matrix];
    PhoneNetInternal.RegisterCongestionProc[pup, CongestionProc];
    matrix.family.stateChanged[driver, matrix.context, add]; -- Bug somewhere ##########
    END;
    
  --**************** Encapsulation and decapsulation **************
  
  DecapsulatePupPhonenet: Protocol1.DecapsulatorProc =
    --PROCEDURE [b: Buffer.Buffer] RETURNS [type: Buffer.Type]
    BEGIN
    network: Driver.Network = b.network;
    state: PhoneNetInternal.State = network.stats; 

    PhoneNetInternal.DataPacketReceived[state];
    IF state.protocolVersion = old THEN RETURN[TypeOfBufferVer1[state, b]];
    
    -- version is # old
    SELECT b.encapsulation.spare1 FROM
      PhoneNetInternal.dataOffsetWords =>
        BEGIN
	OPEN pe: LOOPHOLE[b.encapsulation, data PhoneNetInternal.PhoneNetEncap];
        RETURN[TypeOfBufferInternal[state, b, pe.data.pktType,
	  PhoneNetInternal.dataEncapsulationBytes]];
        END;
      ENDCASE => {IF CommFlags.doDebug THEN ERROR ELSE RETURN[vagrant]};
    END;

  EncapsulatePupPhonenet: Protocol1.EncapsulatorProc =
    BEGIN
    network: Driver.Network = b.network;
    state: PhoneNetInternal.State = network.stats; 
    encapsulationLengthBytes: CARDINAL;

    IF state.state # data THEN
      BEGIN
      IF CommFlags.driverStats THEN StatIncr[@state.statsRec[notInDataState]];
      b.status ← circuitNotReady;
      RETURN;
      END;
    b.status ← pending;

    SELECT TRUE FROM
      state.protocolVersion = old =>
        BEGIN
	OPEN pe: LOOPHOLE[b.encapsulation, ver1 PhoneNetInternal.PhoneNetEncap];
        pe.ver1 ← [
          offset: PhoneNetInternal.phoneEncapsulationOffsetVer1,
	  framing0: 0, framing1: 0, framing2: 0, framing3: 0,
	  recognition: 0,  --for auto-recog
          pnType: pupPhonePacket, pnSrcID: state.myHostNumber];
        encapsulationLengthBytes ← PhoneNetInternal.phoneEncapsulationBytesVer1;
        END;
      state.protocolVersion = version3 =>
        BEGIN
	OPEN pe: LOOPHOLE[b.encapsulation, data PhoneNetInternal.PhoneNetEncap];
        pe.data ← [
          offset: PhoneNetInternal.dataOffsetWords,
	  spare1: 0, spare2: 0, spare3: 0, spare4: 0,
          spare5: 0, pktType: pup, LTA: FALSE, reserved: 0];
        encapsulationLengthBytes ← PhoneNetInternal.dataEncapsulationBytes;
        END;
      ENDCASE => IF CommFlags.doDebug THEN ERROR ELSE NULL;

    b.driver.length ←
      (b.pup.pupLength + 1 + encapsulationLengthBytes) / Environment.bytesPerWord;
    --note that length is in words.  Since software checksum is on words,
    --it expects the driver to pad.
    END;  --EncapsulatePupPhonenet
    
    
  minBytesPerPup: CARDINAL = 22;
  TypeOfBufferInternal: PROCEDURE[
    state: PhoneNetInternal.State, b: Buffer.Buffer,
    pktType: PhoneNetInternal.PacketType,
    encapsulationWidthBytes: CARDINAL]
    RETURNS[type: Buffer.Type] =
    BEGIN
    bytes: CARDINAL ← (Environment.bytesPerWord * b.driver.length);
    IF bytes < encapsulationWidthBytes THEN GOTO Rejected;
    -- control packets can be very short (e.g. null control packets)
    bytes ← bytes - encapsulationWidthBytes;
    SELECT pktType FROM
      pup =>
        BEGIN
        IF (b.pup.pupLength > bytes) OR 
	  (b.pup.pupLength < minBytesPerPup) THEN GOTO Rejected;
        IF CommFlags.driverStats THEN StatIncr[@state.statsRec[pupReceived]];
        type ← pup;
        END;
      ENDCASE => type ← vagrant;
     EXITS Rejected =>
      BEGIN
      IF CommFlags.driverStats THEN
        StatIncr[@state.statsRec[pktsRejectedBadLength]];
      type ← orphan;  --this is a BAD packet
      END;
    END;  --TypeOfBufferInternal
    

  TypeOfBufferVer1: PROCEDURE [state: PhoneNetInternal.State, b: Buffer.Buffer]
    RETURNS[type: Buffer.Type] =
    --determine buffer type
    BEGIN
    bytes: CARDINAL ← (Environment.bytesPerWord * b.driver.length);
    IF bytes < PhoneNetInternal.phoneEncapsulationBytesVer1 THEN GOTO Rejected;
    bytes ← bytes - PhoneNetInternal.phoneEncapsulationBytesVer1;
    SELECT LOOPHOLE[b.encapsulation, PhoneNetInternal.Version1].pnType FROM
      pupPhonePacket =>
        BEGIN
        IF (b.pup.pupLength > bytes) OR 
	  (b.pup.pupLength < minBytesPerPup) THEN GOTO Rejected;
	IF CommFlags.driverStats THEN StatIncr[@state.statsRec[pupReceived]];
        type ← pup;
        END;
      ENDCASE => type ← vagrant;
    EXITS Rejected =>
      BEGIN
      IF CommFlags.driverStats THEN
        StatIncr[@state.statsRec[pktsRejectedBadLength]];
      type ← orphan;
      END;
    END;  --TypeOfBufferVer1

  -- ****** CONGESTION PROCESSING ****
  
  CongestionProc: PhoneNetInternal.CongestionProc =
   -- PROCEDURE [b: Buffer.Buffer, type: TypeOfCongestion];
   -- RETURNS [tryAgain: BOOLEAN ← FALSE];
   BEGIN
   SELECT type FROM
     warning => ForwarderDefs.DoErrorPup[b, gatewayResourceLimitsPupErrorCode, "Warning: Output queue getting full"];
     discarded => ForwarderDefs.DoErrorPup[b, gatewayResourceLimitsPupErrorCode, "Fatal: Output queue is full"];
     ENDCASE => ERROR;
   END;

  StatIncr: PROCEDURE [counter: LONG POINTER TO LONG CARDINAL] = INLINE
    {counter↑ ← (counter↑ + 1) MOD (LAST[LONG CARDINAL] - 1)};
    
    
  END..