<*+M2EXTENSIONS *> <*-CHECKDIV *> <*-CHECKRANGE *> <*-COVERFLOW *> <*-IOVERFLOW*> <*-GENFRAME*> <*+NOPTRALIAS*> <*-DOREORDER*> <*-GENPTRINIT*> <*CPU="PENTIUM"*> <* IF __GEN_C__ THEN *> <*+COMMENT*> <*-GENDATE*> <*-GENCTYPES*> <*-CHECKNIL *> <*-CHECKINDEX*> <*-CHECKSET*> <*+GENCDIV*> <*+NOOPTIMIZE*> <*-GENSIZE*> <*-ASSERT*> <* ELSE *> <*+CHECKNIL *> <*+CHECKINDEX*> <*+CHECKSET*> <* END *> IMPLEMENTATION MODULE l2; (* Interface Layer2 <-> Host: Call L2Init; Call (about) every 10ms Layer2 Call any Time * not during Layer2 running and its Callback Connect * (start a connect) Disconnect * (stop a connect or close a handle with data) Circuit (join 2 connections back to back) if reconn on, the host must not forget this host handle and continue interaction after reconnect GetAdress (get address) Getudp2info (get axudp extention info from modem) GetChar (get 1 received char of a connection, delete it or keep it) GetStr (get more received chars of a connection) SendStr (send data to a connection) SendRaw (send UI or raw frame) GetStat (get status of a connection) PortStat (portwise statistics) Parm (get or set port parameters) GetMon (get monitor data) Callback: contains A handle from layer2, is pointer to a (hidden) link structure. The host has to use this pointer in any call according to this link. An event number A handle given from host to layer2, a pointer, layer2 looks only if NULL for "the host does not want this event and its link any longer". Events: eCONNECTED: a connection (in or out) is esteblished if (from out) and the host is able to accept set the host-handle to not NULL. The handle will always be included in further callbacks for this link and the host MUST call Disconnect with dealloc TRUE to free this handle. eDISCONNECTED: a link ends, if there is a host-handle the host must call Disconnect with dealloc TRUE to free this handle. eBUSY: outgoing connection got busy (DM) if there is a host-handle the host must call disconnect to free this handle. eTXFREE: a connection is able to accept new data eRXDATA: a connection received new data eMONITOR: monitor frames are in buffer eCONNREQ: a SABM (via digicall first in mycall table) arrived, this is only a message with no action of layer2, no host-handle and so no disconnect for freeing. the host has to find route, make a connect and if connect is esteblished make a "back connection" with Connect connect.typ cFLEXback or cCONNAK. If the destination connection is in same layer2, join the 2 connections with call to Circuit with both reconn off. if destination is away, host has to get data with GetStr and transport it any way and reverse send with SendStr eFLEXDATA: a Frame via digicall (first in mycall table) arrived. this is only a message with no action of layer2, no host-handle and so no disconnect for freeing. retrive data immediately during callback vom adress/asize/dbuf route and resent with SendUI. eLISTEN: not used by Layer2 eIDLE: not used by Layer2 *) (* (C)OE5DXL 1993-2015, GPL3 *) FROM SYSTEM IMPORT ADR, ADDRESS, CARD32, CARD16, INT16, FILL, CAST, MOVE; FROM frameio IMPORT GetFrame, SendFrame, DCD, Sending, crcok, Init, Modempoll; FROM osi IMPORT WrStrLn, WrStr, WrInt, time, ALLOCATE, WrHex; CONST SSIDMASK=SET16{1,2,3,4,6}; DAMARR=SET16{5}; RESP =0; FINAL =1; COMMAND=2; POLL =3; NULL= SET16{15}; DAMAIDLE=SET16{14}; SENDQUES=3; HASHSIZE=16; (*greater than number of ports*) MEMRR=20; MEMTX=20; MEMRX=10; MEMSAMM=20; MEMFRMR=1; MEMLINK=5; TILT=4; IGNORE=8; SHORTINFO=80; IPRETRIES=10; MAGIC=2091827346; LOOPSPERS=100; SECMOD=20000; (* timer base cycle range, max timer1 is half *) TIMERBASE=10; (* timer base / layer2 looptime *) T3OFF=MAX(CARD16); TYPE TXSTATE=(TWAIT, TXDEL, TXTAIL, DCDWAIT, ECHO); AFIELDPO=POINTER TO AFIELD; pLINK=POINTER TO LINK; LINK=RECORD Magic:CARD32; (*magic number*) State, (*0 = host is owner of the record*) mycall, (*0=via, MAX(CARD16)=downlink*) Baud:CARD16; (*downlink baud*) Uplinkport:CARD16; cCONNtyp:CONNTYP; (*normal or link is for UI transfer only*) IsInQue:CARD16; (*link is in this send-que*) SendQuenext, (*sendque-chain*) SendQueprev:pLINK; Hash:CARD16; Hashnext, (*address-hash-chain*) Hashprev:pLINK; Qtime, (*time since forced disc*) T3out, (*Second for next T3 end or T3OFF for T1 running *) damasecond, (*last slice in absolut time*) T1out:CARD16; (*second when T1 will run out*) Timernext, (*timer-chain*) Timerprev:pLINK; highspeed, (*for prefered queing*) damastop, (*no frames until dama-slot*) twoway, (*the other is in INFO state too*) discing, (*disc after send-info-buffer is empty*) rejgot, (*got a REJ*) rejsent, (*sent a REJ*) infostop, (*send-info-buffer was empty*) busyend, (*tell the other that i am not longer busy*) flexback, (* flex backlink say no DM if SABM *) haschains, (* link has chains else simple dealloc *) rembusy:BOOLEAN; (*the other is busy*) busy, (*0=not busy*) retry, (*increments after timeout-polls*) ns, (*send-sequence-number*) lowsent, (*till here info is acknowled*) highsent, (*till here -1 info is sent*) sentnr, (*last physically sent nr*) nr:CARD16; (*receive sequence number*) pid:CHAR; (*PID for this link*) sammler:ARRAY[0..7] OF pDATA; summs: ARRAY[0..7] OF CARD16; Adress: AFIELD; (*adress field + command byte*) AdrLen: CARD16; (*length of adress + command byte*) cp:CARD16; (*send response, final, command or poll*) sendcmd:SET16; sendinfo, (*parallel to ns running info-pointer*) txInfoIn, (*entry pointer to transmit buffer*) txInfoOut:pDATA; (*end of transmit data*) txbuffers:CARD16; (*used send-buffers*) txfull, (*tx buffer full, for message or clearbusy*) modifylast:BOOLEAN; (*last buffer not sent, so modifyable*) nudel:pLINK; (*the downlink, else NIL*) exitch:CARD16; (*switch linked links back to host*) host:ADDRESS; (*to there the messages are sent*) saiddisc:BOOLEAN; (*send only 1 disc message to host*) rxInfoIn, (*entry pointer to receive buffer*) rxInfoOut:pDATA; (*end of receive data*) rxbuffers:CARD16; (*used buffers*) readp:CARD16; (*getchar readpointer*) SABMinfo:pDATA; (*extention, SABM with info*) (*statistics*) SentBytes, AckBytes, RcvdBytes, Started:CARD32; (*for statistics: starttime of link*) udp2info:ARRAY[0..30] OF CHAR; (* axudp2 frame infos from modem *) END; VAR PortL2:ARRAY[1..PORTS] OF RECORD sendstate:TXSTATE; damat1, dama, (*dama slave*) dcd:BOOLEAN; (*all diversity ports set it*) dcdguard, (*ignore dcd if it lasts too long*) txwait, (*set while sending, reset when receiving*) Ticker, (* change synchron to main clock *) QTicker:CARD16; (* change only while not send or dcd *) lookinque:BOOLEAN; (*to decide quick if sendques are empty*) damaloop, T1In, (*timer-chain, each link has always a timer*) T1Out:pLINK; SendQues:ARRAY[0..SENDQUES-1] OF RECORD (*que 0 has highest priority*) SendQueIn, (*chain of links that will send*) SendQueOut:pLINK; END; END; AdrHash:ARRAY[0..HASHSIZE-1] OF pLINK; (*hashtable for calls*) MyCalls:ARRAY[0..MYCALLS] OF CALLTYP; (*mycall[0] is digipeat*) maxrxbuffers, maxtxbuffers, DMTIME, (*save downlinks for sending DM*) QTIME, (*time for discing links with unackn. data*) memory, (*used memory*) DAMAMAXTIME, (*longest time till next slot*) DAMAFASTPOLLS:CARD16; (*slots which have fast intervall*) Time, AbsTime:CARD32; (* seconds counting *) VAR Second,monbufs,maxmonbufs,Minute5,ticker5:CARD16; Freechain:POINTER TO ADDRESS; DNot: ARRAY[0..8] OF CALLTYP; mfrom:CALLTYP; monBufOut,monBufIn:pLINK; hosts:CARD16; tick10:CARD32; Event:CALLBACKPROC; PROCEDURE Min(a,b:CARD16):CARD16; BEGIN IF a>=b THEN RETURN b ELSE RETURN a END END Min; PROCEDURE Alloc(VAR p:ADDRESS; reserv:CARD16):BOOLEAN; BEGIN IF (memory<=reserv) OR (Freechain=NIL) THEN RETURN FALSE END; p:=Freechain; Freechain:=Freechain^; DEC(memory); RETURN TRUE; END Alloc; PROCEDURE Dealloc(VAR p:ADDRESS); VAR unnedig:POINTER TO ADDRESS; BEGIN --WrInt(memory, 6); WrStrLn(" before dealloc "); IF p=NIL THEN RETURN END; unnedig:=p; unnedig^:=Freechain; Freechain:=p; INC(memory); p:=NIL; END Dealloc; PROCEDURE Unlock; BEGIN IF LostINT>0 THEN L2Lock:=TRUE; Layer2 END; L2Lock:=FALSE; END Unlock; PROCEDURE Minut5():CARD16; BEGIN RETURN (Time DIV 300) MOD (HIGH(PortSt[0].Flows)+1) END Minut5; PROCEDURE CheckParms(port:CARD16); BEGIN WITH Parms[port] DO IF MaxFrames>7 THEN MaxFrames:=7 END; IF SendPort>PORTS THEN SendPort:=port END; IF Diversity>PORTS THEN Diversity:=0 END; IF BaudSwBytes>100 THEN BaudSwBytes:=100 END; IF T1=0 THEN T1:=1 ELSIF T1>=SECMOD DIV 2 THEN T1:=SECMOD DIV 2 END; END; END CheckParms; PROCEDURE Parm(p:pPARMS); PROCEDURE call(VAR list:ARRAY OF CALLTYP); VAR i,j,k:CARD16; BEGIN k:=0; FOR i:=0 TO HIGH(list)-1 DO FOR j:=0 TO SIZE(CALLTYP)-1 DO IF p^.test THEN p^.str^[k]:=CHR(ORD(list[i][j]) DIV 2) ELSE list[i][j]:=CHR(ORD(p^.str^[k])*2) END; INC(k); END; END; IF p^.test THEN p^.str^[k]:=0C END; END call; PROCEDURE value(VAR x:CARD16); BEGIN IF p^.test THEN p^.val:=x ELSE x:=p^.val END END value; PROCEDURE bool(VAR b:BOOLEAN); BEGIN IF p^.test THEN p^.val:=ORD(b) ELSE b:=p^.val<>0 END END bool; VAR i:CARD16; BEGIN WITH p^ DO IF parm<=6 THEN port:=1 ELSIF (port=0) OR (port>HIGH(Parms)) THEN RETURN END; WITH Parms[port] DO CASE parm OF 0:call(MyCalls) (* set all mycalls, first is digi *) |1:call(DNot) |2:MOVE(str,ADR(mfrom),SIZE(mfrom)) |3:value(DMTIME) |4:value(QTIME) |5:value(maxrxbuffers) |6:value(maxtxbuffers) |7:bool(Digipeat) |8:bool(HalfDuplex) |9:bool(DamaMaster) |10:bool(monitor); |11:bool(passall) |12:bool(Echo) |13:IF test THEN MOVE(ADR(SendBauds),str,SIZE(SendBauds)) ELSE FOR i:=0 TO HIGH(SendBauds) DO IF (str^[i]>="0") & (str^[i]<="7") THEN SendBauds[i]:=str^[i]; END; END; END; ELSE DEC(parm,14); IF parm<=HIGH(card) THEN value(card[parm]); CheckParms(port); END; END; END; END; END Parm; PROCEDURE Getudp2info(l:pLINK; VAR s:ARRAY OF CHAR); VAR i, j:CARD16; BEGIN s[0]:=0C; IF (l<>NIL) & (l^.udp2info[0]<>0C) & (l^.udp2info[1]<>0C) THEN j:=0; i:=2; WHILE (j0C) DO s[j]:=l^.udp2info[i]; INC(j); INC(i) END; s[j]:=0C; END; END Getudp2info; PROCEDURE GetAdress(l:pLINK; p:pGETADRESS); VAR i,len:CARD16; BEGIN WITH p^ DO WITH l^ DO port:=Uplinkport; my:=mycall; len:=Min(HIGH(adress),AdrLen-1); FOR i:=0 TO len DO adress[i]:=CHR(ORD(Adress[i]) DIV 2) END; adress[len]:=0C; IF (rxInfoOut<>NIL) & (rxInfoOut^.len<>0) THEN cpid:=ORD(rxInfoOut^.info[0]); ELSE cpid:=MAX(CARD16) END; END; END; END GetAdress; PROCEDURE GetStat(p:pGETSTAT); VAR i,v:CARD16; BEGIN L2Lock:=TRUE; WITH p^ DO IF l=NIL THEN LOOP v:=n DIV (1+HIGH(AdrHash)); l:=AdrHash[n MOD (1+HIGH(AdrHash))]; i:=0; WHILE (l<>NIL) & (iNIL THEN WITH l^ DO st:=State; port:=Uplinkport; txbu:=txbuffers; my:=mycall; ret:=retry; bsy:=busy<>0; remb:=rembusy; nudl:=nudel; FOR i:=0 TO Min(HIGH(adress),AdrLen) DO adress[i]:=CHR(ORD(Adress[i]) DIV 2) END; IF AdrLen<=HIGH(adress) THEN adress[AdrLen]:=0C END; sent:=SentBytes; ack:=AckBytes; rcvd:=RcvdBytes; since:=Time-Started; END; END; Unlock; END; END GetStat; PROCEDURE PortStat(p:pPORTSTAT):BOOLEAN; VAR i,j:CARD16; BEGIN WITH p^ DO FILL(ADR(sums), 0C, SIZE(sums)); mem:=memory; time:=Time; IF (port>0) & (port<=PORTS) & (Parms[port].Diversity<>0) THEN WITH PortSt[port] DO j:=Minut5(); (*aktual intervall*) FOR i:=1 TO minits5 DO IF j=0 THEN j:=HIGH(Flows) ELSE DEC(j) END; (*prev intervall*) WITH Flows[j] DO INC(sums[0],Igot); INC(sums[1],Isent); INC(sums[2],Iack); INC(sums[3],Bytegot); INC(sums[4],Bytesent); END; END; sums[5]:=Links; sums[6]:=MaxLinks; END; ELSE RETURN FALSE END; RETURN TRUE; END; END PortStat; PROCEDURE ToSendQue(l:pLINK; why:SET16; c:CARD16); BEGIN IF l<>NIL THEN (*for safety*) WITH l^ DO sendcmd:=why; cp:=c; PortL2[Uplinkport].lookinque:=TRUE; (*only to find out quick if what in a que*) IF (IsInQue>=SENDQUES) & NOT damastop THEN IsInQue:=0; IF mycall=0 THEN IsInQue:=2 ELSIF NOT highspeed & (why=NULL) & ((sendinfo<>NIL) & (sendinfo^.len>SHORTINFO) OR (txInfoOut<>NIL) & (txInfoOut^.len>SHORTINFO)) THEN IsInQue:=1; END; WITH PortL2[Uplinkport].SendQues[IsInQue] DO IF SendQueOut=NIL THEN SendQueOut:=l; ELSE l^.SendQueprev:=SendQueIn; SendQueIn^.SendQuenext:=l END; SendQueIn:=l; END; END; END; END; END ToSendQue; PROCEDURE GetSendInfo(l:pLINK):pDATA; BEGIN WITH l^ DO IF infostop OR rembusy OR ((((ns+9)-lowsent) MOD 8>=Parms[Uplinkport].MaxFrames) OR rejgot) & (sendinfo<>NIL) THEN RETURN NIL END; IF sendinfo=NIL THEN RETURN txInfoOut END; RETURN sendinfo^.next; END; END GetSendInfo; PROCEDURE Appendtx(link:pLINK; data:pDATA); BEGIN WITH link^ DO IF (data^.info[0]=360C) & (txInfoOut<>NIL) & (data^.len>1) (*F0 PID*) & modifylast & (txInfoIn^.len+data^.len<=258) THEN (*append to previous buffer*) MOVE(ADR(data^.info[1]),ADR(txInfoIn^.info[txInfoIn^.len]),data^.len-1); INC(txInfoIn^.len,data^.len-1); Dealloc(data); RETURN END; data^.next:=NIL; IF txInfoOut=NIL THEN txInfoOut:=data ELSE txInfoIn^.next:=data END; txInfoIn:=data; INC(txbuffers); modifylast:=TRUE; (*this buffer was not sent till now*) IF (IsInQue>=SENDQUES) & (GetSendInfo(link)<>NIL) THEN PortL2[Uplinkport].damaloop:=PortL2[Uplinkport].T1Out; (*start a quick roundtrip*) ToSendQue(link,NULL,COMMAND); (*schedule link to send info*) END; END; END Appendtx; PROCEDURE Circuit(VAR l1,l2:pLINK; reconn1, reconn2:BOOLEAN; exitchar:CARD16); PROCEDURE CopyI(from,to:pLINK; reconn:BOOLEAN); VAR ip:pDATA; BEGIN WITH from^ DO (*copy rest of rx-info to sender*) IF rxInfoOut<>NIL THEN WITH rxInfoOut^ DO IF (readp<>0) & (len>readp) THEN (*shift info to begin of buffer*) MOVE(ADR(info[readp+1]),ADR(info[1]),len-readp-1); DEC(len,readp); readp:=0; END; END; REPEAT ip:=rxInfoOut^.next; Appendtx(to,rxInfoOut); DEC(rxbuffers); rxInfoOut:=ip; UNTIL rxInfoOut=NIL; END; nudel:=to; IF NOT reconn THEN DEC(hosts); Magic:=0; host:=NIL END; END; END CopyI; BEGIN L2Lock:=TRUE; IF (l1^.Magic=MAGIC) & (l2^.Magic=MAGIC) & (l1^.State<>0) & (l2^.State<>0) THEN CopyI(l1,l2,reconn1); CopyI(l2,l1,reconn2); l1^.exitch:=exitchar; END; IF NOT reconn1 THEN l1:=NIL END; IF NOT reconn2 THEN l2:=NIL END; Unlock; END Circuit; PROCEDURE SendStr(l:pLINK; size:CARD16; buf:pSTRING):BOOLEAN; VAR b:pDATA; BEGIN L2Lock:=TRUE; WITH l^ DO IF (Magic=MAGIC) & (State>=2) & (size<=256) THEN IF size=0 THEN RETURN NOT txfull END; (* check if txbuffer full *) IF (txInfoOut<>NIL) & modifylast & (txInfoIn^.len+size<=256) & (cCONNtyp=cNORMAL) THEN -- IF (txInfoOut<>NIL) & modifylast & (txInfoIn^.len+size<=257) & (cCONNtyp=cNORMAL) THEN MOVE(buf,ADR(txInfoIn^.info[txInfoIn^.len]),size); (*append to previous buffer*) INC(txInfoIn^.len,size); ELSIF (txbuffers<=maxtxbuffers) & Alloc(b,MEMTX) THEN b^.info[0]:=pid; MOVE(buf,ADR(b^.info[1]),size); b^.len:=size+1; Appendtx(l,b); ELSE txfull:=TRUE; Unlock; RETURN FALSE END; END; END; Unlock; RETURN TRUE; END SendStr; PROCEDURE RemoveTimer(l:pLINK); BEGIN WITH PortL2[l^.Uplinkport] DO IF T1Out=T1In THEN T1Out:=NIL ELSIF l=T1Out THEN T1Out:=l^.Timernext ELSIF l=T1In THEN T1In:=l^.Timerprev ELSE l^.Timerprev^.Timernext:=l^.Timernext; l^.Timernext^.Timerprev:=l^.Timerprev; END; END; END RemoveTimer; PROCEDURE StartTimer(l:pLINK); BEGIN --WrStrLn(""); WITH l^ DO WITH PortL2[Uplinkport] DO T1out:=(QTicker+Parms[Uplinkport].T1-1) MOD SECMOD; (*next timeout*) --WrInt(T1out, 5); WrStr(":tout1 "); IF T1Out=NIL THEN T1Out:=l; (*que was empty*) ELSE IF (T1out-T1In^.T1out) MOD SECMOD>SECMOD DIV 2 THEN T1out:=T1In^.T1out; --WrInt(T1out, 5); WrStr(":tout2 "); END; Timerprev:=T1In; T1In^.Timernext:=l; END; (*append to que*) T1In:=l; END; END; END StartTimer; PROCEDURE MakePoll(linkp:pLINK); VAR cmd:SET16; r:CARD16; BEGIN WITH linkp^ DO IF (State>=2) & (cCONNtyp=cNORMAL) THEN r:=Parms[Uplinkport].Retries; IF (State<>5) OR NOT twoway THEN r:=r DIV 4 + 1 END; INC(retry); IF retry>r THEN --WrStrLn("state1"); State:=1; END; (*retried out*) cmd:=DISC; IF State=5 THEN cmd:=RR ELSIF State=2 THEN cmd:=SABM END; ToSendQue(linkp,cmd,POLL); ELSIF (State=2) & ((cCONNtyp=cFLEXback) OR (cCONNtyp=cCONNAK)) THEN State:=5; cmd:=UA; cCONNtyp:=cNORMAL; ToSendQue(linkp,cmd,FINAL); ELSIF (State=2) & (cCONNtyp=cFLEXbusy) THEN --WrStrLn("pollflexbusy"); State:=1; cmd:=DM; ToSendQue(linkp,cmd,FINAL); END; END; END MakePoll; PROCEDURE ClearBusy(l:pLINK); BEGIN WITH l^ DO IF (busy<>0) & (State=5) THEN busy:=0; busyend:=TRUE; (*until the other knows*) ToSendQue(l,RR,RESP); RemoveTimer(l); T3out:=(Second+1) MOD SECMOD; (*start minimal T3*) StartTimer(l); END; END; END ClearBusy; PROCEDURE Disc(l:pLINK); BEGIN WITH l^ DO IF cCONNtyp<>cNORMAL THEN State:=1 ELSIF discing OR (State<>5) THEN (*do it quick*) IF State>=2 THEN ToSendQue(l,DISC,POLL) END; State:=1; ELSE IF txInfoOut=NIL THEN State:=4; (*disc state*) retry:=0; RemoveTimer(l); T3out:=T3OFF; (*start T1*) StartTimer(l); MakePoll(l); (*DISC ...*) ELSE discing:=TRUE; Qtime:=0 END; (*first send all infos*) END; END; END Disc; PROCEDURE Disconnect(VAR l:pLINK; dealloc:BOOLEAN); VAR ip:pDATA; BEGIN --WrInt(memory, 6); WrInt(hosts, 6); WrInt(l^.State, 6); WrInt(ORD(dealloc),1); WrStrLn(" flexdisc "); L2Lock:=TRUE; IF l^.Magic=MAGIC THEN IF l^.State>=2 THEN Disc(l) END; IF dealloc THEN (*host gives link back to l2*) DEC(hosts); WITH l^ DO WHILE rxInfoOut<>NIL DO (*deallocate rx-info*) ip:=rxInfoOut^.next; Dealloc(rxInfoOut); rxInfoOut:=ip; END; Magic:=0; --WrStr("L2: state:"); WrInt(State, 5); WrStrLn(""); IF State=0 THEN Dealloc(l); --WrStrLn("l2:dealloc"); ELSE host:=NIL END; END; l:=NIL; END; END; Unlock; END Disconnect; PROCEDURE Cut(l:pLINK); BEGIN WITH l^ DO IF nudel<>NIL THEN nudel^.nudel:=NIL; (*remove pointer to me*) IF nudel^.host=NIL THEN Disc(nudel) ELSE ClearBusy(nudel) END; nudel:=NIL; END; END; END Cut; PROCEDURE DisposeLink(l:pLINK; immediate:BOOLEAN); VAR ip:pDATA; i:CARD16; BEGIN --WrStrLn("L2: disposelink"); IF l<>NIL THEN (*for safety*) (*throw away bufferd data*) WITH l^ DO FOR i:=0 TO HIGH(sammler) DO Dealloc(sammler[i]) END; WHILE txInfoOut<>NIL DO ip:=txInfoOut^.next; Dealloc(txInfoOut); txInfoOut:=ip; END; Dealloc(SABMinfo); (*send the (sad or lucky) message to host*) IF (host<>NIL) & NOT saiddisc THEN (*do it only once*) --WrStrLn(" say disc/busy"); IF State=2 THEN Event(host, l, eBUSY) ELSE Event(host, l, eDISCONNECTED) END; saiddisc:=TRUE; END; Cut(l); (*disconnect nudelport*) END; --WrStr("dispo:");WrInt(ORD(immediate),2); WrInt(l^.mycall,2);WrStrLn(""); -- IF immediate OR (l^.mycall<>MAX(CARD16)) THEN (*not a downlink*) IF immediate OR NOT l^.flexback THEN (*not a downlink*) (*remove link from sendque*) WITH l^ DO IF IsInQueNIL THEN Hashnext^.Hashprev:=Hashprev END; END; END; RemoveTimer(l); DEC(PortSt[l^.Uplinkport].Links); (*now the link has no relation with L2, so give it to host or deallocate*) IF l^.host<>NIL THEN --WrStrLn("state12"); l^.State:=0; ELSE --WrStrLn("disposedealloc"); l^.Magic:=0; Dealloc(l); END; END; --WrStr("disposelink mem=");WrInt(memory,1); --WrStr(" hosts=");WrInt(hosts,1); --WrStrLn(""); (*for tests*) END; END DisposeLink; PROCEDURE TestMem(l:pLINK); BEGIN IF memory>=MEMRR THEN WITH l^ DO IF nudel<>NIL THEN IF txbuffers+TILT < memory DIV 4 THEN ClearBusy(nudel); txfull:=FALSE; END; ELSIF (host<>NIL) & (txbuffers<=maxtxbuffers) THEN Event(host, l, eTXFREE); txfull:=FALSE; END; END; END; END TestMem; PROCEDURE StartT3(l:pLINK); BEGIN l^.T3out:=(Second + Parms[l^.Uplinkport].T3) MOD SECMOD END StartT3; PROCEDURE T3resttime(l:pLINK):CARD16; VAR th:INT16; BEGIN IF l^.T3out=T3OFF THEN RETURN 0 END; th:=VAL(INT16, l^.T3out)-VAL(INT16, Second); IF th<0 THEN INC(th, SECMOD) END; IF th>SECMOD DIV 2 THEN RETURN 0 END; (* T3 ran out *) RETURN th END T3resttime; PROCEDURE Timeout(l:pLINK); BEGIN WITH l^ DO --WrInt(Qtime, 6);WrInt(State, 6);WrInt(memory, 6); WrStr(":qtime/state/mem "); IF (State<=1) & (Qtime>DMTIME) THEN DisposeLink(l,TRUE); RETURN END; WITH PortL2[Uplinkport] DO (*remove timer from end of que*) IF T1Out=T1In THEN T1Out:=NIL ELSE T1Out:=T1Out^.Timernext END; END; StartTimer(l); IF State>=2 THEN IF T3out<>T3OFF THEN IF discing & (State=5) THEN IF QtimeDAMAIDLE THEN (*there is a frame to be sent*) ToSendQue(l,sendcmd,cp); (*damastop is over, so its qued*) damasecond:=Second; (*last slot in absolute time*) RETURN END; IF (State=5) & (T3out<>T3OFF) (*& damabit*) (*its a dama slot*) & ((T3resttime(l)<=DAMAFASTPOLLS) (*activ link*) OR ((PortL2[Uplinkport].QTicker+1) MOD SECMOD=Second) (*port has still idle times*) OR (((Second+SECMOD)-damasecond) MOD SECMOD>=DAMAMAXTIME)) THEN ToSendQue(l,RR,COMMAND); (*enable the other to send now*) damasecond:=Second; (*store last absolute slot time*) END; END; IF txfull THEN TestMem(l) END; IF (T3out=T3OFF) OR (State<=4) THEN MakePoll(l) END; END; END Timeout; PROCEDURE GetChar(l:pLINK; delete:BOOLEAN; VAR char:CHAR):BOOLEAN; VAR ip:pDATA; ok:BOOLEAN; BEGIN WITH l^ DO WHILE rxInfoOut<>NIL DO ok:=rxInfoOut^.len>1; char:=rxInfoOut^.info[readp+1]; IF NOT delete & ok THEN RETURN TRUE END; IF Magic<>MAGIC THEN RETURN FALSE END; INC(readp); IF readp+1=MEMRR) THEN ClearBusy(l) END; Unlock; IF ok THEN RETURN TRUE END; END; END; RETURN FALSE; END GetChar; PROCEDURE GetStr(l:pLINK; size:CARD16; buf:pSTRING):CARD16; VAR ip:pDATA; retsize,n:CARD16; BEGIN retsize:=0; WITH l^ DO IF Magic=MAGIC THEN WHILE (retsizeNIL) DO n:=rxInfoOut^.len-readp; IF n>1 THEN DEC(n); (*pid*) IF n+retsize>size THEN n:=size-retsize END; MOVE(ADR(rxInfoOut^.info[readp+1]),ADR(buf^[retsize]),n); INC(retsize,n); INC(readp,n); END; IF readp+1>=rxInfoOut^.len THEN readp:=0; L2Lock:=TRUE; ip:=rxInfoOut^.next; Dealloc(rxInfoOut); rxInfoOut:=ip; DEC(rxbuffers); IF (rxbuffers<=maxrxbuffers) & (memory>=MEMRR) THEN ClearBusy(l) END; Unlock; END; END; END; END; RETURN retsize END GetStr; PROCEDURE CP():CARD16; BEGIN RETURN 2*ORD((adress[6])>=200C) + ORD(4 IN CAST(SET16, ORD(adress[asize-1]))); END CP; (*p/f bit*) (*command bit*) PROCEDURE Uselink(linkp:pLINK); CONST SUMLEN=5; VAR n,sum,i,compoll: CARD16; ip : pDATA; j : INTEGER; cmd : SET16; slave, wasbusy : BOOLEAN; PROCEDURE Appendrx(b:pDATA); BEGIN WITH linkp^ DO INC(PortSt[Uplinkport].Flows[Minute5].Igot); RcvdBytes:=RcvdBytes+b^.len; IF (ORD(b^.info[1])=exitch) & (host<>NIL) THEN Cut(linkp) END; IF nudel<>NIL THEN Appendtx(nudel,b); IF nudel^.txbuffers+TILT > memory DIV 4 THEN INC(busy); nudel^.txfull:=TRUE END; ELSIF host<>NIL THEN b^.next:=NIL; IF rxInfoOut=NIL THEN rxInfoOut:=b ELSE rxInfoIn^.next:=b END; rxInfoIn:=b; INC(rxbuffers); IF rxbuffers>=maxrxbuffers THEN INC(busy) END; IF rxbuffers=1 THEN Event(host, linkp, eRXDATA) END; ELSE Dealloc(b) END; END; END Appendrx; PROCEDURE Ack(n:CARD16; resend:BOOLEAN):BOOLEAN; VAR dp:pDATA; BEGIN (*ackowlege my sent info*) WITH linkp^ DO IF (((highsent+8)-lowsent) MOD 8 < ((n+8)-lowsent) MOD 8) THEN ToSendQue(linkp,FRMR,RESP); (*acknowledges never sended info*) RETURN FALSE END; twoway:=TRUE; WHILE lowsent<>n DO (*delete acknowledged from sendbuffer*) IF txInfoOut<>NIL THEN (*only for safety*) IF ns=lowsent THEN resend:=TRUE END; dp:=txInfoOut^.next; AckBytes:=AckBytes+txInfoOut^.len; INC(PortSt[Uplinkport].Flows[Minute5].Iack); Dealloc(txInfoOut); txInfoOut:=dp; DEC(txbuffers); ELSE RETURN FALSE END; (*dramatic internal error*) lowsent:=(lowsent+1) MOD 8; END; IF resend THEN (*all sent acknolled or resend info*) ns:=n; sendinfo:=NIL; retry:=0; END; IF resend OR rembusy THEN infostop:=FALSE; StartT3(linkp); (*restart T3*) END; IF (IsInQue>=SENDQUES) & (GetSendInfo(linkp)<>NIL) THEN ToSendQue(linkp,NULL,COMMAND); (*schedule for sending next info*) PortL2[Uplinkport].damaloop:=PortL2[Uplinkport].T1Out; (*start quick roundtrip*) END; IF txfull THEN TestMem(linkp) END; IF discing & (txInfoOut=NIL) THEN State:=4; MakePoll(linkp); (*DISC ...*) END; END; RETURN TRUE; END Ack; BEGIN (*analyse (addressed to me) frames and reply*) --WrStrLn("uselink"); WITH linkp^ DO slave:=NOT Parms[Uplinkport].DamaMaster & (CAST(SET16, ORD(adress[13]))*DAMARR=SET16{}); cmd:=SET16{0,1,2,3,5,6,7}*CAST(SET16, ORD(adress[asize-1])); (*command field without p/f-bit*) compoll:=CP(); (*response, final, command or poll*) IF cCONNtyp=cNORMAL THEN IF T3out<>T3OFF THEN StartT3(linkp) END; (*the other is still alive *) IF cmd * SET16{0} = SET16{} THEN (*I-frame *) IF State=5 THEN (*info transfer *) n:=(VAL(CARD16, cmd) DIV 2) MOD 8; (*send sequence number *) IF Ack((VAL(CARD16, cmd) DIV 32) MOD 8, slave) THEN (*acknols my frames*) IF State=5 THEN (*maybe disc after all acknolled *) i:=dbuf^.len; IF i>SUMLEN THEN i:=SUMLEN END;(*limit sammler-checksum length*) sum:=0; FOR j:=0 TO INTEGER(i)-1 DO INC(sum,ORD(dbuf^.info[j])) END; IF (n=nr) & (busy sentnr THEN (*check windowsize *) summs[n]:=sum; (*mark it as old *) ip:=sammler[n]; IF (ip<>NIL) OR Alloc(ip,MEMRX) THEN (*do not say rnr with an*) Appendrx(dbuf); (*empty downlink buffer,*) dbuf:=ip; (*noone would clear this*) sammler[n]:=NIL; LOOP nr:=(nr+1) MOD 8; IF sammler[nr]=NIL THEN (*no fitting frames in sammler*) rejsent:=sammler[(nr+1) MOD 8]<>NIL;(*a hole in sammler?*) EXIT (*so request only 1 I-frame by REJ if 1 is missing*) END; Appendrx(sammler[nr]);(*frame from sammler is in sequence*) sammler[nr]:=NIL; END; END; ELSE State:=1; ToSendQue(linkp,FRMR,RESP) END; (*got 8 frames!*) ELSE IF summs[n]<>sum THEN (*frame is from aktual window*) summs[n]:=sum; (*so put it into sammler *) ip:=sammler[n]; IF (ip<>NIL) OR Alloc(ip,MEMSAMM) THEN sammler[n]:=dbuf; dbuf:=ip END; END; rejsent:=(compoll<>POLL); (*say not rej on i-poll *) END; ToSendQue(linkp,RR,ORD(compoll=POLL)); (*make final on poll *) END; END; ELSIF (compoll=POLL) & (State<>2) THEN --WrStrLn("state13"); State:=1; ToSendQue(linkp,DM,FINAL); END; ELSIF cmd * SET16{0,1} = RR THEN (*R?R-Frame, REJ with RNR is allowed*) wasbusy:=rembusy; rembusy:=2 IN cmd; (*remote busy *) rejgot:=3 IN cmd; (*REJ *) IF compoll=POLL THEN busyend:=FALSE END;(*the other polls till he knows*) (* IF wasbusy & NOT rembusy THEN T3out:=T3OFF END; restart T1 *) IF (State<>5) OR Ack((VAL(CARD16, cmd) DIV 32) MOD 8, slave OR (compoll=FINAL) OR rejgot OR wasbusy & NOT rembusy) THEN IF compoll=POLL THEN (*R?R.Poll *) IF State=5 THEN ToSendQue(linkp,RR,FINAL); ELSIF State<>2 THEN --WrStrLn("state14"); State:=1; (*the other is polling*) ToSendQue(linkp,DM,FINAL); END; END; END; ELSIF (cmd=SABM) & (compoll=POLL) THEN (*only SABM.Poll accepted*) Dealloc(SABMinfo); IF (State<>2) & (mycall=MAX(CARD16)) & twoway THEN (*mycall=MAX is a downlink*) --WrStrLn("state15"); State:=1; IF flexback THEN DisposeLink(linkp, TRUE); ELSE ToSendQue(linkp,DM,FINAL) END; (*the other tries a linkreset*) ELSIF (State=2) OR (State=1) & Alloc(ip,0) THEN IF host<>NIL THEN DEC(hosts) END; Event(host, linkp, eCONNECTED); IF host<>NIL THEN INC(hosts) END; IF State=1 THEN (*connect comes from outside*) IF (host<>NIL) & (dbuf^.len<>0) THEN Appendrx(dbuf); dbuf:=ip ELSE Dealloc(ip); END; END; IF host<>NIL THEN (*host accepts that connect*) State:=5; Magic:=MAGIC; ToSendQue(linkp,UA,FINAL); ELSE --WrStrLn("state16"); State:=1; ToSendQue(linkp,DM,FINAL); (*got no host, say busy or nothing if host needs time *) END; ELSIF (State<=4) OR twoway THEN State:=1; (*linkreset, wait for new link. this SABM is lost*) --WrStrLn("dispose3"); DisposeLink(linkp,FALSE); (*a downlink will say DM*) ELSE ToSendQue(linkp,UA,FINAL); RemoveTimer(linkp); T3out:=T3OFF; (*restart T1*) StartTimer(linkp); (*test if the other has a receiver*) infostop:=TRUE; (*before resending the first I-frames*) END; ELSIF (cmd=UA) & (compoll=FINAL) THEN (*only UA.Final because i send SABM.P*) Dealloc(SABMinfo); IF State=2 THEN IF host<>NIL THEN DEC(hosts) END; Event(host, linkp, eCONNECTED); IF host<>NIL THEN (*host is still alive*) INC(hosts); State:=5; twoway:=TRUE; (*both are in state 5 now*) Magic:=MAGIC; (*...a magic feeling*) retry:=0; IF txInfoOut=NIL THEN StartT3(linkp) ELSE T3out:=T3OFF END; (*stop T1 if no info to send*) ELSE --WrStrLn("state17"); State:=1; END; (*host died after it started Conn*) ELSIF State<>5 THEN State:=1; --WrStrLn("dispose4"); DisposeLink(linkp,twoway); (*UA in twoway must be a reply of DISC*) END; ELSIF (cmd=DISC) OR (cmd=FRMR) THEN IF State<>2 THEN (*ignor discs while connecting*) --WrStrLn("state18"); State:=1; ToSendQue(linkp,DM,FINAL); END; (*else ignore DISCs and go ahead with SABM*) ELSIF cmd=DM THEN IF compoll=FINAL THEN --WrStrLn("dispose5"); DisposeLink(linkp,TRUE); END; (*ignore not final*) ELSIF cmd=UI THEN IF (compoll=POLL) & (State=1) THEN ToSendQue(linkp,DM,FINAL) END; ELSE ToSendQue(linkp,FRMR,RESP) END; (*unknown frame *) (* ELSIF cmd=UI THEN IF (State=5) & (dbuf^.len#0) & ((cCONNtyp=cUI) OR (cCONNtyp=cUIpoll)) THEN IF Alloc(ip,MEMRR) THEN Appendrx(dbuf); dbuf:=ip; END; END; *) ELSIF (cCONNtyp=cFLEXback) OR (cCONNtyp=cCONNAK) OR (cCONNtyp=cFLEXbusy) THEN --WrStrLn("uselink back"); IF (cmd=SABM) & (compoll=POLL) THEN (*only SABM.Poll accepted*) Dealloc(SABMinfo); --WrStrLn("dispose15"); DisposeLink(linkp,TRUE); ELSE --WrStrLn("state1fb"); State:=1; ToSendQue(linkp,DM,FINAL); END; END; IF slave THEN PortL2[Uplinkport].dama:=TRUE; IF (cmd*SET16{0}<>SET16{}) OR (compoll=POLL) THEN (*poll or not info*) PortL2[Uplinkport].dcd:=FALSE; PortL2[Uplinkport].damat1:=TRUE; END; END; END; END Uselink; PROCEDURE Mon(port:CARD16; VAR adr:AFIELD; alen:CARD16; info:pDATA); CONST WILD=124C; VAR i:CARD16; lp:pLINK; BEGIN IF mfrom[0]<>0C THEN i:=0; WHILE (i<=5) & ((mfrom[i]=WILD) OR (mfrom[i]=adr[i])) DO INC(i) END; IF i<6 THEN FOR i:=0 TO 5 DO IF (mfrom[i]<>WILD) & (mfrom[i]<>adr[i+7]) THEN RETURN END; END; END; END; IF (monbufs=HIGH(udp2info)) OR (i>=HIGH(udp2buf)) OR (udp2buf[i]=0C); udp2info[i]:=0C; IF (info<>NIL) & Alloc(rxInfoOut,0) THEN rxInfoOut^:=info^; rxInfoOut^.next:=NIL; rxbuffers:=1; END; Hashnext:=NIL; IF monBufOut=NIL THEN monBufOut:=lp ELSE monBufIn^.Hashnext:=lp END; monBufIn:=lp; INC(monbufs); END; END; END Mon; PROCEDURE GetMon():pLINK; VAR l:pLINK; BEGIN L2Lock:=TRUE; l:=monBufOut; IF monBufOut<>NIL THEN monBufOut:=monBufOut^.Hashnext; DEC(monbufs); INC(hosts); END; Unlock; RETURN l; END GetMon; PROCEDURE MH(port:CARD16; VAR adr:AFIELD; alen:CARD16); BEGIN END MH; PROCEDURE SendBufferd(port,baud:CARD16; cmd:SET16); (*send a frame with no link*) VAR linkp:pLINK; BEGIN (*succeeds only if there is enough memory*) IF Alloc(linkp,MEMLINK+1) THEN FILL(linkp, 0C, SIZE(linkp^)); WITH linkp^ DO IsInQue:=SENDQUES; mycall:=0; (*dispose link after sending*) Uplinkport:=port; State:=1; Baud:=baud; damastop:=FALSE; Adress:=adress; (*useing static address field*) AdrLen:=asize; IF dbuf^.len<>0 THEN sendinfo:=dbuf; IF NOT Alloc(dbuf,MEMLINK) THEN Dealloc(linkp) END; (*so forget it all*) ELSE sendinfo:=NIL END; IF linkp<>NIL THEN ToSendQue(linkp,cmd,POLL) END; END; END; END SendBufferd; PROCEDURE SendRaw(p:pSENDUI); VAR pd:CARD16; BEGIN L2Lock:=TRUE; WITH p^ DO IF (port>0) & (port<=PORTS) & (Parms[port].Diversity>0) THEN asize:=0; WHILE (asize0C) DO adress[asize]:=CHR(ORD(path^[asize])*2+ORD(path^[asize+1]=0C)); INC(asize); END; adress[asize]:=cmd; INC(asize); WITH dbuf^ DO len:=datalen; IF len>SIZE(info) THEN len:=SIZE(info) END; MOVE(data, ADR(info), len); END; SendBufferd(port, baud, NULL); END; END; Unlock; END SendRaw; PROCEDURE Initlink(VAR linkp:pLINK; hash,Uport,baud:CARD16):BOOLEAN; BEGIN IF Alloc(linkp,MEMLINK+1) THEN FILL(linkp, 0C, SIZE(linkp^)); WITH PortSt[Uport] DO (*statistics*) INC(Links); IF Links>MaxLinks THEN MaxLinks:=Links END; END; WITH linkp^ DO Hash:=hash; IF AdrHash[Hash]=NIL THEN Hashnext:=NIL ELSE (*append link to hashque*) Hashnext:=AdrHash[Hash]; AdrHash[Hash]^.Hashprev:=linkp; END; AdrHash[Hash]:=linkp; Baud:=baud; IsInQue:=SENDQUES; (*not in sendque*) Uplinkport:=Uport; pid:=360C; Started:=Time; T3out:=T3OFF; (*start T1*) StartTimer(linkp); (*each link has a timer till its disposed*) haschains:=TRUE; END; RETURN TRUE; END; RETURN FALSE; END Initlink; PROCEDURE Hashf(VAR hash:CARD16; port:CARD16); VAR i:CARD16; BEGIN hash:=0; FOR i:=0 TO 5 DO INC(hash,ORD(adress[i])+ORD(adress[i+7])) END;(*hash function*) hash:=(hash DIV 2 + port) MOD (1+HIGH(AdrHash)); END Hashf; PROCEDURE CallComp(p:CARD16; adr:AFIELDPO; pp:CARD16):BOOLEAN; VAR i:CARD16; BEGIN FOR i:=5 TO 0 BY -1 DO IF adress[p+i]<>adr^[pp+i] THEN RETURN FALSE END END; RETURN CAST(SET16, ORD(adress[p+6]))*SSIDMASK=CAST(SET16, ORD(adr^[pp+6]))*SSIDMASK; END CallComp; PROCEDURE Connect(mp:ADDRESS; p:pCONNECT):pLINK; (*NIL if no link*) VAR linkp:pLINK; hash:CARD16; sabmi:pDATA; BEGIN WITH p^ DO IF (port=0) OR (port>PORTS) OR (Parms[port].Diversity=0) THEN RETURN NIL END; L2Lock:=TRUE; asize:=0; (*uses static adress buffer, so not reentrant*) WHILE (asize0C) DO adress[asize]:=CHR(ORD(l2adr^[asize])*2+ORD(l2adr^[asize+1]=0C)); INC(asize); END; INC(asize); Hashf(hash,Parms[port].Diversity); linkp:=AdrHash[hash]; LOOP (*look if a link from-to same calls exists*) IF linkp=NIL THEN EXIT END; IF CallComp(0,ADR(linkp^.Adress),0) & CallComp(7,ADR(linkp^.Adress),7) THEN IF (typ=cTEST) & (linkp^.State>1) THEN Unlock; RETURN linkp END;(*link exists*) --WrStrLn("dispose6"); DisposeLink(linkp,TRUE); (*and let the old one disappeare*) EXIT END; linkp:=linkp^.Hashnext; END; IF typ=cTEST THEN Unlock; RETURN NIL END; (*extention, SABM with info*) sabmi:=NIL; IF (l3adr#NIL) & (l3adr^[0]#0C) THEN IF Alloc(sabmi,MEMLINK) THEN WITH sabmi^ DO len:=0; WHILE (len<=HIGH(info)) & (l3adr^[len]<>0C) DO info[len]:=l3adr^[len]; INC(len); END; END; ELSE Unlock; RETURN NIL END; END; IF Initlink(linkp,hash,port,ORD(Parms[port].SendBauds[0])-ORD("0")) THEN WITH linkp^ DO mycall:=MAX(CARD16); (*is a downlink*) host:=mp; Magic:=MAGIC; pid:=CHR(cpid); cCONNtyp:=typ; State:=2; T3out:=T3OFF; (*start T1*) IF ((typ=cFLEXback) OR (cCONNtyp=cCONNAK)) THEN flexback:=typ=cFLEXback; Event(host, linkp, eCONNECTED); StartT3(linkp); (*restart T3*) END; (*link setup*) SABMinfo:=sabmi; AdrLen:=asize; Adress:=adress; IF Parms[port].DamaMaster THEN Adress[13]:=CAST(CHAR, CAST(SET16, ORD(Adress[13]))-DAMARR); END; MakePoll(linkp); (*SABM ...*) IF host<>NIL THEN INC(hosts) END; END; Unlock; RETURN linkp; ELSE IF sabmi<>NIL THEN Dealloc(sabmi) END; Unlock; RETURN NIL END; END; END Connect; PROCEDURE invertpath(linkp:pLINK; master, flex:BOOLEAN); PROCEDURE CopyCall(to,from:CARD16); (*and clean ssid from additional bits*) VAR i:CARD16; BEGIN WITH linkp^ DO FOR i:=0 TO 5 DO Adress[to+i]:=adress[from+i] END; Adress[to+6]:=CAST(CHAR, CAST(SET16, ORD(adress[from+6]))*(SSIDMASK+DAMARR)); END; END CopyCall; VAR i,j:CARD16; BEGIN CopyCall(0,7); (*invert direction*) CopyCall(7,0); WITH linkp^ DO IF master THEN Adress[13]:=CAST(CHAR, CAST(SET16, ORD(Adress[13]))-DAMARR); (*set master bit*) END; i:=14; (*invert digi-path*) j:=asize-8; WHILE j>=14 DO CopyCall(i,j); INC(i,7); DEC(j,7) END; IF flex THEN Adress[20]:=CAST(CHAR, CAST(SET8, Adress[20])+SET8{7}) END; (* set h-bit for pseudodigi *) Adress[i-1]:=CHR(ORD(Adress[i-1])+1); (*set address-end-bit*) AdrLen:=i+1; END; END invertpath; PROCEDURE rx(Port:CARD16); (*analyse address-field*) VAR i,j,k,hash :CARD16; linkp :pLINK; isflex, digisdone, sabm, v2, poll :BOOLEAN; lh:LINK; BEGIN WITH PortSt[Port].Flows[Minute5] DO Bytegot:=Bytegot+asize+dbuf^.len+3; (* + fcs + 1 flag *) END; digisdone:=(asize<22) OR (adress[asize-2]>=200C); (* no digis or last with h-bit *) v2:=(adress[6]>=200C)=(adress[13]<200C); poll:=CP()=POLL; sabm:=v2 & poll & (SET16{0,1,2,3,5,6,7}*CAST(SET16, ORD(adress[asize-1]))=SABM); (* SABM.P v2.0 *) isflex:=NOT digisdone (* min 1 digi & last h-bit not set *) & ((asize<29) OR (adress[asize-9]>=200C)) (* 1 digi or second last h-bit set *) & CallComp(asize-8, ADR(MyCalls[0]),0); (* my digi ask host for flexnet link *) (*first look in existing links*) Hashf(hash,Parms[Port].Diversity); --WrInt(hash, 10); WrStrLn(":hash "); linkp:=AdrHash[hash]; --WrInt(CAST(CARDINAL, linkp), 15); WrStrLn(":po "); LOOP IF linkp=NIL THEN EXIT END; WITH linkp^ DO IF CallComp(0, ADR(Adress), 7) & CallComp(7, ADR(Adress), 0) THEN i:=14; j:=asize-8; (*all pseudodigis are compared too*) WHILE (j>=14) & (Adress[i+6]>=200C) & (adress[j+6]<200C) & CallComp(j,ADR(Adress),i) DO INC(i,7); DEC(j,7) END; IF (j<14) OR (Adress[i+6]<200C) & (adress[j+6]>=200C) THEN EXIT END; END; linkp:=Hashnext; (*there may be more entries with same hash*) END; END; IF linkp<>NIL THEN (*existing link*) IF v2 THEN Uselink(linkp) END; ELSIF digisdone & poll THEN (* new link *) i:=1; LOOP IF MyCalls[i][0]=0C THEN EXIT END; (*end of mycall-table*) IF CallComp(0,ADR(MyCalls[i]),0) (*fitting mycall found*) & Initlink(linkp,hash,Port,ORD(Parms[Port].SendBauds[i])-ORD("0")) THEN (*memory avaliable*) WITH linkp^ DO mycall:=i; State:=1; invertpath(linkp, Parms[Port].DamaMaster, FALSE); IF sabm THEN lh:=linkp^; --WrStrLn("dispose1"); DisposeLink(linkp, TRUE); (* dispose before same flex backlink does it *) Event(host, ADR(lh), eCONNREQ) (* StartT3(linkp); (*start with T3*) IF sabm THEN Uselink(linkp); (*now it is an existing link*) *) ELSE ToSendQue(linkp,DM,FINAL) END; (* say DM.F to any poll to mycall *) END; EXIT END; INC(i); END; ELSIF isflex THEN IF Initlink(linkp,hash,Port,0) THEN (*memory avaliable*) WITH linkp^ DO mycall:=0; State:=1; invertpath(linkp, Parms[Port].DamaMaster, TRUE); lh:=linkp^; --WrStrLn("dispose1"); DisposeLink(linkp, TRUE); (* dispose before same flex backlink does it *) IF sabm THEN Event(host, ADR(lh), eCONNREQ) (* call router to make link later *) ELSE Event(host, ADR(lh), eFLEXDATA) END; (* give router frame to digipeat *) END; END; END; (* (*else if poll bit look for uplinks to a mycall*) ELSIF (asize<=15) OR (adress[asize-2]>=200C) OR isflex THEN (* no more digis in path or h-bits set *) --WrStrLn(" newlink "); i:=1; LOOP IF NOT isflex & (MyCalls[i][0]=0C) THEN EXIT END; (*end of mycall-table*) IF isflex OR CallComp(0,ADR(MyCalls[i]),0) THEN (*fitting mycall found*) IF (CP()=POLL) (*only polls a interesting*) & ((adress[6]>=200C)=(adress[13]<200C)) (*is v2*) & Initlink(linkp,hash,Port,ORD(Parms[Port].SendBauds[i])-ORD("0")) THEN (*memory avaliable*) WITH linkp^ DO mycall:=i; IF isflex THEN mycall:=0 END; (* flex *) State:=1; CopyCall(0,7); (*invert direction*) CopyCall(7,0); IF Parms[Port].DamaMaster THEN Adress[13]:=CAST(CHAR, CAST(SET16, ORD(Adress[13]))-DAMARR); (*set master bit*) END; i:=14; (*invert digi-path*) j:=asize-8; WHILE j>=14 DO CopyCall(i,j); INC(i,7); DEC(j,7) END; IF isflex THEN Adress[20]:=CAST(CHAR, CAST(SET8, Adress[20])+SET8{7}) END; (* set h-bit for pseudodigi *) Adress[i-1]:=CHR(ORD(Adress[i-1])+1); (*set address-end-bit*) AdrLen:=i+1; IF isflex THEN lh:=linkp^; --WrStrLn("dispose1"); DisposeLink(linkp, TRUE); (* dispose before same flex backlink does it *) IF flexsabm THEN Event(host, ADR(lh), eCONNREQ) ELSE Event(host, ADR(lh), eFLEXDATA) END; ELSE StartT3(linkp); (*start with T3*) Uselink(linkp); (*now it is an existing link*) END; END; END; EXIT END; INC(i); END; (*at least look for digipeating*) ELSIF Parms[Port].Digipeat & (asize>=15) THEN i:=20; LOOP IF adress[i]<200C THEN (*no h-bit*) IF CallComp(i-6,ADR(MyCalls[0]),0) THEN (*is my digicall*) j:=0; LOOP IF DNot[j][0]=0C THEN (*end of dnot-table*) adress[i]:=CHR(ORD(adress[i])+128); (*set h-bit*) --WrStrLn("sendbuf"); SendBufferd(Port,ORD(Parms[Port].SendBauds[0])-ORD("0"),NULL); EXIT END; k:=0; WHILE DNot[j][k]=adress[k+7] DO IF k=5 THEN EXIT END; INC(k); END; INC(j); END; END; EXIT END; INC(i,7); IF i>asize THEN EXIT END; END; END; *) END rx; PROCEDURE tx(VAR SendQueIn, SendQueOut:pLINK); VAR last : pLINK; sendi : pDATA; isent, dealloc: BOOLEAN; BEGIN --WrStrLn("tx"); WHILE SendQueOut<>NIL DO WITH SendQueOut^ DO sendi:=NIL; isent:=FALSE; dealloc:=FALSE; IF mycall<>0 THEN IF (State=5) & (sendcmd=NULL) OR (sendcmd=RR) & (cp<>FINAL) & NOT busyend THEN IF cCONNtyp<>cNORMAL THEN IF sendcmd=NULL THEN sendi:=GetSendInfo(SendQueOut); IF sendi<>NIL THEN sendcmd:=UI; dealloc:=TRUE; txInfoOut:=txInfoOut^.next; DEC(txbuffers); IF txfull THEN TestMem(SendQueOut) END; END; ELSE sendcmd:=NULL END; (*send nothing but UI*) ELSIF (cp=POLL) & (sendcmd=RR) (*& ((ns+9) MOD 8<>highsent)*) & (sendinfo<>NIL) & (sendinfo^.len<=Parms[Uplinkport].IPoll) & NOT rembusy & (retry<=IPRETRIES) THEN sendcmd:=CAST(SET16, 2*ns+32*nr); (*I.P instead of RR.P*) sentnr:=nr; (*only for later check of windowsize*) sendi:=sendinfo; ELSE (*RR may be sent as I*) sendi:=GetSendInfo(SendQueOut); IF sendi<>NIL THEN (*there is info to send*) IF sendi=txInfoIn THEN modifylast:=FALSE END; INC(PortSt[Uplinkport].Flows[Minute5].Isent); SentBytes:=SentBytes+sendi^.len; IF sendinfo<>NIL THEN ns:=(ns+1) MOD 8 END; sendinfo:=sendi; sendcmd:=CAST(SET16, 2*ns+32*nr); (*I-frame*) sentnr:=nr; (*only for later check of windowsize*) IF Parms[Uplinkport].DamaMaster & (GetSendInfo(SendQueOut)=NIL) THEN cp:=POLL; damastop:=TRUE; ELSE cp:=COMMAND END; IF ns=highsent THEN highsent:=(highsent+1) MOD 8 END; isent:=TRUE; END; END; ELSIF sendcmd=SABM THEN sendi:=SABMinfo (*extention, SABM with info*) ELSIF sendcmd=FRMR THEN IF Alloc(sendi,MEMFRMR) THEN WITH sendi^ DO info[0]:=pid; info[1]:=0C; (*may someone make it correct...*) info[2]:=CHR(nr); info[3]:=CHR(ns); len:=4; END; dealloc:=TRUE; END; State:=1; END; ELSE sendi:=sendinfo; dealloc:=sendi<>NIL END; IF sendcmd<>NULL THEN IF sendcmd=RR THEN sendcmd:=CAST(SET16, VAL(CARD16, sendcmd)+4*ORD(busy<>0)+8*ORD(rejsent & (busy=0))+32*nr); sentnr:=nr; (*only for later check of windowsize*) END; Adress[AdrLen-1]:=CHR(VAL(CARD16, sendcmd)+16*ORD(ODD(cp))); (*p/f bits*) Adress[6]:=CHR(128*ORD(cp>=COMMAND) + ORD(Adress[6]) MOD 128); Adress[13]:=CHR(128*ORD(cpNULL) THEN SendFrame(Parms[Uplinkport].SendPort,Baud,Adress,AdrLen,sendi); --WrStr("F:"); --IF sendi<>NIL THEN WrInt(sendi^.len,1) END; --WrStr("[");WrHex(CAST(CARD16,sendcmd), 1);WrStrLn("]"); WITH PortSt[Uplinkport].Flows[Minute5] DO Bytesent:=Bytesent+AdrLen+3; (* + fcs and 1 flag*) IF sendi<>NIL THEN Bytesent:=Bytesent+sendi^.len END; END; IF Parms[Uplinkport].monitor THEN Mon(Parms[Uplinkport].SendPort,Adress,AdrLen,sendi); END; END; IF dealloc THEN Dealloc(sendi) END; IsInQue:=SENDQUES; last:=SendQueOut; IF SendQueOut=SendQueIn THEN SendQueOut:=NIL (*remove link from sendque*) ELSE SendQueOut:=SendQueOut^.SendQuenext END; END; WITH last^ DO IF NOT haschains THEN Dealloc(last); (*simple dealloc, there are no chains*) ELSE sendcmd:=DAMAIDLE; IF State=5 THEN IF GetSendInfo(last)=NIL THEN (*no more info to send*) IF isent THEN (*but info sended*) RemoveTimer(last); T3out:=T3OFF; (*so restart T1*) StartTimer(last); (*infostop:=Parms[Uplinkport].HalfDuplex;*) END; damastop:=Parms[Uplinkport].DamaMaster; ELSE ToSendQue(last, NULL, COMMAND) END; (*reschedule for more I-frames*) ELSIF State<=1 THEN DisposeLink(last,FALSE); END; END; END; END; END tx; PROCEDURE inctime; (* make always forward going time *) VAR t:CARD32; i:CARD32; BEGIN t:=time(); IF AbsTime<>t THEN AbsTime:=t; INC(Time); Minute5:=Minut5(); IF ticker5<>Minute5 THEN (*clear next statistics intervall*) ticker5:=Minute5; FOR i:=1 TO HIGH(PortSt) DO FILL(ADR(PortSt[i].Flows[Minute5]), 0C, SIZE(PortSt[1].Flows[0])); END; END; END; INC(tick10); IF tick10 MOD TIMERBASE=0 THEN Second:=(Second+1) MOD SECMOD END; END inctime; (* PROCEDURE Layer2; (*started by ticker*) VAR port,i:CARD16; norx:BOOLEAN; BEGIN (* IF LostINT>5 THEN LostINT:=5 END; (*IF NOT L2Lock THEN L2Lock:=TRUE; *) (*L2-interrupt must not be reentered!*) *) norx:=FALSE; REPEAT FOR port:=1 TO PORTS DO WITH Parms[port] DO WITH PortL2[port] DO IF Diversity<>0 THEN (*port is enabled*) IF HalfDuplex THEN IF dama THEN IF dcd THEN IF dcdguard0 THEN MH(port,adress,asize) END; IF PortL2[SendPort].sendstate=TWAIT THEN PortL2[SendPort].txwait:=0 END; rx(Diversity); (*dcdguard:=0;*) END; ELSE norx:=TRUE END; END; IF Diversity=port THEN (*this ports has a sender*) IF DamaMaster THEN IF (T1Out<>NIL) & NOT dcd & (PortL2[SendPort].txwait=0) THEN Timeout(T1Out); END; ELSIF Ticker<>Second THEN IF (NOT HalfDuplex OR (PortL2[SendPort].txwait=0) & (NOT dcd OR damat1)) THEN (*timer is running*) IF (T1Out=NIL) OR (T1Out^.T1out<>QTicker) THEN QTicker:=(QTicker+1) MOD SECMOD; (*no timer in this second*) ELSE Timeout(T1Out) END; IF damat1 & (Ticker=Second) THEN damat1:=FALSE END; END; Ticker:=Second; END; IF txwait<>0 THEN IF (sendstate=TXDEL) OR NOT Sending(port) THEN DEC(txwait) END; IF txwait=0 THEN IF sendstate=TXDEL THEN txwait:=TXtail; sendstate:=TXTAIL; ELSIF (sendstate=TXTAIL) OR (sendstate=ECHO) THEN -- StopFlags(port); IF HalfDuplex THEN txwait:=TXwait; (* IF T3count=0 THEN (*so restart T1*) RemoveTimer(last); StartTimer(last); END; *) END; sendstate:=TWAIT; ELSE sendstate:=TWAIT END; END; END; IF dcd & (PortL2[SendPort].sendstate<>TXTAIL) THEN dcd:=dama; IF ((sendstate=TWAIT) OR (sendstate=DCDWAIT)) & (txwait<=1) & (DCDwait<>0) & NOT dama THEN IF NOT DamaMaster THEN txwait:=((DCDwait*(tick10 MOD 256)) DIV 256)+2; -- txwait:=DCDwait; END; sendstate:=DCDWAIT; END; ELSIF lookinque THEN (*maybe sendque has an entry*) WITH PortL2[SendPort] DO IF (sendstate=TXTAIL) OR (txwait=0) THEN i:=0; LOOP WITH PortL2[port].SendQues[i] DO IF (SendQueOut<>NIL) THEN IF (sendstate=TWAIT) & (TXdel<>0) THEN -- StartFlags(Parms[port].SendPort,SendQueOut^.Baud); txwait:=TXdel; sendstate:=TXDEL; EXIT END; tx(SendQueIn,SendQueOut); txwait:=TXtail; IF SendQueOut<>NIL THEN EXIT END; (*sendbuffer is full*) END; END; INC(i); (*next sendque*) IF i>=SENDQUES THEN PortL2[port].lookinque:=FALSE; EXIT END; END; END; END; ELSIF dama THEN dcd:=TRUE END; END; END; END; END; inctime; (* IF LostINT=0 THEN EXIT END; DEC(LostINT); EXIT; *) UNTIL norx; (* L2Lock:=FALSE; END; *) END Layer2; *) PROCEDURE Layer2; (*started by ticker*) VAR port,i:CARD16; norx, test1:BOOLEAN; BEGIN norx:=FALSE; REPEAT FOR port:=1 TO PORTS DO WITH Parms[port] DO WITH PortL2[port] DO IF Diversity<>0 THEN (*port is enabled*) test1:=PortL2[Diversity].dcd; PortL2[Diversity].dcd:=FALSE; IF HalfDuplex & DCD(port) THEN IF dcdguardtest1 THEN WrStr("d"); WrInt(ORD(PortL2[Diversity].dcd),1) END; IF GetFrame(port) THEN --WrStr("R"); IF monitor & (crcok OR passall) THEN Mon(port,adress,asize,dbuf) END; IF crcok THEN IF PortL2[SendPort].sendstate=TWAIT THEN PortL2[SendPort].txwait:=0 END; rx(Diversity); END; ELSE norx:=TRUE END; END; IF Diversity=port THEN (*this ports has a sender*) IF Ticker<>Second THEN IF NOT (PortL2[Diversity].dcd OR Sending(port)) THEN (*timer is running*) IF (T1Out=NIL) OR (T1Out^.T1out<>QTicker) THEN QTicker:=(QTicker+1) MOD SECMOD; (*no timer in this second*) ELSE Timeout(T1Out) END; END; Ticker:=Second; END; IF NOT PortL2[Diversity].dcd THEN i:=0; LOOP WITH PortL2[SendPort].SendQues[i] DO IF SendQueOut<>NIL THEN --WrStr("T"); tx(SendQueIn,SendQueOut); IF SendQueOut<>NIL THEN EXIT END; (*sendbuffer is full*) END; END; INC(i); (*next sendque*) IF i>=SENDQUES THEN EXIT END; END; END; END; END; END; END; inctime; UNTIL norx; END Layer2; PROCEDURE L2Init(bufs:CARD16; portset:SET16; callback:CALLBACKPROC); VAR i,j:CARD16; BEGIN (* init with default parameters *) l2verb:=FALSE; dupchk:=0; Event:=callback; hosts:=0; monbufs:=0; monBufOut:=NIL; FILL(ADR(MyCalls), 0C, SIZE(MyCalls)); FILL(ADR(DNot), 0C, SIZE(DNot)); mfrom[0]:=0C; maxrxbuffers:=bufs DIV 10; maxtxbuffers:=30; maxmonbufs:=bufs DIV 5; QTIME:=48; DMTIME:=60; DAMAMAXTIME:=15; DAMAFASTPOLLS:=4; L2Lock:=FALSE; LostINT:=0; FOR i:=0 TO HIGH(AdrHash) DO AdrHash[i]:=NIL END; Time:=0; AbsTime:=0; Second:=0; Minute5:=0; memory:=0; Freechain:=NIL; i:=SIZE(dbuf^); IF SIZE(LINK)>i THEN i:=SIZE(LINK) END; LOOP ALLOCATE(dbuf, i); IF memory>=bufs THEN EXIT END; Dealloc(dbuf); END; --WrInt(SIZE(dbuf^), 10); WrInt(SIZE(monBufOut^), 10); WrStrLn(" d,l"); FOR i:=1 TO PORTS DO WITH Parms[i] DO FILL(ADR(Parms[i]), 0C, SIZE(Parms[1])); (*defaults*) T1:=20; T3:=1800; BaudSwBytes:=5; TXdel:=20; (* 25 *) txDelByte:=126; TXwait:=2; (* 80 *) TXtail:=1; (* 3 *) DCDwait:=2; DCDlevel:=27000; SendPort:=i; (* IF (i>=8) & (i<12) THEN Diversity:=i; END;*) IF i IN portset THEN Diversity:=i END; Retries:=25; HalfDuplex:=TRUE; DamaMaster:=FALSE; MaxFrames:=7; IPoll:=80; MhTime:=0; monitor:=FALSE; Digipeat:=TRUE; SendBauds:="222222222"; passall:=FALSE; DCDIGNOR:=6000; (* 6000 *) -- MODEMPOLL:=500; END; WITH PortL2[i] DO FILL(ADR(PortL2[i]), 0C, SIZE(PortL2[1])); Ticker:=0; QTicker:=0; txwait:=0; sendstate:=TWAIT; T1Out:=NIL; damaloop:=NIL; FOR j:=0 TO HIGH(SendQues) DO SendQues[j].SendQueOut:=NIL END; END; CheckParms(i); END; FILL(ADR(PortSt), 0C, SIZE(PortSt)); Init; END L2Init; END l2.