2022-08-28 09:35:46 +01:00
|
|
|
|
/*
|
|
|
|
|
Copyright 2001-2018 John Wiseman G8BPQ
|
|
|
|
|
|
|
|
|
|
This file is part of LinBPQ/BPQ32.
|
|
|
|
|
|
|
|
|
|
LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// C replacement for L2Code.asm
|
|
|
|
|
//
|
|
|
|
|
#define Kernel
|
|
|
|
|
|
|
|
|
|
#define _CRT_SECURE_NO_DEPRECATE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#pragma data_seg("_BPQDATA")
|
|
|
|
|
|
|
|
|
|
#include "time.h"
|
|
|
|
|
#include "stdio.h"
|
|
|
|
|
|
|
|
|
|
#include "CHeaders.h"
|
|
|
|
|
#include "tncinfo.h"
|
|
|
|
|
|
|
|
|
|
#define PFBIT 0x10 // POLL/FINAL BIT IN CONTROL BYTE
|
|
|
|
|
|
|
|
|
|
#define REJSENT 1 // SET WHEN FIRST REJ IS SENT IN REPLY
|
|
|
|
|
// TO AN I(P)
|
|
|
|
|
#define RNRSET 2 // RNR RECEIVED FROM OTHER END
|
|
|
|
|
#define DISCPENDING 8 // SEND DISC WHEN ALL DATA ACK'ED
|
|
|
|
|
#define RNRSENT 0x10 // WE HAVE SEND RNR
|
|
|
|
|
#define POLLSENT 0x20 // POLL BIT OUTSTANDING
|
|
|
|
|
|
|
|
|
|
#define ONEMINUTE 60*3
|
|
|
|
|
#define TENSECS 10*3
|
|
|
|
|
#define THREESECS 3*3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L2SENDCOMMAND();
|
|
|
|
|
VOID L2ROUTINE();
|
|
|
|
|
MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD);
|
|
|
|
|
VOID SendSupervisCmd(struct _LINKTABLE * LINK);
|
|
|
|
|
void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF);
|
|
|
|
|
VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD);
|
|
|
|
|
VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD);
|
|
|
|
|
VOID ACKMSG(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID InformPartner(struct _LINKTABLE * LINK, int Reason);
|
|
|
|
|
UINT RR_OR_RNR(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT);
|
|
|
|
|
VOID CLEAROUTLINK(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID SENDFRMR(struct _LINKTABLE * LINK);
|
|
|
|
|
char * SetupNodeHeader(struct DATAMESSAGE * Buffer);
|
|
|
|
|
VOID CLEARSESSIONENTRY(TRANSPORTENTRY * Session);
|
|
|
|
|
VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT);
|
|
|
|
|
VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL);
|
|
|
|
|
VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS);
|
|
|
|
|
VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer);
|
|
|
|
|
VOID RESET2X(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID RESET2(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID CONNECTREFUSED(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL);
|
|
|
|
|
VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG);
|
|
|
|
|
VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG);
|
|
|
|
|
VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG);
|
|
|
|
|
BOOL FindNeighbour(UCHAR * Call, int Port, struct ROUTE ** REQROUTE);
|
|
|
|
|
VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG);
|
|
|
|
|
VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER);
|
|
|
|
|
VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER);
|
|
|
|
|
VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL);
|
|
|
|
|
int COUNTLINKS(int Port);
|
|
|
|
|
VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG);
|
|
|
|
|
TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK);
|
|
|
|
|
BOOL cATTACHTOBBS(TRANSPORTENTRY * Session, UINT Mask, int Paclen, int * AnySessions);
|
|
|
|
|
VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer);
|
|
|
|
|
VOID L2SWAPADDRESSES(MESSAGE * Buffer);
|
|
|
|
|
BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK);
|
|
|
|
|
VOID SENDSABM(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID L2SENDXID(struct _LINKTABLE * LINK);
|
|
|
|
|
VOID __cdecl Debugprintf(const char * format, ...);
|
|
|
|
|
VOID Q_IP_MSG(MESSAGE * Buffer);
|
|
|
|
|
VOID PROCESSNODEMESSAGE(MESSAGE * Msg, struct PORTCONTROL * PORT);
|
|
|
|
|
VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG);
|
|
|
|
|
BOOL CompareAliases(UCHAR * c1, UCHAR * c2);
|
|
|
|
|
VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG);
|
|
|
|
|
VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly);
|
|
|
|
|
VOID DigiToMultiplePorts(struct PORTCONTROL * PORTVEC, PMESSAGE Msg);
|
|
|
|
|
VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer);
|
|
|
|
|
BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg);
|
|
|
|
|
VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL);
|
|
|
|
|
UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg);
|
|
|
|
|
VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG);
|
|
|
|
|
int CountBits(uint32_t in);
|
|
|
|
|
void AttachKISSHF(struct PORTCONTROL * PORT, MESSAGE * Buffer);
|
|
|
|
|
void DetachKISSHF(struct PORTCONTROL * PORT);
|
|
|
|
|
void KISSHFConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK);
|
|
|
|
|
void WriteConnectLog(char * fromcall, char * tocall, UCHAR * Mode);
|
|
|
|
|
|
|
|
|
|
extern int REALTIMETICKS;
|
|
|
|
|
|
|
|
|
|
// MSGFLAG contains CMD/RESPONSE BITS
|
|
|
|
|
|
|
|
|
|
#define CMDBIT 4 // CURRENT MESSAGE IS A COMMAND
|
|
|
|
|
#define RESP 2 // CURRENT MSG IS RESPONSE
|
|
|
|
|
#define VER1 1 // CURRENT MSG IS VERSION 1
|
|
|
|
|
|
|
|
|
|
// FRMR REJECT FLAGS
|
|
|
|
|
|
|
|
|
|
#define SDINVC 1 // INVALID COMMAND
|
|
|
|
|
#define SDNRER 8 // INVALID N(R)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UCHAR NO_CTEXT = 0;
|
|
|
|
|
UCHAR ALIASMSG = 0;
|
|
|
|
|
extern UINT APPLMASK;
|
|
|
|
|
static UCHAR ISNETROMMSG = 0;
|
|
|
|
|
UCHAR MSGFLAG = 0;
|
|
|
|
|
extern UCHAR * ALIASPTR;
|
|
|
|
|
|
|
|
|
|
UCHAR QSTCALL[7] = {'Q'+'Q','S'+'S','T'+'T',0x40,0x40,0x40,0xe0}; // QST IN AX25
|
|
|
|
|
UCHAR NODECALL[7] = {0x9C, 0x9E, 0x88, 0x8A, 0xA6, 0x40, 0xE0}; // 'NODES' IN AX25 FORMAT
|
|
|
|
|
|
|
|
|
|
extern struct TNCINFO * TNCInfo[41]; // Records are Malloc'd
|
|
|
|
|
|
|
|
|
|
extern BOOL LogAllConnects;
|
|
|
|
|
|
|
|
|
|
APPLCALLS * APPL;
|
|
|
|
|
|
|
|
|
|
VOID L2Routine(struct PORTCONTROL * PORT, PMESSAGE Buffer)
|
|
|
|
|
{
|
|
|
|
|
// LEVEL 2 PROCESSING
|
|
|
|
|
|
|
|
|
|
MESSAGE * ADJBUFFER;
|
|
|
|
|
struct _LINKTABLE * LINK;
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
int n;
|
|
|
|
|
UCHAR CTL;
|
|
|
|
|
uintptr_t Work;
|
|
|
|
|
UCHAR c;
|
|
|
|
|
|
|
|
|
|
// Check for invalid length (< 22 7Header + 7Addr + 7Addr + CTL
|
|
|
|
|
|
|
|
|
|
if (Buffer->LENGTH < (18 + sizeof(void *)))
|
|
|
|
|
{
|
|
|
|
|
Debugprintf("BPQ32 Bad L2 Msg Port %d Len %d", PORT->PORTNUMBER, Buffer->LENGTH);
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PORT->L2FRAMES++;
|
|
|
|
|
|
|
|
|
|
ALIASMSG = 0;
|
|
|
|
|
APPLMASK = 0;
|
|
|
|
|
ISNETROMMSG = 0;
|
|
|
|
|
|
|
|
|
|
MSGFLAG = 0; // CMD/RESP UNDEFINED
|
|
|
|
|
|
|
|
|
|
// Check for Corrupted Callsign in Origin (to keep MH list clean)
|
|
|
|
|
|
|
|
|
|
ptr = &Buffer->ORIGIN[0];
|
|
|
|
|
n = 6;
|
|
|
|
|
|
|
|
|
|
c = *(ptr) >> 1;
|
|
|
|
|
|
|
|
|
|
if (c == ' ') // Blank Call
|
|
|
|
|
{
|
|
|
|
|
Debugprintf("BPQ32 Blank Call Port &%", PORT->PORTNUMBER);
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while(n--)
|
|
|
|
|
{
|
|
|
|
|
// Try a bit harder to detect corruption
|
|
|
|
|
|
|
|
|
|
c = *(ptr++);
|
|
|
|
|
|
|
|
|
|
if (c & 1)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = c >> 1;
|
|
|
|
|
|
|
|
|
|
if (!isalnum(c) && !(c == '#') && !(c == ' '))
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check Digis if present
|
|
|
|
|
|
|
|
|
|
if ((Buffer->ORIGIN[6] & 1) == 0) // Digis
|
|
|
|
|
{
|
|
|
|
|
ptr = &Buffer->CTL;
|
|
|
|
|
n = 6;
|
|
|
|
|
|
|
|
|
|
while(n--)
|
|
|
|
|
{
|
|
|
|
|
c = *(ptr++);
|
|
|
|
|
|
|
|
|
|
if (c & 1)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
c = c >> 1;
|
|
|
|
|
|
|
|
|
|
if (!isalnum(c) && !(c == '#') && !(c == ' '))
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BPQTRACE(Buffer, TRUE); // TRACE - RX frames to APRS
|
|
|
|
|
|
|
|
|
|
if (PORT->PORTMHEARD)
|
|
|
|
|
MHPROC(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
/// TAJ added 07/12/2020 for 'all RX traffic as IfinOctects
|
|
|
|
|
|
|
|
|
|
InOctets[PORT->PORTNUMBER] += Buffer->LENGTH - MSGHDDRLEN;
|
|
|
|
|
|
|
|
|
|
// CHECK THAT ALL DIGIS HAVE BEEN ACTIONED,
|
|
|
|
|
// AND ADJUST FOR DIGIPEATERS IF PRESENT
|
|
|
|
|
|
|
|
|
|
n = 8; // MAX DIGIS
|
|
|
|
|
ptr = &Buffer->ORIGIN[6]; // End of Address bit
|
|
|
|
|
|
|
|
|
|
while ((*ptr & 1) == 0)
|
|
|
|
|
{
|
|
|
|
|
// MORE TO COME
|
|
|
|
|
|
|
|
|
|
ptr += 7;
|
|
|
|
|
|
|
|
|
|
if ((*ptr & 0x80) == 0) // Digi'd bit
|
|
|
|
|
{
|
|
|
|
|
// FRAME HAS NOT BEEN REPEATED THROUGH CURRENT DIGI -
|
|
|
|
|
// SEE IF WE ARE MEANT TO DIGI IT
|
|
|
|
|
|
|
|
|
|
struct XDIGI * XDigi = PORT->XDIGIS; // Cross port digi setup
|
|
|
|
|
|
|
|
|
|
ptr -= 6; // To start of Call
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(ptr, MYCALL) || CompareAliases(ptr, MYALIAS) ||
|
|
|
|
|
CompareCalls(ptr, PORT->PORTALIAS) || CompareCalls(ptr, PORT->PORTALIAS2))
|
|
|
|
|
{
|
|
|
|
|
Digipeat(PORT, Buffer, ptr, 0, 0); // Digi it (if enabled)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (XDigi)
|
|
|
|
|
{
|
|
|
|
|
if (CompareCalls(ptr, XDigi->Call))
|
|
|
|
|
{
|
|
|
|
|
Digipeat(PORT, Buffer, ptr, XDigi->Port, XDigi->UIOnly); // Digi it (if enabled)
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
XDigi = XDigi->Next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return; // not complete and not for us
|
|
|
|
|
}
|
|
|
|
|
n--;
|
|
|
|
|
|
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return; // Corrupt - no end of address bit
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reached End of digis, and all actioned, so can process it
|
|
|
|
|
|
|
|
|
|
Work = (uintptr_t)&Buffer->ORIGIN[6];
|
|
|
|
|
ptr -= Work; // ptr is now length of digis
|
|
|
|
|
|
|
|
|
|
Work = (uintptr_t)Buffer;
|
|
|
|
|
ptr += Work;
|
|
|
|
|
|
|
|
|
|
ADJBUFFER = (MESSAGE * )ptr; // ADJBUFFER points to CTL, etc. allowing for digis
|
|
|
|
|
|
|
|
|
|
// GET CMD/RESP BITS
|
|
|
|
|
|
|
|
|
|
if (Buffer->DEST[6] & 0x80)
|
|
|
|
|
{
|
|
|
|
|
if (Buffer->ORIGIN[6] & 0x80) // Both set, assume V1
|
|
|
|
|
MSGFLAG |= VER1;
|
|
|
|
|
else
|
|
|
|
|
MSGFLAG |= CMDBIT;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (Buffer->ORIGIN[6] & 0x80) // Only Dest Set
|
|
|
|
|
MSGFLAG |= RESP;
|
|
|
|
|
else
|
|
|
|
|
MSGFLAG |= VER1; // Neither, assume V1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SEE IF FOR AN ACTIVE LINK SESSION
|
|
|
|
|
|
|
|
|
|
CTL = ADJBUFFER->CTL;
|
|
|
|
|
|
|
|
|
|
// IF A UI, THERE IS NO SESSION
|
|
|
|
|
|
|
|
|
|
if (FindLink(Buffer->ORIGIN, Buffer->DEST, PORT->PORTNUMBER, &LINK))
|
|
|
|
|
{
|
|
|
|
|
L2LINKACTIVE(LINK, PORT, Buffer,ADJBUFFER, CTL, MSGFLAG);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOT FOR ACTIVE LINK - SEE IF ADDRESSED TO OUR ADDRESSES
|
|
|
|
|
|
|
|
|
|
// FIRST TRY PORT ADDR/ALIAS
|
|
|
|
|
|
|
|
|
|
if(PORT->PORTBBSFLAG == 1)
|
|
|
|
|
goto PORTCALLISBBS; // PORT CALL/ALIAS ARE FOR BBS
|
|
|
|
|
|
|
|
|
|
if (NODE)
|
|
|
|
|
goto USING_NODE;
|
|
|
|
|
|
|
|
|
|
PORTCALLISBBS:
|
|
|
|
|
|
|
|
|
|
// NODE IS NOT ACTIVE, SO PASS CALLS TO PORTCALL/ALIAS TO BBS
|
|
|
|
|
|
|
|
|
|
APPLMASK = 1;
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Buffer->DEST, NETROMCALL))
|
|
|
|
|
{
|
|
|
|
|
ISNETROMMSG = 1;
|
|
|
|
|
goto FORUS;
|
|
|
|
|
}
|
|
|
|
|
if (PORT->PORTL3FLAG) // L3 Only Port?
|
|
|
|
|
goto NOTFORUS; // If L3ONLY, only accept calls to NETROMCALL
|
|
|
|
|
|
|
|
|
|
ISNETROMMSG = 0;
|
|
|
|
|
|
|
|
|
|
USING_NODE:
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Buffer->DEST, PORT->PORTCALL))
|
|
|
|
|
goto FORUS;
|
|
|
|
|
|
|
|
|
|
ALIASMSG = 1;
|
|
|
|
|
|
|
|
|
|
if (CompareAliases(Buffer->DEST, PORT->PORTALIAS)) // only compare 6 bits - allow any ssid
|
|
|
|
|
goto FORUS;
|
|
|
|
|
|
|
|
|
|
if (NODE == 0)
|
|
|
|
|
goto TRYBBS; // NOT USING NODE SYSTEM
|
|
|
|
|
|
|
|
|
|
ALIASMSG = 0;
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Buffer->DEST, MYCALL))
|
|
|
|
|
goto FORUS;
|
|
|
|
|
|
|
|
|
|
ALIASMSG = 1;
|
|
|
|
|
|
|
|
|
|
if (CompareAliases(Buffer->DEST, MYALIAS)) // only compare 6 bits - allow any ssid
|
|
|
|
|
goto FORUS;
|
|
|
|
|
|
|
|
|
|
TRYBBS:
|
|
|
|
|
|
|
|
|
|
if (BBS == 0)
|
|
|
|
|
goto NOWTRY_NODES; // NOT USING BBS CALLS
|
|
|
|
|
|
|
|
|
|
// TRY APPLICATION CALLSIGNS/ALIASES
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
APPLMASK = 1;
|
|
|
|
|
ALIASPTR = &CMDALIAS[0][0];
|
|
|
|
|
|
|
|
|
|
n = NumberofAppls;
|
|
|
|
|
|
|
|
|
|
APPL = APPLCALLTABLE;
|
|
|
|
|
|
|
|
|
|
while (n--)
|
|
|
|
|
{
|
|
|
|
|
if (APPL->APPLCALL[0] > 0x40) // Valid ax.25 addr
|
|
|
|
|
{
|
|
|
|
|
// WE MAY NOT BE ALLOWED TO USE THE BBS CALL ON SOME BANDS DUE TO
|
|
|
|
|
// THE RATHER ODD UK LICENCING RULES!
|
|
|
|
|
// For backward compatibility only apply to appl 1
|
|
|
|
|
|
|
|
|
|
if ((PORT->PERMITTEDAPPLS & APPLMASK) != 0)
|
|
|
|
|
{
|
|
|
|
|
ALIASMSG = 0;
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Buffer->DEST, APPL->APPLCALL))
|
|
|
|
|
goto FORUS;
|
|
|
|
|
|
|
|
|
|
ALIASMSG = 1;
|
|
|
|
|
|
|
|
|
|
if (CompareAliases(Buffer->DEST, APPL->APPLALIAS)) // only compare 6 bits - allow any ssid
|
|
|
|
|
goto FORUS;
|
|
|
|
|
|
|
|
|
|
if (CompareAliases(Buffer->DEST, APPL->L2ALIAS)) // only compare 6 bits - allow any ssid
|
|
|
|
|
goto FORUS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
APPLMASK <<= 1;
|
|
|
|
|
ALIASPTR += ALIASLEN;
|
|
|
|
|
APPL++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOT FOR US - SEE IF 'NODES' OR IP/ARP BROADCAST MESSAGE
|
|
|
|
|
|
|
|
|
|
NOWTRY_NODES:
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Buffer->DEST, QSTCALL))
|
|
|
|
|
{
|
|
|
|
|
Q_IP_MSG(Buffer); // IP BROADCAST
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ADJBUFFER->PID != 0xCF) // NETROM MSG?
|
|
|
|
|
goto NOTFORUS; // NO
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Buffer->DEST, NODECALL))
|
|
|
|
|
{
|
|
|
|
|
if (Buffer->L2DATA[0] == 0xff) // Valid NODES Broadcast
|
|
|
|
|
{
|
|
|
|
|
PROCESSNODEMESSAGE(Buffer, PORT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
NOTFORUS:
|
|
|
|
|
//
|
|
|
|
|
// MAY JUST BE A REPLY TO A 'PRIMED' CQ CALL
|
|
|
|
|
//
|
|
|
|
|
if ((CTL & ~PFBIT) == SABM)
|
|
|
|
|
if (CheckForListeningSession(PORT, Buffer))
|
|
|
|
|
return; // Used buffer to send UA
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
FORUS:
|
|
|
|
|
|
|
|
|
|
// if a UI frame and UIHook Specified, call it
|
|
|
|
|
|
|
|
|
|
if (PORT->UIHook && CTL == 3)
|
|
|
|
|
PORT->UIHook(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
|
|
|
|
|
|
|
|
|
|
L2FORUS(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID MHPROC(struct PORTCONTROL * PORT, MESSAGE * Buffer)
|
|
|
|
|
{
|
|
|
|
|
PMHSTRUC MH = PORT->PORTMHEARD;
|
|
|
|
|
PMHSTRUC MHBASE = MH;
|
|
|
|
|
int i;
|
|
|
|
|
int OldCount = 0;
|
|
|
|
|
char Freq[16] = "";
|
|
|
|
|
char DIGI = '*';
|
|
|
|
|
double ReportFreq = 0;
|
|
|
|
|
|
|
|
|
|
// if port has Rigcontrol associated with it, get frequency
|
|
|
|
|
|
|
|
|
|
struct TNCINFO * TNC = PORT->TNC;
|
|
|
|
|
|
|
|
|
|
if (TNC && TNC->RIG && TNC->RIG->Valchar[0])
|
|
|
|
|
{
|
|
|
|
|
if (TNC->Hardware == H_UZ7HO)
|
|
|
|
|
{
|
|
|
|
|
// See if we have Center Freq Info
|
|
|
|
|
if (TNC->AGWInfo->CenterFreq)
|
|
|
|
|
{
|
|
|
|
|
ReportFreq = atof(TNC->RIG->Valchar) + ((TNC->AGWInfo->CenterFreq * 1.0) / 1000000.0);
|
|
|
|
|
}
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
else if (TNC->AGWInfo->hFreq)
|
|
|
|
|
{
|
|
|
|
|
char Centre[16];
|
|
|
|
|
double ModemFreq;
|
|
|
|
|
|
|
|
|
|
SendMessage(TNC->AGWInfo->hFreq, WM_GETTEXT, 15, (LPARAM)Centre);
|
|
|
|
|
|
|
|
|
|
ModemFreq = atof(Centre);
|
|
|
|
|
|
|
|
|
|
ReportFreq = atof(TNC->RIG->Valchar) + (ModemFreq / 1000000);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
else
|
|
|
|
|
ReportFreq = atof(TNC->RIG->Valchar) + 0.0015; // Assume 1500
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
|
|
|
|
|
// Not UZ7HO or Linux
|
|
|
|
|
|
|
|
|
|
ReportFreq = atof(TNC->RIG->Valchar) + 0.0015;
|
|
|
|
|
|
|
|
|
|
_gcvt(ReportFreq, 9, Freq);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (PORT->RIGPort)
|
|
|
|
|
{
|
|
|
|
|
struct TNCINFO * TNC = TNCInfo[PORT->RIGPort];
|
|
|
|
|
|
|
|
|
|
if (TNC && TNC->RIG)
|
|
|
|
|
{
|
|
|
|
|
strcpy(Freq, TNC->RIG->Valchar);
|
|
|
|
|
Freq[11] = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// if (Buffer->ORIGIN[6] & 1)
|
|
|
|
|
DIGI = 0; // DOn't think we want to do this
|
|
|
|
|
|
|
|
|
|
// See if in list
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < MHENTRIES; i++)
|
|
|
|
|
{
|
|
|
|
|
if ((MH->MHCALL[0] == 0) || (CompareCalls(Buffer->ORIGIN, MH->MHCALL) && MH->MHDIGI == DIGI)) // Spare or our entry
|
|
|
|
|
{
|
|
|
|
|
OldCount = MH->MHCOUNT;
|
|
|
|
|
goto DoMove;
|
|
|
|
|
}
|
|
|
|
|
MH++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TABLE FULL AND ENTRY NOT FOUND - MOVE DOWN ONE, AND ADD TO TOP
|
|
|
|
|
|
|
|
|
|
i = MHENTRIES - 1;
|
|
|
|
|
|
|
|
|
|
// Move others down and add at front
|
|
|
|
|
DoMove:
|
|
|
|
|
if (i != 0) // First
|
|
|
|
|
memmove(MHBASE + 1, MHBASE, i * sizeof(MHSTRUC));
|
|
|
|
|
|
|
|
|
|
memcpy (MHBASE->MHCALL, Buffer->ORIGIN, 7 * 9); // Save Digis
|
|
|
|
|
MHBASE->MHDIGI = DIGI;
|
|
|
|
|
MHBASE->MHTIME = time(NULL);
|
|
|
|
|
MHBASE->MHCOUNT = ++OldCount;
|
|
|
|
|
strcpy(MHBASE->MHFreq, Freq);
|
|
|
|
|
MHBASE->MHLocator[0] = 0;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int CountFramesQueuedOnSession(TRANSPORTENTRY * Session)
|
|
|
|
|
{
|
|
|
|
|
// COUNT NUMBER OF FRAMES QUEUED ON A SESSION
|
|
|
|
|
|
|
|
|
|
if (Session == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (Session->L4CIRCUITTYPE & BPQHOST)
|
|
|
|
|
{
|
|
|
|
|
return C_Q_COUNT(&Session->L4TX_Q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Session->L4CIRCUITTYPE & SESSION)
|
|
|
|
|
{
|
|
|
|
|
// L4 SESSION - GET NUMBER UNACKED, AND ADD NUMBER ON TX QUEUE
|
|
|
|
|
|
|
|
|
|
int Count = C_Q_COUNT(&Session->L4TX_Q);
|
|
|
|
|
UCHAR Unacked = Session->TXSEQNO - Session->L4WS;
|
|
|
|
|
|
|
|
|
|
return Count + Unacked;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Session->L4CIRCUITTYPE & PACTOR)
|
|
|
|
|
{
|
|
|
|
|
// PACTOR Type - Frames are queued on the Port Entry
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT = Session->L4TARGET.PORT;
|
|
|
|
|
EXTPORTDATA * EXT = (EXTPORTDATA *)PORT;
|
|
|
|
|
|
|
|
|
|
int ret = EXT->FramesQueued;
|
|
|
|
|
|
|
|
|
|
// Check L4 Queue as messages can stay there briefly
|
|
|
|
|
|
|
|
|
|
ret += C_Q_COUNT(&Session->L4RX_Q);
|
|
|
|
|
|
|
|
|
|
return ret + C_Q_COUNT(&PORT->PORTTX_Q);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// L2 CIRCUIT
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
int SessCount = C_Q_COUNT(&Session->L4TX_Q);
|
|
|
|
|
struct _LINKTABLE * LINK = Session->L4TARGET.LINK;
|
|
|
|
|
int L2 = COUNT_AT_L2(LINK);
|
|
|
|
|
|
|
|
|
|
return SessCount + L2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CHECKIFBUSYL2(TRANSPORTENTRY * Session)
|
|
|
|
|
{
|
|
|
|
|
// RETURN TOP BIT OF AL SET IF SESSION PARTNER IS BUSY
|
|
|
|
|
|
|
|
|
|
if (Session->L4CROSSLINK) // CONNECTED?
|
|
|
|
|
{
|
|
|
|
|
Session = Session->L4CROSSLINK;
|
|
|
|
|
|
|
|
|
|
if (CountFramesQueuedOnSession(Session) > 10)
|
|
|
|
|
return L4BUSY;;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2FORUS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// MESSAGE ADDRESSED TO OUR CALL OR ALIAS, BUT NOT FOR AN ACTIVE SESSION
|
|
|
|
|
|
|
|
|
|
// LINK points to an empty link table entry
|
|
|
|
|
|
|
|
|
|
struct ROUTE * ROUTE;
|
|
|
|
|
int CTLlessPF = CTL & ~PFBIT;
|
|
|
|
|
|
|
|
|
|
PORT->L2FRAMESFORUS++;
|
|
|
|
|
|
|
|
|
|
NO_CTEXT = 0;
|
|
|
|
|
|
|
|
|
|
// ONLY SABM or UI ALLOWED IF NO SESSION
|
|
|
|
|
// Plus XID/TEST/SABME if V2.2 support enabled
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == 3) // UI
|
|
|
|
|
{
|
|
|
|
|
// A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES
|
|
|
|
|
|
|
|
|
|
switch(ADJBUFFER->PID)
|
|
|
|
|
{
|
|
|
|
|
case 0xcf: // Netrom
|
|
|
|
|
|
|
|
|
|
if (Buffer->L2DATA[0] == 0xff) // NODES
|
|
|
|
|
PROCESSNODEMESSAGE(Buffer, PORT);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xcc: // TCP
|
|
|
|
|
case 0xcd: // ARP
|
|
|
|
|
case 0x08: // NOS FRAGMENTED AX25 TCP/IP
|
|
|
|
|
|
|
|
|
|
Q_IP_MSG( Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PORT->PortUIONLY) // Port is for UI only
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == SABME)
|
|
|
|
|
{
|
|
|
|
|
// Although some say V2.2 requires SABME I don't agree!
|
|
|
|
|
|
|
|
|
|
// Reject until we support Mod 128
|
|
|
|
|
|
|
|
|
|
L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == SREJ) // Used to see if other end supports SREJ on 2.0
|
|
|
|
|
{
|
|
|
|
|
// Send FRMR if dont support SREJ
|
|
|
|
|
// Send DM if we do
|
|
|
|
|
|
|
|
|
|
if (SUPPORT2point2)
|
|
|
|
|
L2SENDRESP(PORT, Buffer, ADJBUFFER, DM);
|
|
|
|
|
else
|
|
|
|
|
L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == XID)
|
|
|
|
|
{
|
|
|
|
|
// Send FRMR if we only support V 2.0
|
|
|
|
|
|
|
|
|
|
if (SUPPORT2point2 == FALSE)
|
|
|
|
|
{
|
|
|
|
|
L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// if Support 2.2 drop through
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == TEST)
|
|
|
|
|
{
|
|
|
|
|
// I can't see amy harm in replying to TEST
|
|
|
|
|
|
|
|
|
|
L2SENDRESP(PORT, Buffer, ADJBUFFER, TEST);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// if (CTLlessPF != SABM && CTLlessPF != SABME)
|
|
|
|
|
if (CTLlessPF != SABM && CTLlessPF != XID)
|
|
|
|
|
{
|
|
|
|
|
if ((MSGFLAG & CMDBIT) && (CTL & PFBIT)) // Command with P?
|
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
else
|
|
|
|
|
ReleaseBuffer(Buffer); // Ignore if not
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Exclude and limit tests are done for XID and SABM
|
|
|
|
|
|
|
|
|
|
if (NODE == 0 && BBS == 0) // Don't want any calls
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef EXCLUDEBITS
|
|
|
|
|
|
|
|
|
|
// CHECK ExcludeList
|
|
|
|
|
|
|
|
|
|
if (CheckExcludeList(Buffer->ORIGIN) == 0)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT
|
|
|
|
|
|
|
|
|
|
if (PORT->PERMITTEDCALLS)
|
|
|
|
|
{
|
|
|
|
|
UCHAR * ptr = PORT->PERMITTEDCALLS;
|
|
|
|
|
|
|
|
|
|
while (TRUE)
|
|
|
|
|
{
|
|
|
|
|
if (memcmp(Buffer->ORIGIN, ptr, 6) == 0) // Ignore SSID
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
ptr += 7;
|
|
|
|
|
|
|
|
|
|
if ((*ptr) == 0) // Not in list
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IF CALL REQUEST IS FROM A LOCKED NODE WITH QUALITY ZERO, IGNORE IT
|
|
|
|
|
|
|
|
|
|
if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE))
|
|
|
|
|
{
|
|
|
|
|
// From a known node
|
|
|
|
|
|
|
|
|
|
NO_CTEXT = 1;
|
|
|
|
|
|
|
|
|
|
if (ROUTE->NEIGHBOUR_FLAG == 1 && ROUTE->NEIGHBOUR_QUAL == 0) // Locked, qual 0
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CHECK PORT CONNECT LIMITS
|
|
|
|
|
|
|
|
|
|
if (PORT->USERS)
|
|
|
|
|
{
|
|
|
|
|
if (COUNTLINKS(PORT->PORTNUMBER) >= PORT->USERS)
|
|
|
|
|
{
|
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if KISSHF, check if attached. If so, reject. If not, attach.
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
{
|
|
|
|
|
struct TNCINFO * TNC = PORT->TNC;
|
|
|
|
|
|
|
|
|
|
if (TNC->PortRecord->ATTACHEDSESSIONS[0])
|
|
|
|
|
{
|
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// OK to accept SABM or XID
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == XID)
|
|
|
|
|
{
|
|
|
|
|
ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not XID, so must be SABM
|
|
|
|
|
|
|
|
|
|
L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID ProcessXIDCommand(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// I think it is fairly safe to accept XID as soon as we
|
|
|
|
|
// can process SREJ, but only accept Mod 8 and 256 Byte frames
|
|
|
|
|
|
|
|
|
|
// I think the only way to run 2.2 Mod 8 is to preceed a
|
|
|
|
|
// SABM with XID, but others don't seem to agree!
|
|
|
|
|
|
|
|
|
|
// Run through XID fields, changing any we don't like,
|
|
|
|
|
// then return an XID response
|
|
|
|
|
|
|
|
|
|
// Decode and process XID
|
|
|
|
|
|
|
|
|
|
UCHAR * ptr = &ADJBUFFER->PID;
|
|
|
|
|
UCHAR * ptr1, * ptr2;
|
|
|
|
|
UCHAR TEMPDIGI[57];
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (*ptr++ == 0x82 && *ptr++ == 0x80)
|
|
|
|
|
{
|
|
|
|
|
int Type;
|
|
|
|
|
int Len;
|
|
|
|
|
unsigned int value;
|
|
|
|
|
int xidlen = *(ptr++) << 8;
|
|
|
|
|
xidlen += *ptr++;
|
|
|
|
|
|
|
|
|
|
// XID is set of Type, Len, Value n-tuples
|
|
|
|
|
|
|
|
|
|
while (xidlen > 0)
|
|
|
|
|
{
|
|
|
|
|
Type = *ptr++;
|
|
|
|
|
Len = *ptr++;
|
|
|
|
|
|
|
|
|
|
value = 0;
|
|
|
|
|
xidlen -= (Len + 2);
|
|
|
|
|
|
|
|
|
|
while (Len--)
|
|
|
|
|
{
|
|
|
|
|
value <<=8;
|
|
|
|
|
value += *ptr++;
|
|
|
|
|
}
|
|
|
|
|
switch(Type)
|
|
|
|
|
{
|
|
|
|
|
case 2: //Bin fields
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
|
|
|
|
|
|
if ((value & OPMustHave) != OPMustHave)
|
|
|
|
|
goto BadXID;
|
|
|
|
|
|
|
|
|
|
if ((value & OPMod8) == 0)
|
|
|
|
|
goto BadXID;
|
|
|
|
|
|
|
|
|
|
if ((value & OPSREJMult) == 0)
|
|
|
|
|
goto BadXID;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reply Mod 8 SREJMULTI
|
|
|
|
|
|
|
|
|
|
value = OPMustHave | OPSREJMult | OPMod8;
|
|
|
|
|
ptr -=3;
|
|
|
|
|
*ptr++ = value >> 16;
|
|
|
|
|
*ptr++ = value >> 8;
|
|
|
|
|
*ptr++ = value;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 6: //RX Size
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8: //RX Window
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Send back as XID response
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 1; // XID received
|
|
|
|
|
LINK->Ver2point2 = TRUE; // Must support 2.2 if sent XID
|
|
|
|
|
LINK->L2TIME = PORT->PORTT1;
|
|
|
|
|
|
|
|
|
|
LINK->LINKPORT = PORT;
|
|
|
|
|
|
|
|
|
|
// save calls so we can match up SABM when it comes
|
|
|
|
|
|
|
|
|
|
memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7);
|
|
|
|
|
LINK->LINKCALL[6] &= 0x1e; // Mask SSID
|
|
|
|
|
|
|
|
|
|
memcpy(LINK->OURCALL, Buffer->DEST, 7);
|
|
|
|
|
|
|
|
|
|
LINK->OURCALL[6] &= 0x1e; // Mask SSID
|
|
|
|
|
|
|
|
|
|
memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT
|
|
|
|
|
|
|
|
|
|
if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address
|
|
|
|
|
{
|
|
|
|
|
// THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK
|
|
|
|
|
|
|
|
|
|
memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT
|
|
|
|
|
|
|
|
|
|
ptr1 = &Buffer->ORIGIN[6]; // End of add
|
|
|
|
|
ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi
|
|
|
|
|
|
|
|
|
|
while((*ptr1 & 1) == 0) // End of address bit
|
|
|
|
|
{
|
|
|
|
|
ptr1++;
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr2[6] &= 0x1e; // Mask Repeated and Last bits
|
|
|
|
|
ptr2 -= 7;
|
|
|
|
|
ptr1 += 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY
|
|
|
|
|
|
|
|
|
|
n = PORT->PORTMAXDIGIS;
|
|
|
|
|
|
|
|
|
|
ptr1 = ptr2 + 7; // First in TEMPDIGIS
|
|
|
|
|
ptr2 = &LINK->DIGIS[0];
|
|
|
|
|
|
|
|
|
|
while (*ptr1)
|
|
|
|
|
{
|
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
|
|
|
|
// Too many for us
|
|
|
|
|
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr1 += 7;
|
|
|
|
|
ptr2 += 7;
|
|
|
|
|
n--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ADJBUFFER->CTL = CTL | PFBIT;
|
|
|
|
|
|
|
|
|
|
// Buffer->LENGTH = (UCHAR *)ADJBUFFER - (UCHAR *)Buffer + MSGHDDRLEN + 15; // SET UP BYTE COUNT
|
|
|
|
|
|
|
|
|
|
L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS
|
|
|
|
|
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
BadXID:
|
|
|
|
|
L2SENDINVALIDCTRL(PORT, Buffer, ADJBUFFER, CTL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int COUNTLINKS(int Port)
|
|
|
|
|
{
|
|
|
|
|
//COUNT LINKS ON PORT
|
|
|
|
|
|
|
|
|
|
int i = MAXLINKS, n = 0;
|
|
|
|
|
struct _LINKTABLE * LINK = LINKS;
|
|
|
|
|
|
|
|
|
|
while (i--)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->LINKPORT && LINK->LINKPORT->PORTNUMBER == Port)
|
|
|
|
|
n++;
|
|
|
|
|
|
|
|
|
|
LINK++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L2LINKACTIVE(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// MESSAGE ON AN ACTIVE LINK
|
|
|
|
|
|
|
|
|
|
int CTLlessPF = CTL & ~PFBIT;
|
|
|
|
|
|
|
|
|
|
PORT->L2FRAMESFORUS++;
|
|
|
|
|
|
|
|
|
|
// ONLY SABM or UI ALLOWED IF NO SESSION
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == 3) // UI
|
|
|
|
|
{
|
|
|
|
|
// A UI ADDRESSED TO US - SHOULD ONLY BE FOR IP, or possibly addressed NODES
|
|
|
|
|
|
|
|
|
|
switch(ADJBUFFER->PID)
|
|
|
|
|
{
|
|
|
|
|
case 0xcf: // Netrom
|
|
|
|
|
|
|
|
|
|
if (Buffer->L2DATA[0] == 0xff) // NODES
|
|
|
|
|
PROCESSNODEMESSAGE(Buffer, PORT);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 0xcc: // TCP
|
|
|
|
|
case 0xcd: // ARP
|
|
|
|
|
case 0x08: // NOS FRAGMENTED AX25 TCP/IP
|
|
|
|
|
|
|
|
|
|
Q_IP_MSG( Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == DISC)
|
|
|
|
|
{
|
|
|
|
|
InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 1)
|
|
|
|
|
{
|
|
|
|
|
// XID State. Should be XID response if 2.2 ok or DM/FRMR if not
|
|
|
|
|
|
|
|
|
|
if (MSGFLAG & RESP)
|
|
|
|
|
{
|
|
|
|
|
if (CTLlessPF == DM || CTLlessPF == FRMR)
|
|
|
|
|
{
|
|
|
|
|
// Doesn't support XID - Send SABM
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 2;
|
|
|
|
|
LINK->Ver2point2 = FALSE;
|
|
|
|
|
LINK->L2TIMER = 1; // USe retry to send SABM
|
|
|
|
|
}
|
|
|
|
|
else if (CTLlessPF == XID)
|
|
|
|
|
{
|
|
|
|
|
// Process response to make sure ok, Send SABM or DISC
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 2;
|
|
|
|
|
LINK->Ver2point2 = TRUE;// Must support 2.2 if responded to XID
|
|
|
|
|
LINK->L2TIMER = 1; // USe retry to send SABM
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Command on existing session. Could be due to other end missing
|
|
|
|
|
// the XID response, so if XID just resend response
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == XID && (MSGFLAG & CMDBIT))
|
|
|
|
|
{
|
|
|
|
|
// XID Command on active session. Other end may be restarting. Send Response
|
|
|
|
|
|
|
|
|
|
ProcessXIDCommand(LINK, PORT, Buffer, ADJBUFFER, CTL, MSGFLAG);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (CTLlessPF == SABM)
|
|
|
|
|
{
|
|
|
|
|
// SABM ON EXISTING SESSION - IF DISCONNECTING, REJECT
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 1) // Sent XID?
|
|
|
|
|
{
|
|
|
|
|
L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 4) // DISCONNECTING?
|
|
|
|
|
{
|
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// THIS IS A SABM ON AN EXISTING SESSION
|
|
|
|
|
|
|
|
|
|
// THERE ARE SEVERAL POSSIBILITIES:
|
|
|
|
|
|
|
|
|
|
// 1. RECONNECT COMMAND TO TNC
|
|
|
|
|
// 2. OTHER END THINKS LINK HAS DIED
|
|
|
|
|
// 3. RECOVERY FROM FRMR CONDITION
|
|
|
|
|
// 4. REPEAT OF ORIGINAL SABM COS OTHER END MISSED UA
|
|
|
|
|
|
|
|
|
|
// FOR 1-3 IT IS REASONABLE TO FULLY RESET THE CIRCUIT, BUT IN 4
|
|
|
|
|
// SUCH ACTION WILL LOSE THE INITIAL SIGNON MSG IF CONNECTING TO A
|
|
|
|
|
// BBS. THE PROBLEM IS TELLING THE DIFFERENCE. I'M GOING TO SET A FLAG
|
|
|
|
|
// WHEN FIRST INFO RECEIVED - IF SABM REPEATED BEFORE THIS, I'LL ASSUME
|
|
|
|
|
// CONDITION 4, AND JUST RESEND THE UA
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (LINK->SESSACTIVE == 0) // RESET OF ACTIVE CIRCUIT?
|
|
|
|
|
{
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER); // No, so repeat UA
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END
|
|
|
|
|
|
|
|
|
|
L2SABM(LINK, PORT, Buffer, ADJBUFFER, MSGFLAG); // Process the SABM
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L2_PROCESS(LINK, PORT, Buffer, CTL, MSGFLAG);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L2SABM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// SET UP NEW SESSION (OR RESET EXISTING ONE)
|
|
|
|
|
|
|
|
|
|
TRANSPORTENTRY * Session;
|
|
|
|
|
int CONERROR;
|
|
|
|
|
|
|
|
|
|
if (LINK == 0) // NO LINK ENTRIES - SEND DM RESPONSE
|
|
|
|
|
{
|
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SETUPNEWL2SESSION(LINK, PORT, Buffer, MSGFLAG);
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE != 5) // Setup OK?
|
|
|
|
|
{
|
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER); // Failed
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// IF CONNECT TO APPL ADDRESS, SET UP APPL SESSION
|
|
|
|
|
|
|
|
|
|
if (APPLMASK == 0)
|
|
|
|
|
{
|
|
|
|
|
// Not ATTACH TO APPL
|
|
|
|
|
|
|
|
|
|
// Send CTEXT if connect to NODE/Port Alias, or NODE/Port Call, and FULL_CTEXT set
|
|
|
|
|
// Dont sent to known NODEs, or appl connects
|
|
|
|
|
|
|
|
|
|
struct DATAMESSAGE * Msg;
|
|
|
|
|
int Totallen = 0;
|
|
|
|
|
int Paclen= PORT->PORTPACLEN;
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
AttachKISSHF(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (LogAllConnects)
|
|
|
|
|
{
|
|
|
|
|
char toCall[12], fromCall[12];
|
|
|
|
|
toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
|
|
|
|
|
fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
|
|
|
|
|
WriteConnectLog(fromCall, toCall, "AX.25");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
|
|
|
|
|
if (NO_CTEXT == 1)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (FULL_CTEXT == 0 && !ALIASMSG) // Any connect, or call to alias
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// if Port CTEXT defined, use it
|
|
|
|
|
|
|
|
|
|
if (PORT->CTEXT)
|
|
|
|
|
{
|
|
|
|
|
Totallen = strlen(PORT->CTEXT);
|
|
|
|
|
ptr = PORT->CTEXT;
|
|
|
|
|
}
|
|
|
|
|
else if (CTEXTLEN)
|
|
|
|
|
{
|
|
|
|
|
Totallen = CTEXTLEN;
|
|
|
|
|
ptr = CTEXTMSG;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (Paclen == 0)
|
|
|
|
|
Paclen = PACLEN;
|
|
|
|
|
|
|
|
|
|
while(Totallen)
|
|
|
|
|
{
|
|
|
|
|
Msg = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Msg == NULL)
|
|
|
|
|
break; // No Buffers
|
|
|
|
|
|
|
|
|
|
Msg->PID = 0xf0;
|
|
|
|
|
|
|
|
|
|
if (Paclen > Totallen)
|
|
|
|
|
Paclen = Totallen;
|
|
|
|
|
|
|
|
|
|
memcpy(Msg->L2DATA, ptr, Paclen);
|
|
|
|
|
Msg->LENGTH = Paclen + MSGHDDRLEN + 1;
|
|
|
|
|
|
|
|
|
|
C_Q_ADD(&LINK->TX_Q, Msg);
|
|
|
|
|
|
|
|
|
|
ptr += Paclen;
|
|
|
|
|
Totallen -= Paclen;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Connnect to APPL
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE != 1)
|
|
|
|
|
{
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER); // RESET OF DOWN/CROSSLINK
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->CIRCUITPOINTER)
|
|
|
|
|
{
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IF RUNNING ONLY BBS (NODE=0), THIS MAY BE EITHER A USER OR NODE
|
|
|
|
|
// TRYING TO SET UP A L4 CIRCUIT - WE DONT WANT TO ATTACH A NODE TO
|
|
|
|
|
// THE BBS!
|
|
|
|
|
|
|
|
|
|
if (NODE == 0)
|
|
|
|
|
{
|
|
|
|
|
// NOW THINGS GET DIFICULT - WE MUST EITHER WAIT TO SEE IF A PID CF MSG
|
|
|
|
|
// ARRIVES, OR ASSUME ALL NODES ARE IN NEIGHBOURS - I'LL TRY THE LATTER
|
|
|
|
|
// AND SEE HOW IT GOES. tHIS MEANS THAT YOU MUST DEFINE ALL ROUTES
|
|
|
|
|
// IN CONFIG FILE
|
|
|
|
|
|
|
|
|
|
struct ROUTE * ROUTE;
|
|
|
|
|
|
|
|
|
|
if (FindNeighbour(Buffer->ORIGIN, PORT->PORTNUMBER, &ROUTE))
|
|
|
|
|
{
|
|
|
|
|
// It's a node
|
|
|
|
|
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER); // ALREADY SET UP - MUST BE REPEAT OF SABM OR LINK RESET
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Session = SetupSessionForL2(LINK); // CREATE INCOMING L4 SESSION
|
|
|
|
|
|
|
|
|
|
if (Session == NULL)
|
|
|
|
|
{
|
|
|
|
|
CLEAROUTLINK(LINK);
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NOW TRY A BBS CONNECT
|
|
|
|
|
// IF APPL CONNECT, SEE IF APPL HAS AN ALIAS
|
|
|
|
|
|
|
|
|
|
if (ALIASPTR[0] > ' ')
|
|
|
|
|
{
|
|
|
|
|
struct DATAMESSAGE * Msg;
|
|
|
|
|
|
|
|
|
|
// ACCEPT THE CONNECT, THEN INVOKE THE ALIAS
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
AttachKISSHF(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
if (LogAllConnects)
|
|
|
|
|
{
|
|
|
|
|
char toCall[12], fromCall[12];
|
|
|
|
|
toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
|
|
|
|
|
fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
|
|
|
|
|
WriteConnectLog(fromCall, toCall, "AX.25");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
|
|
|
|
|
Msg = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Msg)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
Msg->PID = 0xf0;
|
|
|
|
|
|
|
|
|
|
memcpy(Msg->L2DATA, APPL->APPLCMD, 12);
|
|
|
|
|
Msg->L2DATA[12] = 13;
|
|
|
|
|
|
|
|
|
|
Msg->LENGTH = MSGHDDRLEN + 12 + 2; // 2 for PID and CR
|
|
|
|
|
|
|
|
|
|
C_Q_ADD(&LINK->RX_Q, Msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cATTACHTOBBS(Session, APPLMASK, PORT->PORTPACLEN, &CONERROR) == 0)
|
|
|
|
|
{
|
|
|
|
|
// NO BBS AVAILABLE
|
|
|
|
|
|
|
|
|
|
CLEARSESSIONENTRY(Session);
|
|
|
|
|
CLEAROUTLINK(LINK);
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
L2SENDDM(PORT, Buffer, ADJBUFFER);
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
AttachKISSHF(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
if (LogAllConnects)
|
|
|
|
|
{
|
|
|
|
|
char toCall[12], fromCall[12];
|
|
|
|
|
toCall[ConvFromAX25(ADJBUFFER->DEST, toCall)] = 0;
|
|
|
|
|
fromCall[ConvFromAX25(ADJBUFFER->ORIGIN, fromCall)] = 0;
|
|
|
|
|
WriteConnectLog(fromCall, toCall, "AX.25");
|
|
|
|
|
}
|
|
|
|
|
L2SENDUA(PORT, Buffer, ADJBUFFER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SETUPNEWL2SESSION(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// COPY ADDRESS INFO TO LINK TABLE
|
|
|
|
|
|
|
|
|
|
UCHAR * ptr1, * ptr2;
|
|
|
|
|
UCHAR TEMPDIGI[57];
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
memcpy(LINK->LINKCALL, Buffer->ORIGIN, 7);
|
|
|
|
|
LINK->LINKCALL[6] &= 0x1e; // Mask SSID
|
|
|
|
|
|
|
|
|
|
memcpy(LINK->OURCALL, Buffer->DEST, 7);
|
|
|
|
|
LINK->OURCALL[6] &= 0x1e; // Mask SSID
|
|
|
|
|
|
|
|
|
|
memset(LINK->DIGIS, 0, 56); // CLEAR DIGI FIELD IN CASE RECONNECT
|
|
|
|
|
|
|
|
|
|
LINK->L2TIME = PORT->PORTT1; // Set tomeoiut for no digis
|
|
|
|
|
|
|
|
|
|
if ((Buffer->ORIGIN[6] & 1) == 0) // End of Address
|
|
|
|
|
{
|
|
|
|
|
// THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK
|
|
|
|
|
|
|
|
|
|
memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT
|
|
|
|
|
|
|
|
|
|
ptr1 = &Buffer->ORIGIN[6]; // End of add
|
|
|
|
|
ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi
|
|
|
|
|
|
|
|
|
|
while((*ptr1 & 1) == 0) // End of address bit
|
|
|
|
|
{
|
|
|
|
|
ptr1++;
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr2[6] &= 0x1e; // Mask Repeated and Last bits
|
|
|
|
|
ptr2 -= 7;
|
|
|
|
|
ptr1 += 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LIST OF DIGI CALLS COMPLETE - COPY TO LINK CONTROL ENTRY
|
|
|
|
|
|
|
|
|
|
n = PORT->PORTMAXDIGIS;
|
|
|
|
|
|
|
|
|
|
ptr1 = ptr2 + 7; // First in TEMPDIGIS
|
|
|
|
|
ptr2 = &LINK->DIGIS[0];
|
|
|
|
|
|
|
|
|
|
while (*ptr1)
|
|
|
|
|
{
|
|
|
|
|
if (n == 0)
|
|
|
|
|
{
|
|
|
|
|
// Too many for us
|
|
|
|
|
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr1 += 7;
|
|
|
|
|
ptr2 += 7;
|
|
|
|
|
n--;
|
|
|
|
|
|
|
|
|
|
LINK->L2TIME += PORT->PORTT1; // Adjust timeout for digis
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// THIS MAY BE RESETTING A LINK - BEWARE OF CONVERTING A CROSSLINK TO
|
|
|
|
|
// AN UPLINK AND CONFUSING EVERYTHING
|
|
|
|
|
|
|
|
|
|
LINK->LINKPORT = PORT;
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE == 0)
|
|
|
|
|
{
|
|
|
|
|
if (ISNETROMMSG && NODE == 0) // Only allow crosslink if node = 0
|
|
|
|
|
LINK->LINKTYPE = 3; // Crosslink
|
|
|
|
|
else
|
|
|
|
|
LINK->LINKTYPE = 1; // Uplink
|
|
|
|
|
}
|
|
|
|
|
LINK->L2TIMER = 0; // CANCEL TIMER
|
|
|
|
|
|
|
|
|
|
LINK->L2SLOTIM = T3; // SET FRAME SENT RECENTLY
|
|
|
|
|
|
|
|
|
|
LINK->LINKWINDOW = PORT->PORTWINDOW;
|
|
|
|
|
|
|
|
|
|
RESET2(LINK); // RESET ALL FLAGS
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 5;
|
|
|
|
|
|
|
|
|
|
// IF VERSION 1 MSG, SET FLAG
|
|
|
|
|
|
|
|
|
|
if (MSGFLAG & VER1)
|
|
|
|
|
LINK->VER1FLAG |= 1;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2SENDUA(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER)
|
|
|
|
|
{
|
|
|
|
|
L2SENDRESP(PORT, Buffer, ADJBUFFER, UA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2SENDDM(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER)
|
|
|
|
|
{
|
|
|
|
|
L2SENDRESP(PORT, Buffer, ADJBUFFER, DM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2SENDRESP(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL)
|
|
|
|
|
{
|
|
|
|
|
// QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY
|
|
|
|
|
|
|
|
|
|
// SET APPROPRIATE P/F BIT
|
|
|
|
|
|
|
|
|
|
ADJBUFFER->CTL = CTL | PFBIT;
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 15; // SET UP BYTE COUNT
|
|
|
|
|
|
|
|
|
|
L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS
|
|
|
|
|
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L2SENDINVALIDCTRL(struct PORTCONTROL * PORT, MESSAGE * Buffer, MESSAGE * ADJBUFFER, UCHAR CTL)
|
|
|
|
|
{
|
|
|
|
|
// Send FRMR Invalid Control field
|
|
|
|
|
|
|
|
|
|
// QUEUE RESPONSE TO PORT CONTROL - MAY NOT HAVE A LINK ENTRY
|
|
|
|
|
|
|
|
|
|
// SET APPROPRIATE P/F BIT
|
|
|
|
|
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
|
|
|
|
|
ADJBUFFER->CTL = FRMR | PFBIT;
|
|
|
|
|
|
|
|
|
|
ptr = &ADJBUFFER->PID;
|
|
|
|
|
|
|
|
|
|
*(ptr++) = CTL; // MOVE REJECT C-BYTE
|
|
|
|
|
*(ptr++) = 0;
|
|
|
|
|
*(ptr++) = SDINVC; // MOVE REJECT FLAGS
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)((UCHAR *)ADJBUFFER - (UCHAR *)Buffer) + MSGHDDRLEN + 18; // SET UP BYTE COUNT
|
|
|
|
|
|
|
|
|
|
L2SWAPADDRESSES(Buffer); // SWAP ADDRESSES AND SET RESP BITS
|
|
|
|
|
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2SWAPADDRESSES(MESSAGE * Buffer)
|
|
|
|
|
{
|
|
|
|
|
// EXCHANGE ORIGIN AND DEST, AND REVERSE DIGIS (IF PRESENT)
|
|
|
|
|
|
|
|
|
|
char TEMPFIELD[7];
|
|
|
|
|
UCHAR * ptr1, * ptr2;
|
|
|
|
|
UCHAR TEMPDIGI[57];
|
|
|
|
|
|
|
|
|
|
memcpy(TEMPFIELD, Buffer->ORIGIN, 7);
|
|
|
|
|
memcpy(Buffer->ORIGIN, Buffer->DEST, 7);
|
|
|
|
|
memcpy(Buffer->DEST, TEMPFIELD, 7);
|
|
|
|
|
|
|
|
|
|
Buffer->ORIGIN[6] &= 0x1e; // Mask SSID
|
|
|
|
|
Buffer->ORIGIN[6] |= 0xe0; // Reserved and Response
|
|
|
|
|
|
|
|
|
|
Buffer->DEST[6] &= 0x1e; // Mask SSID
|
|
|
|
|
Buffer->DEST[6] |= 0x60; // Reserved
|
|
|
|
|
|
|
|
|
|
if ((TEMPFIELD[6] & 1) == 0)
|
|
|
|
|
{
|
|
|
|
|
// THERE ARE DIGIS TO PROCESS - COPY TO WORK AREA reversed, THEN COPY BACK
|
|
|
|
|
|
|
|
|
|
memset(TEMPDIGI, 0, 57); // CLEAR DIGI FIELD IN CASE RECONNECT
|
|
|
|
|
|
|
|
|
|
ptr1 = &Buffer->ORIGIN[6]; // End of add
|
|
|
|
|
ptr2 = &TEMPDIGI[7 * 7]; // Last Temp Digi
|
|
|
|
|
|
|
|
|
|
while((*ptr1 & 1) == 0) // End of address bit
|
|
|
|
|
{
|
|
|
|
|
ptr1++;
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr2[6] &= 0x1e; // Mask Repeated and Last bits
|
|
|
|
|
ptr2 -= 7;
|
|
|
|
|
ptr1 += 6;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LIST OF DIGI CALLS COMPLETE - copy back
|
|
|
|
|
|
|
|
|
|
ptr1 = ptr2 + 7; // First in TEMPDIGIS
|
|
|
|
|
ptr2 = &Buffer->CTL;
|
|
|
|
|
|
|
|
|
|
while (*ptr1)
|
|
|
|
|
{
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr1 += 7;
|
|
|
|
|
ptr2 += 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*(ptr2 - 1) |= 1; // End of addresses
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Buffer->ORIGIN[6] |= 1; // End of address
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL InternalL2SETUPCROSSLINK(PROUTE ROUTE, int Retries)
|
|
|
|
|
{
|
|
|
|
|
// ROUTE POINTS TO A NEIGHBOUR - FIND AN L2 SESSION FROM US TO IT, OR INITIATE A NEW ONE
|
|
|
|
|
|
|
|
|
|
struct _LINKTABLE * LINK;
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
int FRACK;
|
|
|
|
|
|
|
|
|
|
if (FindLink(ROUTE->NEIGHBOUR_CALL, NETROMCALL, ROUTE->NEIGHBOUR_PORT, &LINK))
|
|
|
|
|
{
|
|
|
|
|
// SESSION ALREADY EXISTS
|
|
|
|
|
|
|
|
|
|
LINK->LINKTYPE = 3; // MAKE SURE IT KNOWS ITS A CROSSLINK
|
|
|
|
|
ROUTE->NEIGHBOUR_LINK = LINK;
|
|
|
|
|
LINK->NEIGHBOUR = ROUTE;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SET UP NEW SESSION (OR RESET EXISTING ONE)
|
|
|
|
|
|
|
|
|
|
if (LINK == NULL)
|
|
|
|
|
return FALSE; // No free links
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ROUTE->NEIGHBOUR_LINK = LINK;
|
|
|
|
|
LINK->NEIGHBOUR = ROUTE;
|
|
|
|
|
|
|
|
|
|
LINK->LINKPORT = PORT = GetPortTableEntryFromPortNum(ROUTE->NEIGHBOUR_PORT);
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
return FALSE; // maybe port has been deleted
|
|
|
|
|
|
|
|
|
|
// IF ROUTE HAS A FRACK, SET IT
|
|
|
|
|
|
|
|
|
|
if (ROUTE->NBOUR_FRACK)
|
|
|
|
|
FRACK = ROUTE->NBOUR_FRACK;
|
|
|
|
|
else
|
|
|
|
|
FRACK = PORT->PORTT1;
|
|
|
|
|
|
|
|
|
|
LINK->L2TIME = FRACK; // SET TIMER VALUE
|
|
|
|
|
|
|
|
|
|
// IF ROUTE HAS A WINDOW, SET IT
|
|
|
|
|
|
|
|
|
|
if (ROUTE->NBOUR_MAXFRAME)
|
|
|
|
|
LINK->LINKWINDOW = ROUTE->NBOUR_MAXFRAME;
|
|
|
|
|
else
|
|
|
|
|
LINK->LINKWINDOW = PORT->PORTWINDOW;
|
|
|
|
|
|
|
|
|
|
// if (SUPPORT2point2)
|
|
|
|
|
// LINK->L2STATE = 1; // Send XID
|
|
|
|
|
// else
|
|
|
|
|
LINK->L2STATE = 2;
|
|
|
|
|
|
|
|
|
|
memcpy(LINK->LINKCALL, ROUTE->NEIGHBOUR_CALL, 7);
|
|
|
|
|
memcpy(LINK->OURCALL, NETROMCALL, 7);
|
|
|
|
|
|
|
|
|
|
if (ROUTE->NEIGHBOUR_DIGI1[0])
|
|
|
|
|
{
|
|
|
|
|
memcpy(LINK->DIGIS, ROUTE->NEIGHBOUR_DIGI1, 7);
|
|
|
|
|
LINK->L2TIME += FRACK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ROUTE->NEIGHBOUR_DIGI2[0])
|
|
|
|
|
{
|
|
|
|
|
memcpy(&LINK->DIGIS[7], ROUTE->NEIGHBOUR_DIGI1, 7);
|
|
|
|
|
LINK->L2TIME += FRACK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LINK->LINKTYPE = 3; // CROSSLINK
|
|
|
|
|
|
|
|
|
|
if (Retries)
|
|
|
|
|
LINK->L2RETRIES = PORT->PORTN2 - Retries;
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 1)
|
|
|
|
|
L2SENDXID(LINK);
|
|
|
|
|
else
|
|
|
|
|
SENDSABM(LINK);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
BOOL L2SETUPCROSSLINKEX(PROUTE ROUTE, int Retries)
|
|
|
|
|
{
|
|
|
|
|
// Allows caller to specify number of times SABM should be sent
|
|
|
|
|
|
|
|
|
|
return InternalL2SETUPCROSSLINK(ROUTE, Retries);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL L2SETUPCROSSLINK(PROUTE ROUTE)
|
|
|
|
|
{
|
|
|
|
|
return InternalL2SETUPCROSSLINK(ROUTE, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2_PROCESS(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// PROCESS LEVEL 2 PROTOCOL STUFF
|
|
|
|
|
|
|
|
|
|
// SEE IF COMMAND OR RESPONSE
|
|
|
|
|
|
|
|
|
|
if ((MSGFLAG & CMDBIT) == 0)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// RESPONSE OR VERSION 1
|
|
|
|
|
|
|
|
|
|
// IF RETRYING, MUST ONLY ACCEPT RESPONSES WITH F SET (UNLESS RUNNING V1)
|
|
|
|
|
|
|
|
|
|
if ((CTL & PFBIT) || LINK->VER1FLAG == 1)
|
|
|
|
|
{
|
|
|
|
|
// F SET or V1 - CAN CANCEL TIMER
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = 0; // CANCEL LINK TIMER
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 3)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// FRMR STATE - IF C(P) SEND FRMR, ELSE IGNORE
|
|
|
|
|
|
|
|
|
|
if (CTL & PFBIT)
|
|
|
|
|
{
|
|
|
|
|
if (CTL == (FRMR | PFBIT)) // if both ends in FRMR state, reset link
|
|
|
|
|
{
|
|
|
|
|
RESET2(LINK);
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 2; // INITIALISING
|
|
|
|
|
LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE
|
|
|
|
|
LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM
|
|
|
|
|
|
|
|
|
|
L2SENDCOMMAND(LINK, SABM | PFBIT);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (MSGFLAG & CMDBIT)
|
|
|
|
|
{
|
|
|
|
|
// SEND FRMR AGAIN
|
|
|
|
|
|
|
|
|
|
SENDFRMR(LINK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE >= 5)
|
|
|
|
|
{
|
|
|
|
|
// LINK IN STATE 5 OR ABOVE - LINK RUNNING
|
|
|
|
|
|
|
|
|
|
if ((CTL & 1) == 0) // I frame
|
|
|
|
|
{
|
|
|
|
|
SDIFRM(LINK, PORT, Buffer, CTL, MSGFLAG); // consumes buffer
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((CTL & 2)) // U frame
|
|
|
|
|
{
|
|
|
|
|
SDUFRM(LINK, PORT, Buffer, CTL); //consumes buffer
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ELSE SUPERVISORY, MASK OFF N(R) AND P-BIT
|
|
|
|
|
|
|
|
|
|
switch (CTL & 0x0f)
|
|
|
|
|
{
|
|
|
|
|
// is there any harm in accepoting SREJ even if we don't
|
|
|
|
|
// otherwise support 2.2?
|
|
|
|
|
|
|
|
|
|
case REJ:
|
|
|
|
|
case SREJ:
|
|
|
|
|
|
|
|
|
|
PORT->L2REJCOUNT++;
|
|
|
|
|
|
|
|
|
|
case RR:
|
|
|
|
|
case RNR:
|
|
|
|
|
|
|
|
|
|
SFRAME(LINK, PORT, CTL, MSGFLAG);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
// UNRECOGNISABLE COMMAND
|
|
|
|
|
|
|
|
|
|
LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE
|
|
|
|
|
LINK->SDREJF |= SDINVC; // SET INVALID COMMAND REJECT
|
|
|
|
|
SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NORMAL DISCONNECT MODE
|
|
|
|
|
|
|
|
|
|
// COULD BE UA, DM - SABM AND DISC HANDLED ABOVE
|
|
|
|
|
|
|
|
|
|
switch (CTL & ~PFBIT)
|
|
|
|
|
{
|
|
|
|
|
case UA:
|
|
|
|
|
|
|
|
|
|
// UA RECEIVED
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 2)
|
|
|
|
|
{
|
|
|
|
|
// RESPONSE TO SABM - SET LINK UP
|
|
|
|
|
|
|
|
|
|
RESET2X(LINK); // LEAVE QUEUED STUFF
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 5;
|
|
|
|
|
LINK->L2TIMER = 0; // CANCEL TIMER
|
|
|
|
|
LINK->L2RETRIES = 0;
|
|
|
|
|
LINK->L2SLOTIM, T3; // SET FRAME SENT RECENTLY
|
|
|
|
|
|
|
|
|
|
// IF VERSION 1 MSG, SET FLAG
|
|
|
|
|
|
|
|
|
|
if (MSGFLAG & VER1)
|
|
|
|
|
LINK->VER1FLAG |= 1;
|
|
|
|
|
|
|
|
|
|
// TELL PARTNER CONNECTION IS ESTABLISHED
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
KISSHFConnected(PORT, LINK);
|
|
|
|
|
|
|
|
|
|
SENDCONNECTREPLY(LINK);
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 4) // DISCONNECTING?
|
|
|
|
|
{
|
|
|
|
|
InformPartner(LINK, NORMALCLOSE); // SEND DISC TO OTHER END
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UA, BUT NOT IN STATE 2 OR 4 - IGNORE
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case DM:
|
|
|
|
|
|
|
|
|
|
// DM RESPONSE - IF TO SABM, SEND BUSY MSG
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 2)
|
|
|
|
|
{
|
|
|
|
|
CONNECTREFUSED(LINK); // SEND MESSAGE IF DOWNLINK
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-18 10:09:06 +01:00
|
|
|
|
// DM RESP TO DISC RECEIVED - OTHER END HAS LOST SESSION
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
// CLEAR OUT TABLE ENTRY - IF INTERNAL TNC, SHOULD SEND *** DISCONNECTED
|
|
|
|
|
|
|
|
|
|
InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
case FRMR:
|
|
|
|
|
|
|
|
|
|
// FRAME REJECT RECEIVED - LOG IT AND RESET LINK
|
|
|
|
|
|
|
|
|
|
RESET2(LINK);
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 2; // INITIALISING
|
|
|
|
|
LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE
|
|
|
|
|
LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM
|
|
|
|
|
|
|
|
|
|
PORT->L2FRMRRX++;
|
|
|
|
|
|
|
|
|
|
L2SENDCOMMAND(LINK, SABM | PFBIT);
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
// ANY OTHER - IGNORE
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SDUFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL)
|
|
|
|
|
{
|
|
|
|
|
// PROCESS AN UNSEQUENCED COMMAND (IN LINK UP STATES)
|
|
|
|
|
|
|
|
|
|
switch (CTL & ~PFBIT)
|
|
|
|
|
{
|
|
|
|
|
case UA:
|
|
|
|
|
|
|
|
|
|
// DISCARD - PROBABLY REPEAT OF ACK OF SABM
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case FRMR:
|
|
|
|
|
|
|
|
|
|
// FRAME REJECT RECEIVED - LOG IT AND RESET LINK
|
|
|
|
|
|
|
|
|
|
RESET2(LINK);
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 2; // INITIALISING
|
|
|
|
|
LINK->L2ACKREQ = 0; // DONT SEND ANYTHING ELSE
|
|
|
|
|
LINK->L2RETRIES = 0; // ALLOW FULL RETRY COUNT FOR SABM
|
|
|
|
|
|
|
|
|
|
PORT->L2FRMRRX++;
|
|
|
|
|
|
|
|
|
|
L2SENDCOMMAND(LINK, SABM | PFBIT);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DM:
|
|
|
|
|
|
|
|
|
|
// DM RESPONSE - SESSION MUST HAVE GONE
|
|
|
|
|
|
|
|
|
|
// SEE IF CROSSLINK ACTIVE
|
|
|
|
|
|
|
|
|
|
InformPartner(LINK, LINKLOST); // SEND DISC TO OTHER END
|
|
|
|
|
CLEAROUTLINK(LINK);
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
// UNDEFINED COMMAND
|
|
|
|
|
|
|
|
|
|
LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE
|
|
|
|
|
LINK->SDREJF |= SDINVC;
|
|
|
|
|
SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID SFRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, UCHAR CTL, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
// CHECK COUNTS, AND IF RNR INDICATE _BUFFER SHORTAGE AT OTHER END
|
|
|
|
|
|
|
|
|
|
if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET?
|
|
|
|
|
{
|
|
|
|
|
SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R)
|
|
|
|
|
|
|
|
|
|
if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW?
|
|
|
|
|
{
|
|
|
|
|
SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((CTL & 0xf) == SREJ)
|
|
|
|
|
{
|
|
|
|
|
// Probably safer to handle SREJ completely separately
|
|
|
|
|
|
|
|
|
|
// Can we get SREJ Command with P??(Yes)
|
|
|
|
|
|
|
|
|
|
// Can we just resend missing frame ?? (Think so!)
|
|
|
|
|
|
|
|
|
|
// We support MultiSREJ (can gave additional missing frame
|
|
|
|
|
// numbers in the Info field
|
|
|
|
|
|
|
|
|
|
// I don't see the point of Multi unless we wait fot an F bit,
|
|
|
|
|
// bur maybe not safe to assume others do the same
|
|
|
|
|
|
|
|
|
|
// So if I get SREJ(F) I can send missing frame(s)
|
|
|
|
|
|
|
|
|
|
if (MSGFLAG & RESP)
|
|
|
|
|
{
|
|
|
|
|
// SREJ Response
|
|
|
|
|
|
|
|
|
|
if (CTL & PFBIT)
|
|
|
|
|
{
|
|
|
|
|
// SREJ(F). Send Frames()
|
|
|
|
|
|
|
|
|
|
UCHAR NS = (CTL >> 5) & 7; // Frame to resend
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
UCHAR * ptr1, * ptr2;
|
|
|
|
|
UCHAR CTL;
|
|
|
|
|
int count;
|
|
|
|
|
MESSAGE * Msg;
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
|
|
|
|
|
Msg = LINK->FRAMES[NS]; // is frame available?
|
|
|
|
|
|
|
|
|
|
if (Msg == NULL)
|
|
|
|
|
return; // Wot!!
|
|
|
|
|
|
|
|
|
|
// send the frame
|
|
|
|
|
|
|
|
|
|
// GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES
|
|
|
|
|
|
|
|
|
|
Buffer = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses
|
|
|
|
|
|
|
|
|
|
// ptr2 NOW POINTS TO COMMAND BYTE
|
|
|
|
|
|
|
|
|
|
// GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED
|
|
|
|
|
LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
|
|
|
|
|
LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER
|
|
|
|
|
|
|
|
|
|
CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS
|
|
|
|
|
CTL |= NS << 1; // BITS 1-3 OF CONTROL BYTE
|
|
|
|
|
|
|
|
|
|
// SET P BIT IF NO MORE TO SEND (only more if Multi SREJ)
|
|
|
|
|
|
|
|
|
|
if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1
|
|
|
|
|
{
|
|
|
|
|
CTL |= PFBIT;
|
|
|
|
|
LINK->L2FLAGS |= POLLSENT;
|
|
|
|
|
LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
|
|
|
|
|
|
|
|
|
|
// FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE)
|
|
|
|
|
|
|
|
|
|
Buffer->Linkptr = LINK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*(ptr2++) = CTL; // TO DATA (STARTING WITH PID)
|
|
|
|
|
|
|
|
|
|
count = Msg->LENGTH - MSGHDDRLEN;
|
|
|
|
|
|
|
|
|
|
if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY
|
|
|
|
|
{
|
|
|
|
|
ptr1 = (UCHAR *)Msg;
|
|
|
|
|
ptr1 += MSGHDDRLEN;
|
|
|
|
|
memcpy(ptr2, ptr1, count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer->DEST[6] |= 0x80; // SET COMMAND
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
|
|
|
|
|
if (PORT)
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->PORTNUMBER;
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Buffer->Linkptr = 0;
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VALID RR/RNR RECEIVED
|
|
|
|
|
|
|
|
|
|
LINK->L2FLAGS &= ~RNRSET; //CLEAR RNR
|
|
|
|
|
|
|
|
|
|
if ((CTL & 0xf) == RNR)
|
|
|
|
|
LINK->L2FLAGS |= RNRSET; //Set RNR
|
|
|
|
|
|
|
|
|
|
if (MSGFLAG & CMDBIT)
|
|
|
|
|
{
|
|
|
|
|
// ALWAYS REPLY TO RR/RNR/REJ COMMAND (even if no P bit ??)
|
|
|
|
|
|
|
|
|
|
// FIRST PROCESS RESEQ QUEUE
|
|
|
|
|
|
|
|
|
|
//; CALL PROCESS_RESEQ
|
|
|
|
|
|
|
|
|
|
// IGNORE IF AN 'F' HAS BEEN SENT RECENTLY
|
|
|
|
|
|
|
|
|
|
if (LINK->LAST_F_TIME + 15 > REALTIMETICKS)
|
|
|
|
|
return; // DISCARD
|
|
|
|
|
|
|
|
|
|
CTL = RR_OR_RNR(LINK);
|
|
|
|
|
|
|
|
|
|
CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS
|
|
|
|
|
CTL |= PFBIT;
|
|
|
|
|
|
|
|
|
|
L2SENDRESPONSE(LINK, CTL);
|
|
|
|
|
|
|
|
|
|
LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = 0; // CANCEL DELAYED ACKL2
|
|
|
|
|
|
|
|
|
|
// SAVE TIME IF 'F' SENT'
|
|
|
|
|
|
|
|
|
|
LINK->LAST_F_TIME = REALTIMETICKS;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Response
|
|
|
|
|
|
|
|
|
|
if ((CTL & PFBIT) == 0 && LINK->VER1FLAG == 0)
|
|
|
|
|
{
|
|
|
|
|
// RESPONSE WITHOUT P/F DONT RESET N(S) (UNLESS V1)
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RESPONSE WITH P/F - MUST BE REPLY TO POLL FOLLOWING TIMEOUT OR I(P)
|
|
|
|
|
|
|
|
|
|
// THERE IS A PROBLEM WITH REPEATED RR(F), SAY CAUSED BY DELAY AT L1
|
|
|
|
|
|
|
|
|
|
// AS FAR AS I CAN SEE, WE SHOULD ONLY RESET N(S) IF AN RR(F) FOLLOWS
|
|
|
|
|
// AN RR(P) AFTER A TIMEOUT - AN RR(F) FOLLOWING AN I(P) CANT POSSIBLY
|
|
|
|
|
// INDICATE A LOST FRAME. ON THE OTHER HAND, A REJ(F) MUST INDICATE
|
|
|
|
|
// A LOST FRAME. So dont reset NS if not retrying, unless REJ
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// someone (probably WLE KISS Driver) is sending REJ followed by RR(F)
|
|
|
|
|
// after lost frame and i(p)
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
1:Fm W4DHW-10 To W4DHW <I C P R1 S4 Pid=F0 Len=236> [17:08:03R] [+++]
|
|
|
|
|
<EFBFBD>J<EFBFBD><EFBFBD>ZK<EFBFBD>)x@D<EFBFBD>B<EFBFBD>rN<EFBFBD><EFBFBD>4X<EFBFBD>;i<EFBFBD>#C<EFBFBD>M<EFBFBD>,<EFBFBD>нҼ<EFBFBD><EFBFBD>r<EFBFBD><EFBFBD>O<EFBFBD> N<EFBFBD>X<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>5<EFBFBD>(<EFBFBD>|<EFBFBD><EFBFBD><EFBFBD>#<EFBFBD><EFBFBD><EFBFBD>U<EFBFBD><EFBFBD><EFBFBD>cY<EFBFBD><EFBFBD>l<EFBFBD><EFBFBD><EFBFBD><EFBFBD>)<EFBFBD><EFBFBD>璘o<EFBFBD>ȼ<EFBFBD>><EFBFBD><EFBFBD>9<EFBFBD>*<EFBFBD>G<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(6<EFBFBD>5C<EFBFBD>!<EFBFBD>L<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ُQ<EFBFBD><EFBFBD>p<EFBFBD><EFBFBD>IH<EFBFBD><EFBFBD>;<EFBFBD><EFBFBD>i<EFBFBD><EFBFBD><EFBFBD><EFBFBD>><EFBFBD>9p<EFBFBD>B<EFBFBD><EFBFBD><<EFBFBD>c<EFBFBD>EP<EFBFBD><EFBFBD><<EFBFBD><EFBFBD>{0a<EFBFBD>(<EFBFBD><EFBFBD>YՖ<EFBFBD>M<EFBFBD><EFBFBD><EFBFBD>N<EFBFBD>+<<EFBFBD>I<EFBFBD>[<EFBFBD><EFBFBD><EFBFBD><EFBFBD>Pw<EFBFBD>[^]6<EFBFBD>2\<EFBFBD><EFBFBD>9<EFBFBD><EFBFBD>ov{<EFBFBD><EFBFBD><EFBFBD>Ÿmm<d<EFBFBD>^
|
|
|
|
|
1:Fm W4DHW To W4DHW-10 <RR C P R1> [17:08:03T]
|
|
|
|
|
1:Fm W4DHW To W4DHW-10 <REJ R R1> [17:08:03T]
|
|
|
|
|
1:Fm W4DHW To W4DHW-10 <RR R F R1> [17:08:03T]
|
|
|
|
|
|
|
|
|
|
is there a problem with restting on RR(F) following I(P)?
|
|
|
|
|
|
|
|
|
|
I think the problem is restting NS twice if you get delayed responses to
|
|
|
|
|
I or RR (P). So lets try only resetting NS once for each P sent
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
// if ((CTL & 0xf) == REJ || LINK->L2RETRIES)
|
|
|
|
|
if ((LINK->L2FLAGS & POLLSENT))
|
|
|
|
|
{
|
|
|
|
|
RESETNS(LINK, (CTL >> 5) & 7); // RESET N(S) AND COUNT RETRIED FRAMES
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES = 0;
|
|
|
|
|
LINK->L2TIMER = 0; // WILL RESTART TIMER WHEN RETRY SENT
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) or RR(P) SET
|
|
|
|
|
|
|
|
|
|
if ((CTL & 0xf) == RNR)
|
|
|
|
|
{
|
|
|
|
|
// Dont Clear timer on receipt of RNR(F), spec says should poll for clearing of busy,
|
|
|
|
|
// and loss of subsequent RR will cause hang. Perhaps should set slightly longer time??
|
|
|
|
|
// Timer may have been cleared earlier, so restart it
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = LINK->L2TIME;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//*** PROCESS AN INFORMATION FRAME
|
|
|
|
|
|
|
|
|
|
VOID SDIFRM(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR CTL, UCHAR MSGFLAG)
|
|
|
|
|
{
|
|
|
|
|
int NS;
|
|
|
|
|
|
|
|
|
|
if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET?
|
|
|
|
|
{
|
|
|
|
|
SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDNRCHK(LINK, CTL); // CHECK RECEIVED N(R)
|
|
|
|
|
|
|
|
|
|
if (LINK->SDREJF) // ARE ANY REJECT FLAGS SET NOW?
|
|
|
|
|
{
|
|
|
|
|
SDFRMR(LINK, PORT); // PROCESS FRAME REJECT CONDITION
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LINK->SESSACTIVE = 1; // SESSION IS DEFINITELY SET UP
|
|
|
|
|
|
|
|
|
|
NS = (CTL >> 1) & 7; // ISOLATE RECEIVED N(S)
|
|
|
|
|
|
|
|
|
|
// IPOLL (sending an I(P) frame following timeout instead of RR(P))
|
|
|
|
|
// is a problem. We need to send REJ(F), but shouldn't add to collector.
|
|
|
|
|
// We also need to handle repeated I(P), so shouldn't set REJSENT in
|
|
|
|
|
// this state.
|
|
|
|
|
|
|
|
|
|
if ((((NS + 1) & 7) == LINK->LINKNR) && (CTL & PFBIT))
|
|
|
|
|
{
|
|
|
|
|
// Previous Frame and P set - Assume IPOLL
|
|
|
|
|
|
|
|
|
|
PORT->L2OUTOFSEQ++;
|
|
|
|
|
LINK->L2STATE = 6;
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = 0; // CANCEL RR NEEDED
|
|
|
|
|
|
|
|
|
|
// We need to protect against sending multiple REJ(F) if channel
|
|
|
|
|
// delays mean we get two I(P) close together (how close is close ??)
|
|
|
|
|
// SM has default IPOLL limit of 30 bytes or about a second at 300
|
|
|
|
|
// ACKMODE should avoid this anyway, and resptime of under 3 secs
|
|
|
|
|
// is unlikely so say 2.5 secs ??
|
|
|
|
|
|
|
|
|
|
if (LINK->LAST_F_TIME + 25 > REALTIMETICKS)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SEND_RR_RESP(LINK, PFBIT);
|
|
|
|
|
LINK->LAST_F_TIME = REALTIMETICKS;
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CheckNSLoop:
|
|
|
|
|
|
|
|
|
|
if (NS != LINK->LINKNR) // EQUAL TO OUR N(R)?
|
|
|
|
|
{
|
|
|
|
|
// There is a frame missing.
|
|
|
|
|
// if we have just sent a REJ we have at least one out
|
|
|
|
|
// of sequence frame in RXFRAMES
|
|
|
|
|
|
|
|
|
|
// so if we have frame LINK->LINKNR we can process it
|
|
|
|
|
// and remove it from RXFRAMES. If we are then back
|
|
|
|
|
// in sequence we just carry on.
|
|
|
|
|
|
|
|
|
|
if (LINK->RXFRAMES[LINK->LINKNR])
|
|
|
|
|
{
|
|
|
|
|
// We have the first missing frame. Process it.
|
|
|
|
|
|
|
|
|
|
MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]);
|
|
|
|
|
|
|
|
|
|
Debugprintf("L2 process saved Frame %d", LINK->LINKNR);
|
|
|
|
|
PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer
|
|
|
|
|
|
|
|
|
|
// NR has been updated.
|
|
|
|
|
|
|
|
|
|
goto CheckNSLoop; // See if OK or we have another saved frame
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// BAD FRAME, SEND REJ (AFTER RESPTIME - OR WE MAY SEND LOTS!)
|
|
|
|
|
|
|
|
|
|
// ALSO SAVE THE FRAME - NEXT TIME WE MAY GET A DIFFERENT SUBSET
|
|
|
|
|
// AND SOON WE WILL HANDLE SREJ
|
|
|
|
|
|
|
|
|
|
PORT->L2OUTOFSEQ++;
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 6;
|
|
|
|
|
|
|
|
|
|
// IF RUNNING VER1, AND OTHER END MISSES THIS REJ, LINK WILL FAIL
|
|
|
|
|
// SO TIME OUT REJ SENT STATE (MUST KEEP IT FOR A WHILE TO AVOID
|
|
|
|
|
// 'MULTIPLE REJ' PROBLEM)
|
|
|
|
|
|
|
|
|
|
if (LINK->VER1FLAG == 1)
|
|
|
|
|
LINK->REJTIMER = TENSECS;
|
|
|
|
|
|
|
|
|
|
// SET ACK REQUIRED TIMER - REJ WILL BE SENT WHEN IT EXPIRES
|
|
|
|
|
|
|
|
|
|
// if configured RESPTIME is longer than 3 secs use it (may be longer on HF)
|
|
|
|
|
|
|
|
|
|
if (PORT->PORTT2 > THREESECS)
|
|
|
|
|
LINK->L2ACKREQ = PORT->PORTT2;
|
|
|
|
|
else
|
|
|
|
|
LINK->L2ACKREQ = THREESECS; // EXTRA LONG RESPTIME, AS SENDING TOO MANY REJ'S IS SERIOUS
|
|
|
|
|
|
|
|
|
|
if (LINK->RXFRAMES[NS])
|
|
|
|
|
{
|
|
|
|
|
// Already have a copy, so discard old and keep this
|
|
|
|
|
|
|
|
|
|
Debugprintf ("Frame %d out of seq but already have copy - release it", NS);
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS]));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debugprintf ("Frame %d out of seq - save", NS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer->CHAIN = 0;
|
|
|
|
|
LINK->RXFRAMES[NS] = Buffer;
|
|
|
|
|
goto CheckPF;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IN SEQUENCE FRAME
|
|
|
|
|
|
|
|
|
|
// Remove any stored frame with this seq
|
|
|
|
|
|
|
|
|
|
if (LINK->RXFRAMES[NS])
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->RXFRAMES[NS]));
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 6) // REJ?
|
|
|
|
|
{
|
|
|
|
|
// If using REJ we can cancel REJ state.
|
|
|
|
|
// If using SREJ we only cancel REJ if we have no stored frames
|
|
|
|
|
|
|
|
|
|
if (LINK->Ver2point2)
|
|
|
|
|
{
|
|
|
|
|
// see if any frames saved.
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->RXFRAMES[i])
|
|
|
|
|
goto stayinREJ;
|
|
|
|
|
}
|
|
|
|
|
// Drop through if no stored frames
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CANCEL REJ
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 5;
|
|
|
|
|
LINK->L2FLAGS &= ~REJSENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stayinREJ:
|
|
|
|
|
|
|
|
|
|
PROC_I_FRAME(LINK, PORT, Buffer); // Passes on or releases Buffer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CheckPF:
|
|
|
|
|
|
|
|
|
|
if (LINK->Ver2point2 == 0) // Unless using SREJ
|
|
|
|
|
{
|
|
|
|
|
if (LINK->L2FLAGS & REJSENT)
|
|
|
|
|
{
|
|
|
|
|
return; // DONT SEND ANOTHER TILL REJ IS CANCELLED
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (CTL & PFBIT)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->L2STATE == 6)
|
|
|
|
|
LINK->L2FLAGS |= REJSENT; // Set "REJ Sent"
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// we have all frames. Clear anything in RXFRAMES
|
|
|
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
while (n < 8)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->RXFRAMES[n])
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n]));
|
|
|
|
|
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LINK->L2ACKREQ = 0; // CANCEL RR NEEDED
|
|
|
|
|
|
|
|
|
|
SEND_RR_RESP(LINK, PFBIT);
|
|
|
|
|
|
|
|
|
|
// RECORD TIME
|
|
|
|
|
|
|
|
|
|
LINK->LAST_F_TIME = REALTIMETICKS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID PROC_I_FRAME(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT, MESSAGE * Buffer)
|
|
|
|
|
{
|
|
|
|
|
int Length;
|
|
|
|
|
char * Info;
|
|
|
|
|
UCHAR PID;
|
|
|
|
|
struct DATAMESSAGE * Msg = (struct DATAMESSAGE *)Buffer;
|
|
|
|
|
UCHAR * EOA;
|
|
|
|
|
int n = 8; // Max Digis
|
|
|
|
|
|
|
|
|
|
LINK->LINKNR++; // INCREMENT OUR N(R)
|
|
|
|
|
LINK->LINKNR &= 7; // MODULO 8
|
|
|
|
|
|
|
|
|
|
// ATTACH I FRAMES TO LINK TABLE RX QUEUE - ONLY DATA IS ADDED (NOT ADDRESSES)
|
|
|
|
|
|
|
|
|
|
// IF DISC PENDING SET, IGNORE FRAME
|
|
|
|
|
|
|
|
|
|
if (LINK->L2FLAGS & DISCPENDING)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy data down the buffer so PID comes after Header (DATAMESSAGE format)
|
|
|
|
|
|
|
|
|
|
Length = Buffer->LENGTH - (MSGHDDRLEN + 15); // Buffer Header + addrs + CTL
|
|
|
|
|
Info = &Buffer->PID;
|
|
|
|
|
|
|
|
|
|
// Adjust for DIGIS
|
|
|
|
|
|
|
|
|
|
EOA = &Buffer->ORIGIN[6]; // End of address Bit
|
|
|
|
|
|
|
|
|
|
while (((*EOA & 1) == 0) && n--)
|
|
|
|
|
{
|
|
|
|
|
Length -= 7;
|
|
|
|
|
Info += 7;
|
|
|
|
|
EOA += 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PID = EOA[2];
|
|
|
|
|
|
|
|
|
|
switch(PID)
|
|
|
|
|
{
|
|
|
|
|
case 0xcc:
|
|
|
|
|
case 0xcd:
|
|
|
|
|
|
|
|
|
|
// IP Message
|
|
|
|
|
|
|
|
|
|
if (n < 8) // If digis, move data back down buffer
|
|
|
|
|
{
|
|
|
|
|
memmove(&Buffer->PID, &EOA[2], Length);
|
|
|
|
|
Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_IP_MSG( Buffer);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
|
|
|
|
|
|
// NOS FRAGMENTED IP
|
|
|
|
|
|
|
|
|
|
if (n < 8) // If digis, move data back down buffer
|
|
|
|
|
{
|
|
|
|
|
memmove(&Buffer->PID, &EOA[2], Length);
|
|
|
|
|
Buffer->LENGTH -= (int)(&EOA[2] - &Buffer->PID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
C_Q_ADD(&LINK->L2FRAG_Q, Buffer);
|
|
|
|
|
|
|
|
|
|
if (Buffer->L2DATA[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
// THERE IS A WHOLE MESSAGE ON FRAG_Q - PASS TO IP
|
|
|
|
|
|
|
|
|
|
while(LINK->L2FRAG_Q)
|
|
|
|
|
{
|
|
|
|
|
Buffer = Q_REM(&LINK->L2FRAG_Q);
|
|
|
|
|
Q_IP_MSG( Buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
if (Length < 1 || Length > 257)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy Data back over
|
|
|
|
|
|
|
|
|
|
memmove(&Msg->PID, Info, Length);
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = Length + MSGHDDRLEN;
|
|
|
|
|
|
|
|
|
|
C_Q_ADD(&LINK->RX_Q, Buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = PORT->PORTT2; // SET RR NEEDED
|
|
|
|
|
LINK->KILLTIMER = 0; // RESET IDLE LINK TIMER
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//*** CHECK RECEIVED N(R) COUNT
|
|
|
|
|
|
|
|
|
|
VOID SDNRCHK(struct _LINKTABLE * LINK, UCHAR CTL)
|
|
|
|
|
{
|
|
|
|
|
UCHAR NR = (CTL >> 5) & 7;
|
|
|
|
|
|
|
|
|
|
if (NR >= LINK->LINKWS) // N(R) >= WINDOW START?
|
|
|
|
|
{
|
|
|
|
|
// N(R) ABOVE OR EQUAL TO WINDOW START - OK IF NOT ABOVE N(S), OR N(S) BELOW WS
|
|
|
|
|
|
|
|
|
|
if (NR > LINK->LINKNS) // N(R) <= WINDOW END?
|
|
|
|
|
{
|
|
|
|
|
// N(R) ABOVE N(S) - DOES COUNT WRAP?
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKNS >= LINK->LINKWS) // Doesnt wrap
|
|
|
|
|
goto BadNR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GoodNR:
|
|
|
|
|
|
|
|
|
|
if ((CTL & 0x0f) == SREJ)
|
|
|
|
|
if ((CTL & PFBIT) == 0)
|
|
|
|
|
return; // SREJ without F doesn't ACK anything
|
|
|
|
|
|
|
|
|
|
LINK->LINKWS = NR; // NEW WINDOW START = RECEIVED N(R)
|
|
|
|
|
ACKMSG(LINK); // Remove any acked messages
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// N(R) LESS THAN WINDOW START - ONLY OK IF WINDOW WRAPS
|
|
|
|
|
|
|
|
|
|
if (NR <= LINK->LINKNS) // N(R) <= WINDOW END?
|
|
|
|
|
goto GoodNR;
|
|
|
|
|
|
|
|
|
|
BadNR:
|
|
|
|
|
|
|
|
|
|
// RECEIVED N(R) IS INVALID
|
|
|
|
|
|
|
|
|
|
LINK->SDREJF |= SDNRER; // FLAG A REJECT CONDITION
|
|
|
|
|
LINK->SDRBYTE = CTL; // SAVE FOR FRMR RESPONSE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID RESETNS(struct _LINKTABLE * LINK, UCHAR NS)
|
|
|
|
|
{
|
|
|
|
|
int Resent = (LINK->LINKNS - NS) & 7; // FRAMES TO RESEND
|
|
|
|
|
|
|
|
|
|
LINK->LINKNS = NS; // RESET N(S)
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE == 3) // mode-Node
|
|
|
|
|
{
|
|
|
|
|
if (LINK->NEIGHBOUR)
|
|
|
|
|
LINK->NEIGHBOUR->NBOUR_RETRIES += Resent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int COUNT_AT_L2(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// COUNTS FRAMES QUEUED ON AN L2 SESSION (IN BX)
|
|
|
|
|
|
|
|
|
|
int count = 0, abovelink = 0;
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
if (LINK == NULL)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
abovelink = C_Q_COUNT((UINT *)&LINK->TX_Q);
|
|
|
|
|
|
|
|
|
|
// COUNT FRAMES IN TSLOTS
|
|
|
|
|
|
|
|
|
|
while (n < 8)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->FRAMES[n])
|
|
|
|
|
count++;
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ADD AL,AH ; TOTAL IN AL, NUMBER ABOVE LINK IN AH
|
|
|
|
|
|
|
|
|
|
return abovelink + count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//*** RESET HDLC AND PURGE ALL QUEUES ETC.
|
|
|
|
|
|
|
|
|
|
VOID RESET2X(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
LINK->SDREJF = 0; // CLEAR FRAME REJECT FLAGS
|
|
|
|
|
LINK->LINKWS = 0; // CLEAR WINDOW POINTERS
|
|
|
|
|
LINK->LINKOWS = 0;
|
|
|
|
|
LINK->LINKNR = 0; // CLEAR N(R)
|
|
|
|
|
LINK->LINKNS = 0; // CLEAR N(S)
|
|
|
|
|
LINK->SDTSLOT= 0;
|
|
|
|
|
LINK->L2STATE = 5; // RESET STATE
|
|
|
|
|
LINK->L2FLAGS = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID CLEARL2QUEUES(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// GET RID OF ALL FRAMES THAT ARE QUEUED
|
|
|
|
|
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
while (n < 8)
|
|
|
|
|
{
|
|
|
|
|
while (LINK->FRAMES[n])
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->FRAMES[n]));
|
|
|
|
|
while (LINK->RXFRAMES[n])
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->RXFRAMES[n]));
|
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GET RID OF ALL FRAMES THAT ARE
|
|
|
|
|
// QUEUED ON THE TX HOLDING QUEUE, RX QUEUE AND LEVEL 3 QUEUE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (LINK->TX_Q)
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->TX_Q));
|
|
|
|
|
|
|
|
|
|
while (LINK->RX_Q)
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->RX_Q));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID RESET2(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
CLEARL2QUEUES(LINK);
|
|
|
|
|
RESET2X(LINK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SENDSABM(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
L2SENDCOMMAND(LINK, SABM | PFBIT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID PUT_ON_PORT_Q(struct PORTCONTROL * PORT, MESSAGE * Buffer)
|
|
|
|
|
{
|
|
|
|
|
// TIME STAMP IT
|
|
|
|
|
|
|
|
|
|
time(&Buffer->Timestamp);
|
|
|
|
|
|
|
|
|
|
if (PORT->TXPORT)
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->TXPORT; // update port no in header
|
|
|
|
|
|
|
|
|
|
PORT = GetPortTableEntryFromPortNum(PORT->TXPORT);
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
C_Q_ADD(&PORT->PORTTX_Q, (UINT *)Buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UCHAR * SETUPADDRESSES(struct _LINKTABLE * LINK, PMESSAGE Msg)
|
|
|
|
|
{
|
|
|
|
|
// COPY ADDRESSES FROM LINK TABLE TO MESSAGE _BUFFER
|
|
|
|
|
|
|
|
|
|
UCHAR * ptr1 = &LINK->DIGIS[0];
|
|
|
|
|
UCHAR * ptr2 = &Msg->CTL;
|
|
|
|
|
int Digis = 8;
|
|
|
|
|
|
|
|
|
|
memcpy(&Msg->DEST[0], &LINK->LINKCALL[0], 14); // COPY DEST AND ORIGIN
|
|
|
|
|
|
|
|
|
|
Msg->DEST[6] |= 0x60;
|
|
|
|
|
Msg->ORIGIN[6] |= 0x60;
|
|
|
|
|
|
|
|
|
|
while (Digis)
|
|
|
|
|
{
|
|
|
|
|
if (*(ptr1)) // any more to copy?
|
|
|
|
|
{
|
|
|
|
|
memcpy(ptr2, ptr1, 7);
|
|
|
|
|
ptr1 += 7;
|
|
|
|
|
ptr2 += 7;
|
|
|
|
|
Digis--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*(ptr2 - 1) |= 1; // SET END OF ADDRESSES
|
|
|
|
|
|
|
|
|
|
return ptr2; // Pointer to CTL
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SDETX(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// Start sending frsmes if possible
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
int Outstanding;
|
|
|
|
|
UCHAR * ptr1, * ptr2;
|
|
|
|
|
UCHAR CTL;
|
|
|
|
|
int count;
|
|
|
|
|
MESSAGE * Msg;
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
|
|
|
|
|
// DONT SEND IF RESEQUENCING RECEIVED FRAMES - CAN CAUSE FRMR PROBLEMS
|
|
|
|
|
|
|
|
|
|
// if (LINK->L2RESEQ_Q)
|
|
|
|
|
// return;
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKPORT->PORTNUMBER == 19)
|
|
|
|
|
{
|
|
|
|
|
int i = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Outstanding = LINK->LINKNS - LINK->LINKOWS; // Was WS not NS
|
|
|
|
|
|
|
|
|
|
if (Outstanding < 0)
|
|
|
|
|
Outstanding += 8; // allow for wrap
|
|
|
|
|
|
|
|
|
|
if (Outstanding >= LINK->LINKWINDOW) // LIMIT
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// See if we can load any more frames into the frame holding q
|
|
|
|
|
|
|
|
|
|
while (LINK->TX_Q && LINK->FRAMES[LINK->SDTSLOT] == NULL)
|
|
|
|
|
{
|
|
|
|
|
Msg = Q_REM(&LINK->TX_Q);
|
|
|
|
|
Msg->CHAIN = NULL;
|
|
|
|
|
LINK->FRAMES[LINK->SDTSLOT] = Msg;
|
|
|
|
|
LINK->SDTSLOT ++;
|
|
|
|
|
LINK->SDTSLOT &= 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dont send while poll outstanding
|
|
|
|
|
|
|
|
|
|
while ((LINK->L2FLAGS & POLLSENT) == 0)
|
|
|
|
|
{
|
|
|
|
|
Msg = LINK->FRAMES[LINK->LINKNS]; // is next frame available?
|
|
|
|
|
|
|
|
|
|
if (Msg == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// send the frame
|
|
|
|
|
|
|
|
|
|
// GET BUFFER FOR COPY OF MESSAGE - HAVE TO KEEP ORIGINAL FOR RETRIES
|
|
|
|
|
|
|
|
|
|
Buffer = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ptr2 = SETUPADDRESSES(LINK, Buffer); // copy addresses
|
|
|
|
|
|
|
|
|
|
// ptr2 NOW POINTS TO COMMAND BYTE
|
|
|
|
|
|
|
|
|
|
// GOING TO SEND I FRAME - WILL ACK ANY RECEIVED FRAMES
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED
|
|
|
|
|
LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
|
|
|
|
|
LINK->KILLTIMER = 0; // RESET IDLE CIRCUIT TIMER
|
|
|
|
|
|
|
|
|
|
CTL = LINK->LINKNR << 5; // GET CURRENT N(R), SHIFT IT TO TOP 3 BITS
|
|
|
|
|
CTL |= LINK->LINKNS << 1; // BITS 1-3 OF CONTROL BYTE
|
|
|
|
|
|
|
|
|
|
LINK->LINKNS++; // INCREMENT NS
|
|
|
|
|
LINK->LINKNS &= 7; // mod 8
|
|
|
|
|
|
|
|
|
|
// SET P BIT IF END OF WINDOW OR NO MORE TO SEND
|
|
|
|
|
|
|
|
|
|
if (LINK->VER1FLAG == 0) // NO POLL BIT IF V1
|
|
|
|
|
{
|
|
|
|
|
Outstanding = LINK->LINKNS - LINK->LINKOWS;
|
|
|
|
|
|
|
|
|
|
if (Outstanding < 0)
|
|
|
|
|
Outstanding += 8; // allow for wrap
|
|
|
|
|
|
|
|
|
|
// if at limit, or no more to send, set P)
|
|
|
|
|
|
|
|
|
|
if (Outstanding >= LINK->LINKWINDOW || LINK->FRAMES[LINK->LINKNS] == NULL)
|
|
|
|
|
{
|
|
|
|
|
CTL |= PFBIT;
|
|
|
|
|
LINK->L2FLAGS |= POLLSENT;
|
|
|
|
|
LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
|
|
|
|
|
|
|
|
|
|
// FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND (or ACK if ACKMODE)
|
|
|
|
|
|
|
|
|
|
Buffer->Linkptr = LINK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*(ptr2++) = CTL; // TO DATA (STARTING WITH PID)
|
|
|
|
|
|
|
|
|
|
count = Msg->LENGTH - MSGHDDRLEN;
|
|
|
|
|
|
|
|
|
|
if (count > 0) // SHOULD ALWAYS BE A PID, BUT BETTER SAFE THAN SORRY
|
|
|
|
|
{
|
|
|
|
|
ptr1 = (UCHAR *)Msg;
|
|
|
|
|
ptr1 += MSGHDDRLEN;
|
|
|
|
|
memcpy(ptr2, ptr1, count);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer->DEST[6] |= 0x80; // SET COMMAND
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr2 - (UCHAR *)Buffer) + count; // SET NEW LENGTH
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
|
|
|
|
|
if (PORT)
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->PORTNUMBER;
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Buffer->Linkptr = 0;
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2TimerProc()
|
|
|
|
|
{
|
|
|
|
|
int i = MAXLINKS;
|
|
|
|
|
struct _LINKTABLE * LINK = LINKS;
|
|
|
|
|
struct PORTCONTROL * PORT = PORTTABLE;
|
|
|
|
|
|
|
|
|
|
while (i--)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->LINKCALL[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
LINK++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CHECK FOR TIMER EXPIRY OR BUSY CLEARED
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
{
|
|
|
|
|
LINK++;
|
|
|
|
|
continue; // just ion case!!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2TIMER)
|
|
|
|
|
{
|
|
|
|
|
LINK->L2TIMER--;
|
|
|
|
|
if (LINK->L2TIMER == 0)
|
|
|
|
|
{
|
|
|
|
|
L2TIMEOUT(LINK, PORT);
|
|
|
|
|
LINK++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// TIMER NOT RUNNING - MAKE SURE STATE NOT BELOW 5 - IF
|
|
|
|
|
// IT IS, SOMETHING HAS GONE WRONG, AND LINK WILL HANG FOREVER
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE < 5 && LINK->L2STATE != 2 && LINK->L2STATE != 1) // 2 = CONNECT - PROBABLY TO CQ
|
|
|
|
|
LINK->L2TIMER = 2; // ARBITRARY VALUE
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TEST FOR RNR SENT, AND NOT STILL BUSY
|
|
|
|
|
|
|
|
|
|
if (LINK->L2FLAGS & RNRSENT)
|
|
|
|
|
{
|
|
|
|
|
// Was busy
|
|
|
|
|
|
|
|
|
|
if (RR_OR_RNR(LINK) != RNR) // SEE IF STILL BUSY
|
|
|
|
|
{
|
|
|
|
|
// Not still busy - tell other end
|
|
|
|
|
|
|
|
|
|
// Just sending RR will hause a hang of RR is missed, and other end does not poll on Busy
|
|
|
|
|
// Try sending RR CP, so we will retry if not acked
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = 0; // CLEAR ANY DELAYED ACK TIMER
|
|
|
|
|
|
|
|
|
|
if (LINK->L2RETRIES == 0) // IF RR(P) OUTSTANDING WILl REPORT ANYWAY
|
|
|
|
|
{
|
|
|
|
|
SendSupervisCmd(LINK);
|
|
|
|
|
LINK++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// NOT BUSY
|
|
|
|
|
|
|
|
|
|
if (LINK->L2ACKREQ) // DELAYED ACK TIMER
|
|
|
|
|
{
|
|
|
|
|
if (LINK->L2RETRIES == 0) // DONT SEND RR RESPONSE WHILEST RR(P) OUTSTANDING
|
|
|
|
|
{
|
|
|
|
|
LINK->L2ACKREQ--;
|
|
|
|
|
if (LINK->L2ACKREQ == 0)
|
|
|
|
|
{
|
|
|
|
|
SEND_RR_RESP(LINK, 0); // NO F BIT
|
|
|
|
|
LINK++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CHECK FOR REJ TIMEOUT
|
|
|
|
|
|
|
|
|
|
if (LINK->REJTIMER)
|
|
|
|
|
{
|
|
|
|
|
LINK->REJTIMER--;
|
|
|
|
|
if (LINK->REJTIMER == 0) // {REJ HAS TIMED OUT (THIS MUST BE A VERSION 1 SESSION)
|
|
|
|
|
{
|
|
|
|
|
// CANCEL REJ STATE
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 6) // REJ?
|
|
|
|
|
LINK->L2STATE = 5; // CLEAR REJ
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See if time for link validation poll
|
|
|
|
|
|
|
|
|
|
if (LINK->L2SLOTIM)
|
|
|
|
|
{
|
|
|
|
|
LINK->L2SLOTIM--;
|
|
|
|
|
if (LINK->L2SLOTIM == 0) // Time to poll
|
|
|
|
|
{
|
|
|
|
|
SendSupervisCmd(LINK);
|
|
|
|
|
LINK++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// See if idle too long
|
|
|
|
|
|
|
|
|
|
LINK->KILLTIMER++;
|
|
|
|
|
|
|
|
|
|
if (L2KILLTIME && LINK->KILLTIMER > L2KILLTIME)
|
|
|
|
|
{
|
|
|
|
|
// CIRCUIT HAS BEEN IDLE TOO LONG - SHUT IT DOWN
|
|
|
|
|
|
|
|
|
|
LINK->KILLTIMER = 0;
|
|
|
|
|
LINK->L2TIMER = 1; // TO FORCE DISC
|
|
|
|
|
LINK->L2STATE = 4; // DISCONNECTING
|
|
|
|
|
|
|
|
|
|
// TELL OTHER LEVELS
|
|
|
|
|
|
|
|
|
|
InformPartner(LINK, NORMALCLOSE);
|
|
|
|
|
}
|
|
|
|
|
LINK++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SendSupervisCmd(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// Send Super Command RR/RNR/REJ(P)
|
|
|
|
|
|
|
|
|
|
UCHAR CTL;
|
|
|
|
|
|
|
|
|
|
if (LINK->VER1FLAG == 1)
|
|
|
|
|
{
|
|
|
|
|
// VERSION 1 TIMEOUT
|
|
|
|
|
|
|
|
|
|
// RESET TO RESEND I FRAMES
|
|
|
|
|
|
|
|
|
|
LINK->LINKNS = LINK->LINKOWS;
|
|
|
|
|
|
|
|
|
|
SDETX(LINK); // PREVENT FRMR (I HOPE)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SEND RR COMMAND - EITHER AS LINK VALIDATION POLL OR FOLLOWING TIMEOUT
|
|
|
|
|
|
|
|
|
|
LINK->L2ACKREQ = 0; // CLEAR ACK NEEDED
|
|
|
|
|
|
|
|
|
|
CTL = RR_OR_RNR(LINK);
|
|
|
|
|
|
|
|
|
|
// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK'
|
|
|
|
|
|
|
|
|
|
CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS
|
|
|
|
|
CTL |= PFBIT;
|
|
|
|
|
|
|
|
|
|
LINK->L2FLAGS |= POLLSENT;
|
|
|
|
|
|
|
|
|
|
L2SENDCOMMAND(LINK, CTL);
|
|
|
|
|
|
|
|
|
|
LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SEND_RR_RESP(struct _LINKTABLE * LINK, UCHAR PF)
|
|
|
|
|
{
|
|
|
|
|
UCHAR CTL;
|
|
|
|
|
|
|
|
|
|
CTL = RR_OR_RNR(LINK);
|
|
|
|
|
|
|
|
|
|
// MOV L2STATE[EBX],5 ; CANCEL REJ - ACTUALLY GOING TO 'PENDING ACK'
|
|
|
|
|
|
|
|
|
|
CTL |= LINK->LINKNR << 5; // SHIFT N(R) TO TOP 3 BITS
|
|
|
|
|
CTL |= PF;
|
|
|
|
|
|
|
|
|
|
L2SENDRESPONSE(LINK, CTL);
|
|
|
|
|
|
|
|
|
|
ACKMSG(LINK); // SEE IF STILL WAITING FOR ACK
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID ACKMSG(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// RELEASE ANY ACKNOWLEDGED FRAMES
|
|
|
|
|
|
|
|
|
|
while (LINK->LINKOWS != LINK->LINKWS) // is OLD WINDOW START EQUAL TO NEW WINDOW START?
|
|
|
|
|
{
|
|
|
|
|
// No, so frames to ack
|
|
|
|
|
|
|
|
|
|
if (LINK->FRAMES[LINK->LINKOWS])
|
|
|
|
|
ReleaseBuffer(Q_REM(&LINK->FRAMES[LINK->LINKOWS]));
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char Call1[12], Call2[12];
|
|
|
|
|
|
|
|
|
|
Call1[ConvFromAX25(LINK->LINKCALL, Call1)] = 0;
|
|
|
|
|
Call2[ConvFromAX25(LINK->OURCALL, Call2)] = 0;
|
|
|
|
|
|
|
|
|
|
Debugprintf("Missing frame to ack Seq %d Calls %s %s", LINK->LINKOWS, Call1, Call2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LINK->LINKOWS++; // INCREMENT OLD WINDOW START
|
|
|
|
|
LINK->LINKOWS &= 7; // MODULO 8
|
|
|
|
|
|
|
|
|
|
// SOMETHING HAS BEEN ACKED - RESET RETRY COUNTER
|
|
|
|
|
|
|
|
|
|
if (LINK->L2RETRIES)
|
|
|
|
|
LINK->L2RETRIES = 1; // MUSTN'T SET TO ZERO - COULD CAUSE PREMATURE RETRANSMIT
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKWS != LINK->LINKNS) // IS N(S) = NEW WINDOW START?
|
|
|
|
|
{
|
|
|
|
|
// NOT ALL I-FRAMES HAVE BEEN ACK'ED - RESTART TIMER
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = LINK->L2TIME;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ALL FRAMES HAVE BEEN ACKED - CANCEL TIMER UNLESS RETRYING
|
|
|
|
|
// IF RETRYING, MUST ONLY CANCEL WHEN RR(F) RECEIVED
|
|
|
|
|
|
|
|
|
|
if (LINK->VER1FLAG == 1 || LINK->L2RETRIES == 0) // STOP TIMER IF LEVEL 1 or not retrying
|
|
|
|
|
{
|
|
|
|
|
LINK->L2TIMER = 0;
|
|
|
|
|
LINK->L2FLAGS &= ~POLLSENT; // CLEAR I(P) SET (IN CASE TALKING TO OLD BPQ!)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IF DISCONNECT REQUEST OUTSTANDING, AND NO FRAMES ON TX QUEUE, SEND DISC
|
|
|
|
|
|
|
|
|
|
if (LINK->L2FLAGS & DISCPENDING && LINK->TX_Q == 0)
|
|
|
|
|
{
|
|
|
|
|
LINK->L2FLAGS &= ~DISCPENDING;
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = 1; // USE TIMER TO SEND DISC
|
|
|
|
|
LINK->L2STATE = 4; // DISCONNECTING
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CONNECTFAILED();
|
|
|
|
|
|
|
|
|
|
VOID L2TIMEOUT(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT)
|
|
|
|
|
{
|
|
|
|
|
// TIMER EXPIRED
|
|
|
|
|
|
|
|
|
|
// IF LINK UP (STATE 5 OR ABOVE) SEND RR/RNR AS REQUIRED
|
|
|
|
|
// IF S2, REPEAT SABM
|
|
|
|
|
// IF S3, REPEAT FRMR
|
|
|
|
|
// IF S4, REPEAT DISC
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PORT->L2TIMEOUTS++; // FOR STATS
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 1)
|
|
|
|
|
{
|
|
|
|
|
// XID
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES++;
|
|
|
|
|
if (LINK->L2RETRIES >= PORT->PORTN2)
|
|
|
|
|
{
|
|
|
|
|
// RETRIED N2 TIMES - Give up
|
|
|
|
|
|
|
|
|
|
CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L2SENDXID(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 2)
|
|
|
|
|
{
|
|
|
|
|
// CONNECTING
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES++;
|
|
|
|
|
if (LINK->L2RETRIES >= PORT->PORTN2)
|
|
|
|
|
{
|
|
|
|
|
// RETRIED N2 TIMES - Give up
|
|
|
|
|
|
|
|
|
|
CONNECTFAILED(LINK); // TELL LEVEL 4 IT FAILED
|
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
|
|
|
|
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SENDSABM(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 4)
|
|
|
|
|
{
|
|
|
|
|
// DISCONNECTING
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES++;
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
if (LINK->L2RETRIES >= PORT->PORTN2)
|
|
|
|
|
{
|
|
|
|
|
// RETRIED N2 TIMES - JUST CLEAR OUT LINK
|
2022-10-18 10:09:06 +01:00
|
|
|
|
|
|
|
|
|
if (PORT->TNC && PORT->TNC->Hardware == H_KISSHF)
|
|
|
|
|
DetachKISSHF(PORT);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
CLEAROUTLINK(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
L2SENDCOMMAND(LINK, DISC | PFBIT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 3)
|
|
|
|
|
{
|
|
|
|
|
// FRMR
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES++;
|
|
|
|
|
if (LINK->L2RETRIES >= PORT->PORTN2)
|
|
|
|
|
{
|
|
|
|
|
// RETRIED N2 TIMES - RESET LINK
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES = 0;
|
|
|
|
|
LINK->L2STATE = 2;
|
|
|
|
|
SENDSABM(LINK);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// STATE 5 OR ABOVE
|
|
|
|
|
|
|
|
|
|
// SEND RR(P) UP TO N2 TIMES
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES++;
|
|
|
|
|
|
|
|
|
|
if (LINK->L2RETRIES >= PORT->PORTN2)
|
|
|
|
|
{
|
|
|
|
|
// RETRIED N TIMES SEND A COUPLE OF DISCS AND THEN CLOSE
|
|
|
|
|
|
|
|
|
|
InformPartner(LINK, RETRIEDOUT); // TELL OTHER END ITS GONE
|
|
|
|
|
|
|
|
|
|
LINK->L2RETRIES -= 1; // Just send one DISC
|
|
|
|
|
LINK->L2STATE = 4; // CLOSING
|
|
|
|
|
|
|
|
|
|
L2SENDCOMMAND(LINK, DISC | PFBIT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SendSupervisCmd(LINK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SDFRMR(struct _LINKTABLE * LINK, struct PORTCONTROL * PORT)
|
|
|
|
|
{
|
|
|
|
|
PORT->L2FRMRTX++;
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 3; // ENTER FRMR STATE
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = LINK->L2TIME; //SET TIMER
|
|
|
|
|
|
|
|
|
|
SENDFRMR(LINK);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SENDFRMR(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// RESEND FRMR
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
|
|
|
|
|
Buffer = SETUPL2MESSAGE(LINK, FRMR);
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE
|
|
|
|
|
|
|
|
|
|
ptr = &Buffer->PID;
|
|
|
|
|
|
|
|
|
|
*(ptr++) = LINK->SDRBYTE; // MOVE REJECT C-BYTE
|
|
|
|
|
|
|
|
|
|
*(ptr++) = LINK->LINKNR << 5 | LINK->LINKNS << 1;
|
|
|
|
|
|
|
|
|
|
*(ptr++) = LINK->SDREJF; // MOVE REJECT FLAGS
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH += 3;
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
Buffer->PORT = PORT->PORTNUMBER;
|
|
|
|
|
|
|
|
|
|
if (PORT)
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
else
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID CLEAROUTLINK(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
CLEARL2QUEUES(LINK); // TO RELEASE ANY BUFFERS
|
|
|
|
|
|
|
|
|
|
memset(LINK, 0, sizeof(struct _LINKTABLE));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L2SENDXID(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// Set up and send XID
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
unsigned int xidval;
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKPORT == 0)
|
|
|
|
|
return; //??? has been zapped
|
|
|
|
|
|
|
|
|
|
Buffer = SETUPL2MESSAGE(LINK, XID | PFBIT);
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
{
|
|
|
|
|
// NO BUFFERS - SET TIMER TO FORCE RETRY
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = 10*3; // SET TIMER
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer->DEST[6] |= 0x80; // SET COMMAND
|
|
|
|
|
|
|
|
|
|
ptr = &Buffer->PID;
|
|
|
|
|
|
|
|
|
|
// Set up default XID Mod 8
|
|
|
|
|
|
|
|
|
|
*ptr++ = 0x82; // FI
|
|
|
|
|
*ptr++ = 0x80; // GI
|
|
|
|
|
*ptr++ = 0x0;
|
|
|
|
|
*ptr++ = 0x10; // Length 16
|
|
|
|
|
|
|
|
|
|
*ptr++ = 0x02; // Classes of Procedures
|
|
|
|
|
*ptr++ = 0x02; // Length
|
|
|
|
|
*ptr++ = 0x00; //
|
|
|
|
|
*ptr++ = 0x21; // ABM Half Duplex
|
|
|
|
|
|
|
|
|
|
// We offer REJ, SREJ and SREJ Multiframe
|
|
|
|
|
|
|
|
|
|
*ptr++ = 0x03; // Optional Functions
|
|
|
|
|
*ptr++ = 0x03; // Len
|
|
|
|
|
|
|
|
|
|
// Sync TX, SREJ Multiframe 16 bit FCS, Mod 8, TEST,
|
|
|
|
|
// Extended Addressing, REJ, SREJ
|
|
|
|
|
|
|
|
|
|
xidval = OPMustHave | OPSREJ | OPSREJMult | OPREJ | OPMod8;
|
|
|
|
|
*ptr++ = xidval >> 16;
|
|
|
|
|
*ptr++ = xidval >> 8;
|
|
|
|
|
*ptr++ = xidval;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*ptr++ = 0x06; // RX Packet Len
|
|
|
|
|
*ptr++ = 0x02; // Len
|
|
|
|
|
*ptr++ = 0x08; //
|
|
|
|
|
*ptr++ = 0x00; // 2K bits (256) Bytes
|
|
|
|
|
|
|
|
|
|
*ptr++ = 0x08; // RX Window
|
|
|
|
|
*ptr++ = 0x01; // Len
|
|
|
|
|
*ptr++ = 0x07; // 7
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH
|
|
|
|
|
|
|
|
|
|
LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
|
|
|
|
|
|
|
|
|
|
// FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND
|
|
|
|
|
|
|
|
|
|
Buffer->Linkptr = LINK;
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
|
|
|
|
|
if (PORT)
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->PORTNUMBER;
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Buffer->Linkptr = 0;
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L2SENDCOMMAND(struct _LINKTABLE * LINK, int CMD)
|
|
|
|
|
{
|
|
|
|
|
// SEND COMMAND IN CMD
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKPORT == 0)
|
|
|
|
|
return; //??? has been zapped
|
|
|
|
|
|
|
|
|
|
Buffer = SETUPL2MESSAGE(LINK, CMD);
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
{
|
|
|
|
|
// NO BUFFERS - SET TIMER TO FORCE RETRY
|
|
|
|
|
|
|
|
|
|
if (CMD & PFBIT) // RESPONSE EXPECTED?
|
|
|
|
|
LINK->L2TIMER = 10*3; // SET TIMER
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer->DEST[6] |= 0x80; // SET COMMAND
|
|
|
|
|
|
|
|
|
|
if (CMD & PFBIT) // RESPONSE EXPECTED?
|
|
|
|
|
{
|
|
|
|
|
LINK->L2TIMER = ONEMINUTE; // (RE)SET TIMER
|
|
|
|
|
|
|
|
|
|
// FLAG BUFFER TO CAUSE TIMER TO BE RESET AFTER SEND
|
|
|
|
|
|
|
|
|
|
Buffer->Linkptr = LINK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
|
|
|
|
|
if (PORT)
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->PORTNUMBER;
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Buffer->Linkptr = 0;
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L2SENDRESPONSE(struct _LINKTABLE * LINK, int CMD)
|
|
|
|
|
{
|
|
|
|
|
// SEND Response IN CMD
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT;
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
|
|
|
|
|
Buffer = SETUPL2MESSAGE(LINK, CMD);
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
{
|
|
|
|
|
// NO BUFFERS - SET TIMER TO FORCE RETRY
|
|
|
|
|
|
|
|
|
|
if (CMD & PFBIT) // RESPONSE EXPECTED?
|
|
|
|
|
LINK->L2TIMER = 10*3; // SET TIMER
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Buffer->ORIGIN[6] |= 0x80; // SET RESPONSE
|
|
|
|
|
|
|
|
|
|
LINK->L2SLOTIM = T3 + rand() % 15; // SET FRAME SENT RECENTLY
|
|
|
|
|
|
|
|
|
|
PORT = LINK->LINKPORT;
|
|
|
|
|
Buffer->PORT = PORT->PORTNUMBER;
|
|
|
|
|
|
|
|
|
|
if (PORT)
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
else
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MESSAGE * SETUPL2MESSAGE(struct _LINKTABLE * LINK, UCHAR CMD)
|
|
|
|
|
{
|
|
|
|
|
MESSAGE * Buffer;
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
|
|
|
|
|
Buffer = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
ptr = SETUPADDRESSES(LINK, Buffer); // copy addresses
|
|
|
|
|
|
|
|
|
|
// ptr NOW POINTS TO COMMAND BYTE
|
|
|
|
|
|
|
|
|
|
*(ptr)++ = CMD;
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer); // SET LENGTH
|
|
|
|
|
|
|
|
|
|
return Buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID L3LINKCLOSED(struct _LINKTABLE * LINK, int Reason);
|
|
|
|
|
|
|
|
|
|
VOID InformPartner(struct _LINKTABLE * LINK, int Reason)
|
|
|
|
|
{
|
|
|
|
|
// LINK IS DISCONNECTING - IF THERE IS A CROSSLINK, SEND DISC TO IT
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE == 3)
|
|
|
|
|
{
|
|
|
|
|
L3LINKCLOSED(LINK, Reason);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->CIRCUITPOINTER)
|
|
|
|
|
CloseSessionPartner(LINK->CIRCUITPOINTER);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UINT RR_OR_RNR(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
UCHAR Temp;
|
|
|
|
|
TRANSPORTENTRY * Session;
|
|
|
|
|
|
|
|
|
|
LINK->L2FLAGS &= ~RNRSENT;
|
|
|
|
|
|
|
|
|
|
// SET UP APPROPRIATE SUPER COMMAND
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE == 3)
|
|
|
|
|
|
|
|
|
|
// Node to Node - only busy if short of buffers
|
|
|
|
|
|
|
|
|
|
goto CHKBUFFS;
|
|
|
|
|
|
|
|
|
|
// UP OR DOWN LINK - SEE IF SESSION IS BUSY
|
|
|
|
|
|
|
|
|
|
if (LINK->CIRCUITPOINTER == 0)
|
|
|
|
|
goto CHKBUFFS; // NOT CONNECTED
|
|
|
|
|
|
|
|
|
|
Session = LINK->CIRCUITPOINTER; // TO CIRCUIT ENTRY
|
|
|
|
|
|
|
|
|
|
Temp = CHECKIFBUSYL2(Session); //TARGET SESSION BUSY?
|
|
|
|
|
|
|
|
|
|
if (Temp & L4BUSY)
|
|
|
|
|
goto SENDRNR; // BUSY
|
|
|
|
|
|
|
|
|
|
CHKBUFFS:
|
|
|
|
|
|
|
|
|
|
if (QCOUNT < 20)
|
|
|
|
|
goto SENDRNR; // NOT ENOUGH
|
|
|
|
|
|
|
|
|
|
// SEND REJ IF IN REJ STATE
|
|
|
|
|
|
|
|
|
|
if (LINK->L2STATE == 6)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// We may have the needed frame in RXFRAMES
|
|
|
|
|
|
|
|
|
|
CheckNSLoop2:
|
|
|
|
|
|
|
|
|
|
if (LINK->RXFRAMES[LINK->LINKNR])
|
|
|
|
|
{
|
|
|
|
|
// We have the first missing frame. Process it.
|
|
|
|
|
|
|
|
|
|
struct PORTCONTROL * PORT = LINK->LINKPORT;
|
|
|
|
|
MESSAGE * OldBuffer = Q_REM(&LINK->RXFRAMES[LINK->LINKNR]);
|
|
|
|
|
|
|
|
|
|
Debugprintf("L2 about to send REJ - process saved Frame %d", LINK->LINKNR);
|
|
|
|
|
PROC_I_FRAME(LINK, PORT, OldBuffer); // Passes on or releases Buffer
|
|
|
|
|
|
|
|
|
|
// NR has been updated.
|
|
|
|
|
|
|
|
|
|
// Clear REJ if we have no more saved
|
|
|
|
|
|
|
|
|
|
if (LINK->Ver2point2) // Using SREJ?
|
|
|
|
|
{
|
|
|
|
|
// see if any frames saved.
|
|
|
|
|
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
|
{
|
|
|
|
|
if (LINK->RXFRAMES[i])
|
|
|
|
|
goto stayinREJ2;
|
|
|
|
|
}
|
|
|
|
|
// Drop through if no stored frames
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 5;
|
|
|
|
|
LINK->L2FLAGS &= ~REJSENT;
|
|
|
|
|
stayinREJ2:
|
|
|
|
|
LINK->L2ACKREQ = 0; // Cancel Resptime (Set by PROC_I_FRAME)
|
|
|
|
|
|
|
|
|
|
goto CheckNSLoop2; // See if OK or we have another saved frame
|
|
|
|
|
}
|
|
|
|
|
if (LINK->L2STATE == 6)
|
|
|
|
|
|
|
|
|
|
// if we support SREJ send that instesd or REJ
|
|
|
|
|
|
|
|
|
|
if (LINK->Ver2point2) // We only allow 2.2 with SREJ Multi
|
|
|
|
|
return SREJ;
|
|
|
|
|
else
|
|
|
|
|
return REJ;
|
|
|
|
|
}
|
|
|
|
|
return RR;
|
|
|
|
|
|
|
|
|
|
SENDRNR:
|
|
|
|
|
|
|
|
|
|
LINK->L2FLAGS |= RNRSENT; // REMEMBER
|
|
|
|
|
|
|
|
|
|
return RNR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg);
|
|
|
|
|
|
|
|
|
|
VOID CONNECTFAILED(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
ConnectFailedOrRefused(LINK, "Failure with");
|
|
|
|
|
}
|
|
|
|
|
VOID CONNECTREFUSED(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
ConnectFailedOrRefused(LINK, "Busy from");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID L3CONNECTFAILED();
|
|
|
|
|
|
|
|
|
|
VOID ConnectFailedOrRefused(struct _LINKTABLE * LINK, char * Msg)
|
|
|
|
|
{
|
|
|
|
|
// IF DOWNLINK, TELL PARTNER
|
|
|
|
|
// IF CROSSLINK, TELL ROUTE CONTROL
|
|
|
|
|
|
|
|
|
|
struct DATAMESSAGE * Buffer;
|
|
|
|
|
UCHAR * ptr1;
|
|
|
|
|
char Normcall[10];
|
|
|
|
|
TRANSPORTENTRY * Session;
|
|
|
|
|
TRANSPORTENTRY * InSession;
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE == 3)
|
|
|
|
|
{
|
|
|
|
|
L3CONNECTFAILED(LINK); // REPORT TO LEVEL 3
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (LINK->CIRCUITPOINTER == 0) // No crosslink??
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Buffer = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// SET UP HEADER
|
|
|
|
|
|
|
|
|
|
Buffer->PID = 0xf0;
|
|
|
|
|
|
|
|
|
|
ptr1 = SetupNodeHeader(Buffer);
|
|
|
|
|
|
|
|
|
|
Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0;
|
|
|
|
|
|
|
|
|
|
ptr1 += sprintf(ptr1, "%s %s\r", Msg, Normcall);
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer);
|
|
|
|
|
|
|
|
|
|
Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY
|
|
|
|
|
InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION
|
|
|
|
|
|
|
|
|
|
CLEARSESSIONENTRY(Session);
|
|
|
|
|
|
|
|
|
|
if (InSession)
|
|
|
|
|
{
|
|
|
|
|
InSession->L4CROSSLINK = NULL; // CLEAR REVERSE LINK
|
|
|
|
|
C_Q_ADD(&InSession->L4TX_Q, Buffer);
|
|
|
|
|
PostDataAvailable(InSession);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID SENDCONNECTREPLY(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
// LINK SETUP COMPLETE
|
|
|
|
|
|
|
|
|
|
struct DATAMESSAGE * Buffer;
|
|
|
|
|
UCHAR * ptr1;
|
|
|
|
|
char Normcall[10];
|
|
|
|
|
TRANSPORTENTRY * Session;
|
|
|
|
|
TRANSPORTENTRY * InSession;
|
|
|
|
|
|
|
|
|
|
if (LINK->LINKTYPE == 3)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// UP/DOWN LINK
|
|
|
|
|
|
|
|
|
|
if (LINK->CIRCUITPOINTER == 0) // No crosslink??
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Buffer = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// SET UP HEADER
|
|
|
|
|
|
|
|
|
|
Buffer->PID = 0xf0;
|
|
|
|
|
|
|
|
|
|
ptr1 = SetupNodeHeader(Buffer);
|
|
|
|
|
|
|
|
|
|
Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0;
|
|
|
|
|
|
|
|
|
|
ptr1 += sprintf(ptr1, "Connected to %s\r", Normcall);
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr1 - (UCHAR *)Buffer);
|
|
|
|
|
|
|
|
|
|
Session = LINK->CIRCUITPOINTER; // GET CIRCUIT TABLE ENTRY
|
|
|
|
|
Session->L4STATE = 5;
|
|
|
|
|
InSession = Session->L4CROSSLINK; // TO INCOMMONG SESSION
|
|
|
|
|
|
|
|
|
|
if (InSession)
|
|
|
|
|
{
|
|
|
|
|
C_Q_ADD(&InSession->L4TX_Q, Buffer);
|
|
|
|
|
PostDataAvailable(InSession);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TRANSPORTENTRY * SetupSessionForL2(struct _LINKTABLE * LINK)
|
|
|
|
|
{
|
|
|
|
|
TRANSPORTENTRY * NewSess = L4TABLE;
|
|
|
|
|
int Index = 0;
|
|
|
|
|
|
|
|
|
|
while (Index < MAXCIRCUITS)
|
|
|
|
|
{
|
|
|
|
|
if (NewSess->L4USER[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
// Got One
|
|
|
|
|
|
|
|
|
|
LINK->CIRCUITPOINTER = NewSess; // SETUP LINK-CIRCUIT CONNECTION
|
|
|
|
|
|
|
|
|
|
memcpy(NewSess->L4USER, LINK->LINKCALL, 7);
|
|
|
|
|
memcpy(NewSess->L4MYCALL, MYCALL, 7); // ALWAYS USE _NODE CALL
|
|
|
|
|
|
|
|
|
|
NewSess->CIRCUITINDEX = Index; //OUR INDEX
|
|
|
|
|
NewSess->CIRCUITID = NEXTID;
|
|
|
|
|
|
|
|
|
|
NEXTID++;
|
|
|
|
|
if (NEXTID == 0)
|
|
|
|
|
NEXTID++; // kEEP nON-ZERO
|
|
|
|
|
|
|
|
|
|
NewSess->L4TARGET.LINK = LINK;
|
|
|
|
|
|
|
|
|
|
NewSess->L4CIRCUITTYPE = L2LINK | UPLINK;
|
|
|
|
|
|
|
|
|
|
NewSess->L4STATE = 5; // SET LINK ACTIVE
|
|
|
|
|
|
|
|
|
|
NewSess->SESSPACLEN = LINK->LINKPORT->PORTPACLEN;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NewSess->SESSIONT1 = L4T1; // Default
|
|
|
|
|
NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
|
|
|
|
|
|
|
|
|
|
return NewSess;
|
|
|
|
|
}
|
|
|
|
|
Index++;
|
|
|
|
|
NewSess++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VOID Digipeat(struct PORTCONTROL * PORT, MESSAGE * Buffer, UCHAR * OurCall, int toPort, int UIOnly) // Digi it (if enabled)
|
|
|
|
|
{
|
|
|
|
|
// WE MAY HAVE DISABLED DIGIPEAT ALTOGETHER, (DIGIFLAG=0),
|
|
|
|
|
// OR ALLOW ALLOW ONLY UI FRAMES TO BE DIGIED (DIGIFLAG=-1)
|
|
|
|
|
|
|
|
|
|
// toPort and UIOnly are used for Cross Port digi feature
|
|
|
|
|
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (PORT->DIGIFLAG == 0 && toPort == 0)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OurCall[6] |= 0x80; // SET HAS BEEN REPEATED
|
|
|
|
|
|
|
|
|
|
// SEE IF UI FRAME - scan forward for end of address bit
|
|
|
|
|
|
|
|
|
|
n = 8;
|
|
|
|
|
|
|
|
|
|
while ((OurCall[6] & 1) == 0)
|
|
|
|
|
{
|
|
|
|
|
OurCall += 7;
|
|
|
|
|
|
|
|
|
|
if ((OurCall - &Buffer->CTL) > 56)
|
|
|
|
|
{
|
|
|
|
|
// Run off end before findin end of address
|
|
|
|
|
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (toPort) // Cross port digi
|
|
|
|
|
{
|
|
|
|
|
if (((OurCall[7] & ~PFBIT) == 3) || UIOnly == 0)
|
|
|
|
|
{
|
|
|
|
|
// UI or Digi all
|
|
|
|
|
|
|
|
|
|
Buffer->PORT = toPort; // update port no in header
|
|
|
|
|
PORT = GetPortTableEntryFromPortNum(toPort);
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
else
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((OurCall[7] & ~PFBIT) == 3)
|
|
|
|
|
{
|
|
|
|
|
// UI
|
|
|
|
|
|
|
|
|
|
// UI FRAME. IF DIGIMASK IS NON-ZERO, SEND TO ALL PORTS SET, OTHERWISE SEND TO DIGIPORT
|
|
|
|
|
|
|
|
|
|
PORT->L2DIGIED++;
|
|
|
|
|
|
|
|
|
|
if (toPort)
|
|
|
|
|
{
|
|
|
|
|
// Cross port digi
|
|
|
|
|
|
|
|
|
|
PORT = GetPortTableEntryFromPortNum(toPort);
|
|
|
|
|
Buffer->PORT = PORT->DIGIPORT; // update port no in header
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
else
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (PORT->DIGIMASK == 0)
|
|
|
|
|
{
|
|
|
|
|
if (PORT->DIGIPORT) // Cross Band Digi?
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->DIGIPORT; // update port no in header
|
|
|
|
|
|
|
|
|
|
PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT);
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DigiToMultiplePorts(PORT, Buffer);
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Not UI - Only Digi if Digiflag not -1
|
|
|
|
|
|
|
|
|
|
if (PORT->DIGIFLAG == -1)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PORT->L2DIGIED++;
|
|
|
|
|
|
|
|
|
|
if (PORT->DIGIPORT) // Cross Band Digi?
|
|
|
|
|
{
|
|
|
|
|
Buffer->PORT = PORT->DIGIPORT; // update port no in header
|
|
|
|
|
|
|
|
|
|
PORT = GetPortTableEntryFromPortNum(PORT->DIGIPORT);
|
|
|
|
|
|
|
|
|
|
if (PORT == NULL)
|
|
|
|
|
{
|
|
|
|
|
ReleaseBuffer(Buffer);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
PUT_ON_PORT_Q(PORT, Buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CheckForListeningSession(struct PORTCONTROL * PORT, MESSAGE * Msg)
|
|
|
|
|
{
|
|
|
|
|
TRANSPORTENTRY * L4 = L4TABLE;
|
|
|
|
|
struct DATAMESSAGE * Buffer;
|
|
|
|
|
int i = MAXCIRCUITS;
|
|
|
|
|
UCHAR * ptr;
|
|
|
|
|
|
|
|
|
|
while (i--)
|
|
|
|
|
{
|
|
|
|
|
if ((CountBits(L4->LISTEN) == 1) && ((1 << ((Msg->PORT & 0x7f) - 1) && L4->LISTEN)))
|
|
|
|
|
{
|
|
|
|
|
// See if he is calling our call
|
|
|
|
|
|
|
|
|
|
UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted
|
|
|
|
|
memcpy(ourcall, L4->L4USER, 7);
|
|
|
|
|
ourcall[6] ^= 0x1e; // Flip SSID
|
|
|
|
|
|
|
|
|
|
if (CompareCalls(Msg->DEST, ourcall))
|
|
|
|
|
{
|
|
|
|
|
// Get Session Entry for Downlink
|
|
|
|
|
|
|
|
|
|
TRANSPORTENTRY * NewSess = L4TABLE;
|
|
|
|
|
struct _LINKTABLE * LINK;
|
|
|
|
|
char Normcall[10];
|
|
|
|
|
|
|
|
|
|
int Index = 0;
|
|
|
|
|
|
|
|
|
|
while (Index < MAXCIRCUITS)
|
|
|
|
|
{
|
|
|
|
|
if (NewSess->L4USER[0] == 0)
|
|
|
|
|
{
|
|
|
|
|
// Got One
|
|
|
|
|
|
|
|
|
|
L4->L4CROSSLINK = NewSess;
|
|
|
|
|
NewSess->L4CROSSLINK = L4;
|
|
|
|
|
|
|
|
|
|
memcpy(NewSess->L4USER, L4->L4USER, 7);
|
|
|
|
|
memcpy(NewSess->L4MYCALL, L4->L4USER, 7);
|
|
|
|
|
|
|
|
|
|
NewSess->CIRCUITINDEX = Index; //OUR INDEX
|
|
|
|
|
NewSess->CIRCUITID = NEXTID;
|
|
|
|
|
NewSess->L4STATE = 5;
|
|
|
|
|
NewSess->L4CIRCUITTYPE = L2LINK+UPLINK;
|
|
|
|
|
|
|
|
|
|
NEXTID++;
|
|
|
|
|
if (NEXTID == 0)
|
|
|
|
|
NEXTID++; // kEEP nON-ZERO
|
|
|
|
|
|
|
|
|
|
NewSess->SESSIONT1 = L4->SESSIONT1;
|
|
|
|
|
NewSess->L4WINDOW = (UCHAR)L4DEFAULTWINDOW;
|
|
|
|
|
|
|
|
|
|
// SET UP NEW SESSION (OR RESET EXISTING ONE)
|
|
|
|
|
|
|
|
|
|
FindLink(Msg->ORIGIN, ourcall, L4->LISTEN, &LINK);
|
|
|
|
|
|
|
|
|
|
if (LINK == NULL)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
|
|
memcpy(LINK->LINKCALL, Msg->ORIGIN, 7);
|
|
|
|
|
LINK->LINKCALL[6] &= 0xFE;
|
|
|
|
|
memcpy(LINK->OURCALL, ourcall, 7);
|
|
|
|
|
|
|
|
|
|
LINK->LINKPORT = PORT;
|
|
|
|
|
|
|
|
|
|
LINK->L2TIME = PORT->PORTT1;
|
|
|
|
|
/*
|
|
|
|
|
// Copy Digis
|
|
|
|
|
|
|
|
|
|
n = 7;
|
|
|
|
|
ptr = &LINK->DIGIS[0];
|
|
|
|
|
|
|
|
|
|
while (axcalls[n])
|
|
|
|
|
{
|
|
|
|
|
memcpy(ptr, &axcalls[n], 7);
|
|
|
|
|
n += 7;
|
|
|
|
|
ptr += 7;
|
|
|
|
|
|
|
|
|
|
LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
LINK->LINKTYPE = 2; // DOWNLINK
|
|
|
|
|
LINK->LINKWINDOW = PORT->PORTWINDOW;
|
|
|
|
|
|
|
|
|
|
RESET2(LINK); // RESET ALL FLAGS
|
|
|
|
|
|
|
|
|
|
LINK->L2STATE = 5; // CONNECTED
|
|
|
|
|
|
|
|
|
|
LINK->CIRCUITPOINTER = NewSess;
|
|
|
|
|
|
|
|
|
|
NewSess->L4TARGET.LINK = LINK;
|
|
|
|
|
|
|
|
|
|
if (PORT->PORTPACLEN)
|
|
|
|
|
NewSess->SESSPACLEN = L4->SESSPACLEN = PORT->PORTPACLEN;
|
|
|
|
|
|
|
|
|
|
L2SENDUA(PORT, Msg, Msg); // RESET OF DOWN/CROSSLINK
|
|
|
|
|
|
|
|
|
|
L4->LISTEN = FALSE; // Take out of listen mode
|
|
|
|
|
|
|
|
|
|
// Tell User
|
|
|
|
|
|
|
|
|
|
Buffer = GetBuff();
|
|
|
|
|
|
|
|
|
|
if (Buffer == NULL)
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
// SET UP HEADER
|
|
|
|
|
|
|
|
|
|
Buffer->PID = 0xf0;
|
|
|
|
|
|
|
|
|
|
ptr = &Buffer->L2DATA[0];
|
|
|
|
|
|
|
|
|
|
Normcall[ConvFromAX25(LINK->LINKCALL, Normcall)] = 0;
|
|
|
|
|
|
|
|
|
|
ptr += sprintf(ptr, "Incoming call from %s\r", Normcall);
|
|
|
|
|
|
|
|
|
|
Buffer->LENGTH = (int)(ptr - (UCHAR *)Buffer);
|
|
|
|
|
|
|
|
|
|
C_Q_ADD(&L4->L4TX_Q, Buffer);
|
|
|
|
|
PostDataAvailable(L4);
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
Index++;
|
|
|
|
|
NewSess++;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
L4++;
|
|
|
|
|
}
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|