-- SetGMTUsingEthernet (September 1, 1982 4:16 pm by Taft)

DIRECTORY
  EthernetFace USING [AddCleanup, ControlBlock, controlBlockSize,
    DeviceHandle, GetNextDevice, GetPacketLength, GetStatus, GlobalStatePtr,
    globalStateSize, nullDeviceHandle, QueueInput, QueueOutput,
    RemoveCleanup, TurnOff, TurnOn],
  Environment USING [Base, Long],
  Inline USING [BITOR],
  PilotMP USING [cClient, cTimeNotAvailable],
  OISCPConstants USING [timeServerSocket],
  System USING [gmtEpoch, LocalTimeParameters, WestEast],
  ProcessorFace USING [GreenwichMeanTime, ProcessorID, processorID,
    SetGreenwichMeanTime, SetMP],
  ResidentHeap USING [first64K, MakeNode],
  TemporarySetGMT USING [],
  Zone USING [Status];

SetGMTUsingEthernet: PROGRAM
  IMPORTS EthernetFace, Inline, ProcessorFace, ResidentHeap
  EXPORTS TemporarySetGMT
  SHARES ProcessorFace =
BEGIN OPEN System;

SetGMT: PUBLIC PROC RETURNS [
    valid: BOOLEAN, params: LocalTimeParameters] =
  BEGIN
  valid ← FALSE;
  DO
    ProcessorFace.SetMP[PilotMP.cTimeNotAvailable];
    IF EthernetExists[] THEN
      BEGIN
      time: ProcessorFace.GreenwichMeanTime;
      [time: time, ltp: params] ← GetTime[];
      IF time#gmtEpoch THEN
        {ProcessorFace.SetGreenwichMeanTime[time]; valid ← TRUE;
         ProcessorFace.SetMP[PilotMP.cClient]; RETURN};
      END;
    ENDLOOP;
  END;

Byte: TYPE = [0..377B];

-- Following started as a copy of GTime.mesa
-- as edited by Johnsson on July 12, 1979  8:31 AM
NetTime: TYPE = MACHINE DEPENDENT RECORD [
  -- this is what comes in a TimeReply Packet
  timeHigh, timeLow: CARDINAL,
  direction: WestEast, -- depends on being same as in System
  zone: [0..127],
  zoneMinutes: [0..255],
  beginDST, endDST: CARDINAL,
  spare: ARRAY [5..8) OF WORD];

packetTypeOis: CARDINAL = 3000B;
protocolTypeReqRep: CARDINAL = 123;
timeRequest: CARDINAL = 1;
timeReply: CARDINAL = 2;

broadcast: ProcessorFace.ProcessorID = [177777B, 177777B, 177777B];

Address: TYPE = RECORD [
net: RECORD [a, b: WORD] ← [0,0],
host: ProcessorFace.ProcessorID,
socket: WORD ];

Encapsulation: TYPE = MACHINE DEPENDENT RECORD [
  eDest, eSource: ProcessorFace.ProcessorID,
  packetType: CARDINAL ];

Header: TYPE = MACHINE DEPENDENT RECORD [
  checksum, length: CARDINAL,
  transportControl, oisType: Byte,
  dest, source: Address,
  id1, id2: WORD,
  clientType: WORD ];

Request: TYPE = MACHINE DEPENDENT RECORD [
  encap: Encapsulation, head: Header];

Reply: TYPE = MACHINE DEPENDENT RECORD [
  encap: Encapsulation, head: Header, data: NetTime];

DriverNeedsSomeGlobalStorage: PUBLIC ERROR = CODE;
ResidentHeapError: ERROR = CODE;


