linbpq/KAMPactor.c

2087 lines
48 KiB
C
Raw Permalink Normal View History

2022-08-28 09:35:46 +01:00
/*
2022-11-14 14:02:28 +00:00
Copyright 2001-2022 John Wiseman G8BPQ
2022-08-28 09:35:46 +01:00
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
*/
//
// DLL to inteface KAM TNC in Pactor Mode to BPQ32 switch
//
// Uses BPQ EXTERNAL interface
//
// Version 1.2.1.2 July 2010
// Send Change to ISS before each transmission
// Support up to 32 BPQ Ports
// Version 1.2.1.3 August 2010
// Drop RTS as well as DTR on close
// Version 1.2.1.4 August 2010
// Save Minimized State
// Version 1.2.1.5 September 2010
// Fix Freq Display after Node reconfig
// Only use AutoConnect APPL for Pactor Connects
// Version 1.2.2.1 September 2010
// Add option to get config from bpq32.cfg
//#define WIN32_LEAN_AND_MEAN
#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include "time.h"
#include "CHeaders.h"
#include "tncinfo.h"
#include "bpq32.h"
#define NARROWMODE Report_P1
#define WIDEMODE Report_P1 // Only supports PI
static char ClassName[]="KAMPACTORSTATUS";
static char WindowTitle[] = "KAM Pactor";
static int RigControlRow = 165;
extern UCHAR LogDirectory[];
static RECT Rect;
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
static FILE * LogHandle[32] = {0};
//char * Logs[4] = {"1", "2", "3", "4"};
static char BaseDir[MAX_PATH]="c:\\";
static BOOL WRITELOG = FALSE;
BOOL KAMStopPort(struct PORTCONTROL * PORT)
{
// Disable Port - close TCP Sockets or Serial Port
struct TNCINFO * TNC = PORT->TNC;
TNC->CONNECTED = FALSE;
TNC->Alerted = FALSE;
if (TNC->Streams[0].Attached)
TNC->Streams[0].ReportDISC = TRUE;
if (TNC->hDevice)
{
CloseCOMPort(TNC->hDevice);
TNC->hDevice = 0;
}
TNC->HostMode = FALSE;
sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped");
MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE);
return TRUE;
}
BOOL KAMStartPort(struct PORTCONTROL * PORT)
{
// Restart Port - Open Sockets or Serial Port
struct TNCINFO * TNC = PORT->TNC;
TNC->ReopenTimer = 999;
TNC->ReinitState = 0;
sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted");
MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE);
return TRUE;
}
static VOID CloseLogFile(int Flags)
{
if (WRITELOG)
{
fclose(LogHandle[Flags]);
LogHandle[Flags] = NULL;
}
}
static BOOL OpenLogFile(int Flags)
{
if (WRITELOG)
{
UCHAR FN[MAX_PATH];
time_t T;
struct tm * tm;
T = time(NULL);
tm = gmtime(&T);
sprintf(FN,"%s/logs/KAMLog_%02d%02d_%d.txt", LogDirectory, tm->tm_mon + 1, tm->tm_mday, Flags);
LogHandle[Flags] = fopen(FN, "ab");
return (LogHandle[Flags] != NULL);
}
return 0;
}
static void WriteLogLine(int Port, char * Msg, int MsgLen)
{
if (WRITELOG)
{
OpenLogFile(Port);
if (LogHandle[Port])
{
fwrite(Msg, 1, MsgLen, LogHandle[Port]);
fwrite("\r\n", 1, 2, LogHandle[Port]);
}
CloseLogFile(Port);
}
}
int ProcessLine(char * buf, int Port)
{
UCHAR * ptr,* p_cmd;
char * p_ipad = 0;
char * p_port = 0;
unsigned short WINMORport = 0;
int BPQport;
int len=510;
struct TNCINFO * TNC;
char errbuf[256];
strcpy(errbuf, buf);
BPQport = Port;
TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO));
memset(TNC, 0, sizeof(struct TNCINFO));
TNC->InitScript = malloc(1000);
TNC->InitScript[0] = 0;
goto ConfigLine;
// Read Initialisation lines
while(TRUE)
{
if (GetLine(buf) == 0)
return TRUE;
ConfigLine:
strcpy(errbuf, buf);
if (memcmp(buf, "****", 4) == 0)
return TRUE;
ptr = strchr(buf, ';');
if (ptr)
{
*ptr++ = 13;
*ptr = 0;
}
if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log
WRITELOG = atoi(&buf[9]);
else if (_memicmp(buf, "APPL", 4) == 0)
{
p_cmd = strtok(&buf[5], " \t\n\r");
if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#')
TNC->ApplCmd=_strdup(_strupr(p_cmd));
}
else if (_memicmp(buf, "OLDMODE", 7) == 0)
TNC->OldMode = TRUE;
else if (_memicmp(buf, "VERYOLDMODE", 7) == 0)
{
TNC->OldMode = TRUE;
TNC->VeryOldMode = TRUE;
}
else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy
TNC->BusyWait = atoi(&buf[8]);
else if (_memicmp(buf, "WL2KREPORT", 10) == 0)
TNC->WL2K = DecodeWL2KReportLine(buf);
else
strcat (TNC->InitScript, buf);
}
return (TRUE);
}
#define FEND 0xC0 // KISS CONTROL CODES
#define FESC 0xDB
#define TFEND 0xDC
#define TFESC 0xDD
static int MaxStreams = 26;
BOOL CloseConnection(struct TNCINFO * conn);
static BOOL WriteCommBlock(struct TNCINFO * TNC);
BOOL DestroyTTYInfo(int port);
void CheckRXKAM(struct TNCINFO * TNC);
VOID KAMPoll(int Port);
VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len);
static VOID ProcessTermModeResponse(struct TNCINFO * TNC);
static VOID DoTNCReinit(struct TNCINFO * TNC);
static VOID DoTermModeTimeout(struct TNCINFO * TNC);
VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer);
VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len);
// Note that Kantronics host Mode uses KISS format Packets (without a KISS COntrol Byte)
VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len);
static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len);
static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
int txlen;
PMSGWITHLEN buffptr;
struct TNCINFO * TNC = TNCInfo[port];
struct STREAMINFO * STREAM;
int Stream;
size_t Param;
struct ScanEntry * Scan;
if (TNC == NULL)
return 0;
if (TNC->hDevice == 0)
{
// Clear anything from UI_Q
while (TNC->PortRecord->UI_Q)
{
buffptr = Q_REM(&TNC->PortRecord->UI_Q);
ReleaseBuffer(buffptr);
}
// Try to reopen every 30 secs
if (fn > 3 && fn < 7)
goto ok;
TNC->ReopenTimer++;
if (TNC->ReopenTimer < 300)
return 0;
TNC->ReopenTimer = 0;
if (TNC->PortRecord->PORTCONTROL.PortStopped == 0)
OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE);
if (TNC->hDevice == 0)
return 0;
}
ok:
switch (fn)
{
case 7:
// 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances
2023-06-21 08:21:04 +01:00
// G7TAJ's code to record activity for stats display
if ( TNC->BusyFlags && CDBusy )
TNC->PortRecord->PORTCONTROL.ACTIVE += 2;
if ( TNC->PTTState )
TNC->PortRecord->PORTCONTROL.SENDING += 2;
2022-08-28 09:35:46 +01:00
CheckRXKAM(TNC);
KAMPoll(port);
return 0;
case 1: // poll
while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q
{
buffptr = Q_REM(&TNC->PortRecord->UI_Q);
ReleaseBuffer(buffptr);
}
for (Stream = 0; Stream <= MaxStreams; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (STREAM->ReportDISC)
{
STREAM->ReportDISC = FALSE;
buff->PORT = Stream;
return -1;
}
}
for (Stream = 0; Stream <= MaxStreams; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (STREAM->PACTORtoBPQ_Q !=0)
{
int datalen;
buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);
datalen = (int)buffptr->Len;
buff->PORT = Stream;
buff->PID= 0xf0;
memcpy(buff->L2DATA, buffptr->Data, datalen); // Data goes to +7, but we have an extra byte
datalen += (MSGHDDRLEN + 1);
PutLengthinBuffer(buff, datalen);
ReleaseBuffer(buffptr);
return (1);
}
}
return 0;
case 2: // send
buffptr = GetBuff();
if (buffptr == 0) return (0); // No buffers, so ignore
// Find TNC Record
Stream = buff->PORT;
STREAM = &TNC->Streams[Stream];
if (!TNC->TNCOK)
{
// Send Error Response
buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return 0;
}
txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1);
buffptr->Len = txlen;
memcpy(buffptr->Data, buff->L2DATA, txlen);
C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);
if(STREAM->Connected)
{
STREAM->FramesOutstanding++;
STREAM->FramesQueued++;
STREAM->BytesOutstanding += txlen;
}
return (0);
case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding
Stream = (int)(size_t)buff;
STREAM = &TNC->Streams[Stream];
if (Stream == 0)
{
if (TNC->HFPacket)
{
if (TNC->Mem1 < 2000 || TNC->Streams[0].FramesOutstanding > 4)
return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
}
else
{
if (TNC->Streams[0].FramesQueued > 4)
return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
}
}
else
{
if (STREAM->FramesOutstanding > 3 || STREAM->BytesOutstanding > 500 || TNC->Mem1 < 500)
return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
}
return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting
case 4: // reinit
return (0);
case 5: // Close
EncodeAndSend(TNC, "Q", 1); // Exit Host Mode
Sleep(50);
CloseCOMPort(TNCInfo[port]->hDevice);
return (0);
case 6: // Scan Control
// Use P0 to Disable Pactor, P1 to enable
Param = (size_t)buff;
if (Param == 2) // Check Permission (shouldn't happen)
{
return 1; // OK to change
}
if (!TNC->HostMode)
return 0; // No connection so no interlock
if (Param == 1) // Request Permission
return 0; // OK to Change // Dont want to mess with disabling it
if (Param == 3) // Release Permission
return 0;
// Param is Address of a struct ScanEntry
Scan = (struct ScanEntry *)buff;
if (Scan->PMaxLevel >= '0' && Scan->PMaxLevel < '5') // 1 - 4
{
if (TNC->Bandwidth != Scan->PMaxLevel)
{
// Enable or disable by setting mycall
TNC->Bandwidth = Scan->PMaxLevel;
if (Scan->PMaxLevel == '0')
{
EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
return 0;
}
if (TNC->OldMode)
EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen
else
EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen
TNC->InternalCmd = 'T';
}
}
}
return 0;
}
static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
int Len = sprintf(Buff, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
"<head><title>KAM Pactor Status</title></head><body><h3>KAM Pactor Status</h3>");
Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 480px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");
Len += sprintf(&Buff[Len], "<tr><td width=90px>Comms State</td><td>%s</td></tr>", TNC->WEB_COMMSSTATE);
Len += sprintf(&Buff[Len], "<tr><td>TNC State</td><td>%s</td></tr>", TNC->WEB_TNCSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_MODE);
Len += sprintf(&Buff[Len], "<tr><td>Status</td><td>%s</td></tr>", TNC->WEB_STATE);
Len += sprintf(&Buff[Len], "<tr><td>TX/RX State</td><td>%s</td></tr>", TNC->WEB_TXRX);
Len += sprintf(&Buff[Len], "<tr><td>Free Space</td><td>%s</td></tr>", TNC->WEB_BUFFERS);
Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
Len += sprintf(&Buff[Len], "</table>");
Len += sprintf(&Buff[Len], "<textarea rows=10 style=\"width:500px; height:250px;\" id=textarea >%s</textarea>", TNC->WebBuffer);
Len = DoScanLine(TNC, Buff, Len);
return Len;
}
void * KAMExtInit(EXTPORTDATA * PortEntry)
{
char msg[500];
struct TNCINFO * TNC;
int port;
char * ptr;
char * TempScript;
port=PortEntry->PORTCONTROL.PORTNUMBER;
sprintf(msg,"KAM Pactor %s", PortEntry->PORTCONTROL.SerialPortName);
WritetoConsole(msg);
ReadConfigFile(port, ProcessLine);
TNC = TNCInfo[port];
if (TNC == NULL)
{
// Not defined in Config file
sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n");
WritetoConsole(msg);
return ExtProc;
}
TNC->Port = port;
TNC->Hardware = H_KAM;
if (TNC->BusyWait == 0)
TNC->BusyWait = 10;
PortEntry->MAXHOSTMODESESSIONS = 11; // Default
TNC->PortRecord = PortEntry;
if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
memcpy(TNC->NodeCall, MYNODECALL, 10);
else
ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor
PortEntry->PORTCONTROL.PORTQUALITY = 0;
PortEntry->SCANCAPABILITIES = NONE; // No Scan Control
if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
PortEntry->PORTCONTROL.PORTPACLEN = 100;
PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort;
PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort;
ptr=strchr(TNC->NodeCall, ' ');
if (ptr) *(ptr) = 0; // Null Terminate
// Set Essential Params and MYCALL
TempScript = malloc(4000);
strcpy(TempScript, "MARK 1400\r");
strcat(TempScript, "SPACE 1600\r");
strcat(TempScript, "SHIFT MODEM\r");
strcat(TempScript, "INV ON\r");
strcat(TempScript, "PTERRS 30\r"); // Default Retries
strcat(TempScript, "MAXUSERS 1/10\r");
strcat(TempScript, TNC->InitScript);
free(TNC->InitScript);
TNC->InitScript = TempScript;
// Others go on end so they can't be overriden
strcat(TNC->InitScript, "ECHO OFF\r");
strcat(TNC->InitScript, "XMITECHO ON\r");
strcat(TNC->InitScript, "TXFLOW OFF\r");
strcat(TNC->InitScript, "XFLOW OFF\r");
strcat(TNC->InitScript, "TRFLOW OFF\r");
strcat(TNC->InitScript, "AUTOCR 0\r");
strcat(TNC->InitScript, "AUTOLF OFF\r");
strcat(TNC->InitScript, "CRADD OFF\r");
strcat(TNC->InitScript, "CRSUP OFF\r");
strcat(TNC->InitScript, "CRSUP OFF/OFF\r");
strcat(TNC->InitScript, "LFADD OFF/OFF\r");
strcat(TNC->InitScript, "LFADD OFF\r");
strcat(TNC->InitScript, "LFSUP OFF/OFF\r");
strcat(TNC->InitScript, "LFSUP OFF\r");
strcat(TNC->InitScript, "RING OFF\r");
strcat(TNC->InitScript, "ARQBBS OFF\r");
// Set the ax.25 MYCALL
sprintf(msg, "MYCALL %s/%s\r", TNC->NodeCall, TNC->NodeCall);
strcat(TNC->InitScript, msg);
// look for the MAXUSERS config line, and get the limits
TNC->InitScript = _strupr(TNC->InitScript);
ptr = strstr(TNC->InitScript, "MAXUSERS");
if (ptr)
{
ptr = strchr(ptr,'/'); // to the separator
if (ptr)
PortEntry->MAXHOSTMODESESSIONS = atoi(++ptr) + 1;
}
if (PortEntry->MAXHOSTMODESESSIONS > 26)
PortEntry->MAXHOSTMODESESSIONS = 26;
PortEntry->PORTCONTROL.TNC = TNC;
TNC->WebWindowProc = WebProc;
TNC->WebWinX = 510;
TNC->WebWinY = 280;
TNC->WEB_COMMSSTATE = zalloc(100);
TNC->WEB_TNCSTATE = zalloc(100);
strcpy(TNC->WEB_TNCSTATE, "Free");
TNC->WEB_MODE = zalloc(100);
TNC->WEB_TRAFFIC = zalloc(100);
TNC->WEB_BUFFERS = zalloc(100);
TNC->WEB_STATE = zalloc(100);
TNC->WEB_TXRX = zalloc(100);
TNC->WebBuffer = zalloc(5000);
#ifndef LINBPQ
CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 500, ForcedClose);
CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Free Space", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,374,20 , TNC->hDlg, NULL, hInstance, NULL);
TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
0,RigControlRow + 44,250,300, TNC->hDlg, NULL, hInstance, NULL);
TNC->ClientHeight = 500;
TNC->ClientWidth = 500;
MoveWindows(TNC);
#endif
OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE);
WritetoConsole("\n");
return ExtProc;
}
void CheckRXKAM(struct TNCINFO * TNC)
{
int Length, Len;
char debug[512] = "RX: ";
// only try to read number of bytes in queue
if (TNC->RXLen == 500)
TNC->RXLen = 0;
Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen);
if (Len == 0)
return;
TNC->RXLen += Len;
memcpy(&debug[4], TNC->RXBuffer, TNC->RXLen);
debug[TNC->RXLen + 4] = 0;
WriteLogLine(TNC->Port, debug, TNC->RXLen + 4);
Length = TNC->RXLen;
// If first char != FEND, then probably a Terminal Mode Frame. Wait for CR on end
if (TNC->RXBuffer[0] != FEND)
{
// Char Mode Frame I think we need to see cmd: on end
// If we think we are in host mode, then to could be noise - just discard.
if (TNC->HostMode)
{
TNC->RXLen = 0; // Ready for next frame
return;
}
TNC->RXBuffer[TNC->RXLen] = 0;
// if (TNC->RXBuffer[TNC->RXLen-2] != ':')
if (strstr(TNC->RXBuffer, "cmd:") == 0)
return; // Wait for rest of frame
// Complete Char Mode Frame
TNC->RXLen = 0; // Ready for next frame
if (TNC->HostMode == 0)
{
// We think TNC is in Terminal Mode
ProcessTermModeResponse(TNC);
return;
}
// We thought it was in Host Mode, but are wrong.
TNC->HostMode = FALSE;
return;
}
// Receiving a Host Mode frame
if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery
{
TNC->RXLen = 0; // Ready for next frame
return;
}
if (Length < 3) // Minimum Frame Sise
return;
if (TNC->RXBuffer[Length-1] != FEND)
return; // Wait till we have a full frame
ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer
TNC->RXLen = 0; // Ready for next frame
return;
}
VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len)
{
UCHAR * FendPtr;
int NewLen;
// Split into KISS Packets. By far the most likely is a single KISS frame
// so treat as special case
if (rxbuffer[1] == FEND) // Two FENDS - probably got out of sync
{
rxbuffer++;
Len--;
}
FendPtr = memchr(&rxbuffer[1], FEND, Len-1);
if (FendPtr == &rxbuffer[Len-1])
{
ProcessKHOSTPacket(TNC, &rxbuffer[1], Len - 2);
return;
}
// Process the first Packet in the buffer
NewLen = (int)(FendPtr - rxbuffer - 1);
ProcessKHOSTPacket(TNC, &rxbuffer[1], NewLen);
// Loop Back
ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2);
return;
}
static BOOL WriteCommBlock(struct TNCINFO * TNC)
{
char debug[512] = "TX: ";
memcpy(&debug[4], TNC->TXBuffer, TNC->TXLen);
debug[TNC->TXLen + 4] = 0;
WriteLogLine(TNC->Port, debug, TNC->TXLen + 4);
WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen);
return TRUE;
}
VOID KAMPoll(int Port)
{
struct TNCINFO * TNC = TNCInfo[Port];
struct STREAMINFO * STREAM;
UCHAR * Poll = TNC->TXBuffer;
char Status[80];
int Stream;
if (TNC->PortRecord == 0)
Stream = 0;
// If Pactor Session has just been attached, drop back to cmd mode and set Pactor Call to
// the connecting user's callsign
for (Stream = 0; Stream <= MaxStreams; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0)
{
// New Attach
STREAM->Attached = TRUE;
if (Stream == 0) // HF Port
{
int calllen;
UCHAR TXMsg[1000] = "D20";
int datalen;
char Msg[80];
TNC->HFPacket = FALSE;
TNC->TimeInRX = 0;
calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall);
TNC->Streams[0].MyCall[calllen] = 0;
EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
if (TNC->VeryOldMode)
datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall);
else
datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall);
EncodeAndSend(TNC, TXMsg, datalen);
TNC->InternalCmd = 'M';
TNC->NeedPACTOR = 0; // Cancel enter Pactor
sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
// Stop Scanning
sprintf(Msg, "%d SCANSTOP", TNC->Port);
2023-05-16 16:40:12 +01:00
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
2022-08-28 09:35:46 +01:00
}
}
}
if (TNC->Timeout)
{
TNC->Timeout--;
if (TNC->Timeout) // Still waiting
return;
// Timed Out
if (TNC->HostMode == 0)
{
DoTermModeTimeout(TNC);
return;
}
// Timed out in host mode - Clear any connection and reinit the TNC
Debugprintf("KAM PACTOR - Link to TNC Lost");
TNC->TNCOK = FALSE;
TNC->HostMode = 0;
TNC->ReinitState = 0;
sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName);
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
for (Stream = 0; Stream <= MaxStreams; Stream++)
{
PMSGWITHLEN buffptr;
STREAM = &TNC->Streams[Stream];
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected
{
STREAM->Connected = FALSE; // Back to Command Mode
STREAM->ReportDISC = TRUE; // Tell Node
}
STREAM->FramesQueued = 0;
while(STREAM->BPQtoPACTOR_Q)
{
buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
ReleaseBuffer(buffptr);
}
while(STREAM->PACTORtoBPQ_Q)
{
buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);
ReleaseBuffer(buffptr);
}
}
}
for (Stream = 0; Stream <= MaxStreams; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (STREAM->Attached)
CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);
}
// if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence
if (!TNC->HostMode)
{
DoTNCReinit(TNC);
return;
}
if (TNC->BusyDelay) // Waiting to send connect
{
// Still Busy?
if (InterlockedCheckBusy(TNC) == 0)
{
// No, so send
EncodeAndSend(TNC, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd));
free(TNC->ConnectCmd);
TNC->Timeout = 50;
TNC->InternalCmd = 'C'; // So we dont send the reply to the user.
STREAM->Connecting = TRUE;
TNC->Streams[0].Connecting = TRUE;
sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
TNC->BusyDelay = 0;
return;
}
else
{
// Wait Longer
TNC->BusyDelay--;
if (TNC->BusyDelay == 0)
{
// Timed out - Send Error Response
PMSGWITHLEN buffptr = GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r");
C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
free(TNC->ConnectCmd);
}
}
}
if (TNC->NeedPACTOR)
{
TNC->NeedPACTOR--;
if (TNC->NeedPACTOR == 0)
{
int datalen;
UCHAR TXMsg[80] = "D20";
if (TNC->VeryOldMode)
datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall);
else
datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall);
EncodeAndSend(TNC, TXMsg, datalen);
if (TNC->OldMode)
EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen
else
EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen
TNC->InternalCmd = 'T';
TNC->Timeout = 50;
TNC->IntCmdDelay--;
// Restart Scanning
sprintf(Status, "%d SCANSTART 15", TNC->Port);
2023-05-16 16:40:12 +01:00
Rig_Command( (TRANSPORTENTRY *) -1, Status);
2022-08-28 09:35:46 +01:00
return;
}
}
for (Stream = 0; Stream <= MaxStreams; Stream++)
{
STREAM = &TNC->Streams[Stream];
// If in HF Packet mode, normal flow control doesn't seem to work
// If more that 4 packets sent, send a status poll. and use response to
// reset frames outstanding
if ((Stream == 0) && (TNC->HFPacket) && (TNC->Streams[0].FramesOutstanding > 4))
{
EncodeAndSend(TNC, "C10S", 4);
TNC->InternalCmd = 'S';
TNC->Timeout = 50;
return;
}
if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q)
{
int datalen;
UCHAR TXMsg[1000] = "D20";
PMSGWITHLEN buffptr;
UCHAR * MsgPtr;
char Status[80];
if (STREAM->Connected)
{
int Next;
if (Stream > 0)
sprintf(TXMsg, "D1%c", Stream + '@');
else if (TNC->HFPacket)
memcpy(TXMsg, "D2A", 3);
else
{
// Pactor
// Limit amount in TX, so we keep some on the TX Q and don't send turnround too early
2024-11-05 21:03:15 +00:00
if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200)
2022-08-28 09:35:46 +01:00
continue;
// Dont send if IRS State
// If in IRS state for too long, force turnround
if (TNC->TXRXState == 'R')
{
if (TNC->TimeInRX++ > 15)
EncodeAndSend(TNC, "T", 1); // Changeover to ISS
else
goto Poll;
}
TNC->TimeInRX = 0;
}
buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
STREAM->FramesQueued--;
datalen = buffptr->Len;
MsgPtr = buffptr->Data;
if (TNC->SwallowSignon && Stream == 0)
{
TNC->SwallowSignon = FALSE;
if (strstr(MsgPtr, "Connected")) // Discard *** connected
{
ReleaseBuffer(buffptr);
return;
}
}
Next = 0;
2024-11-05 21:03:15 +00:00
STREAM->bytesTXed += datalen;
2022-08-28 09:35:46 +01:00
if (Stream == 0)
{
while (datalen > 100) // Limit Pactor Sends
{
memcpy(&TXMsg[3], &MsgPtr[Next], 100);
EncodeAndSend(TNC, TXMsg, 103);
Next += 100;
datalen -= 100;
WritetoTrace(TNC, &TXMsg[3], 100);
}
}
memcpy(&TXMsg[3], &MsgPtr[Next], datalen);
EncodeAndSend(TNC, TXMsg, datalen + 3);
WritetoTrace(TNC, &TXMsg[3], datalen);
ReleaseBuffer(buffptr);
if (Stream == 0)
{
sprintf(Status, "RX %d TX %d ACKED %d ",
2024-11-05 21:03:15 +00:00
TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
2022-08-28 09:35:46 +01:00
SetWindowText(TNC->xIDC_TRAFFIC, Status);
if ((TNC->HFPacket == 0) && (TNC->Streams[0].BPQtoPACTOR_Q == 0)) // Nothing following
{
EncodeAndSend(TNC, "E", 1); // Changeover when all sent
}
}
if (STREAM->Disconnecting)
{
Debugprintf("Send with Disc Pending, Q = %x", STREAM->BPQtoPACTOR_Q);
if (STREAM->BPQtoPACTOR_Q == 0) // All Sent
// KAM doesnt have a tidy close!
STREAM->DisconnectingTimeout = 100; // Give 5 secs to get to other end
}
return;
}
else // Not Connected
{
buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
datalen = buffptr->Len;
MsgPtr = buffptr->Data;
// Command. Do some sanity checking and look for things to process locally
datalen--; // Exclude CR
MsgPtr[datalen] = 0; // Null Terminate
_strupr(MsgPtr);
if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0)
{
sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]);
2023-05-16 16:40:12 +01:00
if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40]))
2022-08-28 09:35:46 +01:00
{
ReleaseBuffer(buffptr);
}
else
{
buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
return;
}
if (_memicmp(MsgPtr, "D\r", 2) == 0)
{
STREAM->ReportDISC = TRUE; // Tell Node
return;
}
if ((Stream == 0) && memcmp(MsgPtr, "HFPACKET", 8) == 0)
{
TNC->HFPacket = TRUE;
buffptr->Len = sprintf(buffptr->Data, "KAM} OK\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return;
}
if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect
{
memcpy(STREAM->RemoteCall, &MsgPtr[2], 9);
STREAM->Connecting = TRUE;
// If Stream 0, Convert C CALL to PACTOR CALL
if (Stream == 0)
{
if (TNC->HFPacket)
datalen = sprintf(TXMsg, "C2AC %s", TNC->Streams[0].RemoteCall);
else
datalen = sprintf(TXMsg, "C20PACTOR %s", TNC->Streams[0].RemoteCall);
// If Pactor, check busy detecters on any interlocked ports
if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0)
{
// Channel Busy. Wait
TNC->ConnectCmd = _strdup(TXMsg);
sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
TNC->BusyDelay = TNC->BusyWait * 10;
return;
}
TNC->OverrideBusy = FALSE;
sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s",
TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
}
else
datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall);
EncodeAndSend(TNC, TXMsg, datalen);
TNC->Timeout = 50;
TNC->InternalCmd = 'C'; // So we dont send the reply to the user.
ReleaseBuffer(buffptr);
STREAM->Connecting = TRUE;
return;
}
if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect
{
if (Stream == 0)
{
if (TNC->HFPacket)
EncodeAndSend(TNC, "C2AD", 4); // ??Return to packet mode??
else
EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
TNC->NeedPACTOR = 50;
}
else
{
sprintf(TXMsg, "C1%cD", Stream + '@');
EncodeAndSend(TNC, TXMsg, 4);
TNC->CmdStream = Stream;
TNC->Timeout = 50;
}
TNC->Timeout = 0; // Don't expect a response
STREAM->Connecting = FALSE;
STREAM->ReportDISC = TRUE;
ReleaseBuffer(buffptr);
return;
}
// Other Command ??
if (Stream > 0)
datalen = sprintf(TXMsg, "C1%c%s", Stream + '@', MsgPtr);
else
datalen = sprintf(TXMsg, "C20%s", MsgPtr);
EncodeAndSend(TNC, TXMsg, datalen);
ReleaseBuffer(buffptr);
TNC->Timeout = 50;
TNC->InternalCmd = 0;
TNC->CmdStream = Stream;
}
}
}
Poll:
// Need to poll data and control channel (for responses to commands)
// Also check status if we have data buffered (for flow control)
if (TNC->TNCOK)
{
if (TNC->IntCmdDelay == 50)
{
EncodeAndSend(TNC, "C10S", 4);
TNC->InternalCmd = 'S';
TNC->Timeout = 50;
TNC->IntCmdDelay--;
return;
}
if (TNC->IntCmdDelay <=0)
{
if (TNC->VeryOldMode == FALSE)
{
EncodeAndSend(TNC, "?", 1);
TNC->InternalCmd = '?';
TNC->Timeout = 50;
}
TNC->IntCmdDelay = 100; // Every 30
return;
}
else
TNC->IntCmdDelay--;
}
return;
}
static VOID DoTNCReinit(struct TNCINFO * TNC)
{
UCHAR * Poll = TNC->TXBuffer;
if (TNC->ReinitState == 1) // Forcing back to Term
TNC->ReinitState = 0; // Got Response, so must be back in term mode
if (TNC->ReinitState == 0)
{
// Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode
sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName);
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
Poll[0] = 13;
TNC->TXLen = 1;
WriteCommBlock(TNC);
TNC->Timeout = 50;
return;
}
if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands
{
char * start, * end;
int len;
start = TNC->InitPtr;
if (*(start) == 0) // End of Script
{
// Put into Host Mode
memcpy(Poll, "INTFACE HOST\r", 13);
TNC->TXLen = 13;
WriteCommBlock(TNC);
TNC->Timeout = 50;
TNC->ReinitState = 4; // Need Reset
return;
}
end = strchr(start, 13);
len = (int)(++end - start);
TNC->InitPtr = end;
memcpy(Poll, start, len);
TNC->TXLen = len;
WriteCommBlock(TNC);
TNC->Timeout = 50;
return;
}
}
static VOID DoTermModeTimeout(struct TNCINFO * TNC)
{
UCHAR * Poll = TNC->TXBuffer;
if (TNC->ReinitState == 0)
{
//Checking if in Terminal Mode - Try to set back to Term Mode
TNC->ReinitState = 1;
Poll[0] = 3;
Poll[1] = 0x58; // ?? Back to cmd: mode ??
TNC->TXLen = 2;
Poll[0] = 0xc0;
Poll[1] = 'Q'; // ?? Back to cmd: mode ??
Poll[2] = 0xc0;
TNC->TXLen = 3;
WriteCommBlock(TNC);
return;
}
if (TNC->ReinitState == 1)
{
// Forcing back to Term Mode
TNC->ReinitState = 0;
DoTNCReinit(TNC); // See if worked
return;
}
if (TNC->ReinitState == 3)
{
// Entering Host Mode
// Assume ok
TNC->HostMode = TRUE;
return;
}
}
static VOID ProcessTermModeResponse(struct TNCINFO * TNC)
{
UCHAR * Poll = TNC->TXBuffer;
if (TNC->ReinitState == 0 || TNC->ReinitState == 1)
{
// Testing if in Term Mode. It is, so can now send Init Commands
TNC->InitPtr = TNC->InitScript;
TNC->ReinitState = 2;
DoTNCReinit(TNC); // Send First Command
return;
}
if (TNC->ReinitState == 2)
{
// Sending Init Commands
DoTNCReinit(TNC); // Send Next Command
return;
}
if (TNC->ReinitState == 4) // Send INTFACE, Need RESET
{
TNC->ReinitState = 5;
memcpy(Poll, "RESET\r", 6);
TNC->TXLen = 6;
WriteCommBlock(TNC);
TNC->Timeout = 50;
TNC->HostMode = TRUE; // Should now be in Host Mode
TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs
return;
}
if (TNC->ReinitState == 5) // RESET sent
{
TNC->ReinitState = 5;
return;
}
}
VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len)
{
PMSGWITHLEN buffptr;
char * Buffer = &Msg[3]; // Data portion of frame
char * Call;
char Status[80];
int Stream = 0;
struct STREAMINFO * STREAM;
// Any valid frame is an ACK
TNC->TNCOK = TRUE;
Len = KissDecode(Msg, Msg, Len); // Remove KISS transparency
if (Msg[1] == '0' && Msg[2] == '0')
Stream = 0;
else
if (Msg[1] == '2') Stream = 0; else Stream = Msg[2] - '@';
STREAM = &TNC->Streams[Stream];
// See if Poll Reply or Data
Msg[Len] = 0; // Terminate
if (Msg[0] == 'M') // Monitor
{
DoMonitor(TNC, Msg, Len);
return;
}
if (Msg[0] == 'E') // Data Echo
{
if (Msg[1] == '2') // HF Port
{
2024-11-05 21:03:15 +00:00
if (TNC->Streams[0].bytesTXed)
2022-08-28 09:35:46 +01:00
TNC->Streams[0].BytesAcked += Len - 3; // We get an ack before the first send
sprintf(Status, "RX %d TX %d ACKED %d ",
2024-11-05 21:03:15 +00:00
TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
2022-08-28 09:35:46 +01:00
SetWindowText(TNC->xIDC_TRAFFIC, Status);
2024-11-05 21:03:15 +00:00
if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked < 500)
2022-08-28 09:35:46 +01:00
TNC->Streams[0].FramesOutstanding = 0;
}
return;
}
if (Msg[0] == 'D') // Data
{
// Pass to Appl
buffptr = GetBuff();
if (buffptr == NULL) return; // No buffers, so ignore
Len-=3; // Remove Header
buffptr->Len = Len; // Length
2024-11-05 21:03:15 +00:00
STREAM->bytesRXed += Len;
2022-08-28 09:35:46 +01:00
memcpy(buffptr->Data, Buffer, Len);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
if (Stream == 0)
{
sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ",
2024-11-05 21:03:15 +00:00
TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
2022-08-28 09:35:46 +01:00
SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
}
WritetoTrace(TNC, Buffer, Len);
return;
}
if (Msg[0] == 'C') // Command Reponse
{
TNC->Timeout = 0;
// See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc
// See if a response to internal command
if (TNC->InternalCmd)
{
// Process it
if (TNC->InternalCmd == 'S') // Status
{
char * Line;
char * ptr;
// Message is line giving free bytes, followed by a line for each active (packet) stream
// FREE BYTES 1366/5094
// A/2 #1145(12) CONNECTED to KE7XO-3
// S/2 CONNECTED to NLV
// each line is teminated by CR, and by the time it gets here it is null terminated
//FREE BYTES 2628
//A/H #80(1) CONNECTED to DK0MNL..
if (TNC->HFPacket)
TNC->Streams[0].FramesOutstanding = 0;
Line = strchr(&Msg[3], 13);
if (Line == 0) return;
*(Line) = 0;
ptr = strchr(&Msg[13], '/');
TNC->Mem1 = atoi(&Msg[13]);
if (ptr)
TNC->Mem2 = atoi(++ptr);
else
TNC->Mem2 = 0;
SetWindowText(TNC->xIDC_BUFFERS, &Msg[14]);
strcpy(TNC->WEB_BUFFERS, &Msg[14]);
while (Line[1] != 0) // End of stream
{
Stream = Line[1] - '@';
STREAM = &TNC->Streams[Stream];
if (Line[5] == '#')
{
STREAM->BytesOutstanding = atoi(&Line[6]);
ptr = strchr(&Line[6], '(');
if (ptr)
STREAM->FramesOutstanding = atoi(++ptr);
}
else
{
STREAM->BytesOutstanding = 0;
STREAM->FramesOutstanding = 0;
}
Line = strchr(&Line[1], 13);
}
return;
}
return;
}
// Pass to Appl
Stream = TNC->CmdStream;
buffptr = GetBuff();
if (buffptr == NULL) return; // No buffers, so ignore
buffptr->Len = sprintf(buffptr->Data,"KAM} %s", Buffer);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return;
}
if (Msg[0] == 'I') // ISS/IRS State
{
if (Msg[2] == '1')
{
strcpy(TNC->WEB_TXRX, "Sender");
SetWindowText(TNC->xIDC_TXRX, "Sender");
TNC->TXRXState = 'S';
}
else
{
strcpy(TNC->WEB_TXRX, "Receiver");
SetWindowText(TNC->xIDC_TXRX, "Receiver");
TNC->TXRXState = 'R';
}
return;
}
if (Msg[0] == '?') // Status
{
TNC->Timeout = 0;
return;
}
if (Msg[0] == 'S') // Status
{
if (Len < 4)
{
// Reset Response FEND FEND S00 FEND
TNC->Timeout = 0;
sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName);
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
return;
}
// Pass to Appl
if (strstr(Buffer, "STANDBY>") || strstr(Buffer, "*** DISCONNECTED"))
{
if ((STREAM->Connecting | STREAM->Connected) == 0)
{
// Not connected or Connecting. Probably response to going into Pactor Listen Mode
return;
}
if (STREAM->Connecting && STREAM->Disconnecting == FALSE)
{
// Connect Failed
buffptr = GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
STREAM->Connecting = FALSE;
STREAM->Connected = FALSE; // In case!
STREAM->FramesOutstanding = 0;
return;
}
// Must Have been connected or disconnecting - Release Session
STREAM->Connecting = FALSE;
STREAM->Connected = FALSE; // Back to Command Mode
STREAM->FramesOutstanding = 0;
if (STREAM->Disconnecting == FALSE)
STREAM->ReportDISC = TRUE; // Tell Node
STREAM->Disconnecting = FALSE;
if (Stream == 0)
{
// Need to reset Pactor Call in case it was changed
EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
TNC->NeedPACTOR = 20;
}
return;
}
if (Msg[2] == '0')
Call = strstr(Buffer, "<LINKED TO");
else
Call = strstr(Buffer, "NNECTED to");
if (Call)
{
Call+=11; // To Callsign
if (Stream == 0 && TNC->HFPacket == 0)
{
Buffer[Len-4] = 0;
}
2024-11-05 21:03:15 +00:00
STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0;
2022-08-28 09:35:46 +01:00
STREAM->ConnectTime = time(NULL);
if (Stream == 0)
{
// Stop Scanner
char Msg[80];
sprintf(Msg, "%d SCANSTOP", TNC->Port);
2023-05-16 16:40:12 +01:00
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
2022-08-28 09:35:46 +01:00
sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ",
2024-11-05 21:03:15 +00:00
TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
2022-08-28 09:35:46 +01:00
SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
}
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0)
{
// Incoming Connect
TRANSPORTENTRY * SESS;
if (Msg[1] == '2' && Msg[2] == 'A')
TNC->HFPacket = TRUE;
ProcessIncommingConnect(TNC, Call, Stream, TRUE);
SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream];
if (Stream == 0)
{
struct WL2KInfo * WL2K = TNC->WL2K;
char FreqAppl[10] = ""; // Frequecy-specific application
if (TNC->RIG && TNC->RIG != &TNC->DummyRig)
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->NodeCall, TNC->RIG->Valchar);
SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq
// If Scan Entry has a Appl, save it
if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0])
strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]);
}
else
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall);
if (WL2K)
{
SESS->Frequency = WL2K->Freq;
}
}
SESS->Mode = 11; // P1
if (WL2K)
strcpy(SESS->RMSCall, WL2K->RMSCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
EncodeAndSend(TNC, "T", 1); // Changeover to ISS
// If an autoconnect APPL is defined, send it
if (FreqAppl[0]) // Frequency spcific APPL overrides TNC APPL
{
buffptr = GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl);
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
TNC->SwallowSignon = TRUE;
return;
}
if (TNC->ApplCmd)
{
buffptr = GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
buffptr->Len = sprintf(buffptr->Data, "%s\r", TNC->ApplCmd);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
TNC->SwallowSignon = TRUE;
return;
}
}
if (FULL_CTEXT && HFCTEXTLEN == 0)
{
char CTBuff[300] = "D20";
int Len = CTEXTLEN, CTPaclen = 50;
int Next = 0;
if (Stream > 0)
sprintf(CTBuff, "D1%c", Stream + '@');
while (Len > CTPaclen) // CTEXT Paclen
{
memcpy(&CTBuff[3], &CTEXTMSG[Next], CTPaclen);
EncodeAndSend(TNC, CTBuff, CTPaclen + 3);
Next += CTPaclen;
Len -= CTPaclen;
}
memcpy(&CTBuff[3], &CTEXTMSG[Next], Len);
EncodeAndSend(TNC, CTBuff, Len + 3);
EncodeAndSend(TNC, "E", 1); // Changeover when all sent
2024-11-05 21:03:15 +00:00
TNC->Streams[0].bytesTXed += CTEXTLEN;
2022-08-28 09:35:46 +01:00
}
return;
}
else
{
// Connect Complete
buffptr = GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", Call);;
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
STREAM->Connecting = FALSE;
STREAM->Connected = TRUE; // Subsequent data to data channel
if (Stream == 0)
{
if (TNC->RIG)
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar);
else
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
UpdateMH(TNC, Call, '+', 'O');
}
return;
}
}
}
}
static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
int i,txptr=0;
UCHAR c;
for (i=0;i<len;i++)
{
c=inbuff[i];
if (c == FESC)
{
c=inbuff[++i];
{
if (c == TFESC)
c=FESC;
else
if (c == TFEND)
c=FEND;
}
}
outbuff[txptr++]=c;
}
return txptr;
}
VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len)
{
// Send A Packet With KISS Encoding
TNC->TXLen = KissEncode(txbuffer, TNC->TXBuffer, Len);
WriteCommBlock(TNC);
}
static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
int i,txptr=0;
UCHAR c;
outbuff[0]=FEND;
txptr=1;
for (i=0;i<len;i++)
{
c=inbuff[i];
switch (c)
{
case FEND:
outbuff[txptr++]=FESC;
outbuff[txptr++]=TFEND;
break;
case FESC:
outbuff[txptr++]=FESC;
outbuff[txptr++]=TFESC;
break;
default:
outbuff[txptr++]=c;
}
}
outbuff[txptr++]=FEND;
return txptr;
}
static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len)
{
// Update HM list and maube pass to monitor somehow
UCHAR * ptr;
ptr = strchr(&Msg[3], '>');
if (ptr) *(ptr) = 0;
UpdateMH(TNC, &Msg[3], ' ', 0);
}
VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
if (Stream == 0) // Pactor Stream
{
TNC->TimeInRX = 0;
if (TNC->HFPacket)
EncodeAndSend(TNC, "C2AD", 4); // Disconnect
else
EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
// EncodeAndSend(TNC, "C20TOR", 6); // Disconnect
TNC->HFPacket = FALSE;
}
else
{
UCHAR TXMsg[10];
sprintf(TXMsg, "C1%cD", Stream + '@');
EncodeAndSend(TNC, TXMsg, 4);
TNC->Timeout = 50;
}
}
VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
if (Stream == 0) // Pactor Stream
{
TNC->TimeInRX = 0;
if (TNC->HFPacket)
EncodeAndSend(TNC, "C2AD", 4); // Disconnect
else
EncodeAndSend(TNC, "X", 1); // ??Return to packet mode??
TNC->HFPacket = FALSE;
}
else
{
UCHAR TXMsg[10];
sprintf(TXMsg, "C1%cD", Stream + '@');
EncodeAndSend(TNC, TXMsg, 4); // Send twice - must force a disconnect
TNC->Timeout = 50;
}}
VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
TNC->NeedPACTOR = 50;
}
/*
MARK 1400
SPACE 1600
SHIFT MODEM
INV ON
MAXUSERS 1/10
PTERRS 30
// Others go on end so they can't be overriden
ECHO OFF
XMITECHO ON
TXFLOW OFF
XFLOW OFF
TRFLOW OFF
AUTOCR 0
AUTOLF OFF
CRADD OFF
CRSUP OFF
CRSUP OFF/OFF
LFADD OFF/OFF
LFADD OFF
LFSUP OFF/OFF
LFSUP OFF
RING OFF
ARQBBS OFF
MYCALL
*/