-- File: SimplePUPIOEthernetOneImpl.mesa - last edit:
-- AOF                  9-Feb-88 10:17:34
-- WDK                 25-May-83 11:07:42
-- HGM                 12-Dec-83  0:59:23, fixup timeouts
-- Copyright (C) 1983, 1988 by Xerox Corporation. All rights reserved. 

-- This module implements SimplePUPIO using the EthernetOne (NOT the Ethernet).

-- NOTE: The modules SimpleNSIOEthernetImpl and SimpleNSIOEthernetOneImpl are similar to this module. IF YOU MAKE A CHANGE HERE, it perhaps should be made to them too.

DIRECTORY
  BootChannel USING [Result],
  Device USING [Type],
  DeviceTypes USING [ethernetOne],
  EthernetOneDriverTypes USING [Encapsulation],
  Environment USING [Byte, first64K],
  EthernetOneFace USING [
    AddCleanup, ControlBlock, controlBlockSize, DeviceHandle, GetEthernet1Address,
    GetNextDevice, GetStatus, GlobalStatePtr, globalStateSize, nullDeviceHandle,
    QueueInput, QueueOutput, RemoveCleanup, TurnOff, TurnOn],
  GermOps USING [GermWorldError],
  Inline USING [LongCOPY, LowHalf],
  PilotMP USING [cGermERROR],
  PupTypes USING [
    BufferBody, maxDataWordsPerGatewayPup, Pair, PupAddress, PupHostID, PupNetID,
    PupSocketID, PupType],
  ResidentHeap USING [FreeNode, MakeNode],
  System USING [GetClockPulses, Microseconds, MicrosecondsToPulses, Pulses],
  
  SimplePUPIO USING [
    ByteCount, cantHandleDevice, Cleanups, timedOutBytes, timedOutResult];

