/*
Copyright 2001-2022 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
*/	

//
//	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);
VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);

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

		// 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;

		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;
}

VOID KAMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC)
{
	struct STREAMINFO * STREAM = &TNC->Streams[0];

	strcpy(TNC->WEB_TNCSTATE, "Interlocked");
	MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

//	STREAM->CmdSet = STREAM->CmdSave = zalloc(100);
//	sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC");		// Should prevent connects

}

VOID KAMReleasePort(struct TNCINFO * TNC)
{
	struct STREAMINFO * STREAM = &TNC->Streams[0];

	strcpy(TNC->WEB_TNCSTATE, "Free");
	MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

}


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->PortRecord = PortEntry;

	TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KAM;

	if (TNC->BusyWait == 0)
		TNC->BusyWait = 10;

	PortEntry->MAXHOSTMODESESSIONS = 11;		// Default


	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;

//	TNC->SuspendPortProc = KAMSuspendPort;
//	TNC->ReleasePortProc = KAMReleasePort;



	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);	
					EncodeAndSend(TNC, TXMsg, datalen);
				}
				else
				{
					datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall);
					EncodeAndSend(TNC, TXMsg, datalen);
						
					if (TNC->OldMode == 0)
					{
						EncodeAndSend(TNC, TXMsg, datalen);
						datalen = sprintf(TXMsg, "C20MYGTCALL %s", TNC->Streams[0].MyCall);
					}
				}

				TNC->InternalCmd = 'M';
		
				TNC->NeedPACTOR = 0;		// Cancel enter Pactor

				SuspendOtherPorts(TNC);

				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);
		
				Rig_Command( (TRANSPORTENTRY *) -1, Msg);

			}
		}
	}
	
	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);
				EncodeAndSend(TNC, TXMsg, datalen);
			}
			else
			{
				datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall);
				EncodeAndSend(TNC, TXMsg, datalen);

				if (TNC->OldMode == 0)
				{
					datalen = sprintf(TXMsg, "C20MYGTCALL %s", TNC->NodeCall);
					EncodeAndSend(TNC, TXMsg, datalen);	
				}
			}

			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);
		
			Rig_Command( (TRANSPORTENTRY *) -1, Status);

			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

					if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200)
						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;
				STREAM->bytesTXed += datalen; 

				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 ",
						TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
					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]);
					if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40]))
					{
						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, "GTOR ", 5) == 0)	// GTOR Connect
				{
					memcpy(STREAM->RemoteCall, &MsgPtr[5], 9);
					STREAM->Connecting = TRUE;

					// If Stream 0, Convert C CALL to PACTOR CALL

					if (Stream == 0)
					{
						datalen = sprintf(TXMsg, "C20GTOR %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
		{
			if (TNC->Streams[0].bytesTXed)
				TNC->Streams[0].BytesAcked += Len - 3;	// We get an ack before the first send

			sprintf(Status, "RX %d TX %d ACKED %d ",
				TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
			SetWindowText(TNC->xIDC_TRAFFIC, Status);

			if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked < 500)
				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
		STREAM->bytesRXed += Len;
		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 ", 
					TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
			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;
		}


		WritetoTrace(TNC, Buffer, Len);


		// 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;

/*
    The response frame from the TNC will be:    
           <FEND>?0MSXY<FEND> - where MSXY are coded as follows:    
           M    One byte indicating the current mode of operation.    
                A=Packet
                B=RTTY
                C=ASCII
                D=AMTOR
                E=FEC
                F=SELFEC
                G=LAMTOR
                H=PACTOR
                I=PTLISTEN
                J=GTOR
                K=NAVTEX
                L=CW
                M=TOR Standby     
				N=GMON
				O=PSK31
           S    One byte indicating a sub-mode of operation. The byte
                contains an ASCII character as follows:    
                0=Standby
                1=Phasing
                2=Connected
                3=Disconnecting
                4=FEC
                5=SELFEC
                6=PTFEC    
           X    One byte (called status byte X). This byte is bit-
                encoded to indicate specific conditions as follows:    
                Bit 0 =    (IDLE) set to 1 when receiving IDLE
                          characters in a frame.
                Bit 1 =   (ERR) set to 1 to indicate the received frame
                          failed CRC check, or was not a valid CS
                          response frame.
                Bit 2 =   (Combined receive) set to 1 to indicate that
                          the data was constructed through the use of
                          error correction (i.e. Golay error correction
                          for G-TOR or Memory ARQ for Pactor).
                Bit 3 =   (RQ) set to 1 to indicate an RQ frame. If you
                          are the ISS, it indicates that the receiving
                          station has asked for a repeat of the last
                          data due to received errors. When you are the
                          IRS, it indicates that the transmitting
                          station has sent the same data that you have
                          already received. This means that the sending
                          station did not properly copy your
                          acknowledgement (CS code).
                Bit 4 =   (Huffman) set to 1 to indicate that this
                          frame contains data which uses Huffman
                          compression.
                Bit 5 =   (ISS) set to 1 to indicate that your station
                          is currently the ISS.
                Bit 6&7 = (Speed) these two bits indicate the current
                          speed of an ARQ link or the PSK31 mode. The
                          coding of the bits is:
                          00 = 100 baud or BPSK31
                          01 = 200 baud or QPSK31
                          10 = 300 baud
           Y    One byte (called status byte Y). This byte is bit-
                encoded to indicate specific conditions as follows:    
                Bit 0 =    reserved (set to 0).
                Bit 1 =   (PTT) PTT is active.
                Bit 2 =   (Changeover) changeover in progress
                Bits 3-7 = reserved (set to 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;
			}

			STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0;
			STREAM->ConnectTime = time(NULL); 

			if (Stream == 0)
			{
				// Stop Scanner

				char Msg[80];
				
				sprintf(Msg, "%d SCANSTOP", TNC->Port);
		
				Rig_Command( (TRANSPORTENTRY *) -1, Msg);

				sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", 
					TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked);
				
				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;

				// Stop other ports in same group

				SuspendOtherPorts(TNC);

				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
					TNC->Streams[0].bytesTXed += CTEXTLEN;
				}
				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)
{
	sprintf(TNC->WEB_TNCSTATE, "Free");
	SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

	ReleaseOtherPorts(TNC);
	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 

*/
