<*+M2EXTENSIONS *>
<* IF __GEN_C__ THEN *>
  <*+COMMENT*>
  <*+GENCTYPES*>
<* END *>

(* (C)OE5DXL 1993-2015, GPL3 *)

DEFINITION MODULE l2;

FROM SYSTEM IMPORT CARD32, CARD16, ADDRESS;

TYPE SET16=SET OF [0..15];
     SET8=SET OF [0..7];

CONST UA = SET16{0,1,5,6};
      DM = SET16{0,1,2,3};
      SABM=SET16{0,1,2,3,5};
      DISC=SET16{0,1,6};
      FRMR=SET16{0,1,2,7};
      UI = SET16{0,1};
      RR = SET16{0};
      REJ= SET16{0,3};
      RNR= SET16{0,2};

      PORTS=15;                           (* portset is SET16 *)
      MYCALLS=16;

TYPE CALLTYP=ARRAY[0..6] OF CHAR;
     pSTRING=POINTER TO ARRAY[0..65535] OF CHAR;
     pDATA=POINTER TO DFIELD;
     DFIELD=RECORD
              next:pDATA;
              len: CARD16;
              info:ARRAY[0..256] OF CHAR;  (*incl PID*)
            END;
     pLINK;
     AFIELD=ARRAY[0..70] OF CHAR;
     EVENT=(eCONNECTED, eDISCONNECTED, eBUSY, eTXFREE, eRXDATA, eMONITOR, 
            eLISTEN, eIDLE, eCONNREQ, eFLEXDATA);

VAR
    L2Lock : BOOLEAN;               (*disable interrupt starting L2*)
    LostINT: CARD16;
    dbuf   : pDATA;                 (*must be new allocated after use*)
    adress : AFIELD;                (*static adress field*)
    asize  : CARD16;                (*length of static adress*)
    l2verb : BOOLEAN;
    dupchk : CARDINAL;
    udp2buf:ARRAY[0..99] OF CHAR;   (* axudp2 header after Getframe *)

    PortSt:ARRAY[0..PORTS] OF RECORD
      Links,                        (*statistics: links, including inactives*)
      MaxLinks:CARD16;
      Flows:ARRAY[0..12] OF RECORD
        Isent,                      (*I-frames sent, without I.P*)
        Iack,                       (*therefrom ackowledged*)
        Igot:CARD16;                (*I-frames got and accepted*)
        Bytesent,                   (*all frames + FCS + 1 flag*)
        Bytegot:CARD32;             (*all frames + FCS + 1 flag*)
      END;
    END;

    Parms:ARRAY[1..PORTS] OF RECORD (* (*c*) = rangecheck necessary*)
      CASE : BOOLEAN OF
      TRUE: card:ARRAY[0..16] OF CARD16
     |FALSE:
        SendPort,        (*c*)        (*associated send port to that uplinkport*)
        Diversity,       (*c*)        (*accept frames to existing links from ports with same numbers*)
        DCDlevel,
        TXwait,                       (*wait after sending before sending again*)
        TXtail,                       (*for fullduplex*)
        TXdel,                        (*txdelay*)
        DCDwait,                      (*wait after dcd before sending*)
        BaudSwBytes,     (*c*)        (*number of flags sent after baud-switch*)
        txDelByte,                    (*bit pattern sent for sync*)
        MaxFrames,       (*c*)        (*send windowsize*)
        T1,
        T3,
        IPoll,                        (*max info length for I.Poll*)
        Retries,
        MhTime,                       (*0 = mh off*)
        DCDIGNOR:CARD16;              (*ignore dcd longer than this time/dama slave off*)
      END;

      monitor,
      passall,
      DamaMaster,                     
      HalfDuplex,
      Digipeat,
      Echo:BOOLEAN;
      SendBauds:ARRAY[0..MYCALLS] OF CHAR; (*relation mycall <-> baud*)
    END;

TYPE CMD=CARD16;

     CONNTYP=(cNORMAL, cCONNAK, cFLEXback, cFLEXbusy, cTEST);
     pCONNECT=POINTER TO CONNECT;
     CONNECT=RECORD 
