-- File: SptpImpl.mesa - last edit: -- AOF 3-Feb-88 11:26:40 -- Copyright (C) 1987, 1988 by Xerox Corporation. All rights reserved. DIRECTORY Buffer USING [Buffer, Byte, Dequeue, Device], CommFlags USING [doStats, driverStats], CommHeap USING [zone], CommunicationInternal USING [NSPackageDestroy, NSPackageMake], Driver USING [Device, GetInputBuffer, ReturnFreeBuffer], Environment USING [Byte, bytesPerWord], Inline USING [HighByte, LongCOPY, LowByte], Mopcodes USING [zADD, zAND, zLI1, zLINB], NetworkStreamImpl USING [xmtr, rcvr, rexmtr], NSBuffer USING [Body], NSTypes USING [bytesPerIDPHeader], PhoneAdoption USING [], PhoneNet USING [EntityClass, Negotiation], Protocol1 USING [ AddFamilyMember, DecapsulatorProc, EncapsulatorProc, Family, GetContext, GetFamilyUnit, MatrixRecord, RemoveFamilyMember], RoutingTable USING [ContextObject, NetworkContext], RS232C USING [ChannelHandle, CommParamHandle], SptpOps USING [ CreateDriver, defaultMaxRS232CBytes, GetDevice, GetEntityClass, GetVersion, maxRS232CBytes, minRS232CBytes, point5Duplex, ReservationObject, siuSupport, StatsRecord], SptpProtocol USING [ Control, ControlBody, Encapsulation, EncapsulationFromBlock, EncapsulationObject, EntityClass, MasterSlaveRelationship, PacketType, Protocol, ProtocolProc, ProtocolVersion], SptpStats USING [Incr], SppOps USING [SetSppSpy, SppSpyProc], System USING [ GetClockPulses, GetGreenwichMeanTime, HostNumber, MicrosecondsToPulses, nullHostNumber, nullNetworkNumber]; SptpImpl: MONITOR LOCKS protocol.lock↑ USING protocol: SptpProtocol.Protocol IMPORTS Buffer, CommunicationInternal, CommHeap, Driver, Inline, Protocol1, SptpOps, SptpProtocol, SptpStats, SppOps, System EXPORTS Buffer, PhoneAdoption, PhoneNet, SptpProtocol = BEGIN --This doesn't support 1/2 duplex even though the interface says it does point5Duplex: BOOLEAN = ~SptpOps.point5Duplex; bpw: NATURAL = Environment.bytesPerWord; Device: PUBLIC <<Buffer>> TYPE = Driver.Device; Unsupported: PUBLIC <<PhoneNet>> ERROR = CODE; IllegalEntityClass: PUBLIC <<PhoneNet>> ERROR = CODE; StatsPointer: TYPE = LONG POINTER TO SptpOps.StatsRecord; InvalidLineNumber: PUBLIC <<PhoneNet, PhoneAdoption>> ERROR = CODE; pvVersionLow: SptpProtocol.ProtocolVersion = version3; --really low pvVersionHigh: SptpProtocol.ProtocolVersion = version4; --includes 'more' bit TwoBytes: TYPE = RECORD[hi, lo: Environment.Byte]; Forty8Bitter: TYPE = MACHINE DEPENDENT RECORD[one, two, three: WORD]; clock: RECORD[to1, to2, to3, to4, to5, to6: LONG CARDINAL]; --pulses << The following variable assumes that we are connected via a single local rs232c port. Watch out for multi ports, etc. >> AdoptForNS: PUBLIC <<PhoneAdoption>> PROC[lineNumber: CARDINAL] = BEGIN family: Protocol1.Family; matrix: Protocol1.MatrixRecord; device: Device = SptpOps.GetDevice[lineNumber]; IF device = NIL THEN ERROR InvalidLineNumber; family ← CommunicationInternal.NSPackageMake[]; matrix ← [ family: family, context: , encapsulator: NsEncapsulation, decapsulator: NsDecapsulation]; matrix.context ← CommHeap.zone.NEW[RoutingTable.ContextObject ← [ netNumber: System.nullNetworkNumber, network: device, stats: NIL]]; Protocol1.AddFamilyMember[device, @matrix]; IF CommFlags.doStats THEN SptpStats.Incr[adoptNS]; END; --AdoptForNS DisownFromNS: PUBLIC <<PhoneAdoption>> PROC [lineNumber: CARDINAL] = BEGIN family: Protocol1.Family; context: RoutingTable.NetworkContext; device: Device = SptpOps.GetDevice[lineNumber]; IF device = NIL THEN ERROR InvalidLineNumber; context ← Protocol1.GetContext[device, ns]; family ← Protocol1.GetFamilyUnit[ns]; Protocol1.RemoveFamilyMember[device, family]; CommHeap.zone.FREE[@context]; CommunicationInternal.NSPackageDestroy[]; IF CommFlags.doStats THEN SptpStats.Incr[disownNS]; END; --DisownFromNS Initialize: PUBLIC <<PhoneNet>> PROC[ lineNumber: CARDINAL, channel: RS232C.ChannelHandle, commParams: RS232C.CommParamHandle, negotiationMode: PhoneNet.Negotiation, hardwareStatsAvailable: BOOLEAN, clientData: LONG UNSPECIFIED ← 0, ourEntityClass: PhoneNet.EntityClass, clientHostNumber: System.HostNumber ← System.nullHostNumber] = -- REPORTS IllegalEntityClass, Unsupported BEGIN reservation: SptpOps.ReservationObject ← [ lineSpeed: commParams.lineSpeed, duplex: commParams.duplex, lineNumber: lineNumber, entity: ourEntityClass]; --IF lineNumber # 0 THEN ERROR Unsupported; IF ~point5Duplex AND (reservation.duplex = half) THEN ERROR Unsupported; IF ourEntityClass = siu THEN ERROR IllegalEntityClass; SptpOps.CreateDriver[@reservation]; IF CommFlags.doStats THEN SptpStats.Incr[createDriver]; END; --Initialize Destroy: PUBLIC <<PhoneNet>> PROC[lineNumber: CARDINAL] = BEGIN device: Driver.Device = SptpOps.GetDevice[lineNumber]; IF device = NIL THEN RETURN WITH ERROR InvalidLineNumber; device.deleteDriver[]; --else delete the driver END; --Destroy --**************** Encapsulation and decapsulation ************** NsDecapsulation: PUBLIC Protocol1.DecapsulatorProc = --PROCEDURE [b: Buffer.Buffer] RETURNS [type: Buffer.Type] BEGIN << When coming in here we are assured that b.linkLayer.blockPointer points to the beginning of the physical frame. That frame starts with the sptp's encapsulation record. From that information we should be able to compute the address of the data portion. These calls to SptpOps.Get* are expensive. It would be nice to be able to get to some state object more cheaply to determine whether we are connected to an SIU or not. >> size: NATURAL; body: NSBuffer.Body; device: Device = b.fo.network; --get pointer to network version: SptpProtocol.ProtocolVersion = SptpOps.GetVersion[device]; WITH e: LOOPHOLE[b.linkLayer.blockPointer, SptpProtocol.Encapsulation] SELECT FROM ns => size ← IF version > version2 THEN SIZE[ns SptpProtocol.EncapsulationObject] ELSE SIZE[siuSet SptpProtocol.EncapsulationObject]; siuSet, siuEcho, siuDetermineLS => RETURN[orphan]; --gun these down << SIU knows nothing but ns, so that's as good as packet type. The SIU doesn't always set the packet type to be ns. We'll **assume** that the packet is an ns packet, and count on the level 1 checksums to help us out. >> ENDCASE => --special test for SIUs IF SptpOps.GetEntityClass[device] # siu THEN RETURN[vagrant] --go away ELSE size ← SIZE[siuSet SptpProtocol.EncapsulationObject]; --see above body ← LOOPHOLE[b.linkLayer.blockPointer + size]; --that's fair b.highLayer ← [LOOPHOLE[body], 0, body.pktLength]; --from the packet b.linkLayer.stopIndexPlusOne ← size ← (bpw * size); --'size' now bytes SELECT TRUE FROM (b.fo.driver.length < NSTypes.bytesPerIDPHeader) => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[decapOrphan]; RETURN[orphan]; --busted END; (body.pktLength < NSTypes.bytesPerIDPHeader) => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[decapOrphan]; RETURN[orphan]; --busted END; ENDCASE => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[decapNS]; RETURN[ns]; --busted END; END; --NsDecapsulation NsEncapsulation: PUBLIC Protocol1.EncapsulatorProc = BEGIN << Coming in here we know that the packet is an NS packet and that b.highLayer.blockPointer points to the NSBuffer.BodyBody. We need to back off some number of bytes from that point and stick in the proper encap- sulation. NB: The Inline.LongCOPYs are here because the compiler generates somewhat unuseful code if you use a more direct approach to things. It first blaps in a non-descriminated record which is several bytes longer than an ns SptpProtocol.EncapsulationObject. Then it goes back and fills in the particulars. Regettably by then the first 'n' bytes of the original packet are destroyed. >> size: NATURAL; --this variable is overloaded link: LONG POINTER; --computed to point to data link fields encapsulation: SptpProtocol.EncapsulationObject; --this is our local copy device: Device = b.fo.network; --get pointer to network body: NSBuffer.Body = LOOPHOLE[b.highLayer.blockPointer]; of: NATURAL = body.pktLength / SptpOps.defaultMaxRS232CBytes; version: SptpProtocol.ProtocolVersion = SptpOps.GetVersion[device]; SELECT version FROM version4 => BEGIN encapsulation ← [ns[LTA: FALSE, more: (of # 0), fragment: [0, of]]]; size ← SIZE[ns SptpProtocol.EncapsulationObject]; END; version3 => BEGIN IF (of # 0) THEN ERROR Unsupported; encapsulation ← [ns[LTA: FALSE, more: FALSE, fragment: [0, 0]]]; size ← SIZE[ns SptpProtocol.EncapsulationObject]; END; version2 => BEGIN encapsulation ← [siuSet[LTA: FALSE, reserved: 0, immedDest: NARROW[immediate, LONG POINTER TO System.HostNumber]↑]]; SmashPacketType[@encapsulation, ns]; --looks bad, feels worse! size ← SIZE[siuSet SptpProtocol.EncapsulationObject]; END; ENDCASE => ERROR Unsupported; link ← body - size; --this is backoff to beginning of link layer Inline.LongCOPY[to: link, from: @encapsulation, nwords: size]; size ← bpw * size; -- now represents data link size in bytes b.linkLayer ← [link, 0, size]; --set the link layer values b.fo.driver.length ← Roundup[body.pktLength] + size; --frame is sum of both IF CommFlags.doStats THEN SptpStats.Incr[encapNS]; END; --NsEncapsulation Roundup: PROC[NATURAL] RETURNS[NATURAL] = MACHINE CODE { Mopcodes.zLI1; Mopcodes.zADD; Mopcodes.zLINB, 376B; Mopcodes.zAND}; SmashPacketType: PROC[ p: SptpProtocol.Encapsulation, t: SptpProtocol.PacketType] = INLINE BEGIN PacketTypeFieldPointer: TYPE = LONG POINTER TO PacketTypeField; PacketTypeField: TYPE = MACHINE DEPENDENT RECORD[ t(0: 0..7): SptpProtocol.PacketType, r(0: 8..15): BOOLEAN]; LOOPHOLE[p, PacketTypeFieldPointer].t ← t; END; --SmashPacketType --********* Protocol State Machine Procedures **********-- ActiveNegotiation: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; timein: LONG CARDINAL = System.GetClockPulses[]; UNTIL (System.GetClockPulses[] - timein) > clock.to3 DO timeout: LONG CARDINAL ← clock.to1; SendMyOptions[protocol, TRUE]; --get things rolling UNTIL (b ← WaitForControl[protocol, timeout]) = NIL DO ProcessControl[protocol, b]; timeout ← LAST[LONG CARDINAL]; ENDLOOP; IF protocol.object.state # option1 THEN EXIT; --done here REPEAT FINISHED => protocol.object.state ← terminate2; --give up ENDLOOP; END; --ActiveNegotiation AwaitingOptions: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; timein: LONG CARDINAL = System.GetClockPulses[]; UNTIL (System.GetClockPulses[] - timein) > clock.to3 DO timeout: LONG CARDINAL ← clock.to1; UNTIL (b ← WaitForControl[protocol, timeout]) = NIL DO ProcessControl[protocol, b]; timeout ← LAST[LONG CARDINAL]; ENDLOOP; IF protocol.object.state # option3 THEN EXIT; --done here SendMyOptions[protocol, TRUE]; --ask again REPEAT FINISHED => protocol.object.state ← terminate2; --give up ENDLOOP; END; --AwaitingOptions AwaitingOptionAck: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; timein: LONG CARDINAL = System.GetClockPulses[]; UNTIL (System.GetClockPulses[] - timein) > clock.to2 DO timeout: LONG CARDINAL ← clock.to1; UNTIL (b ← WaitForControl[protocol, timeout]) = NIL DO ProcessControl[protocol, b]; timeout ← LAST[LONG CARDINAL]; ENDLOOP; IF protocol.object.state # option4 THEN EXIT; --done here SendMyOptions[protocol, TRUE]; --ask again REPEAT FINISHED => protocol.object.state ← terminate2; --give up ENDLOOP; END; --AwaitingOptionAck; AwaitTerminateReply: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; timein: LONG CARDINAL = System.GetClockPulses[]; UNTIL (System.GetClockPulses[] - timein) > clock.to6 DO timeout: LONG CARDINAL ← clock.to4; UNTIL (b ← WaitForControl[protocol, timeout]) = NIL DO ProcessControl[protocol, b]; timeout ← LAST[LONG CARDINAL]; ENDLOOP; IF protocol.object.state # terminate1 THEN EXIT; --done here SendTerminateRequest[protocol, TRUE]; --ask again REPEAT FINISHED => protocol.object.state ← terminate2; --give up ENDLOOP; END; --AwaitTerminateReply EntityClash: PROC[mine, his: SptpProtocol.EntityClass] RETURNS[BOOLEAN] = INLINE BEGIN --returns TRUE of there is a clash clash: <<PACKED>> ARRAY SptpProtocol.EntityClass OF <<PACKED>> ARRAY SptpProtocol.EntityClass OF BOOLEAN = -- inr cnr siu ws --inr--[[FALSE, FALSE, FALSE, TRUE], --cnr-- [FALSE, TRUE, TRUE, FALSE], --siu-- [FALSE, TRUE, TRUE, FALSE], --ws-- [TRUE, FALSE, FALSE, FALSE]]; RETURN[clash[mine][his]]; END; --EntityClash FrameSize: PROC[hi, lo: Environment.Byte] RETURNS[va: WORD] = INLINE {OPEN bn: LOOPHOLE[va, TwoBytes]; bn.hi ← hi; bn.lo ← lo}; --FrameSize LatchSiu: PROC = INLINE {[] ← SppOps.SetSppSpy[SiuSingleBufferSpy]}; PassiveNegotiation: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; IF (b ← WaitForControl[protocol, clock.to3]) # NIL THEN ProcessControl[protocol, b]; END; --PassiveNegotiation ProcessControl: PUBLIC <<SptpProtocol>> PROC[protocol: SptpProtocol.Protocol, b: Buffer.Buffer] = BEGIN WITH encapsulation: SptpProtocol.EncapsulationFromBlock[b.linkLayer.blockPointer] SELECT FROM control => BEGIN hisAnswer: SptpProtocol.Control = LOOPHOLE[ @encapsulation + SIZE[control SptpProtocol.EncapsulationObject]]; WITH c: hisAnswer SELECT encapsulation.control FROM myOptions => BEGIN frameSize: NATURAL = FrameSize[ --may be bogus c.maxPktSizeHiByte, c.maxPktSizeLowByte]; IF CommFlags.doStats THEN SptpStats.Incr[rcvMyOptions]; SELECT TRUE FROM (c.highestVersionNumber < pvVersionLow) => BEGIN protocol.object.state ← terminate2; --just drop the line GOTO returnBufferAndExit; --get out of here END; (c.lowestVersionNumber > pvVersionHigh) => BEGIN protocol.object.state ← terminate2; --just drop the line GOTO returnBufferAndExit; --get out of here END; ((protocol.object.him ← c.sourceHost) = protocol.object.me) => BEGIN SendAddressReject[protocol]; --that's loopback mode protocol.object.state ← terminate1; --and it's fatal GOTO returnBufferAndExit; --get out of here END; (EntityClash[protocol.object.ourEntityClass, c.entityClass]) => BEGIN SendClassReject[protocol]; --tell him we're going down protocol.object.state ← terminate1; --and it's fatal GOTO returnBufferAndExit; --get out of here END; (c.highestVersionNumber > protocol.object.protocolVersion) => BEGIN SendVersionReject[protocol, FALSE]; --but not fatal SendMyOptions[protocol]; --send my preferences again GOTO returnBufferAndExit; --get out of here END; (frameSize > SptpOps.maxRS232CBytes) => BEGIN SendSizeReject[protocol, FALSE]; --send him the bad news SendMyOptions[protocol]; --send my preferences again GOTO returnBufferAndExit; --get out of here END; ENDCASE; IF point5Duplex AND (protocol.object.duplex = half) THEN protocol.object.master ← SetMasterSlaveRelationship[ protocol.object.me, protocol.object.him]; protocol.myDevice.receiveBufferLen ← frameSize; protocol.object.theirEntityClass ← c.entityClass; protocol.object.protocolVersion ← c.highestVersionNumber; SELECT protocol.object.state FROM option1, option2 => BEGIN protocol.object.started ← System.GetGreenwichMeanTime[]; protocol.object.state ← option4; --advance the state SendOptionsAck[protocol, FALSE]; --but we can ack him SendMyOptions[protocol, TRUE]; --we're not in data yet END; option3 => BEGIN protocol.object.established ← System.GetGreenwichMeanTime[]; IF CommFlags.driverStats THEN BEGIN OPEN s: NARROW[protocol.myDevice.stats, StatsPointer]; s.protocolUp ← s.protocolUp.SUCC; END; protocol.myDevice.alive ← TRUE; --what we were waiting for protocol.object.state ← data; --and we're up SendOptionsAck[protocol]; --ack him SELECT TRUE FROM (~SptpOps.siuSupport) => NULL; --this isn't necessary (protocol.object.theirEntityClass = siu) => LatchSiu[]; ENDCASE => UnlatchSiu[]; END; option4 => BEGIN SendOptionsAck[protocol, FALSE]; --ack him again SendMyOptions[protocol, TRUE]; --another copy of options END; data => SendOptionsAck[protocol]; --ack him again ENDCASE; END; optionsAck => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvOptionsAck]; IF c.destinationHost # protocol.object.me THEN BEGIN SendAddressReject[protocol]; protocol.object.state ← terminate1; END ELSE BEGIN SELECT protocol.object.state FROM option1 => BEGIN protocol.object.started ← System.GetGreenwichMeanTime[]; protocol.object.state ← option3; --advance state SendMyOptions[protocol]; --send him another copy END; option3 => SendMyOptions[protocol]; --send him another copy option4 => BEGIN IF CommFlags.driverStats THEN BEGIN OPEN s: NARROW[protocol.myDevice.stats, StatsPointer]; s.protocolUp ← s.protocolUp.SUCC; END; protocol.object.established ← System.GetGreenwichMeanTime[]; protocol.myDevice.alive ← TRUE; --what we were waiting for protocol.object.state ← data; --and we're up SendMyOptions[protocol]; --send him another copy SELECT TRUE FROM (~SptpOps.siuSupport) => NULL; --this isn't necessary (protocol.object.theirEntityClass = siu) => LatchSiu[]; ENDCASE => UnlatchSiu[]; END; ENDCASE => SendNull[protocol]; --to provide LTA if needed END; END; terminateRequest => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvTermReq]; SendTerminateReply[protocol]; --so reply to him protocol.myDevice.alive ← FALSE; --we're dropping out protocol.object.state ← terminate2; --he wants to quit END; terminateReply => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvTermRep]; protocol.myDevice.alive ← FALSE; --we're dropping out protocol.object.state ← terminate2; --he knows we want to quit END; versionReject => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvVersionReject]; protocol.object.protocolVersion ← c.highestVersionNumber; --protocol state should be active negotiation - still END; areYouThere => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvYouThere]; SELECT protocol.object.state FROM data => SendIAmHere[protocol]; terminate1, terminate2 => SendTerminateRequest[protocol]; ENDCASE => SendMyOptions[protocol]; --somewhere in options END; addressReject => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvAddrReject]; SendTerminateRequest[protocol]; --okay, we'll give up protocol.object.state ← terminate1; --and get out of this state END; classReject => BEGIN IF CommFlags.doStats THEN SptpStats.Incr[rcvClassReject]; SendTerminateRequest[protocol]; --okay, we'll give up protocol.object.state ← terminate1; --and get out of this state END; null => IF CommFlags.doStats THEN SptpStats.Incr[rcvNull]; ENDCASE; --don't know what it was, but I'm going to ignore it EXITS returnBufferAndExit => NULL; END; ENDCASE; Driver.ReturnFreeBuffer[b]; --return the buffer END; --ProcessControl SendAddressReject: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; addressRejectLength: NATURAL = 2; AddressReject: TYPE = LONG POINTER TO AddressRejectFrame; AddressRejectFrame: TYPE = RECORD[ encapsulation: SptpProtocol.EncapsulationObject.control, options: SptpProtocol.ControlBody.addressReject]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, AddressReject].encapsulation ← [control[LTA: lta, reserved: FALSE, control: addressReject]]; protocol.sendControlFrame[b, addressRejectLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtAddrReject]; END; --SendAddressReject SendAreYouThere: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; areYouThereLength: NATURAL = 2; AreYouThere: TYPE = LONG POINTER TO AreYouThereFrame; AreYouThereFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: areYouThere SptpProtocol.ControlBody]; IF (b ← Driver.GetInputBuffer[FALSE, SptpOps.minRS232CBytes]) = NIL THEN RETURN; --not too serious about sending this one LOOPHOLE[b.linkLayer.blockPointer, AreYouThere].encapsulation ← [control[LTA: lta, reserved: FALSE, control: areYouThere]]; protocol.sendControlFrame[b, areYouThereLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtYouThere]; END; --SendAreYouThere SendClassReject: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; classRejectLength: NATURAL = 2; ClassReject: TYPE = LONG POINTER TO ClassRejectFrame; ClassRejectFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: classReject SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, ClassReject].encapsulation ← [control[LTA: lta, reserved: FALSE, control: classReject]]; protocol.sendControlFrame[b, classRejectLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtClassReject]; END; --SendClassReject SendIAmHere: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; iAmHereLength: NATURAL = 2; IAmHere: TYPE = LONG POINTER TO IAmHereFrame; IAmHereFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: iAmHere SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, IAmHere].encapsulation ← [control[LTA: lta, reserved: FALSE, control: iAmHere]]; protocol.sendControlFrame[b, iAmHereLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtIHere]; END; --SendIAmHere SendMyOptions: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; myOptionsLength: NATURAL = 13; MyOptions: TYPE = LONG POINTER TO MyOptionsFrame; MyOptionsFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: myOptions SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, MyOptions]↑ ← [ encapsulation: [control[LTA: lta, reserved: FALSE, control: myOptions]], options: [myOptions[ highestVersionNumber: protocol.object.protocolVersion, lowestVersionNumber: pvVersionLow, sourceHost: protocol.object.me, entityClass: protocol.object.ourEntityClass, maxPktSizeHiByte: Inline.HighByte[SptpOps.maxRS232CBytes], maxPktSizeLowByte: Inline.LowByte[SptpOps.maxRS232CBytes]]]]; protocol.sendControlFrame[b, myOptionsLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtMyOptions]; END; --SendMyOptions SendNull: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN IF point5Duplex AND (protocol.object.duplex = half) THEN BEGIN b: Buffer.Buffer; nullFrameLength: NATURAL = 2; Null: TYPE = LONG POINTER TO NullFrame; NullFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: null SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, Null].encapsulation ← [control[LTA: lta, reserved: FALSE, control: null]]; protocol.sendControlFrame[b, nullFrameLength]; --now send the buffer END; END; --SendNull SendOptionsAck: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; optionsAckLength: NATURAL = 8; OptionsAck: TYPE = LONG POINTER TO OptionsAckFrame; OptionsAckFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: optionsAck SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, OptionsAck]↑ ← [ encapsulation: [control[ LTA: lta, reserved: FALSE, control: optionsAck]], options: [optionsAck[IF protocol.object.theirEntityClass = siu THEN protocol.object.me ELSE protocol.object.him]]]; protocol.sendControlFrame[b, optionsAckLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtOptionsAck]; END; --SendOptionsAck SendSizeReject: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; sizeRejectLength: NATURAL = 2; SizeReject: TYPE = LONG POINTER TO SizeRejectFrame; SizeRejectFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: sizeReject SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, SizeReject].encapsulation ← [control[LTA: lta, reserved: FALSE, control: sizeReject]]; protocol.sendControlFrame[b, sizeRejectLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtSizeReject]; END; --SendSizeReject SendTerminateRequest: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; terminateRequestLength: NATURAL = 2; TerminateRequest: TYPE = LONG POINTER TO TerminateRequestFrame; TerminateRequestFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: terminateRequest SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, TerminateRequest].encapsulation ← [control[LTA: lta, reserved: FALSE, control: terminateRequest]]; protocol.sendControlFrame[b, terminateRequestLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtTermReq]; END; --SendTerminateRequest SendTerminateReply: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; terminateReplyLength: NATURAL = 2; TerminateReply: TYPE = LONG POINTER TO TerminateReplyFrame; TerminateReplyFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: terminateReply SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, TerminateReply].encapsulation ← [control[LTA: lta, reserved: FALSE, control: terminateReply]]; protocol.sendControlFrame[b, terminateReplyLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtTermRep]; END; --SendTerminateReply SendVersionReject: SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; versionRejectLength: NATURAL = 3; VersionReject: TYPE = LONG POINTER TO VersionRejectFrame; VersionRejectFrame: TYPE = RECORD[ encapsulation: control SptpProtocol.EncapsulationObject, options: versionReject SptpProtocol.ControlBody]; UNTIL (b ← Driver.GetInputBuffer[TRUE, SptpOps.minRS232CBytes]) # NIL DO ENDLOOP; LOOPHOLE[b.linkLayer.blockPointer, VersionReject]↑ ← [ encapsulation: [control[ LTA: lta, reserved: FALSE, control: versionReject]], options: [versionReject[ highestVersionNumber: protocol.object.protocolVersion]]]; protocol.sendControlFrame[b, versionRejectLength]; IF CommFlags.doStats THEN SptpStats.Incr[xmtVersionReject]; END; --SendVersionReject SetMasterSlaveRelationship: PROC[me, him: System.HostNumber] RETURNS[SptpProtocol.MasterSlaveRelationship] = --INLINE BEGIN OPEN m: LOOPHOLE[me, Forty8Bitter], h: LOOPHOLE[him, Forty8Bitter]; RETURN[SELECT TRUE FROM (him = System.nullHostNumber) => him, --stupid ole SIU? (h.one > m.one) => him, (h.one < m.one) => me, (h.two > m.two) => him, (h.two < m.two) => me, (h.three > m.three) => him, (h.three < m.three) => me, ENDCASE => me]; --and if they're equal, welllllll! END; --SetMasterSlaveRelationship SiuSingleBufferSpy: SppOps.SppSpyProc = BEGIN IF SptpOps.siuSupport THEN BEGIN gf: LONG POINTER TO FRAME[NetworkStreamImpl] ← LOOPHOLE[link]; IF action = create THEN BEGIN gf.xmtr.maxAlloc ← 0; --that's the most we'll try to send gf.rcvr.maxAlloc ← 0; --that's the most we want sent to us gf.rexmtr.interval ← System.MicrosecondsToPulses[6000000]; END; END; END; --SiuSingleBufferSpy TerminationDally: PUBLIC <<SptpProtocol>> SptpProtocol.ProtocolProc = BEGIN b: Buffer.Buffer; UNTIL (b ← WaitForControl[protocol, clock.to5]) = NIL DO ProcessControl[protocol, b]; --keep this up until he quits ENDLOOP; protocol.object.state ← idle; --now I'm idle END; --TerminationDally UnlatchSiu: PROC = INLINE {[] ← SppOps.SetSppSpy[NIL]}; WaitForControl: PUBLIC ENTRY <<SptpProtocol LOCKS protocol.lock↑>> PROC[protocol: SptpProtocol.Protocol, timeout: LONG CARDINAL] RETURNS[Buffer.Buffer] = BEGIN ENABLE UNWIND => NULL; timein: LONG CARDINAL = System.GetClockPulses[]; --when we enter WHILE protocol.q.length = 0 DO --already have something to do IF timeout = LAST[LONG CARDINAL] THEN EXIT; --just checking queue WAIT protocol.engine; --wait for a notable event SELECT TRUE FROM (protocol.q.length > 0) => EXIT; --we can do it now (timeout = 0) => EXIT; --just waiting for the interrupt ((System.GetClockPulses[] - timein) > timeout) => EXIT; --timeout ENDCASE; ENDLOOP; RETURN[Buffer.Dequeue[@protocol.q]]; END; --WaitForControl clock.to1 ← System.MicrosecondsToPulses[2D6]; --2 seconds clock.to2 ← System.MicrosecondsToPulses[20D6]; --20 secs clock.to3 ← System.MicrosecondsToPulses[10D6]; --10 secs clock.to4 ← System.MicrosecondsToPulses[5D6]; --5 secs clock.to5 ← System.MicrosecondsToPulses[2D6]; --2 seconds clock.to6 ← System.MicrosecondsToPulses[20D6]; --20 secs END.. LOG 29-Sep-87 18:32:20 AOF Created file. 2-Nov-87 12:23:10 AOF Adjusting window for SIUs. 7-Nov-87 16:24:09 AOF Moved in state machine procs from SptpDriver. 11-Nov-87 19:07:53 AOF Export missing procedures and error. 13-Nov-87 12:00:54 AOF Mods to Adoption routines. 2-Dec-87 14:48:24 AOF State machine cleanup. 2-Dec-87 15:48:30 AOF Set master-slave relationship sooner. 15-Dec-87 13:24:41 AOF SIUs - they don't use same encapsulation. 18-Jan-88 14:38:49 AOF Sutting down allocation for SIUs. 3-Feb-88 11:26:19 AOF PupGateway (no .5 duplex).