-- Copyright (C) 1983  by Xerox Corporation. All rights reserved. 
-- SimpleNSIOPhoneImpl.mesa, HGM, 31-Oct-83 21:33:58
-- From Dale's SimpleNSIOEthernetImpl of 9-Aug-83 17:05:33

-- This module implements SimpleNSIO on the Ethernet (NOT the EthernetOne). It supplies level one and level zero encapsulation.

-- NOTE: The module SimpleNSIOEthernetOneImpl is a near-copy of this one. IF YOU MAKE A CHANGE HERE, it probably should be made to it too. SimplePUPIOEthernetOneImpl is another, somewhat less-similar module.

DIRECTORY
  BootChannel USING [Result],
  Checksum USING [ComputeChecksum],
  Device USING [Type],
  DriverTypes USING [DeviceType, Encapsulation],
  Environment USING [Byte, bytesPerWord, first64K],
  PhoneFace USING [
    controlBlockSize, GetPacketLength, GetRecvStatus, GetSendStatus, Line,
    maxLines, phoneLine, QueueInput, QueueOutput, TurnOff, TurnOn],
  GermOps USING [GermWorldError],
  Inline USING [LowHalf],
  NSTypes USING [BufferBody, bytesPerIDPHeader, maxIDPWordsPerPacket, PacketType],
  PilotMP USING [cGermERROR],
  ProcessorFace USING [processorID],
  ResidentHeap USING [FreeNode, MakeNode],
  SimpleNSIO USING [
    ByteCount, cantHandleDevice, Cleanups, timedOutBytes, timedOutResult],
  SpecialSystem USING [
    HostNumber, NetworkNumber, nullNetworkNumber, ProcessorID, SocketNumber],
  System USING [
    GetClockPulses, Microseconds, MicrosecondsToPulses, NetworkAddress,
    nullHostNumber, nullNetworkNumber, nullSocketNumber, Pulses, SocketNumber];