(*       cmd:CARD16;*)
       handle:CARD16;
       port,           (*the port number of uplink port*)
       baud,           (*baud is the index of sendbaud-table*)
       cpid:CARD16;  (*PID*)
       l2adr,          (*0-terminated unshifted raw ax.25 adress*)
       l3adr:pSTRING;  (*0-terminated SABM-with-info text for routing
                        unused if points to NIL*)
       typ:CONNTYP;    (*TEST returns an existing link with same adress*)
     END;

     pSENDUI=POINTER TO SENDUI;
     SENDUI=RECORD
       cmd:CHAR;          (* frame cmd *)
       port,
       baud:CARD16;
       path:pSTRING;      (*0-terminated, raw, unshifted ax.25 adress-field*)
       datalen:CARD16[0..256]; 
       data:pSTRING;      (* PID is part of data *)
     END;

     pGETADRESS=POINTER TO GETADRESS;
     GETADRESS=RECORD
       cmd:CARD16;
       port,                        (*number of uplink port*)
       my,                          (*number of mycall*)
       cpid:CARD16;                 (*65535 if not known*)
       adress:ARRAY[0..71] OF CHAR; (*0-terminated, raw ax.25 adress*)
     END;         

     pGETSTAT=POINTER TO GETSTAT;
     GETSTAT=RECORD
       cmd:CARD16;
       l:pLINK; 
       n,
       st,
       port,
       my,
       txbu,
       ret:CARD16;
       bsy,
       remb:BOOLEAN;
       nudl:pLINK; 
       adress:ARRAY[0..71] OF CHAR;
       sent, 
       ack,
       rcvd,
       since:CARD32;
     END;         

     SUMMS=ARRAY[0..6] OF CARD32;
     pPORTSTAT=POINTER TO PORTSTAT;
     PORTSTAT=RECORD
       cmd:CARD16;
       port,
       minits5 : CARD16; 
       sums   : SUMMS;
       mem    : CARD16;
       time   : CARD32;
     END;         

     pPARMS=POINTER TO PARMS;
     PARMS=RECORD
       cmd:CARD16;
       port,
       parm : CARD16;
       test : BOOLEAN;              (* get instead of set parm *)
       val  : CARDINAL;             (* for numeric values *)
       str  : pSTRING;              (* for string values *)
     END;

     CALLBACKPROC=PROCEDURE(VAR ADDRESS, pLINK, EVENT);

PROCEDURE Connect(mp:ADDRESS; p:pCONNECT):pLINK;   (* NIL if no link *)
   (*mp: adress that is enclosed in all messages from L2 to
   the host and may point to host-task-context*) 

PROCEDURE Disconnect(VAR l:pLINK; dealloc:BOOLEAN);  
   (*if dealloc, the owner of the link will be l2, else owner stays 
   the host and it must send a  disconnect with dealloc later to 
   release memory and timers*)

PROCEDURE Circuit(VAR l1,l2:pLINK; reconn1, reconn2:BOOLEAN; exitchar:CARD16);
   (*links streams of l1 and l2, if not reconn, then
   the link will then belong to L2 again 
   reconn: if the other link disconnects, this links
   datastream will be switched back to host
   exitchar: if first char in l1-input-stream equals exitchar
   the stream of l1 will be switched to host*)

PROCEDURE GetAdress(l:pLINK; p:pGETADRESS);

PROCEDURE Getudp2info(l:pLINK; VAR s:ARRAY OF CHAR);

PROCEDURE GetChar(l:pLINK; delete:BOOLEAN; VAR char:CHAR):BOOLEAN;
   (*returns false if buffer is empty
   delete: delete char from buffer after get*)

PROCEDURE GetStr(l:pLINK; size:CARD16; buf:pSTRING):CARD16;
   (*return is length of data *)

PROCEDURE SendStr(l:pLINK; size:CARD16; buf:pSTRING):BOOLEAN;
 (* max 256 byte, false if data not sent (sendbuffer full) *)

PROCEDURE SendRaw(p:pSENDUI);             (* pid >255 send no pid *)
PROCEDURE GetStat(p:pGETSTAT);
PROCEDURE PortStat(p:pPORTSTAT):BOOLEAN; (* true if port is enabled *)
PROCEDURE Parm(p:pPARMS);
PROCEDURE GetMon():pLINK;
PROCEDURE Layer2;          (*started by 10ms interrupt-ticker if not L2Lock*)
PROCEDURE L2Init(bufs:CARD16; portset:SET16; callback:CALLBACKPROC);

END l2.