SimplePUPIOEthernetOneImpl: PROGRAM
  IMPORTS EthernetOneFace, GermOps, Inline, ResidentHeap, System
  EXPORTS SimplePUPIO SHARES GermOps =
  BEGIN

  bufferAlign: CARDINAL = 0;  -- align encapsulation.ethernetOneDest to this boundary..
  bufferMod: CARDINAL = 4;  -- .. modulo this.

  slop: CARDINAL = 2 + 12;  -- Hal says we need this
  ethernetOneHeaderWds: CARDINAL =  -- significant part of encapsulation.
    SIZE[ethernetOne EthernetOneDriverTypes.Encapsulation];
  bufferSize: CARDINAL =
    ethernetOneHeaderWds + PupTypes.maxDataWordsPerGatewayPup + slop;

  LevelZeroPacket: TYPE = MACHINE DEPENDENT RECORD [
    encapsulation(0): ethernetOne EthernetOneDriverTypes.Encapsulation,
    pup(2): PupTypes.BufferBody];

  b: LONG POINTER TO LevelZeroPacket;

  last: {in, out, reset};

  myHost: PupTypes.PupHostID;
  myNet: PupTypes.PupNetID;

  device: EthernetOneFace.DeviceHandle ← EthernetOneFace.nullDeviceHandle;
  longIocb: EthernetOneFace.ControlBlock;
  globalStorage: EthernetOneFace.GlobalStatePtr = LOOPHOLE[0];

  wordsPerPupHeader: CARDINAL = 11;
  bytesPerPupHeader: CARDINAL = wordsPerPupHeader*2;

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

  Finalize: PUBLIC PROCEDURE [cleanups: SimplePUPIO.Cleanups] =
    BEGIN
    IF device = EthernetOneFace.nullDeviceHandle THEN Error[];  -- not initialized
    EthernetOneFace.TurnOff[device];
    IF cleanups = doCleanup THEN EthernetOneFace.RemoveCleanup[device];
    device ← EthernetOneFace.nullDeviceHandle;
    [] ← ResidentHeap.FreeNode[Inline.LowHalf[longIocb]];
    END;

  GetEthernetHostNumber: PUBLIC PROCEDURE RETURNS [CARDINAL] = {
    IF device = EthernetOneFace.nullDeviceHandle THEN Error[];  -- not initialized
    RETURN[myHost]};

  GetEthernetNetNumber: PUBLIC PROCEDURE RETURNS [CARDINAL] = {
    IF device = EthernetOneFace.nullDeviceHandle THEN Error[];  -- not initialized
    RETURN[myNet]};

  GetRawBufferSize: PUBLIC PROCEDURE RETURNS [rawBufferSize: CARDINAL] = {
    RETURN[bufferSize + bufferMod - 1]};

  Initialize: PUBLIC PROCEDURE [
    deviceType: Device.Type, deviceOrdinal: CARDINAL, rawBuffer: LONG POINTER,
    cleanups: SimplePUPIO.Cleanups] RETURNS [result: BootChannel.Result] =
    BEGIN
    netNbr, hostNbr: Environment.Byte;
    IF device # EthernetOneFace.nullDeviceHandle THEN Error[];  -- already initialized
    IF deviceType # DeviceTypes.ethernetOne THEN
      RETURN[SimplePUPIO.cantHandleDevice];
    THROUGH [0..deviceOrdinal] DO
      IF (device ← EthernetOneFace.GetNextDevice[device]) =
        EthernetOneFace.nullDeviceHandle THEN
        RETURN[SimplePUPIO.cantHandleDevice];
      ENDLOOP;
    longIocb ← @Environment.first64K[
      ResidentHeap.MakeNode[EthernetOneFace.controlBlockSize, a4].node];
    b ← LOOPHOLE[((LOOPHOLE[rawBuffer, LONG CARDINAL] + bufferMod - 1) /
       bufferMod) * bufferMod + bufferAlign];
    [net: netNbr, host: hostNbr] ← EthernetOneFace.GetEthernet1Address[device];
    myNet ← [netNbr];
    myHost ← [hostNbr];
    IF cleanups = doCleanup THEN EthernetOneFace.AddCleanup[device];
    -- allocate global storage here if any required.
    TurnOn[];
    last ← reset;
    RETURN[[ok[]]];
    END;

  ReceivePacket: PUBLIC PROCEDURE [
    source: LONG POINTER TO PupTypes.PupAddress, me: PupTypes.PupSocketID,
    data: LONG POINTER, maxWords: CARDINAL, timeout: System.Microseconds]
    RETURNS [
      result: BootChannel.Result, bytes: CARDINAL, id: PupTypes.Pair,
      type: PupTypes.PupType] =
    BEGIN
    startTime: System.Pulses = System.GetClockPulses[];
    IF device = EthernetOneFace.nullDeviceHandle THEN Error[];  -- not initialized
    DO
      EthernetOneFace.QueueInput[device, b, bufferSize, longIocb];
      UNTIL EthernetOneFace.GetStatus[longIocb] # pending DO
        IF TimeExpired[timeout: timeout, startTime: startTime] THEN
          BEGIN
          EthernetOneFace.TurnOff[device];
          TurnOn[];
          last ← reset;
          RETURN[
            SimplePUPIO.timedOutResult, SimplePUPIO.timedOutBytes, TRASH, TRASH];
          END;
        ENDLOOP;
      IF EthernetOneFace.GetStatus[longIocb] # ok THEN LOOP;
      IF b.encapsulation.ethernetOneType # pup THEN LOOP;
      -- check here for routing table if we ever get that complicated
      IF myNet # 0 AND myNet # b.pup.dest.net AND b.pup.dest.net # 0 THEN LOOP;
      IF myHost # b.pup.dest.host THEN LOOP;  -- Can't recv broadcast
      IF me # b.pup.dest.socket THEN LOOP;
      IF (source.net # 0)
        AND (source.net # b.pup.source.net)
        AND (b.pup.source.net # 0) THEN LOOP;
      IF source.host # 0 AND source.host # b.pup.source.host THEN LOOP;
      IF source.socket # [0, 0] AND source.socket # b.pup.source.socket THEN LOOP;
      IF ~TestPupChecksum[@b.pup] THEN LOOP;
      IF myNet = 0 THEN myNet ← b.pup.dest.net;
      IF (b.pup.pupLength - bytesPerPupHeader) > maxWords*2 THEN  --
        LOOP;  -- ignore packets larger than client wants
      bytes ← b.pup.pupLength - bytesPerPupHeader;
      Inline.LongCOPY[to: data, from: @b.pup.pupWords, nwords: (bytes + 1)/2];
      IF source.net = 0 THEN source.net ← b.pup.source.net;
      IF source.host = 0 THEN source.host ← b.pup.source.host;
      IF source.socket = [0, 0] THEN source.socket ← b.pup.source.socket;
      last ← in;
      RETURN[[ok[]], bytes, b.pup.pupID, b.pup.pupType];
      ENDLOOP;
    END;

  ReturnPacket: PUBLIC PROCEDURE [
    type: PupTypes.PupType, data: LONG POINTER, bytes: CARDINAL]
    RETURNS [result: BootChannel.Result] =
    BEGIN
    words: CARDINAL ← 2 + wordsPerPupHeader + (bytes + 1)/2;
    temp: PupTypes.PupAddress;
    IF device = EthernetOneFace.nullDeviceHandle THEN Error[];  -- not initialized
    IF words > bufferSize THEN Error[];  -- buffer overflow.
    IF last # in THEN Error[];  -- returned to strange place.
    -- Don't use constructor since that clobbers preceding 4 words
    b.encapsulation.ethernetOneDest ← b.encapsulation.ethernetOneSource;
    b.encapsulation.ethernetOneSource ← myHost;
    b.encapsulation.ethernetOneType ← pup;
    b.pup.pupLength ← bytesPerPupHeader + bytes;
    b.pup.pupTransportControl ← 0;
    b.pup.pupType ← type;
    temp ← b.pup.dest;
    b.pup.dest ← b.pup.source;
    b.pup.source ← temp;
    Inline.LongCOPY[to: @b.pup.pupWords, from: data, nwords: (bytes + 1)/2];
    SetPupChecksum[@b.pup];
    last ← out;
    EthernetOneFace.QueueOutput[device, b, words, longIocb];
    THROUGH [0..LAST[CARDINAL]) DO
      IF EthernetOneFace.GetStatus[longIocb] # pending THEN EXIT;
      REPEAT FINISHED => BEGIN EthernetOneFace.TurnOff[device]; TurnOn[]; END;
      ENDLOOP;
    RETURN[[ok[]]];
    END;

  SendPacket: PUBLIC PROCEDURE [
    dest: PupTypes.PupAddress, me: PupTypes.PupSocketID, type: PupTypes.PupType,
    id: PupTypes.Pair, data: LONG POINTER, bytes: CARDINAL]
    RETURNS [result: BootChannel.Result] =
    BEGIN
    words: CARDINAL ← 2 + wordsPerPupHeader + (bytes + 1)/2;
    IF device = EthernetOneFace.nullDeviceHandle THEN Error[];  -- not initialized.
    IF words > bufferSize THEN Error[];  -- buffer overflow
    -- Don't use constructor since that clobbers preceding 5 words
    -- (which are not really allocated).
    b.encapsulation.ethernetOneDest ← dest.host;
    b.encapsulation.ethernetOneSource ← myHost;
    b.encapsulation.ethernetOneType ← pup;
    b.pup.pupLength ← bytesPerPupHeader + bytes;
    b.pup.pupTransportControl ← 0;
    b.pup.pupType ← type;
    b.pup.pupID ← id;
    b.pup.dest ← dest;
    b.pup.source ← [myNet, myHost, me];
    Inline.LongCOPY[to: @b.pup.pupWords, from: data, nwords: (bytes + 1)/2];
    SetPupChecksum[@b.pup];
    last ← out;
    EthernetOneFace.QueueOutput[device, b, words, longIocb];
    THROUGH [0..LAST[CARDINAL]) DO
      IF EthernetOneFace.GetStatus[longIocb] # pending THEN EXIT;
      REPEAT FINISHED => {EthernetOneFace.TurnOff[device]; TurnOn[]};
      ENDLOOP;
    RETURN[[ok[]]];
    END;

  --==============================
  -- Private Procedures
  --==============================

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

  SetPupChecksum: PROCEDURE [b: LONG POINTER TO PupTypes.BufferBody] =
    BEGIN
    size: CARDINAL ← (b.pupLength - 1)/2;
    checksumLoc: LONG POINTER ← @b.pupLength + size;
    cs, t: CARDINAL;
    p: LONG POINTER TO ARRAY [0..0) OF WORD = LOOPHOLE[@b.pupLength];
    i: CARDINAL;
    cs ← 0;
    FOR i IN [0..size) DO
      t ← cs + p[i];
      cs ← (IF cs > t THEN t + 1 ELSE t);
      cs ← (IF cs >= 100000B THEN cs*2 + 1 ELSE cs*2);
      ENDLOOP;
    IF cs = 177777B THEN cs ← 0;
    checksumLoc↑ ← cs;
    END;

  TestPupChecksum: PROCEDURE [b: LONG POINTER TO PupTypes.BufferBody]
    RETURNS [BOOLEAN] =
    BEGIN
    size: CARDINAL ← ((LOOPHOLE[b.pupLength - 1, CARDINAL])/2);
    checksumLoc: LONG POINTER ← @b.pupLength + size;
    cs, t: CARDINAL;
    p: LONG POINTER TO ARRAY [0..0) OF WORD = LOOPHOLE[@b.pupLength];
    i: CARDINAL;
    IF checksumLoc↑ = 177777B THEN RETURN[TRUE];
    cs ← 0;
    FOR i IN [0..size) DO
      t ← cs + p[i];
      cs ← (IF cs > t THEN t + 1 ELSE t);
      cs ← (IF cs >= 100000B THEN cs*2 + 1 ELSE cs*2);
      ENDLOOP;
    IF cs = 177777B THEN cs ← 0;
    RETURN[checksumLoc↑ = cs];
    END;
    
  TimeExpired: PROC [timeout, startTime: System.Microseconds] RETURNS [BOOLEAN] =
    BEGIN
    IF timeout = LAST[System.Microseconds] THEN RETURN[FALSE];
    RETURN[(System.GetClockPulses[] - startTime) > System.MicrosecondsToPulses[timeout]];
    END;


  TurnOn: PROC = {EthernetOneFace.TurnOn[device, myHost, 0, 0, globalStorage]};

  -- MAIN:
  IF EthernetOneFace.globalStateSize # 0 THEN Error[];  -- not implemented
  END.


LOG

Jan 29, 80  6:00 PM   Dalal   fixed Core Software AR 2658.
Feb  7, 80 11:18 PM   Knutsen
   ioPage now a long pointer.  Initialize host socket right. INTEGER Divide
Feb  9, 80  2:43 PM   Murray   fix clobber of 4 words before buffer
Apr 20, 80  4:03 PM   Murray   Faceification, add ReturnPacket and timedOut
Apr 28, 80  7:36 PM   Murray   Fix QueueOutput bug (again!) in RecvPacket
Jul  2, 80  5:44 PM   Murray   Delete "DriverTypes." in front of pupEthernetPacket
Aug 20, 80  2:23 PM   BLyon    renamed EthernetFace to EthernetOneFace.
 3-Nov-81  9:21:07   Knutsen
  Use ResidentHeap to allocate IOCB.  Module renamed from
  MiniEthernetDriver to MiniEthernetOneDriver.
12-Apr-83 10:52:15   DKnutsen
   Renamed module MiniEthernetOneDriver.mesa => SimplePUPIOEthernetOneImpl.
   Stop using Buffer(Defs). Make compatible with new DriverTypes. Make compatible with new SimplePUPIO.