<> <> <> <> <> <> <> <<>> <> <<>> DIRECTORY Basics USING [bytesPerWord], Checksum USING [ComputeChecksum], CommBuffer USING [], CommDriverType USING [ethernetOneEncapsulationBytes, ethernetOneEncapsulationOffset, Encapsulation], Endian USING [FWORD, HWORD, bytesPerHWord], EthernetOneFace USING [GetHostNumber, GetNextDevice, GetStatusAndCollisions, GetStatusAndLength, Handle, IOCB, nullHandle, QueueInput, QueueOutput, TurnOff, TurnOn], MiniEthernetDefs USING [timedOut], PrincOpsUtils USING [LongCopy], Pup USING [Address, allHosts, Host, Net, nullHost, nullNet, nullSocket, Socket], PupBuffer USING [noChecksum, PupBufferPointer, WordsWithoutChecksum], PupType USING [bytesOfPupOverhead, Type]; MiniEthernetOneDriver: PROGRAM IMPORTS Checksum, EthernetOneFace, PrincOpsUtils, PupBuffer EXPORTS CommBuffer, MiniEthernetDefs = { Encapsulation: PUBLIC TYPE = CommDriverType.Encapsulation; active: BOOL _ FALSE; last: {in, out, reset}; myHost: Pup.Host; myNet: Pup.Net; <> bufWords: CARDINAL = 300; bufferBytes: CARDINAL = bufWords*Basics.bytesPerWord; fudge: CARDINAL = 30; eBuff: ARRAY [0..bufWords + fudge) OF WORD; eBuffer: POINTER = LOOPHOLE[@eBuff]; b: PupBuffer.PupBufferPointer; -- Global buffer for actual data transfers ether: EthernetOneFace.Handle; longIocb: EthernetOneFace.IOCB; bytesPerPupHeader: CARDINAL = PupType.bytesOfPupOverhead; DriverNotActive: PUBLIC ERROR = CODE; BufferOverflow: PUBLIC ERROR = CODE; SendToStrangePlace: PUBLIC ERROR = CODE; ReturnToStrangePlace: PUBLIC ERROR = CODE; ActivateDriver: PUBLIC PROC [iocb: LONG POINTER] RETURNS [BOOL] = { p: LONG POINTER _ @b.ovh.encap + CommDriverType.ethernetOneEncapsulationOffset; q: LONG POINTER _ b; fudge: LONG CARDINAL _ p - q; IF ~FindTheBoard[] THEN RETURN[FALSE]; b _ eBuffer - fudge; longIocb _ iocb; myHost _ [EthernetOneFace.GetHostNumber[ether]]; myNet _ Pup.nullNet; EthernetOneFace.TurnOn[ether, 0, 0]; active _ TRUE; last _ reset; RETURN[TRUE]; }; FindTheBoard: PROC RETURNS [BOOL] = { ether _ EthernetOneFace.GetNextDevice[EthernetOneFace.nullHandle]; RETURN[ether # EthernetOneFace.nullHandle]; }; KillDriver: PUBLIC PROC = { EthernetOneFace.TurnOff[ether]; active _ FALSE; }; BorrowTheBuffer: PUBLIC PROC RETURNS [p: LONG POINTER, bytes: CARDINAL] = { RETURN[eBuffer+fudge, bufferBytes]; }; SendPacket: PUBLIC PROC [dest: Pup.Address, me: Pup.Socket, type: PupType.Type, id: Endian.FWORD, data: LONG POINTER, bytes: CARDINAL] = { words: CARDINAL = (bytes + Basics.bytesPerWord-1)/Basics.bytesPerWord; SELECT TRUE FROM dest.host = Pup.allHosts => b.ovh.encap.ethernetOneDest _ Pup.allHosts; ENDCASE => ERROR SendToStrangePlace; b.byteLength _ bytesPerPupHeader + bytes; b.type _ type; b.id _ id; b.dest _ dest; b.source.socket _ me; PrincOpsUtils.LongCopy[to: @b.byte, from: data, nwords: words]; SendPupBuffer[]; }; ReturnPacket: PUBLIC PROC [type: PupType.Type, id: Endian.FWORD, data: LONG POINTER, bytes: CARDINAL] = { words: CARDINAL = (bytes + Basics.bytesPerWord-1)/Basics.bytesPerWord; temp: Pup.Socket; IF last # in THEN ERROR ReturnToStrangePlace; b.ovh.encap.ethernetOneDest _ b.ovh.encap.ethernetOneSource; b.byteLength _ bytesPerPupHeader + bytes; b.type _ type; b.id _ id; temp _ b.dest.socket; b.dest _ b.source; b.source.socket _ temp; PrincOpsUtils.LongCopy[to: @b.byte, from: data, nwords: words]; SendPupBuffer[]; }; SendPupBuffer: PROC = { b.hopCount _ 0; b.spares _ 0; b.source.net _ myNet; b.source.host _ myHost; SetPupChecksum[b]; b.ovh.encap.ethernetOneType _ pup; SendBuffer[b.byteLength]; }; SendBuffer: PROC [bytes: CARDINAL] = { IF ~active THEN ERROR DriverNotActive; IF bytes > bufferBytes THEN ERROR BufferOverflow; b.ovh.encap.ethernetOneSource _ myHost; last _ out; EthernetOneFace.QueueOutput[ ether, @b.ovh.encap + CommDriverType.ethernetOneEncapsulationOffset, bytes + CommDriverType.ethernetOneEncapsulationBytes, longIocb]; THROUGH [0..LAST[CARDINAL]) DO IF EthernetOneFace.GetStatusAndCollisions[longIocb].status # pending THEN EXIT; REPEAT FINISHED => { EthernetOneFace.TurnOff[ether]; EthernetOneFace.TurnOn[ether, 0, 0]; }; ENDLOOP; }; RecvPacket: PUBLIC PROC [source: LONG POINTER TO Pup.Address, me: Pup.Socket, data: LONG POINTER, maxBytes: CARDINAL, timeout: PROC RETURNS [BOOL]] RETURNS [bytes: CARDINAL, id: Endian.FWORD, type: PupType.Type] = { IF ~active THEN ERROR DriverNotActive; DO EthernetOneFace.QueueInput[ ether, @b.ovh.encap + CommDriverType.ethernetOneEncapsulationOffset, bufferBytes, longIocb]; UNTIL EthernetOneFace.GetStatusAndLength[longIocb].status # pending DO IF timeout[] THEN { EthernetOneFace.TurnOff[ether]; EthernetOneFace.TurnOn[ether, 0, 0]; last _ reset; RETURN[MiniEthernetDefs.timedOut, [0, 0], LOOPHOLE[0]]; }; ENDLOOP; IF EthernetOneFace.GetStatusAndLength[longIocb].status # ok THEN LOOP; bytes _ EthernetOneFace.GetStatusAndLength[longIocb].bytes; IF b.ovh.encap.ethernetOneType # pup THEN LOOP; <> <> IF myNet # Pup.nullNet AND myNet # b.dest.net AND b.dest.net # Pup.nullNet THEN LOOP; IF myHost # b.dest.host THEN LOOP; -- Can't recv broadcast IF me # b.dest.socket THEN LOOP; IF source.net # Pup.nullNet AND source.net # b.source.net AND b.source.net # Pup.nullNet THEN LOOP; IF source.host # Pup.nullHost AND source.host # b.source.host THEN LOOP; IF source.socket # Pup.nullSocket AND source.socket # b.source.socket THEN LOOP; IF myNet = Pup.nullNet THEN myNet _ b.dest.net; bytes _ b.byteLength - bytesPerPupHeader; IF bytes > maxBytes THEN LOOP; IF ~TestPupChecksum[b] THEN LOOP; PrincOpsUtils.LongCopy[to: data, from: @b.byte, nwords: (bytes + 1)/2]; IF source.net = Pup.nullNet THEN source.net _ b.source.net; IF source.host = Pup.nullHost THEN source.host _ b.source.host; IF source.socket = Pup.nullSocket THEN source.socket _ b.source.socket; last _ in; RETURN[bytes, b.id, b.type]; ENDLOOP; }; SetPupChecksum: PROC [b: PupBuffer.PupBufferPointer] = { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO Endian.HWORD _ @b.byteLength + words; checksumLoc^ _ Checksum.ComputeChecksum[0, words, @b.byteLength]; }; TestPupChecksum: PROC [b: PupBuffer.PupBufferPointer] RETURNS [BOOL] = { words: NAT = PupBuffer.WordsWithoutChecksum[b.byteLength]; checksumLoc: LONG POINTER TO Endian.HWORD _ @b.byteLength + words; hisChecksum: Endian.HWORD _ checksumLoc^; checksum: Endian.HWORD; IF hisChecksum = PupBuffer.noChecksum THEN RETURN[TRUE]; checksum _ Checksum.ComputeChecksum[0, words, @b.byteLength]; RETURN[checksum = hisChecksum]; }; }.