SimpleNSIOPhoneImpl: PROGRAM
  IMPORTS
    Checksum, PhoneFace, GermOps, Inline, ProcessorFace, ResidentHeap, System
  EXPORTS SimpleNSIO, System
  SHARES GermOps =
  BEGIN
  
  -- PARAMETERS:
  
  sendTimeOut: System.Microseconds = 3000000;

  -- TYPES AND VARIABLES:

  HostNumber: PUBLIC --System-- TYPE = SpecialSystem.HostNumber;
  NetworkNumber: PUBLIC --System-- TYPE = SpecialSystem.NetworkNumber;
  SocketNumber: PUBLIC --System-- TYPE = SpecialSystem.SocketNumber;

  NetworkAddress: TYPE = System.NetworkAddress;

  ByteCount: TYPE = SimpleNSIO.ByteCount;
  
  Byte: TYPE = Environment.Byte;

  PhonePacketType: TYPE = MACHINE DEPENDENT{
    pupPhonePacket(100B),
    nsPhonePacket(300B),
    turnAroundPhonePacket(301B),
    turnAroundMTSPhonePacket(302B),
    terminatePhonePacket(303B),
    terminateAckPhonePacket(304B),
    (LAST[Byte])};

  Encapsulation: TYPE = MACHINE DEPENDENT RECORD [
    SELECT OVERLAID DriverTypes.DeviceType FROM
      phonenet => [
	framing0, framing1, framing2, framing3, framing4, framing5: Byte,
	recognition: Byte, -- 0 for auto recognition of NS vs SDLC/HDLC
	pnType: PhonePacketType,
	pnSrcID: SpecialSystem.ProcessorID],
      ENDCASE];

  phoneEncapsulationOffset: CARDINAL = 3;
  phoneEncapsulationBytes: CARDINAL = 8;

  encapsulationTrap: BOOLEAN [TRUE..TRUE] =
    (SIZE[Encapsulation] = SIZE[DriverTypes.Encapsulation]);
  
  -- Level zero Ethernet packet format:
  LevelZeroPacket: TYPE = MACHINE DEPENDENT RECORD [
    encapsulation(0): Encapsulation,
    levelOnePacket(7): NSTypes.BufferBody];

  slop: CARDINAL = 12;  -- Hal says we need this. We should ask why.
  bufferSize: CARDINAL =
    SIZE[DriverTypes.Encapsulation] +
    NSTypes.maxIDPWordsPerPacket +
    slop;
  buffer: LONG POINTER TO LevelZeroPacket;

  line: PhoneFace.Line;
  longIocb: LONG POINTER ← NIL;

  returnData: RECORD [returnTo, returnedFrom: NetworkAddress];


  --==============================
  -- Public Procedures
  --==============================

  GetRawBufferSize: PUBLIC PROCEDURE RETURNS [rawBufferSize: CARDINAL] = {
    RETURN[bufferSize]};

  Initialize: PUBLIC PROC [
    deviceType: Device.Type, deviceOrdinal: CARDINAL, rawBuffer: LONG POINTER,
    cleanups: SimpleNSIO.Cleanups]
    RETURNS [
      result: BootChannel.Result, bufferBody: LONG POINTER TO NSTypes.BufferBody] =
    BEGIN
    IF deviceType # PhoneFace.phoneLine THEN RETURN[SimpleNSIO.cantHandleDevice, NIL];
    IF line >= PhoneFace.maxLines THEN GermOps.GermWorldError[123];
    IF line # 0 THEN GermOps.GermWorldError[124];
    line ← deviceOrdinal;
    longIocb ← @Environment.first64K[
      ResidentHeap.MakeNode[PhoneFace.controlBlockSize, a4].node];
    buffer ← rawBuffer;
    bufferBody ← @buffer.levelOnePacket;
    TurnOn[];
    RETURN[[ok[]], @buffer.levelOnePacket];
    END;  --Initialize--

  Finalize: PUBLIC PROC [cleanups: SimpleNSIO.Cleanups] =
    BEGIN
    PhoneFace.TurnOff[];
    [] ← ResidentHeap.FreeNode[Inline.LowHalf[longIocb]];
    longIocb ← NIL;
    END;

  ReceivePacket: PUBLIC PROC [
    getFrom: NetworkAddress, mySocket: System.SocketNumber,
    timeout: System.Microseconds]
    RETURNS [
      result: BootChannel.Result, dataBytes: ByteCount, type: NSTypes.PacketType,
      source, destination: LONG POINTER TO NetworkAddress] =
    BEGIN
    startTime: System.Pulses = System.GetClockPulses[];
    DO  --UNTIL packet arrives on requested socket--
      PhoneFace.QueueInput[
        line,
	longIocb,
	@buffer.encapsulation + phoneEncapsulationOffset,
	bufferSize*Environment.bytesPerWord];
      UNTIL PhoneFace.GetRecvStatus[longIocb] # pending DO
        IF TimeExpired[timeout: timeout, startTime: startTime] THEN
          BEGIN
          PhoneFace.TurnOff[];  -- abort the input.
          TurnOn[];
          RETURN[
	    dataBytes: SimpleNSIO.timedOutBytes,
	    type: TRASH, source: TRASH, destination: TRASH,
	    result: SimpleNSIO.timedOutResult];
          END;
        ENDLOOP;
      IF PhoneFace.GetRecvStatus[longIocb] ~= ok THEN LOOP;
      IF buffer.encapsulation.pnType#nsPhonePacket THEN LOOP;
      IF ~TestChecksum[@buffer.levelOnePacket] THEN LOOP;
      IF buffer.levelOnePacket.destination.host ~= ProcessorFace.processorID THEN LOOP;
      IF getFrom.net ~= System.nullNetworkNumber
        AND getFrom.net ~= buffer.levelOnePacket.source.net THEN LOOP;
      IF getFrom.host ~= System.nullHostNumber
        AND getFrom.host ~= buffer.levelOnePacket.source.host THEN LOOP;
      IF getFrom.socket ~= System.nullSocketNumber
        AND getFrom.socket ~= buffer.levelOnePacket.source.socket THEN LOOP;
      IF mySocket ~= System.nullSocketNumber
        AND mySocket ~= buffer.levelOnePacket.destination.socket THEN LOOP;
      IF PhoneFace.GetPacketLength[longIocb] <
        BytesPerPhonePacket[
          buffer.levelOnePacket.pktLength] THEN LOOP;
      IF buffer.levelOnePacket.pktLength >= NSTypes.bytesPerIDPHeader THEN EXIT;
      ENDLOOP;
    returnData.returnTo ← (source ← @buffer.levelOnePacket.source)↑;
    returnData.returnedFrom ← (destination ← @buffer.levelOnePacket.destination)↑;
    RETURN[
      dataBytes: buffer.levelOnePacket.pktLength - NSTypes.bytesPerIDPHeader,
      type: buffer.levelOnePacket.packetType,
      source: @buffer.levelOnePacket.source,
      destination: @buffer.levelOnePacket.destination,
      result: [ok[]]];
    END;  --ReceivePacket--

  ReturnPacket: PUBLIC PROC [dataBytes: ByteCount, type: NSTypes.PacketType]
    RETURNS [result: BootChannel.Result] =
    BEGIN
    buffer.levelOnePacket.source ← returnData.returnedFrom;
    buffer.levelOnePacket.destination ← returnData.returnTo;
    RETURN SendBuffer[dataBytes, type];
    END;

  SendPacket: PUBLIC PROC [
    dataBytes: ByteCount, type: NSTypes.PacketType, sourceSocket: SocketNumber,
    dest: NetworkAddress]
    RETURNS [result: BootChannel.Result] =
    BEGIN
    buffer.encapsulation ← [
      phonenet[
	framing0: 0, framing1: 0, framing2: 0, framing3: 0, framing4: 0,
	framing5: 0, recognition: 0,
	pnType: nsPhonePacket, pnSrcID: LOOPHOLE[ProcessorFace.processorID]] ];
    buffer.levelOnePacket.source ← [
      net: SpecialSystem.nullNetworkNumber, host: ProcessorFace.processorID,
      socket: sourceSocket];
    buffer.levelOnePacket.destination ← dest;
    [] ← SendBuffer[dataBytes, type];  -- %%%%%% STARTUP KROCK
    RETURN SendBuffer[dataBytes, type];
    END;

  --==============================
  -- Private Procedures
  --==============================

  Error: PROC = {GermOps.GermWorldError[PilotMP.cGermERROR]};

  SendBuffer: PROC [dataBytes: ByteCount, type: NSTypes.PacketType]
    RETURNS [result: BootChannel.Result] =
    -- Sends buffer; doesn't return till it is sent (but times out if can't send).
    BEGIN
    startTime: System.Pulses;
    buffer.encapsulation ← [
      phonenet[
	framing0: 0, framing1: 0, framing2: 0, framing3: 0, framing4: 0,
	framing5: 0, recognition: 0,
	pnType: nsPhonePacket, pnSrcID: LOOPHOLE[ProcessorFace.processorID]] ];
    buffer.levelOnePacket.pktLength ← dataBytes + NSTypes.bytesPerIDPHeader;
    buffer.levelOnePacket.transportControl ← [
      trace: FALSE, filler: 0, hopCount: 0];
    buffer.levelOnePacket.packetType ← type;
    SetChecksum[@buffer.levelOnePacket];
    PhoneFace.QueueOutput[
      line, longIocb,
      @buffer.encapsulation + phoneEncapsulationOffset,
      BytesPerPhonePacket[buffer.levelOnePacket.pktLength]];
    startTime ← System.GetClockPulses[];
    DO  --until sent or timed out
      IF PhoneFace.GetSendStatus[longIocb] # pending THEN EXIT;
      IF TimeExpired[timeout: sendTimeOut, startTime: startTime] THEN {
        PhoneFace.TurnOff[]; TurnOn[]; EXIT};
      ENDLOOP;
    RETURN[[ok[]]];
    END;  --SendBuffer--

  SetChecksum: PROC [b: LONG POINTER TO NSTypes.BufferBody] = {
    b.checksum ← Checksum.ComputeChecksum[
      0, (b.pktLength + 1 - 2)/2, @b.pktLength]};

  TestChecksum: PROC [b: LONG POINTER TO NSTypes.BufferBody] RETURNS [BOOLEAN] = {
    RETURN[
      b.checksum = Checksum.ComputeChecksum[
        0, (b.pktLength + 1 - 2)/2, @b.pktLength] OR b.checksum = 177777B]};

  TimeExpired: PROC [timeout, startTime: System.Microseconds] RETURNS [BOOLEAN] =
    BEGIN
    IF timeout = LAST[System.Microseconds] THEN RETURN[FALSE]
    ELSE
      RETURN[
        (System.GetClockPulses[] - startTime) > System.MicrosecondsToPulses[
          timeout]];
    END;

  TurnOn: PROC = {PhoneFace.TurnOn[0]};

  BytesPerPhonePacket: PROC [bodyBytes: CARDINAL] RETURNS [bytes: CARDINAL] =
    BEGIN
    words: CARDINAL ←
      SIZE[DriverTypes.Encapsulation] - phoneEncapsulationOffset +
      (bodyBytes + Environment.bytesPerWord-1)/Environment.bytesPerWord;
    bytes ← words * Environment.bytesPerWord;
    END;


  END.