GetTime: PROC RETURNS [
    time: ProcessorFace.GreenwichMeanTime, ltp: LocalTimeParameters] =
  BEGIN OPEN EthernetFace;
  minWordsPerEthernetPacket: CARDINAL = (64/2)-2;  --*** Should move to DriverTypes
  ether: DeviceHandle = GetNextDevice[nullDeviceHandle];
  global: GlobalStatePtr; -- Allocate space when needed
  status: Zone.Status;
  iocbRelative: Environment.Base RELATIVE POINTER;
  iocb: ControlBlock;
  inputSize: CARDINAL = MAX[SIZE[Reply], 32] + slop;
  slop: CARDINAL = 12;
  in: ARRAY [0..inputSize + 3) OF CARDINAL;
  out: ARRAY [0..SIZE[Request] + 3) OF CARDINAL;
  request: POINTER TO Request = Inline.BITOR[@out,3];
  reply: POINTER TO Reply = Inline.BITOR[@in,3];
  id1: WORD = 1234B;
  id2: WORD = 56710B;
  socket: WORD = 111213B;


  IF globalStateSize # 0 THEN ERROR DriverNeedsSomeGlobalStorage;


  [iocbRelative, status] ← ResidentHeap.MakeNode[controlBlockSize, a4];
  IF status#okay THEN ERROR ResidentHeapError;
  iocb ← LOOPHOLE[@ResidentHeap.first64K[iocbRelative]];
  request↑ ← [ [
      eDest: broadcast, eSource: ProcessorFace.processorID,
      packetType: packetTypeOis], [
      checksum: 177777B, length: 2*SIZE[Header],
      transportControl: 0, oisType: protocolTypeReqRep,
      dest: [host: broadcast, socket: OISCPConstants.timeServerSocket],
      source: [host: ProcessorFace.processorID, socket: socket],
      id1: id1, id2: id2,
      clientType: timeRequest] ];

  AddCleanup[ether];
  THROUGH [0..3) DO
    THROUGH [0..3) DO
      TurnOff[ether];
      TurnOn[ether, LOOPHOLE[ProcessorFace.processorID], 0, 0, global];
      reply.encap.packetType ← 0;
      QueueOutput[ether, request, MAX[SIZE[Request], minWordsPerEthernetPacket], iocb];
      THROUGH [0..LAST[CARDINAL]/2) DO
        IF GetStatus[iocb] # pending THEN
          BEGIN
          IF GetStatus[iocb] = ok
            AND GetPacketLength[iocb] >= SIZE[Reply]
            AND reply.encap.packetType = packetTypeOis
            AND reply.head.dest.host = ProcessorFace.processorID
            AND reply.head.dest.socket = socket
            AND reply.head.source.socket = OISCPConstants.timeServerSocket
            AND reply.head.id1 = id1 AND reply.head.id2 = id2
            AND reply.head.clientType = timeReply THEN
            BEGIN
            nt: POINTER TO NetTime = @reply.data;
            LOOPHOLE[time, Environment.Long] ←
              [num[lowbits: nt.timeLow, highbits: nt.timeHigh]];
            ltp ←
              [direction: nt.direction, zone: nt.zone, beginDST: nt.beginDST,
                zoneMinutes: nt.zoneMinutes, endDST: nt.endDST];
            TurnOff[ether];
            RemoveCleanup[ether];
            RETURN
            END;
          -- Start (another) read either because the send just finshed or we didn't like the
          -- packet that just arrived.
          QueueInput[ether, reply, inputSize, iocb];
          END;
        ENDLOOP;
      ENDLOOP;
    ENDLOOP;
  TurnOff[ether];
  RemoveCleanup[ether];
  time ← gmtEpoch;
  RETURN
  END;

EthernetExists: PROCEDURE RETURNS [BOOLEAN] =
  BEGIN OPEN EthernetFace;
  RETURN[GetNextDevice[nullDeviceHandle] # nullDeviceHandle];
  END;

END.
LOG

Time: November 10, 1980  6:35 PM	By: Forrest
        Action: Create from EthernetOne version of August 20, 1980  2:18 PM

Time: November 13, 1980  6:47 PM	By: HGM+RKJ
        Action: ?

Time: November 14, 1980  5:25 PM	By: Johnsson
        Action: ?

Time: January 12, 1981  2:15 PM	By: Forrest+RKJ
        Action: ?

Time: January 22, 1981  12:40 PM	By: Gobbel
        Action: Get iocb storage from resident heap instead of local frame; add LocalTimeParameters

Time: February 4, 1981  6:04 PM	By: McJones
        Action: SystemExtras => System; use OISCPConstants.TimeServerSocket

Time: February 6, 1981  10:05 AM	By: Luniewski
        Action: Typo: TimeServerSocket => timeServerSocket

Time: February 25, 1981  4:30 PM	By: BLyon
        Action: enforced minimun packet size on ethernet.

Time: March 12, 1981  5:34 PM	By: Yokota
        Action: Bug fix in SetGMT, MP is always set to 937 if time is not set properly.

Time: March 19, 1981  2:06 PM	By: Yokota
        Action: 990 instead of 930 is set at exit from SetGMT.

September 1, 1982 4:14 pm  Taft  Get time from net even if already known locally, in order to obtain the local time parameters.  (This makes it the same as SetGMTUsingEthernetOne.  Why did they change it?)