/*
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 SCS TNC in Pactor Mode to BPQ32 switch 
//
//	Uses BPQ EXTERNAL interface


// Dec 29 2009

//	Add Scan Control using %W Hostmode Command
//	Map Rig control port to a Virtual Serial Port.
//	Add Support for packet port(s).

// July 2010

// Support up to 32 BPQ Ports

// Version 1.1.1.14 August 2010 

// Drop RTS as well as DTR on close

// Version 1.2.1.1 August 2010 

// Save Minimized State

// Version 1.2.1.2 August 2010 

// Implement scan bandwidth change

// Version 1.2.1.3 September 2010 

// Don't connect if channel is busy
// Add WL2K reporting
// Add PACKETCHANNELS config command
// And Port Selector (P1 or P2) for Packet Ports

// Version 1.2.1.4 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

// October 2011

// Changes for P4Dragon

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE


#include <stdio.h>
#include <stdlib.h>
#include "time.h"

//#include <process.h>
//#include <time.h>

#define MaxStreams 10			// First is used for Pactor, even though Pactor uses channel 31

#include "cheaders.h"
#include "tncinfo.h"

#include "bpq32.h"

#ifndef WIN32
#ifndef MACBPQ
#ifndef FREEBSD

#include <sys/ioctl.h>
#include <linux/serial.h>
#endif
#endif
#endif

static char ClassName[]="PACTORSTATUS";
static char WindowTitle[] = "SCS Pactor";
static int RigControlRow = 185;


#define NARROWMODE 12		// PI/II
#define WIDEMODE 16			// PIII only

extern UCHAR LogDirectory[];

static RECT Rect;

VOID __cdecl Debugprintf(const char * format, ...);

char NodeCall[11];		// Nodecall, Null Terminated

int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);

VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);

VOID PTCSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC);
VOID PTCReleasePort(struct TNCINFO * TNC);
int	KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
int CheckMode(struct TNCINFO * TNC);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
void SCSTryToSendDATA(struct TNCINFO * TNC, int Stream);
VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction);
int standardParams(struct TNCINFO * TNC, char * buf);
int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length);

#define	FEND	0xC0	// KISS CONTROL CODES 
#define	FESC	0xDB
#define	TFEND	0xDC
#define	TFESC	0xDD

static FILE * LogHandle[32] = {0};

//char * Logs[4] = {"1", "2", "3", "4"};

static char BaseDir[MAX_PATH]="c:\\";

static BOOL WRITELOG = FALSE;

BOOL SCSStopPort(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 SCSStartPort(struct PORTCONTROL * PORT)
{
	// Restart Port - Open Sockets or Serial Port

	struct TNCINFO * TNC = PORT->TNC;

	TNC->ReopenTimer = 999;				// Reopen immediately
		
	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/SCSLog_%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 Flags, char * Msg, int MsgLen)
{
	if (WRITELOG)
	{
		if (LogHandle[Flags])
		{
			fwrite(Msg, 1, MsgLen, LogHandle[Flags]); 
			fwrite("\r\n", 1, 2, LogHandle[Flags]); 
		}
	}
}


static int DontAddPDUPLEX = 0;


static 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];

	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, "PACKETCHANNELS", 14) == 0)	// Packet Channels
			TNC->PacketChannels = atoi(&buf[14]);

		else
		if (_memicmp(buf, "SCANFORROBUSTPACKET", 19) == 0)
		{
			// Spend a percentage of scan time in Robust Packet Mode

			double Robust = atof(&buf[20]);
			#pragma warning(push)
			#pragma warning(disable : 4244)
			TNC->RobustTime = Robust * 10;
			#pragma warning(pop)
		}
		else
		if (_memicmp(buf, "USEAPPLCALLS", 12) == 0 && buf[12] != 'F' && buf[12] != 'f')
			TNC->UseAPPLCalls = TRUE;
		else
		if (_memicmp(buf, "USEAPPLCALLSFORPACTOR", 21) == 0)
			TNC->UseAPPLCallsforPactor = TRUE;
		else
		if (_memicmp(buf, "DRAGON", 6) == 0)
		{
			TNC->Dragon = TRUE;
			if (_memicmp(&buf[7], "SINGLE", 6) == 0)
				TNC->DragonSingle = TRUE;

			if (_memicmp(&buf[7], "KISS", 4) == 0)
				TNC->DragonKISS = TRUE;
		}
		else
		if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0)
			TNC->RobustDefault = TRUE;
		else
		if (_memicmp(buf, "DontAddPDUPLEX", 14) == 0)
			DontAddPDUPLEX = TRUE;
		else
		if (_memicmp(buf, "FORCE ROBUST", 12) == 0)
			TNC->ForceRobust = TNC->RobustDefault = TRUE;
		else
		if (_memicmp(buf, "MAXLEVEL", 8) == 0)		// Maximum Pactor Level to use.
			TNC->MaxLevel = atoi(&buf[8]);
		else
		if (_memicmp(buf, "DATE", 4) == 0)
		{
			char Cmd[32];
			time_t T;
			struct tm * tm;

			T = time(NULL);
			tm = gmtime(&T);	

			sprintf(Cmd,"DATE %02d%02d%02d\r",  tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100);

			strcat (TNC->InitScript, Cmd);
		}
		else if (_memicmp(buf, "TIME", 4) == 0)
		{
			char Cmd[32];
			time_t T;
			struct tm * tm;

			T = time(NULL);
			tm = gmtime(&T);	

			sprintf(Cmd,"TIME %02d%02d%02d\r",  tm->tm_hour, tm->tm_min, tm->tm_sec);

			strcat (TNC->InitScript, Cmd);
		}
		else if (standardParams(TNC, buf) == FALSE)
			strcat (TNC->InitScript, buf);
	}
	
	return (TRUE);
	
}

struct TNCINFO * CreateTTYInfo(int port, int speed);

BOOL CloseConnection(struct TNCINFO * conn);
BOOL WriteCommBlock(struct TNCINFO * TNC);
BOOL DestroyTTYInfo(int port);
void SCSCheckRX(struct TNCINFO * TNC);
VOID SCSPoll(int Port);
VOID CRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len);
unsigned short int compute_crc(unsigned char *buf,int len);
int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len);
VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len);
VOID ProcessTermModeResponse(struct TNCINFO * TNC);
static VOID ExitHost(struct TNCINFO * TNC);
static VOID DoTNCReinit(struct TNCINFO * TNC);
static VOID DoTermModeTimeout(struct TNCINFO * TNC);
static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len);
int Switchmode(struct TNCINFO * TNC, int Mode);
VOID SwitchToPactor(struct TNCINFO * TNC);
VOID SwitchToPacket(struct TNCINFO * TNC);


char status[8][8] = {"ERROR",  "REQUEST", "TRAFFIC", "IDLE", "OVER", "PHASE", "SYNCH", ""};

char ModeText[8][14] = {"STANDBY", "AMTOR-ARQ",  "PACTOR-ARQ", "AMTOR-FEC", "PACTOR-FEC", "RTTY / CW", "LISTEN", "Channel-Busy"};

char PactorLevelText[5][14] = {"Not Connected", "PACTOR-I", "PACTOR-II", "PACTOR-III", "PACTOR-IV"};

char PleveltoMode[5] = {30, 11, 14, 16, 20};	// WL2K Reporting Modes - RP, P1, P2, P3, P4


static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
	int txlen = 0;
	PMSGWITHLEN buffptr;
	struct TNCINFO * TNC = TNCInfo[port];
	size_t Param;
	int Stream = 0;
	struct STREAMINFO * STREAM = &TNC->Streams[0];
	char PLevel;
	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;

#ifndef WIN32
#ifndef MACBPQ
#ifndef FREEBSD

		if (TNC->Dragon)
		{
			struct serial_struct sstruct;

			// Need to set custom baud rate

			if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0)
			{
				Debugprintf("Error: Dragon could not get comm ioctl\n");
			}
			else
			{
				// set custom divisor to get 829440 baud
	
				sstruct.custom_divisor = 29;
				sstruct.flags |= ASYNC_SPD_CUST;

				// set serial_struct
		
				if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0)
					Debugprintf("Error: Dragon could not set custom comm baud divisor\n");
				else
					Debugprintf("Dragon custom baud rate set\n");
			}
		}
#endif
#endif
#endif

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

		SCSCheckRX(TNC);
		SCSPoll(port);

		return 0;

	case 1:				// poll

		// Check session limit timer

		if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting)
		{
			if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime))
			{
				STREAM->CmdSet = STREAM->CmdSave = malloc(100);
				sprintf(STREAM->CmdSet, "D\r");	
				STREAM->Disconnecting = TRUE;
			}
		}

		for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			if (TNC->Streams[Stream].ReportDISC)
			{
				TNC->Streams[Stream].ReportDISC = FALSE;
				buff->PORT = Stream;

				return -1;
			}
		}
	
		if (TNC->EnterExit)
			return 0;						// Switching to Term mode to change bandwidth
		
		for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0)
			{
				int datalen;
			
				buffptr = Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q);
				datalen = (int)buffptr->Len;

				buff->PORT = Stream;						// Compatibility with Kam Driver
				buff->PID = 0xf0;
				memcpy(&buff->L2DATA, &buffptr->Data[0], datalen);		// Data goes to + 7, but we have an extra byte
				datalen += sizeof(void *) + 4;

				PutLengthinBuffer(buff, datalen);
				
				ReleaseBuffer(buffptr);
	
				return (1);
			}
		}
	
		return 0;

	case 2:				// send

		buffptr = GetBuff();

		if (buffptr == 0) return 0;			// No buffers, so ignore

		Stream = buff->PORT;

		if (!TNC->TNCOK)
		{
			// Send Error Response

			buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r");
			C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
			
			return 0;
		}

		txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1);		// 1 as no PID

		if (txlen == 1 && buff->L2DATA[0] == 0)			// Keepalive Packet
		{
			ReleaseBuffer(buffptr);
			return 0;
		}

		buffptr->Len = txlen;
		memcpy(buffptr->Data, buff->L2DATA, txlen);
		
		C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);

		TNC->Streams[Stream].FramesOutstanding++;
		
		// See if possible to send immediately

		SCSTryToSendDATA(TNC, Stream);

		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->Dragon)
			{
				if (STREAM->FramesOutstanding  > 25)
					return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
			}
			else
			{
				if (STREAM->FramesOutstanding  > 10)
					return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);
			}
		}
		else
		{
			if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200)	
				return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15);		}

		return TNC->HostMode << 8 | STREAM->Disconnecting << 15;		// OK, but lock attach if disconnecting


	case 4:				// reinit

		// Ensure in Pactor

		TNC->TXBuffer[2] = 31;
		TNC->TXBuffer[3] = 0x1;
		TNC->TXBuffer[4] = 0x1;
		memcpy(&TNC->TXBuffer[5], "PT", 2);

		CRCStuffAndSend(TNC, TNC->TXBuffer, 7);

		Sleep(25);
		ExitHost(TNC);
		Sleep(50);
		CloseCOMPort(TNC->hDevice);
		TNC->hDevice =(HANDLE)0;
		TNC->ReopenTimer = 250;
		TNC->HostMode = FALSE;

		return (0);

	case 5:				// Close

		// Ensure in Pactor

		TNC->TXBuffer[2] = 31;
		TNC->TXBuffer[3] = 0x1;
		TNC->TXBuffer[4] = 0x1;
		memcpy(&TNC->TXBuffer[5], "PT", 2);

		CRCStuffAndSend(TNC, TNC->TXBuffer, 7);

		Sleep(25);

		ExitHost(TNC);

		Sleep(25);

		CloseCOMPort(TNCInfo[port]->hDevice);
				
		return (0);

	case 6:				// Scan Interface

		Param = (size_t)buff;

		switch (Param)
		{
		case 1:		// Request Permission

			if (TNC->TNCOK)
			{
				// If been in Sync a long time, or if using applcalls and
				// Scan had been locked too long just let it change
				
				if (TNC->UseAPPLCallsforPactor)
				{
					if (TNC->PTCStatus == 6)	// Sync
					{
						int insync = (int)(time(NULL) - TNC->TimeEnteredSYNCMode);
						if (insync > 4)
						{
							Debugprintf("SCS Scan - in SYNC for %d Secs - allow change regardless", insync);
							return 0;
						}
					}
					else if (TNC->TimeScanLocked)
					{
						time_t timeLocked = time(NULL) - TNC->TimeScanLocked;
						if (timeLocked > 4)
						{
							Debugprintf("SCS Scan - Scan Locked for %d Secs - allow change regardless", timeLocked);
							TNC->TimeScanLocked = 0;
							return 0;
						}
					}
				}

				TNC->WantToChangeFreq = TRUE;
				TNC->OKToChangeFreq = FALSE;
				return TRUE;
			}
			return 0;		// Don't lock scan if TNC isn't responding
		

		case 2:		// Check  Permission
			return TNC->OKToChangeFreq;

		case 3:		// Release  Permission
		
			TNC->WantToChangeFreq = FALSE;
			
			if (TNC->DontReleasePermission)			// Disable connects during this interval?
			{
				TNC->DontReleasePermission = FALSE;
				if (TNC->SyncSupported == FALSE)
					TNC->TimeScanLocked = time(NULL) + 100;	// Make sure doesnt time out
				return 0;
			}

			TNC->DontWantToChangeFreq = TRUE;
			return 0;

		default: // Param is Address of a struct ScanEntry

			Scan = (struct ScanEntry *)buff;

			PLevel = Scan->PMaxLevel;

			if (PLevel == 0 && (Scan->HFPacketMode || Scan->RPacketMode))
			{
				// Switch to Packet for this Interval
				
				if (TNC->RIG->RIG_DEBUG)
					Debugprintf("SCS Switching to Packet, %d", TNC->HFPacket);

				if (TNC->HFPacket == FALSE)
					SwitchToPacket(TNC);

				return 0;
			}

			if (PLevel > '0' && PLevel < '5')		// 1 - 4 
			{
				if (TNC->Bandwidth != PLevel  || TNC->MinLevel != (Scan->PMinLevel - '0'))
				{
					TNC->Bandwidth = PLevel;
					TNC->MinLevel = Scan->PMinLevel - '0';
					Switchmode(TNC, PLevel - '0');
				}

				if (TNC->UseAPPLCallsforPactor && Scan->APPLCALL[0])
				{
					// Switch callsign

					STREAM = &TNC->Streams[0];
					STREAM->CmdSet = STREAM->CmdSave = malloc(100);

					strcpy(STREAM->MyCall, Scan->APPLCALL);

					sprintf(STREAM->CmdSet, "I%s\rI\r", STREAM->MyCall);
					if (TNC->RIG->RIG_DEBUG)
						Debugprintf("SCS Pactor APPLCALL Set to  %s", STREAM->MyCall);
				}

				else
				{
					if (TNC->HFPacket)
						SwitchToPactor(TNC);
				}
			}

			if (Scan->RPacketMode)
				if (TNC->RobustTime)
					SwitchToPacket(TNC);			// Always start in packet, switch to pactor after RobustTime ticks
			
			if (PLevel == '0')
				TNC->DontReleasePermission = TRUE;	// Dont allow connects in this interval
			else
				TNC->DontReleasePermission = FALSE;

			return 0;
		}
	}
	return 0;
}

int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len)
{
	struct RIGINFO * RIG = TNC->RIG;

	if (RIG && RIG->WEB_Label)
	{
		Len += sprintf(&Buff[Len], "<table style=\"font-family: monospace; align=center \"><tr>");
		Len += sprintf(&Buff[Len], "<td width=90px>%s</td>", RIG->WEB_Label);
		Len += sprintf(&Buff[Len], "<td width=90px>%s</td>", RIG->WEB_FREQ);
		Len += sprintf(&Buff[Len], "<td width=90px>%s</td>", RIG->WEB_MODE);
		Len += sprintf(&Buff[Len], "<td width=90px>%c</td>", RIG->WEB_SCAN);
		Len += sprintf(&Buff[Len], "<td width=90px>%c</td></tr>", RIG->WEB_PTT);


		if (TNC->TXRIG && TNC->TXRIG != TNC->RIG)
		{
			struct RIGINFO * RIG = TNC->TXRIG;

			Len += sprintf(&Buff[Len], "<td width=90px>%s</td>", RIG->WEB_Label);
			Len += sprintf(&Buff[Len], "<td width=90px>%s</td>", RIG->WEB_FREQ);
			Len += sprintf(&Buff[Len], "<td width=90px>%s</td>", RIG->WEB_MODE);
			Len += sprintf(&Buff[Len], "<td width=90px>%c</td>", RIG->WEB_SCAN);
			Len += sprintf(&Buff[Len], "<td width=90px>%c</td></tr></table>", RIG->WEB_PTT);
		}
		Len += sprintf(&Buff[Len], "</table>");
	}
	return Len;
}

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>SCS Pactor Status</title></head><body><h3>SCS 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>Buffers</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], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_PACTORLEVEL);
	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 * SCSExtInit(EXTPORTDATA *  PortEntry)
{
	char msg[500];
	struct TNCINFO * TNC;
	int port;
	char * ptr;
	int Stream = 0;
	char * TempScript;

	//
	//	Will be called once for each Pactor Port
	//	The COM port number is in IOBASE
	//

	DontAddPDUPLEX = 0;

	sprintf(msg,"SCS Pactor %s", PortEntry->PORTCONTROL.SerialPortName);
	WritetoConsole(msg);

	port=PortEntry->PORTCONTROL.PORTNUMBER;

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

	OpenLogFile(TNC->Port);
	CloseLogFile(TNC->Port);


	if (TNC->BusyHold == 0)
		TNC->BusyHold = 3;

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

	if (TNC->MaxLevel == 0)
		TNC->MaxLevel = 3;

	// Set up DED addresses for streams (first stream (Pactor) = DED 31
	
	TNC->Streams[0].DEDStream = 31;

	for (Stream = 1; Stream <= MaxStreams; Stream++)
	{
		TNC->Streams[Stream].DEDStream = Stream;
	}

	if (TNC->PacketChannels > MaxStreams)
		TNC->PacketChannels = MaxStreams;

	PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1;
	PortEntry->PERMITGATEWAY = TRUE;					// Can change ax.25 call on each stream
	PortEntry->SCANCAPABILITIES = CONLOCK;				// Scan Control 3 stage/conlock 

	if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
		TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;

	if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
		memcpy(TNC->NodeCall, MYNODECALL, 10);
	else
		ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
		
	PortEntry->PORTCONTROL.PROTOCOL = 10;
	PortEntry->PORTCONTROL.PORTQUALITY = 0;

	if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
		PortEntry->PORTCONTROL.PORTPACLEN = 100;

	TNC->SuspendPortProc = PTCSuspendPort;
	TNC->ReleasePortProc = PTCReleasePort;

	PortEntry->PORTCONTROL.PORTSTARTCODE = SCSStartPort;
	PortEntry->PORTCONTROL.PORTSTOPCODE = SCSStopPort;

	PortEntry->PORTCONTROL.UICAPABLE = TRUE;

	ptr=strchr(TNC->NodeCall, ' ');
	if (ptr) *(ptr) = 0;					// Null Terminate

	// get NODECALL for RP tests

	memcpy(NodeCall, MYNODECALL, 10);
		
	ptr=strchr(NodeCall, ' ');
	if (ptr) *(ptr) = 0;					// Null Terminate


	// Set TONES to 4

	TempScript = malloc(1000);

	strcpy(TempScript, "QUIT\r");				// In case in pac: mode
	strcat(TempScript, "TONES 4\r");			// Tones may be changed but I want this as standard
	strcat(TempScript, "MAXERR 30\r");			// Max retries 
	strcat(TempScript, "MODE 0\r");				// ASCII mode, no PTC II compression (Forwarding will use FBB Compression)
	strcat(TempScript, "MAXSUM 20\r");			// Max count for memory ARQ
	strcat(TempScript, "CWID 0 2\r");			// CW ID disabled
	strcat(TempScript, "PTCC 0\r");				// Dragon out of PTC Compatibility Mode
	strcat(TempScript, "VER\r");				// Try to determine Controller Type

	sprintf(msg, "MYLEVEL %d\r", TNC->MaxLevel);
	strcat(TempScript, msg);					// Default Level to MAXLEVEL

	strcat(TempScript, TNC->InitScript);

	free(TNC->InitScript);
	TNC->InitScript = TempScript;

	// Others go on end so they can't be overriden

	strcat(TNC->InitScript, "ADDLF 0\r");      //      Auto Line Feed disabled
	strcat(TNC->InitScript, "ARX 0\r");      //        Amtor Phasing disabled
	strcat(TNC->InitScript, "BELL 0\r");      //       Disable Bell
	strcat(TNC->InitScript, "BC 0\r");      //         FEC reception is disabled
	strcat(TNC->InitScript, "BKCHR 2\r");      //      Breakin Char = 2
	strcat(TNC->InitScript, "CHOBELL 0\r");      //    Changeover Bell off
	strcat(TNC->InitScript, "CMSG 0\r");      //       Connect Message Off
	strcat(TNC->InitScript, "LFIGNORE 0\r");      //   No insertion of Line feed
	strcat(TNC->InitScript, "LISTEN 0\r");      //     Pactor Listen disabled
	strcat(TNC->InitScript, "MAIL 0\r");      //       Disable internal mailbox reporting
	strcat(TNC->InitScript, "REMOTE 0\r");      //     Disable remote control
	strcat(TNC->InitScript, "PAC CBELL 0\r");      //  
	strcat(TNC->InitScript, "PAC CMSG 0\r");      //  
	strcat(TNC->InitScript, "PAC PRBOX 0\r");      //  	Turn off Packet Radio Mailbox
	
	//  Automatic Status must be enabled for BPQ32
	//  Pactor must use Host Mode Chanel 31
	//  PDuplex must be set. The Node code relies on automatic IRS/ISS changeover
	//	5 second duplex timer

	strcat(TNC->InitScript, "STATUS 2\rPTCHN 31\rPDTIMER 5\r");

	if (DontAddPDUPLEX == 0)
		strcat(TNC->InitScript, "PDUPLEX 1\r");

	sprintf(msg, "MYCALL %s\rPAC MYCALL %s\r", TNC->NodeCall, TNC->NodeCall);
	strcat(TNC->InitScript, msg);

	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->WEB_PACTORLEVEL = 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", "Buffers", 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->xIDC_PACTORLEVEL = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE,10,160,430,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;

	sprintf(TNC->WEB_BUFFERS, "%05d Queued %05d", TNC->Buffers, TNC->Streams[0].FramesOutstanding);
	SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS);


	MoveWindows(TNC);
#endif
	OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE);

#ifndef WIN32
#ifndef MACBPQ
#ifndef FREEBSD

	if (TNC->Dragon)
	{
		struct serial_struct sstruct;

		// Need to set custom baud rate

		if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0)
		{
			printf("Error: Dragon could not get comm ioctl\n");
		}
		else
		{
			// set custom divisor to get 829440 baud
	
			sstruct.custom_divisor = 29;
			sstruct.flags |= ASYNC_SPD_CUST;

			// set serial_struct
		
			if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0)
				Debugprintf("Error: Dragon could not set custom comm baud divisor\n");
			else
				Debugprintf("Dragon custom baud rate set\n");
		}
	}
#endif
#endif
#endif
	if (TNC->RobustDefault)
		SwitchToPacket(TNC);

	WritetoConsole("\n");

	return ExtProc;
}

void SCSCheckRX(struct TNCINFO * TNC)
{
	int Length, Len;
	unsigned short crc;
	char UnstuffBuffer[500];

	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;

	Length = TNC->RXLen;

	// DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame

	// Fortunately this is a polled protocol, so we only get one frame at a time

	// If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end

	// If first char is 170, we could check rhe length field, but that could be corrupt, as
	// we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is
	// complete. If CRC is duff, we will eventually time out and get a retry. The retry code
	// can clear the RC buffer

	if (TNC->UsingTermMode)
	{
		// Send response to Host if connected

		PMSGWITHLEN buffptr;
		int Len = TNC->RXLen;
		int Posn = 0;

		// First message is probably ACK of JHO4T -  AA AA 1F 00 1E 19

		if (TNC->RXBuffer[0] == 0xaa && Len > 6)
		{
			memmove(TNC->RXBuffer, &TNC->RXBuffer[6], Len - 6);
			Len -= 6;
		}

		// TNC seems to send 1e f7 or 1e 87 regularly

		while (TNC->RXBuffer[0] == 0x1e && Len > 1)
		{
			memmove(TNC->RXBuffer, &TNC->RXBuffer[2], Len - 2);
			Len -= 2;
		}

		if (Len == 0)
		{
			TNC->RXLen = 0;		// Ready for next frame
			return;
		}

		while (Len > 250)
		{
			buffptr = GetBuff();
			buffptr->Len = 250;
			memcpy(buffptr->Data, &TNC->RXBuffer[Posn], 250);
			C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
			Len -= 250;
			Posn += 250;
		}

		buffptr = GetBuff();
		buffptr->Len = Len;
		memcpy(buffptr->Data, &TNC->RXBuffer[Posn], Len);
		C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);

		TNC->RXLen = 0;		// Ready for next frame
		return;
	}

			
	if (TNC->RXBuffer[0] != 170)
	{
		// 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->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':')

		if (strlen(TNC->RXBuffer) < TNC->RXLen)
			TNC->RXLen = 0;

		if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0))

			return;				// Wait for rest of frame

		// Complete Char Mode Frame

		OpenLogFile(TNC->Port);
		WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer));
		CloseLogFile(TNC->Port);

		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 (Length < 6)				// Minimum Frame Sise
		return;

	if (TNC->RXBuffer[2] == 170)
	{
		// Retransmit Request
	
		TNC->RXLen = 0;
		return;				// Ignore for now
	}

	// Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice


	Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2);

	if (Length == -1)
	{
		// Unstuff returned an errors (170 not followed by 0)

		TNC->RXLen = 0;
		return;				// Ignore for now
	}
	crc = compute_crc(&UnstuffBuffer[2], Length);

	if (crc == 0xf0b8)		// Good CRC
	{
		TNC->RXLen = 0;		// Ready for next frame
		ProcessDEDFrame(TNC, UnstuffBuffer, Length);

		// If there are more channels to poll (more than 1 entry in general poll response,
		// and link is not active, poll the next one

		if (TNC->Timeout == 0)
		{
			UCHAR * Poll = TNC->TXBuffer;

			if (TNC->NexttoPoll[0])
			{
				UCHAR Chan = TNC->NexttoPoll[0] - 1;

				memmove(&TNC->NexttoPoll[0], &TNC->NexttoPoll[1], 19);

				Poll[2] = Chan;			// Channel
				Poll[3] = 0x1;			// Command

				if (Chan == 254)		// Status - Send Extended Status (G3)
				{
					Poll[4] = 1;			// Len-1
					Poll[5] = 'G';			// Extended Status Poll
					Poll[6] = '3';
				}
				else
				{
					Poll[4] = 0;			// Len-1
					Poll[5] = 'G';			// Poll
				}

				CRCStuffAndSend(TNC, Poll, Poll[4] + 6);
				TNC->InternalCmd = FALSE;

				return;
			}
			else
			{
				// if last message wasn't a general poll, send one now

				if (TNC->PollSent)
					return;

				TNC->PollSent = TRUE;

				// Use General Poll (255)

				Poll[2] = 255 ;			// Channel
				Poll[3] = 0x1;			// Command

				Poll[4] = 0;			// Len-1
				Poll[5] = 'G';			// Poll

				CRCStuffAndSend(TNC, Poll, 6);
				TNC->InternalCmd = FALSE;
			}
		}
		return;
	}

	// Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link.

	return;
}

BOOL WriteCommBlock(struct TNCINFO * TNC)
{
	BOOL ret = WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen);

	TNC->Timeout = 20;				// 2 secs
	return ret;
}

VOID SCSPoll(int Port)
{
	struct TNCINFO * TNC = TNCInfo[Port];
	UCHAR * Poll = TNC->TXBuffer;
	char Status[80];
	int Stream = 0;
	int nn;
	struct STREAMINFO * STREAM;

	if (TNC->UsingTermMode)
	{
		if (TNC->Streams[Stream].BPQtoPACTOR_Q)
		{
			PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q);

			// See if enter host mode command

			if (_memicmp(buffptr->Data, "ENTERHOST\r", buffptr->Len) == 0)
			{
				TNC->UsingTermMode = FALSE;	
				
				memcpy(Poll, "JHOST4\r", 7);
				TNC->TXLen = 7;
				WriteCommBlock(TNC);

				// No response expected

				Sleep(10);

				Poll[2] = 255;			// Channel
				TNC->Toggle = 0;
				Poll[3] = 0x41;
				Poll[4] = 0;			// Len-1
				Poll[5] = 'G';			// Poll

				CRCStuffAndSend(TNC, Poll, 6);
				TNC->InternalCmd = FALSE;
				TNC->Timeout = 5;		// 1/2 sec - In case missed


			}
			else
			{
				// Send to TNC

				memcpy(&Poll[0], buffptr->Data, buffptr->Len);
				TNC->TXLen = buffptr->Len;;
				WriteCommBlock(TNC);
			}
			ReleaseBuffer(buffptr);
		}
		return;
	}

	if (TNC->MinLevelTimer)
	{
		TNC->MinLevelTimer--;

		if (TNC->MinLevelTimer == 0)
		{
			// Failed to reach min level in 15 secs

			STREAM = &TNC->Streams[0];

			if (STREAM->Connected)
			{
				PMSGWITHLEN buffptr;

				Debugprintf("Required Min Level not reached - disconnecting");

				// Discard Queued Data, Send a Message, then a disconnect

				while (STREAM->BPQtoPACTOR_Q)
					ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q));

				STREAM->NeedDisc = 15;				// 1 secs

				buffptr = GetBuff();
				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len = sprintf(buffptr->Data,
					"This port only allows Pactor Level %d or above - Disconnecting\r\n", TNC->MinLevel);

				C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);
			}
		}
	}

	if (TNC->SwitchToPactor)
	{
		TNC->SwitchToPactor--;

		if (TNC->SwitchToPactor == 0)
			SwitchToPactor(TNC);
	}

	for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0)
		{
			// New Attach

			// If Pactor, stop scanning and take out of listen mode.

			// Set call to connecting user's call

			// If Stream 0 Put in Pactor Mode so Busy Detect will work

			int calllen=0;

			STREAM = &TNC->Streams[Stream];
			Debugprintf("SCS New Attach Stream %d DEDStream %d", Stream, STREAM->DEDStream);

			if (Stream == 0)
				STREAM->DEDStream = 31;	// Pactor

			STREAM->Attached = TRUE;

			calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall);

			STREAM->MyCall[calllen] = 0;

			STREAM->CmdSet = STREAM->CmdSave = malloc(100);

			if (Stream == 0)
			{
				// Release Scan Lock if it is held

				TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit;		// Reset Limit

				if (TNC->DontReleasePermission)
				{
					TNC->DontReleasePermission = FALSE;
					TNC->DontWantToChangeFreq = TRUE;
				}

				sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC");

				Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet);

				SuspendOtherPorts(TNC);			// Prevent connects on other ports in same scan gruop

				sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall);
				SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

				// Stop Scanner

				sprintf(Status, "%d SCANSTOP", TNC->Port);
				TNC->SwitchToPactor = 0;						// Cancel any RP to Pactor switch

				Rig_Command((TRANSPORTENTRY *) -1, Status);
			}
			else
			{
				sprintf(STREAM->CmdSet, "I%s\r", STREAM->MyCall);
				Debugprintf("SCS Pactor Attach CMDSet = %s", STREAM->CmdSet);
			}
		}
	}

	if (TNC->Timeout)
	{
		TNC->Timeout--;

		if (TNC->Timeout)			// Still waiting
			return;

		TNC->Retries--;

		if(TNC->Retries)
		{
			WriteCommBlock(TNC);	// Retransmit Block
			return;
		}

		// Retried out.

		if (TNC->HostMode == 0)
		{
			DoTermModeTimeout(TNC);
			return;
		}

		// Retried out in host mode - Clear any connection and reinit the TNC

		Debugprintf("PACTOR - Link to TNC Lost");
		TNC->TNCOK = FALSE;

		sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName);
		SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

		// Clear anything from UI_Q

		while (TNC->PortRecord->UI_Q)
		{
			UINT * buffptr = Q_REM(&TNC->PortRecord->UI_Q);
			ReleaseBuffer(buffptr);
		}


		TNC->HostMode = 0;
		TNC->ReinitState = 0;

		for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			if (TNC->PortRecord->ATTACHEDSESSIONS[Stream])		// Connected
			{
				TNC->Streams[Stream].Connected = FALSE;		// Back to Command Mode
				TNC->Streams[Stream].ReportDISC = TRUE;		// Tell Node
			}
		}
	}

	// We delay clearing busy for BusyHold secs

	if (TNC->Busy)
		if (TNC->Mode != 7)
			TNC->Busy--;

	if (TNC->BusyDelay)		// Waiting to send connect
	{
		// Still Busy?

		if (InterlockedCheckBusy(TNC) == 0)
		{
			// No, so send

			TNC->Streams[0].CmdSet = TNC->ConnectCmd;
			TNC->Streams[0].Connecting = TRUE;
			TNC->Streams[0].ConnectTime = time(NULL);

			sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall);
			SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

			Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[0].CmdSet);

			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 = 39;
				memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39);

				C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);

				free(TNC->ConnectCmd);

			}
		}
	}

	for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		STREAM = &TNC->Streams[Stream];

		if (STREAM->Attached)
			CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);

		if (STREAM->NeedDisc)
		{
			STREAM->NeedDisc--;

			if (STREAM->NeedDisc == 0)
				STREAM->ReportDISC = TRUE;

		}

		if (TNC->Timeout)
			return;				// We've sent something
	}

	// if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence

	if (!TNC->HostMode)
	{
		DoTNCReinit(TNC);
		return;
	}

	TNC->PollSent = FALSE;

	//If sending internal command list, send next element

	for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		if (TNC->Streams[Stream].CmdSet)
		{
			unsigned char * start, * end;
			int len;

			start = TNC->Streams[Stream].CmdSet;

			if (*(start) == 250)			// 2nd part of long KISS packet
			{
				len = start[1];

				Poll[2] = 250;				// KISS Channel
				Poll[3] = 0;				// Data
				Poll[4] = len - 1;
				memcpy(&Poll[5], &start[2], len);

				CRCStuffAndSend(TNC, Poll, len + 5);

				free(TNC->Streams[Stream].CmdSave);
				TNC->Streams[Stream].CmdSet = NULL;
				return;
			}

			if (*(start) == 0)			// End of Script
			{
				free(TNC->Streams[Stream].CmdSave);
				TNC->Streams[Stream].CmdSet = NULL;
			}
			else
			{
				end = strchr(start, 13);
				len = (int)(++end - start - 1);	// exclude cr
				TNC->Streams[Stream].CmdSet = end;

				if (*(start) == 1)
				{
					// This is UI data, not a command. Send it to channel 0

					len --;

					Poll[2] = 0;				// UI Channel
					Poll[3] = 0;				// Data
					Poll[4] = len - 1;
					memcpy(&Poll[5], &start[1], len);

					CRCStuffAndSend(TNC, Poll, len + 5);

					return;
				}

				if (*(start) == 2)
				{
					// This is a UI command Send it to channel 0

					len--;

					Poll[2] = 0;				// UI Channel
					Poll[3] = 1;				// Command
					Poll[4] = len - 1;
					memcpy(&Poll[5], &start[1], len);

					CRCStuffAndSend(TNC, Poll, len + 5);

					return;
				}

				Poll[2] = TNC->Streams[Stream].DEDStream;		// Channel
				Poll[3] = 1;			// Command
				Poll[4] = len - 1;
				memcpy(&Poll[5], start, len);


				OpenLogFile(TNC->Port);
				WriteLogLine(TNC->Port, &Poll[5], len);
				CloseLogFile(TNC->Port);

				CRCStuffAndSend(TNC, Poll, len + 5);

				return;
			}
		}
	}
	// if Freq Change needed, check if ok to do it.

	if (TNC->TNCOK)
	{
		if (TNC->WantToChangeFreq)
		{
			Poll[2] = 31;			// Command
			Poll[3] = 1;			// Command
			Poll[4] = 2;			// Len -1
			Poll[5] = '%';
			Poll[6] = 'W';
			Poll[7] = '0';

			CRCStuffAndSend(TNC, Poll, 8);

			TNC->InternalCmd = TRUE;
			TNC->WantToChangeFreq = FALSE;

			if (TNC->RIG->RIG_DEBUG)
				Debugprintf("Scan Debug SCS Pactor Requesting permission from TNC");

			return;
		}

		if (TNC->DontWantToChangeFreq)
		{
			Poll[2] = 31;			// Command
			Poll[3] = 1;			// Command
			Poll[4] = 2;			// Len -1
			Poll[5] = '%';
			Poll[6] = 'W';
			Poll[7] = '1';

			CRCStuffAndSend(TNC, Poll, 8);

			TNC->InternalCmd = TRUE;
			TNC->DontWantToChangeFreq = FALSE;
			TNC->OKToChangeFreq = FALSE;

			if (TNC->RIG->RIG_DEBUG)
				Debugprintf("Scan Debug SCS Pactor Release Scan Lock sent to TNC");

			return;
		}
	}

	// Send Radio Command if avail

	if (TNC->TNCOK && TNC->BPQtoRadio_Q)
	{
		int datalen;
		PMSGWITHLEN buffptr;

		buffptr = Q_REM(&TNC->BPQtoRadio_Q);

		datalen = (int)buffptr->Len;

		Poll[2] = 253;		// Radio Channel
		Poll[3] = 0;		// Data?
		Poll[4] = datalen - 1;

		memcpy(&Poll[5], buffptr->Data, datalen);

		ReleaseBuffer(buffptr);

		CRCStuffAndSend(TNC, Poll, datalen + 5);

		if (TNC->RIG->RIG_DEBUG)
		{
			Debugprintf("SCS Rig Command Queued, Len = %d", datalen );
			Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
				Poll[5], Poll[6], Poll[7], Poll[8], Poll[9], Poll[10], Poll[11], Poll[12],
				Poll[13], Poll[14], Poll[15], Poll[16], Poll[17], Poll[18], Poll[19], Poll[20]);
		}

		//		Debugprintf("SCS Sending Rig Command");

		return;
	}

	if (TNC->TNCOK && TNC->PortRecord->UI_Q)
	{
		int datalen;
		char * Buffer;
		char ICall[16];
		char CCMD[80] = "C";
		char Call[12] = "           ";	
		struct _MESSAGE * buffptr;

		buffptr = Q_REM(&TNC->PortRecord->UI_Q);

		datalen = buffptr->LENGTH - MSGHDDRLEN;
		Buffer = &buffptr->DEST[0];		// Raw Frame

		Buffer[datalen] = 0;

		// If a Dragon with KISS over Hostmade we can just send it

		if (TNC->DragonKISS)
		{
			int EncLen;

			Poll[2] = 250;				// KISS Channel
			Poll[3] = 0;				// CMD
			Poll[4] = datalen + 2;		// 3 extrac chars, but need Len - 1

			Buffer--;
			*(Buffer) = 0;			// KISS Control on front
			EncLen = KissEncode(Buffer, &Poll[5], datalen + 1);

			// We can only send 256 bytes in HostMode, so if longer will
			// have to fragemt

			if (EncLen > 256)
			{
				//We have to wait for response before sending rest, so use CmdSet

				// We need to save the extra first, as CRC will overwrite the first two bytes

				UCHAR * ptr = TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400);

				(*ptr++) = 250;			// KISS Channel
				(*ptr++) = EncLen - 256;
				memcpy(ptr, &Poll[5 + 256], EncLen - 256);

				// Send first 256

				Poll[4] = 255;			//need Len - 1
				CRCStuffAndSend(TNC, Poll, 261);	
			}
			else
			{
				Poll[4] = EncLen - 1;	//need Len - 1
				CRCStuffAndSend(TNC, Poll, EncLen + 5);	
			}

			ReleaseBuffer((UINT *)buffptr);
			return;
		}

		// Not dragon KISS

		// Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address
		// before sending the beacon

		// We also need to set Chan 0 Mycall so digi'ing can work, and put
		// it back after so incoming calls will work

		// But we cant set digipeated bit in call, so if we find one, skip message

		// This doesn't seem to work


		ConvFromAX25(Buffer + 7, ICall);		// Origin
		strlop(ICall, ' ');

		ConvFromAX25(Buffer, &Call[1]);			// Dest
		strlop(&Call[1], ' ');
		strcat(CCMD, Call);
		Buffer += 14;							// Skip Origin
		datalen -= 7;

		while ((Buffer[-1] & 1) == 0)
		{
			if (Buffer[6] & 0x80)				// Digied bit set?
			{
				ReleaseBuffer((UINT *)buffptr);
				return;
			}

			ConvFromAX25(Buffer, &Call[1]);
			strlop(&Call[1], ' ');
			strcat(CCMD, Call);
			Buffer += 7;	// End of addr
			datalen -= 7;
		}

		if (Buffer[0] == 3)				// UI
		{
			Buffer += 2;
			datalen -= 2;

			Poll[2] = 0;				// UI Channel
			Poll[3] = 1;				// CMD
			Poll[4] = (int)strlen(CCMD) - 1;
			strcpy(&Poll[5], CCMD);
			CRCStuffAndSend(TNC, Poll, Poll[4] + 6);	// Set Dest and Path

			TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400);
			sprintf(TNC->Streams[0].CmdSet, "%cI%s\r%c%s\r%cI%s\r",
				2, ICall,			// Flag as Chan 0 Command
				1, Buffer,			// Flag CmdSet as Data
				2, TNC->NodeCall);	// Flag as Chan 0 Command
		}

		ReleaseBuffer((UINT *)buffptr);
		return;
	}


	// Check status Periodically

	if (TNC->TNCOK)
	{
		if (TNC->IntCmdDelay == 6)
		{
			Poll[2] = 254;			// Channel
			Poll[3] = 0x1;			// Command
			Poll[4] = 1;			// Len-1
			Poll[5] = 'G';			// Extended Status Poll
			Poll[6] = '3';

			CRCStuffAndSend(TNC, Poll, 7);

			TNC->InternalCmd = TRUE;
			TNC->IntCmdDelay--;

			return;
		}

		if (TNC->IntCmdDelay == 4)
		{
			Poll[2] = 31;			 // Channel
			Poll[3] = 0x1;			// Command
			Poll[4] = 1;			// Len-1
			Poll[5] = '%';			// Bytes acked Status
			Poll[6] = 'T';

			CRCStuffAndSend(TNC, Poll, 7);

			TNC->InternalCmd = TRUE;
			TNC->IntCmdDelay--;

			return;
		}

		if (TNC->IntCmdDelay <=0)
		{
			Poll[2] = 31;			// Channel
			Poll[3] = 0x1;			// Command
			Poll[4] = 1;			// Len-1
			Poll[5] = '@';			// Buffer Status
			Poll[6] = 'B';

			CRCStuffAndSend(TNC, Poll, 7);

			TNC->InternalCmd = TRUE;
			TNC->IntCmdDelay = 20;	// Every 2 secs

			return;
		}
		else
			TNC->IntCmdDelay--;
	}

	// If busy, send status poll, send Data if avail

	// We need to start where we last left off, or a busy stream will lock out the others

	for (nn = 0; nn <= MaxStreams; nn++)
	{
		Stream = TNC->LastStream++;

		if (TNC->LastStream > MaxStreams)
			TNC->LastStream = 0;

		if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q)
		{
			int datalen;
			PMSGWITHLEN buffptr;
			char * Buffer;

			// Dont send to Pactor if waiting for Min Level to be reached

			if (TNC->MinLevelTimer && Stream == 0)
				continue;

			buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q);

			datalen = (int)buffptr->Len;
			Buffer = buffptr->Data;	// Data portion of frame

			Poll[2] = TNC->Streams[Stream].DEDStream;		// Channel

			if (TNC->Streams[Stream].Connected)
			{
				if (TNC->SwallowSignon && Stream == 0)
				{
					TNC->SwallowSignon = FALSE;	
					if (strstr(Buffer, "Connected"))	// Discard *** connected
					{
						ReleaseBuffer(buffptr);
						return;
					}
				}

				Poll[3] = 0;			// Data?
				TNC->Streams[Stream].bytesTXed += datalen;

				Poll[4] = datalen - 1;
				memcpy(&Poll[5], Buffer, datalen);

				WritetoTrace(TNC, Buffer, datalen);

				ReleaseBuffer(buffptr);
				OpenLogFile(TNC->Port);
				WriteLogLine(TNC->Port, &Poll[5], datalen);
				CloseLogFile(TNC->Port);

				CRCStuffAndSend(TNC, Poll, datalen + 5);

				TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected;

				if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0)
					TidyClose(TNC, 0);

				return;
			}

			// Command. Do some sanity checking and look for things to process locally

			Poll[3] = 1;			// Command
			datalen--;				// Exclude CR
			Buffer[datalen] = 0;	// Null Terminate
			_strupr(Buffer);

			if (_memicmp(Buffer, "DD", 2) == 0)
			{
				// Send DD (Dirty Disconnect)

				// Uses "Hidden" feature where you can send any normal mode command
				// in host mode by preceeding with a #

				Poll[2] = 31;
				Poll[3] = 0x1;
				Poll[4] = 2;
				sprintf(&Poll[5], "#DD\r");
				CRCStuffAndSend(TNC, Poll, 8);

				// It looks like there isn't a response

				TNC->Timeout = 0;	

				OpenLogFile(TNC->Port);
				WriteLogLine(TNC->Port, &Poll[5], 4);
				CloseLogFile(TNC->Port);

				ReleaseBuffer(buffptr);
				return;
			}

			if (_memicmp(Buffer, "D", 1) == 0)
			{
				TNC->Streams[Stream].ReportDISC = TRUE;		// Tell Node
				ReleaseBuffer(buffptr);
				return;
			}

			if (memcmp(Buffer, "RADIO ", 6) == 0)
			{
				sprintf(&Buffer[40], "%d %s", TNC->Port, &Buffer[6]);

				if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &Buffer[40]))
				{
					ReleaseBuffer(buffptr);
				}
				else
				{
					buffptr->Len = sprintf(buffptr->Data, "%s", &Buffer[40]);
					C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
				}
				return;
			}

			if (_memicmp(Buffer, "SessionTimeLimit", 16) == 0)
			{
				if (Buffer[16] != 13)
				{					
					PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

					TNC->SessionTimeLimit = atoi(&Buffer[16]) * 60;

					if (buffptr)
					{
						buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SCS} OK\r");
						C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
					}
					return;
				}
			}

			if (memcmp(Buffer, "MYLEVEL ", 8) == 0)
			{
				Switchmode(TNC, Buffer[8] - '0');
				TNC->Bandwidth = Buffer[8]; // so scanner knows where we are

				buffptr->Len = sprintf(buffptr->Data, "Ok\r");		
				C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);

				return;
			}

			if (memcmp(Buffer, "CHECKLEVEL", 10) == 0)
			{
				CheckMode(TNC);

				buffptr->Len = sprintf(buffptr->Data, "%s\r", &TNC->RXBuffer[2]);		
				C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);

				return;
			}

			if (_memicmp(Buffer, "OVERRIDEBUSY", 12) == 0)
			{
				TNC->OverrideBusy = TRUE;

				buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r");
				C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);

				return;
			}

			if ((Stream == 0) && memcmp(Buffer, "RPACKET", 7) == 0)
			{
				TNC->HFPacket = TRUE;
				buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r");
				C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
				return;
			}

			if ((Stream == 0) && memcmp(Buffer, "PACTOR", 6) == 0)
			{
				TNC->HFPacket = FALSE;
				buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r");
				C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
				return;
			}


			if ((Stream == 0) && memcmp(Buffer, "EXITHOST", 8) == 0)
			{
				UCHAR * Poll = TNC->TXBuffer;

				TNC->UsingTermMode = 1;
				
				ExitHost(TNC);

				// Send CR to get prompt from TNC
				
				Poll[0] = 13;
				TNC->TXLen = 1;
				WriteCommBlock(TNC);
				
				ReleaseBuffer(buffptr);
				return;
			}
			if (Stream == 0 && Buffer[0] == 'C' && datalen > 2)	    // Pactor Connect
				Poll[2] = TNC->Streams[0].DEDStream = 31;			// Pactor Channel

			if (Stream == 0 && Buffer[0] == 'R' && Buffer[1] == 'C')	// Robust Packet Connect
			{
				Poll[2] = TNC->Streams[0].DEDStream = 30;			// Last Packet Channel
				memmove(Buffer, &Buffer[1], datalen--);
			}

			if (Buffer[0] == 'C' && datalen > 2)	// Connect
			{
				if (*(++Buffer) == ' ') Buffer++;		// Space isn't needed

				if ((memcmp(Buffer, "P1 ", 3) == 0) ||(memcmp(Buffer, "P2 ", 3) == 0))
				{
					// Port Selector for Packet Connect convert to 2:CALL

					Buffer[0] = Buffer[1];		
					Buffer[1] = ':';
					memmove(&Buffer[2], &Buffer[3], datalen--);
					//Buffer += 2;
				}

				memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9);

				TNC->Streams[Stream].Connecting = TRUE;

				if (Stream == 0)
				{
					// Send Call, Mode Command followed by connect 

					TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100);

					if (TNC->Dragon)
						sprintf(TNC->Streams[0].CmdSet, "I%s\r%s\r", TNC->Streams[0].MyCall, buffptr->Data);
					else
					{
						if (TNC->Streams[0].DEDStream == 31)
							sprintf(TNC->Streams[0].CmdSet, "I%s\rPT\r%s\r", TNC->Streams[0].MyCall, buffptr->Data);
						else
							sprintf(TNC->Streams[0].CmdSet, "I%s\rPR\r%s\r", TNC->Streams[0].MyCall, buffptr->Data);
					}

					ReleaseBuffer(buffptr);

					// See if Busy

					if (InterlockedCheckBusy(TNC))
					{
						// Channel Busy. Unless override set, wait

						if (TNC->OverrideBusy == 0)
						{
							// Send Mode Command now, save command, and wait up to 10 secs
							// No, leave in Pactor, or Busy Detect won't work. Queue the whole conect sequence

							TNC->ConnectCmd = TNC->Streams[0].CmdSet;
							TNC->Streams[0].CmdSet = NULL;

							sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
							SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

							TNC->BusyDelay = TNC->BusyWait * 10;
							TNC->Streams[Stream].Connecting = FALSE;	// Not connecting Yet

							return;
						}
					}

					TNC->OverrideBusy = FALSE;

					sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[Stream].MyCall, TNC->Streams[Stream].RemoteCall);
					SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

					Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[Stream].CmdSet);

					TNC->Streams[0].InternalCmd = FALSE;
					return;
				}
			}
	

			Poll[4] = datalen - 1;
			memcpy(&Poll[5], buffptr->Data, datalen);

			// if it starts with # the tnc won't respond, so send OK now.

			if (Buffer[0] == '#')
			{
				TNC->HFPacket = TRUE;
				buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r");
				C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
			}
			else
				ReleaseBuffer(buffptr);

			OpenLogFile(TNC->Port);
			WriteLogLine(TNC->Port, &Poll[5], datalen);
			CloseLogFile(TNC->Port);

			CRCStuffAndSend(TNC, Poll, datalen + 5);

			TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected;

			return;
		}

		// if frames outstanding, issue a poll

		if (TNC->Streams[Stream].FramesOutstanding)
		{
			Poll[2] = TNC->Streams[Stream].DEDStream;
			Poll[3] = 0x1;			// Command
			Poll[4] = 0;			// Len-1
			Poll[5] = 'L';			// Status

			CRCStuffAndSend(TNC, Poll, 6);

			TNC->InternalCmd = TRUE;
			TNC->IntCmdDelay--;
			return;
		}

	}

	TNC->PollSent = TRUE;

	// Use General Poll (255)

	Poll[2] = 255 ;			// Channel
	Poll[3] = 0x1;			// Command

	if (TNC->ReinitState == 3)
	{
		TNC->ReinitState = 0;
		Poll[3] = 0x41;
	}

	Poll[4] = 0;			// Len-1
	Poll[5] = 'G';			// Poll

	CRCStuffAndSend(TNC, Poll, 6);
	TNC->InternalCmd = FALSE;

	return;

}

void SCSTryToSendDATA(struct TNCINFO * TNC, int Stream)
{
	// Used after queuing data to see if it can be sent immediately

	struct STREAMINFO * STREAM = &TNC->Streams[Stream];
	int datalen;
	PMSGWITHLEN buffptr;
	char * Buffer;
	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->TNCOK == 0 || STREAM->BPQtoPACTOR_Q == 0 || STREAM->Connected == 0)
		return;


	// Dont send to Pactor if waiting for Min Level to be reached

	if (TNC->MinLevelTimer && Stream == 0)
		return;;

	Sleep(10);				// Give TNC time to respond
	
	SCSCheckRX(TNC);		// See if anything received

	if (TNC->Timeout)
		return;				// Link busy
			
	buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);

	datalen = (int)buffptr->Len;
	Buffer = buffptr->Data;	// Data portion of frame

	Poll[2] = STREAM->DEDStream;		// Channel

	if (TNC->SwallowSignon && Stream == 0)
	{
		TNC->SwallowSignon = FALSE;	
	
		if (strstr(Buffer, "Connected"))	// Discard *** connected
		{
			ReleaseBuffer(buffptr);
			return;
		}
	}

	Poll[3] = 0;			// Data
	STREAM->bytesTXed += datalen;

	Poll[4] = datalen - 1;
	memcpy(&Poll[5], Buffer, datalen);
				
	WritetoTrace(TNC, Buffer, datalen);
	
	ReleaseBuffer(buffptr);
	OpenLogFile(TNC->Port);
	
	WriteLogLine(TNC->Port, &Poll[5], datalen);
	CloseLogFile(TNC->Port);
		
	CRCStuffAndSend(TNC, Poll, datalen + 5);

	if (STREAM->Disconnecting && STREAM->BPQtoPACTOR_Q == 0)
		TidyClose(TNC, Stream);

	return;
}
			

VOID DoTNCReinit(struct TNCINFO * TNC)
{
	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->ReinitState == 0)
	{
		// Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode
	
		TNC->TNCOK = FALSE;
		sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName);
		SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

		Poll[0] = 13;
		Poll[1] = 0x1B;
		TNC->TXLen = 2;

		if (WriteCommBlock(TNC) == FALSE)
		{
			CloseCOMPort(TNC->hDevice);
			OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE);
		}

		TNC->Retries = 1;
	}

	if (TNC->ReinitState == 1)		// Forcing back to Term
		TNC->ReinitState = 0;

	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

			Debugprintf("DOTNCReinit Complete - Entering Hostmode");

			TNC->TXBuffer[2] = 0;
			TNC->Toggle = 0;

			memcpy(Poll, "JHOST4\r", 7);

			TNC->TXLen = 7;
			WriteCommBlock(TNC);

			// Timeout will enter host mode

			TNC->Timeout = 1;
			TNC->Retries = 1;
			TNC->Toggle = 0;
			TNC->ReinitState = 3;	// Set toggle force bit
			TNC->OKToChangeFreq = 1;	// In case failed whilst waiting for permission

			return;
		}
		
		end = strchr(start, 13);
		len = (int)(++end - start);
		TNC->InitPtr = end;
		memcpy(Poll, start, len);

		OpenLogFile(TNC->Port);
		WriteLogLine(TNC->Port, Poll, len);
		CloseLogFile(TNC->Port);

		TNC->TXLen = len;
		WriteCommBlock(TNC);

		TNC->Retries = 2;
	}
}

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;
		ExitHost(TNC);
		TNC->Retries = 1;

		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;
		TNC->IntCmdDelay = 10;

		return;
	}
}

BOOL CheckRXText(struct TNCINFO * TNC)
{
	int Length;

	// only try to read number of bytes in queue 

	if (TNC->RXLen == 500)
		TNC->RXLen = 0;

	Length = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen);

	if (Length == 0)
		return FALSE;					// Nothing doing
	
	TNC->RXLen += Length;

	Length = TNC->RXLen;

	TNC->RXBuffer[TNC->RXLen] = 0;

	if (strlen(TNC->RXBuffer) < TNC->RXLen)
		TNC->RXLen = 0;

	if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0))
		return 0;				// Wait for rest of frame

	// Complete Char Mode Frame

	OpenLogFile(TNC->Port);
	WriteLogLine(TNC->Port, TNC->RXBuffer, TNC->RXLen);
	CloseLogFile(TNC->Port);

	TNC->RXBuffer[TNC->RXLen] = 0;
	
	if (TNC->RIG->RIG_DEBUG)
		Debugprintf(TNC->RXBuffer);

	TNC->RXLen = 0;		// Ready for next frame

	return TRUE;				
}

BOOL CheckRXHost(struct TNCINFO * TNC, char * UnstuffBuffer)
{
	int Length;
	unsigned short crc;

	// only try to read number of bytes in queue 

	if (TNC->RXLen == 500)
		TNC->RXLen = 0;

	Length = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen);

	if (Length == 0)
		return FALSE;					// Nothing doing
	
	TNC->RXLen += Length;

	Length = TNC->RXLen;

	if (Length < 6)				// Minimum Frame Sise
		return FALSE;

	if (TNC->RXBuffer[2] == 170)
	{
		// Retransmit Request
	
		TNC->RXLen = 0;
		return FALSE;				// Ignore for now
	}

	// Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice

	Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2);

	if (Length == -1)
	{
		// Unstuff returned an errors (170 not followed by 0)

		TNC->RXLen = 0;
		return FALSE;				// Ignore for now
	}

	crc = compute_crc(&UnstuffBuffer[2], Length);

	if (crc == 0xf0b8)		// Good CRC
	{
		TNC->RXLen = 0;		// Ready for next frame
		return TRUE;
	}

	// Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link.

	return FALSE;
}


//#include "Mmsystem.h"

int Sleeptime = 250;

int CheckMode(struct TNCINFO * TNC)
{
	int n;
	char UnstuffBuffer[500];	

	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->HostMode == 0)
		return 0;							// Don't try if initialising

	TNC->EnterExit = TRUE;

	Poll[2] = 31;
	Poll[3] = 0x41;
	Poll[4] = 0x5;
	memcpy(&Poll[5], "JHOST0", 6);

	CRCStuffAndSend(TNC, Poll, 11);

	n = 0;

	while (CheckRXHost(TNC, UnstuffBuffer) == FALSE)
	{
		Sleep(5);
		n++;

		if (n > 100) break;
	}


	sprintf(Poll, "MYL\r");
	
	OpenLogFile(TNC->Port);
	WriteLogLine(TNC->Port, Poll, 3);
	CloseLogFile(TNC->Port);

	TNC->TXLen = 4;
	WriteCommBlock(TNC);

	n = 0;

	while (CheckRXText(TNC) == FALSE)
	{
		Sleep(5);
		n++;
		if (n > 100) break;
	}

	memcpy(Poll, "JHOST4\r", 7);

	TNC->TXLen = 7;
	WriteCommBlock(TNC);

	// No response expected

	Sleep(10);

	Poll[2] = 255;			// Channel
	TNC->Toggle = 0;
	Poll[3] = 0x41;
	Poll[4] = 0;			// Len-1
	Poll[5] = 'G';			// Poll

	CRCStuffAndSend(TNC, Poll, 6);
	TNC->InternalCmd = FALSE;
	TNC->Timeout = 5;		// 1/2 sec - In case missed

	TNC->EnterExit = FALSE;
	return 0;
}


int Switchmode(struct TNCINFO * TNC, int Mode)
{
	int n;
	char UnstuffBuffer[500];	

	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->HostMode == 0)
		return 0;							// Don't try if initialising

	if (TNC->HFPacket)
	{
		Poll[2] = 31;
		Poll[3] = 0x1;
		Poll[4] = 0x1;
		memcpy(&Poll[5], "PT", 2);
		CRCStuffAndSend(TNC, Poll, 7);
		OpenLogFile(TNC->Port);
		WriteLogLine(TNC->Port, "SwitchModes - Setting Pactor", 28);
		CloseLogFile(TNC->Port);

		TNC->HFPacket = FALSE;
		TNC->Streams[0].DEDStream = 31;		// Pactor Channel

		n = 0;
		while (CheckRXHost(TNC, UnstuffBuffer) == FALSE)
		{
			Sleep(5);
			n++;
			if (n > 100) break;
		}

//		Debugprintf("Set Pactor ACK received in %d mS, Sleeping for %d", 5 * n, Sleeptime);
		Sleep(Sleeptime);
	}

	// Uses "Hidden" feature where you can send any normal mode command
	// in host mode by preceeding with a #

	Poll[2] = 31;
	Poll[3] = 0x1;
	Poll[4] = 5;
	sprintf(&Poll[5], "#MYL %d\r", Mode);
	CRCStuffAndSend(TNC, Poll, 11);
			
	// It looks like there isn't a response

	TNC->Timeout = 0;	

	OpenLogFile(TNC->Port);
	WriteLogLine(TNC->Port, &Poll[5], 6);
	CloseLogFile(TNC->Port);

	return 0;
}

VOID SwitchToPactor(struct TNCINFO * TNC)
{
	if (TNC->ForceRobust)
		return;

	TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100);
	sprintf(TNC->Streams[0].CmdSet, "PT\r");

	TNC->HFPacket = FALSE;
	TNC->Streams[0].DEDStream = 31;		// Pactor Channel

	if (TNC->RIG->RIG_DEBUG)
		Debugprintf("BPQ32 Scan - switch to Pactor");
}

VOID SwitchToPacket(struct TNCINFO * TNC)
{
	TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100);
	sprintf(TNC->Streams[0].CmdSet, "PR\r");

	TNC->HFPacket = TRUE;
	TNC->Streams[0].DEDStream = 30;		// Packet Channel

	TNC->SwitchToPactor = TNC->RobustTime;

	if (TNC->RIG->RIG_DEBUG)
		Debugprintf("BPQ32 Scan - switch to Packet");
}

VOID ExitHost(struct TNCINFO * TNC)
{
	UCHAR * Poll = TNC->TXBuffer;

	// Try to exit Host Mode

	TNC->TXBuffer[2] = 31;
	TNC->TXBuffer[3] = 0x41;
	TNC->TXBuffer[4] = 0x5;
	memcpy(&TNC->TXBuffer[5], "JHOST0", 6);

	CRCStuffAndSend(TNC, Poll, 11);
	return;
}

VOID CRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len)
{
	unsigned short int crc;
	UCHAR StuffedMsg[500];
	int i, j;

    Msg[3] |= TNC->Toggle;
	TNC->Toggle ^= 0x80;

	crc = compute_crc(&Msg[2], Len-2);
	crc ^= 0xffff;

	Msg[Len++] = (crc&0xff);
	Msg[Len++] = (crc>>8);

	for (i = j = 2; i < Len; i++)
	{
		StuffedMsg[j++] = Msg[i];
		if (Msg[i] == 170)
		{
			StuffedMsg[j++] = 0;
		}
	}

	if (j != i)
	{
		Len = j;
		memcpy(Msg, StuffedMsg, j);
	}

	TNC->TXLen = Len;

	Msg[0] = 170;
	Msg[1] = 170;

	WriteCommBlock(TNC);

	TNC->Retries = 5;
}

int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len)
{
	int i, j=0;

	for (i=0; i<len; i++, j++)
	{
		MsgOut[j] = MsgIn[i];
		if (MsgIn[i] == 170)			// Remove next byte
		{
			i++;
			if (MsgIn[i] != 0)
			{
				if (MsgIn[i] == 170)	// 170 170 Probably start of next packet
					return j;

				if (i != len) return -1;
			}
		}
	}

	return j;
}

VOID ProcessTermModeResponse(struct TNCINFO * TNC)
{
	UCHAR * Poll = TNC->TXBuffer;

	if (TNC->ReinitState == 0)
	{
		// Testing if in Term Mode. It is, so can now send Init Commands

		TNC->InitPtr = TNC->InitScript;
		TNC->ReinitState = 2;

		// Send Restart to make sure PTC is in a known state

		strcpy(Poll, "RESTART\r");

		OpenLogFile(TNC->Port);
		WriteLogLine(TNC->Port, Poll, 7);
		CloseLogFile(TNC->Port);

		TNC->TXLen = 8;
		WriteCommBlock(TNC);

		TNC->Timeout = 60;				// 6 secs

		return;
	}
	if (TNC->ReinitState == 2)
	{
		// Sending Init Commands

		if (strstr(TNC->RXBuffer, "SCS P4dragon"))
		{
			TNC->Dragon = TRUE;
			Debugprintf("SCSPactor in P4dragon mode");
		}

		DoTNCReinit(TNC);		// Send Next Command
		return;
	}
}

VOID ProcessIncomingCall(struct TNCINFO * TNC, struct STREAMINFO * STREAM, int Stream)
{
	APPLCALLS * APPL;
	char * ApplPtr = APPLS;
	int App;
	char Appl[10];
	char FreqAppl[10] = "";				// Frequecy-specific application
	char DestCall[10];
	TRANSPORTENTRY * SESS;
	struct WL2KInfo * WL2K = TNC->WL2K;
	UCHAR * ptr;
	UCHAR Buffer[80];	
	PMSGWITHLEN buffptr;
	BOOL PactorCall = FALSE;
	
	char * Call = STREAM->RemoteCall;

	if (Stream > 0 && Stream < 30)
		ProcessIncommingConnectEx(TNC, Call, Stream, FALSE, TRUE);	// No CTEXT
	else
		ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, TRUE);

	SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream];

	if (SESS == NULL)
		return;							// Cant do much without one

	if (Stream > 0 && Stream < 30)
	{
		// Packet Connect. Much safer to process here, even though it means
		// duplicating some code, or the Pactor/RP mode tests get very complicated

		int Len = 0;
		struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL;

		strcpy(DestCall, STREAM->MyCall);
		Debugprintf("PTC Packet Incoming Call - MYCALL = *%s*", DestCall);
					
		for (App = 0; App < 32; App++)
		{
			APPL=&APPLCALLTABLE[App];
			memcpy(Appl, APPL->APPLCALL_TEXT, 10);

			ptr=strchr(Appl, ' ');
								
			if (ptr)
				*ptr = 0;

			if (_stricmp(DestCall, Appl) == 0)
				break;
		}

		if (App < 32)
		{
			char AppName[13];
			
			memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12);
			AppName[12] = 0;

			// Make sure app is available

			Debugprintf("Connect is to APPL %s", AppName);

			if (CheckAppl(TNC, AppName))
			{
				int MsgLen = sprintf(Buffer, "%s\r", AppName);

				buffptr = GetBuff();

				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len = MsgLen;

				memcpy(buffptr->Data, Buffer, MsgLen);

				C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
				TNC->SwallowSignon = TRUE;
			}
			else
			{
				char Msg[] = "Application not available\r\n";
					
				// Send a Message, then a disconenct
					
				buffptr = GetBuff();

				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len = (int)strlen(Msg);
				memcpy(buffptr->Data, Msg, strlen(Msg));
				C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);

				STREAM->NeedDisc = 100;	// 10 secs
			}							
			return;			
		}				
		
		// Not to a known appl - drop through to Node

		if (PORT->CTEXT)
		{
			Len = strlen(PORT->CTEXT);
			ptr = PORT->CTEXT;
		}
		else if (CTEXTLEN)
		{
			Len = CTEXTLEN;
			ptr = CTEXTMSG;
		}
		else
			return;

		while (Len > 0)
		{
			int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN;

			if (sendLen == 0)
				sendLen = 80;

			if (Len < sendLen)
				sendLen = Len;

			buffptr = GetBuff();
			if (buffptr == 0) return;			// No buffers, so ignore

			buffptr->Len = sendLen;
			memcpy(buffptr->Data, ptr, sendLen);
			C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);

			ptr += sendLen;
			Len -= sendLen;
		}
		return;
	}

	//Connect on HF port. May be Pactor or RP on some models
	
	if (STREAM->DEDStream == 31)
		PactorCall = TRUE;

	if (TNC->RIG && TNC->RIG != &TNC->DummyRig)
	{
		sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", STREAM->RemoteCall, TNC->NodeCall, TNC->RIG->Valchar);
		SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500;		// Convert to Centre Freq

		// If Scan Entry has a Appl, save it

		if (PactorCall && 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", STREAM->RemoteCall, TNC->NodeCall);
		if (WL2K)
			SESS->Frequency = WL2K->Freq;
	}

	if (WL2K)
		strcpy(SESS->RMSCall, WL2K->RMSCall);						

	SESS->Mode = PleveltoMode[TNC->Streams[Stream].PTCStatus1];

	SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

	if (PactorCall && TNC->MinLevel > 1)
		TNC->MinLevelTimer = 150;		// Check we have reached right level
					
	// See which application the connect is for
	
	strcpy(DestCall, STREAM->MyCall);

	if (PactorCall)
		Debugprintf("Pactor Incoming Call - MYCALL = *%s*", DestCall);					
	else					
		Debugprintf("HF Packet/RP Incoming Call - MYCALL = *%s*", DestCall);					

	if ((PactorCall && TNC->UseAPPLCallsforPactor) || (PactorCall == 0 && TNC->UseAPPLCalls))
		//   Test for Richard - Should drop through to Node if not to an APPLCALL  
		//&& strcmp(DestCall, TNC->NodeCall) != 0)		// Not Connect to Node Call
	{		
		for (App = 0; App < 32; App++)
		{
			APPL=&APPLCALLTABLE[App];
			memcpy(Appl, APPL->APPLCALL_TEXT, 10);
			ptr=strchr(Appl, ' ');

			if (ptr)
				*ptr = 0;
	
			if (_stricmp(DestCall, Appl) == 0)
				break;
		}

		if (App < 32)
		{
			char AppName[13];

			memcpy(AppName, &ApplPtr[App * sizeof(struct CMDX)], 12);
			AppName[12] = 0;

			// if SendTandRtoRelay set and Appl is RMS change to RELAY

			if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0
				&& (strstr(Call, "-T" ) || strstr(Call, "-R")))
					strcpy(AppName, "RELAY       ");

			// Make sure app is available

			Debugprintf("Connect is to APPL %s", AppName);

			if (CheckAppl(TNC, AppName))
			{
				int MsgLen = sprintf(Buffer, "%s\r", AppName);
				buffptr = GetBuff();

				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len = MsgLen;
				memcpy(buffptr->Data, Buffer, MsgLen);

				C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
				TNC->SwallowSignon = TRUE;
			}
			else
			{
				char Msg[] = "Application not available\r\n";
					
				// Send a Message, then a disconenct
					
				buffptr = GetBuff();
				if (buffptr == 0) return;			// No buffers, so ignore

				buffptr->Len = strlen(Msg);
				memcpy(buffptr->Data, Msg, strlen(Msg));
				C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);

				STREAM->NeedDisc = 100;	// 10 secs
			}
			return;		
		}

		// Not to a known appl - drop through to Node		
	}

	if (!PactorCall && TNC->UseAPPLCalls)
		goto DontUseAPPLCmd;				// Don't use APPL= for Packet Calls

	// if SendTandRtoRelay set and Appl is RMS change to RELAY

	if (TNC->SendTandRtoRelay && strcmp(FreqAppl, "RMS") == 0
		&& (strstr(Call, "-T" ) || strstr(Call, "-R")))
			strcpy(FreqAppl, "RELAY");

	Debugprintf("Pactor Call is %s Freq Specific Appl is %s Freq is %s",
		DestCall, FreqAppl, TNC->RIG->Valchar);
						
	if (FreqAppl[0])			// Frequency specific APPL overrides TNC APPL
	{
		buffptr = GetBuff();
		if (buffptr == 0) return;			// No buffers, so ignore

		Debugprintf("Using Freq Specific Appl %s", FreqAppl);

		buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl);
		C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
		TNC->SwallowSignon = TRUE;
		return;
	}

	// If an autoconnect APPL is defined, send it
					
	if (TNC->ApplCmd)	
	{
		char App[16];

		buffptr = GetBuff();
		if (buffptr == 0) return;			// No buffers, so ignore

		strcpy(App, TNC->ApplCmd);

		Debugprintf("Using Default Appl *%s*, connecting call is %s", App, Call);

		// if SendTandRtoRelay set and Appl is RMS change to RELAY

		if (TNC->SendTandRtoRelay && memcmp(App, "RMS", 3) == 0
			&& (strstr(Call, "-T" ) || strstr(Call, "-R")))
		{
			strcpy(App, "RELAY");
			Debugprintf("Radio Only Call - Connecting to RELAY");
		}

		buffptr->Len = sprintf(buffptr->Data, "%s\r", App);
		C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
		TNC->SwallowSignon = TRUE;
		return;
	}
		
DontUseAPPLCmd:
			
	if (FULL_CTEXT && CTEXTLEN && HFCTEXTLEN == 0)
	{
		int Len = CTEXTLEN, CTPaclen = 100;
		int Next = 0;

		while (Len > CTPaclen)		// CTEXT Paclen
		{
			buffptr = GetBuff();
			if (buffptr == 0) return;			// No buffers, so ignore

			buffptr->Len = CTPaclen;
			memcpy(buffptr->Data, &CTEXTMSG[Next], CTPaclen);
			C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);

			Next += CTPaclen;
			Len -= CTPaclen;
		}

		buffptr = GetBuff();
		if (buffptr == 0) return;			// No buffers, so ignore

		buffptr->Len = Len;
		memcpy(buffptr->Data, &CTEXTMSG[Next], Len);
		C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);
	}
}

VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen)
{
	PMSGWITHLEN buffptr;
	UCHAR * Buffer;				// Data portion of frame
	char Status[80];
	unsigned int Stream = 0, RealStream;

	if (TNC->HostMode == 0)
		return;

	// Any valid frame is an ACK

	TNC->Timeout = 0;

	if (TNC->TNCOK == FALSE)
	{
		// Just come up

		struct RIGPORTINFO * PORT;
		
		TNC->TNCOK = TRUE;
		sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName);
		SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

		// If using an FT847 on PTC Port it needa a "Cat On" Command. Send it here

		if (TNC->RIG->PORT && TNC->RIG->PORT->PTC)
		{
			PORT = TNC->RIG->PORT;

			if (strcmp(PORT->Rigs[0].RigName, "FT847") == 0)
			{
				UCHAR CATON[6] = {0,0,0,0,0};
				SendPTCRadioCommand(PORT->PTC, CATON, 5);
			}
		}
	}

	Stream = RealStream = Msg[2];

	if (Stream > 29)
		Stream = 0;				// 31 = Pactor or 30 = Robust Packet Outgoing

	//	if in Dragon Single Mode (Pactor and Packet on Same Port)
	//	we only use stream 0, so if a packet frame, set DEDStream

	//	Im not convinced this is the bast place to do this, but let's try

	if (TNC->DragonSingle && RealStream && RealStream < 31)	// Not a Pactor or control frame
	{
		//	must be packet

		TNC->Streams[0].DEDStream = RealStream;		// Packet Channel
		Stream = 0;
	}

	if (TNC->TXBuffer[5] == '#')	// Shouldnt happen!
		return;


	//	See if Poll Reply or Data
	
	if (Msg[3] == 0)
	{
		// Success - Nothing Follows

		if (Stream < 32)
			if (TNC->Streams[Stream].CmdSet)
				return;						// Response to Command Set

		if ((TNC->TXBuffer[3] & 1) == 0)	// Data
			return;

		// If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc

		if (TNC->TXBuffer[5] == 'G')	// Poll
			return;

		if (TNC->TXBuffer[5] == 'C')	// Connect - reply we need is async
			return;

		if (TNC->TXBuffer[5] == 'L')	// Shouldnt happen!
			return;

		if (TNC->TXBuffer[5] == '#')	// Shouldnt happen!
			return;

		if (TNC->TXBuffer[5] == '%' && TNC->TXBuffer[6] == 'W')	// Scan Control - Response to W1
			if (TNC->InternalCmd)
				return;					// Just Ignore

		if (TNC->TXBuffer[5] == 'J')	// JHOST
		{
			if (TNC->TXBuffer[10] == '0')	// JHOST0
			{
				TNC->Timeout = 1;			// 
				return;
			}
		}
		
		if (TNC->Streams[Stream].Connected)
			return;

		buffptr = GetBuff();

		if (buffptr == NULL) return;			// No buffers, so ignore

		buffptr->Len = sprintf(buffptr->Data,"Pactor} Ok\r");

		OpenLogFile(TNC->Port);
		WriteLogLine(TNC->Port, buffptr->Data, (int)buffptr->Len);
		CloseLogFile(TNC->Port);

		C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
		return;
	}

	if (Msg[3] > 0 && Msg[3] < 6)
	{
		// Success with message - null terminated

		UCHAR * ptr;
		int len;

		if (Msg[2] == 0xff)			// General Poll Response
		{
			UCHAR * Poll = TNC->TXBuffer;
			UCHAR Chan = Msg[4] - 1;

			if (Chan == 255)			// Nothing doing
				return;	
		
			if (Msg[5] != 0)
			{
				// More than one to poll - save the list of channels to poll

				strcpy(TNC->NexttoPoll, &Msg[5]);
			}

			// Poll the channel that had data

			Poll[2] = Chan;			// Channel
			Poll[3] = 0x1;			// Command

			if (Chan == 254)		// Status - Send Extended Status (G3)
			{
				Poll[4] = 1;			// Len-1
				Poll[5] = 'G';			// Extended Status Poll
				Poll[6] = '3';
			}
			else
			{
				Poll[4] = 0;			// Len-1
				Poll[5] = 'G';			// Poll
			}

			CRCStuffAndSend(TNC, Poll, Poll[4] + 6);
			TNC->InternalCmd = FALSE;

			return;
		}

		Buffer = &Msg[4];
		
		ptr = strchr(Buffer, 0);

		if (ptr == 0)
			return;

		*(ptr++) = 13;
		*(ptr) = 0;

		len = (int)(ptr - Buffer);

		if (len > 256)
			return;

		// See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc

		if (Msg[3] < 3)						// 1 or 2 - Success or Fail
		{
			char LastCmd = TNC->TXBuffer[5];
			struct STREAMINFO * STREAM = &TNC->Streams[Stream];
			
			// See if a response to internal command

			if (TNC->RIG->RIG_DEBUG)
				if (LastCmd == 'I')
					Debugprintf("SCS I Cmd Response %s", Buffer);

			if (LastCmd == 'I' && STREAM->CheckingCall == TRUE)
			{
				// We've received a connect and are checking MYCALL

				Debugprintf("SCS Incoming Call I Cmd Response %s Stream %d DED Stream %d", Buffer, Stream, RealStream);

				strlop(Buffer, 13);
				strcpy(STREAM->MyCall, Buffer);

				ProcessIncomingCall(TNC, STREAM, Stream);
				STREAM->CheckingCall = FALSE;
				return;
			}

			if (TNC->InternalCmd)
			{
				// Process it

				if (LastCmd == 'L')		// Status
				{
					int s1, s2, s3, s4, s5, s6, num;

					num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6);
			
					TNC->Streams[Stream].FramesOutstanding = s3;
	
					// flow control debug

					sprintf(TNC->WEB_BUFFERS, "%d Q %d", TNC->Buffers, 	TNC->Streams[0].FramesOutstanding);
					SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS);

					return;
				}

				if (LastCmd == '@')		// @ Commands
				{
					if (TNC->TXBuffer[6]== 'B')	// Buffer Status
					{
						TNC->Buffers = atoi(Buffer);
						sprintf(TNC->WEB_BUFFERS, "%d Q %d", TNC->Buffers, 	TNC->Streams[0].FramesOutstanding);
						SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS);
						return;
					}
				}

				if (LastCmd == '%')		// % Commands
				{					
					if (TNC->TXBuffer[6]== 'T')	// TX count Status
					{
						sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %s", TNC->Streams[Stream].bytesRXed, TNC->Streams[Stream].bytesTXed, Buffer);
						SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
						return;
					}

					if (TNC->TXBuffer[6] == 'W')	// Scan Control
					{
						if (Msg[4] == '1')			// Ok to Change
						{
							TNC->OKToChangeFreq = 1;
							TNC->TimeScanLocked = 0;
							if (TNC->RIG->RIG_DEBUG)
								Debugprintf("Scan Debug SCS Pactor TNC gave permission");
						}
						else
						{
							TNC->OKToChangeFreq = -1;
							if (TNC->SyncSupported == FALSE && TNC->UseAPPLCallsforPactor && TNC->TimeScanLocked == 0)	
								TNC->TimeScanLocked = time(NULL);

							if (TNC->RIG->RIG_DEBUG)
								Debugprintf("Scan Debug SCS Pactor TNC refused permission");

						}
					}
				}
				return;
			}
		}

		if (Msg[3] == 3)					// Status
		{			
			struct STREAMINFO * STREAM = &TNC->Streams[Stream];

			if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE"))
			{
				if ((STREAM->Connecting | STREAM->Connected) == 0)
					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", TNC->Streams[Stream].RemoteCall);

					C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
	
					STREAM->Connecting = FALSE;
					STREAM->Connected = FALSE;				// In case!
					STREAM->FramesOutstanding = 0;

					if (Stream == 0)
					{
						sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall);
						SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
					}

					STREAM->ReportDISC = TRUE;		// Tell Node
					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;
				return;
			}

			if (strstr(Buffer, "CONNECTED"))
			{
				char * Call = strstr(Buffer, " to ");
				char * ptr;
				char MHCall[30];

				// Do we need to protect against 2nd call in Dragon Single Mode???

				Call += 4;

				if (Call[1] == ':')
					Call +=2;

				ptr = strchr(Call, ' ');	
				if (ptr) *ptr = 0;

				ptr = strchr(Call, 13);	
				if (ptr) *ptr = 0;

				STREAM->Connected = TRUE;			// Subsequent data to data channel
				STREAM->Connecting = FALSE;
				STREAM->ConnectTime = time(NULL); 
				STREAM->bytesRXed = STREAM->bytesTXed = 0;

				//	Stop Scanner

				if (Stream == 0 || TNC->HFPacket)
				{
					TNC->SwitchToPactor = 0;						// Cancel any RP to Pactor switch

					sprintf(Status, "%d SCANSTOP", TNC->Port);
					Rig_Command((TRANSPORTENTRY *) -1, Status);

					SuspendOtherPorts(TNC);			// Prevent connects on other ports in same scan gruop

					memcpy(MHCall, Call, 9);
					MHCall[9] = 0;
				}

				if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0)
				{
					// Incoming Connect

					TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit;		// Reset Limit
			
					// Check for ExcludeList

					if (ExcludeList[0])
					{
						UCHAR AXCALL[7];
						
						ConvToAX25(MHCall, AXCALL);			//Permitted calls are stored in ax.25 format

						if (CheckExcludeList(AXCALL) == FALSE)
						{
							TidyClose(TNC, Stream);
							sprintf(Status, "%d SCANSTART 15", TNC->Port);
							Rig_Command((TRANSPORTENTRY *) -1, Status);
							Debugprintf("SCS Call from %s rejected", MHCall);
							return;
						}
					}

					//	IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT

					if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS)
					{
						UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS;
						UCHAR AXCALL[7];
						
						ConvToAX25(MHCall, AXCALL);			//Permitted calls are stored in ax.25 format

						while (TRUE)
						{
							if (memcmp(AXCALL, ptr, 6) == 0)	// Ignore SSID
								break;

							ptr += 7;

							if ((*ptr) == 0)							// Not in list
							{
								char Status[64];

								TidyClose(TNC, 0);
								sprintf(Status, "%d SCANSTART 15", TNC->Port);
								Rig_Command((TRANSPORTENTRY *) -1, Status);
								Debugprintf("Pactor Call from %s not in ValidCalls - rejected", Call);
								return;
							}
						}
					}

					// Check that we think we are in the right mode

					if (Stream == 0 && TNC->Dragon == 0)	// Dragon runs both at the same time
					{
						if (TNC->HFPacket && RealStream == 31)
						{
							Debugprintf("Incoming Pactor Call while in Packet Mode");
							TNC->HFPacket = FALSE;
							STREAM->DEDStream = 31;
						}
						else
						if (TNC->HFPacket == 0 && RealStream == 30)
						{
							Debugprintf("Incoming Packet Call while in Pactor Mode");
							TNC->HFPacket = TRUE;
							STREAM->DEDStream = 30;
						}
					}

					if (TNC->HFPacket)
					{
						char Save = TNC->RIG->CurrentBandWidth;
						TNC->RIG->CurrentBandWidth = 'R';
						UpdateMH(TNC, MHCall, '+', 'I');
						TNC->RIG->CurrentBandWidth = Save;
					}

					memcpy(STREAM->RemoteCall, Call, 9);	// Save Text Callsign

					//	We need to check what MYCALL is set to, either in case
					//	Appl Scan has failed to change the callsign or if a
					//	Packet Call to MYALIAS

					STREAM->CmdSet = STREAM->CmdSave = malloc(100);
					sprintf(STREAM->CmdSet, "I\r");

					STREAM->CheckingCall = TRUE;
					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);

					if (Stream == 0)
					{
						if (TNC->RIG)
							sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", STREAM->MyCall, STREAM->RemoteCall, TNC->RIG->Valchar);
						else
							sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", STREAM->MyCall, STREAM->RemoteCall);

						SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

						if (STREAM->DEDStream == 30)	// Robust Mode
						{
							char Save = TNC->RIG->CurrentBandWidth;
							TNC->RIG->CurrentBandWidth = 'R';
							UpdateMH(TNC, Call, '+', 'O');
							TNC->RIG->CurrentBandWidth = Save;
						}
						else
						{
							UpdateMH(TNC, Call, '+', 'O');
						}
					}
					return;
				}
			}
			return;
		}

		if (Msg[3] == 4 || Msg[3] == 5)
		{
			struct STREAMINFO * STREAM = &TNC->Streams[1];		// RP Stream

			// Monitor

			if ((TNC->HFPacket || TNC->DragonSingle) && TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Connected == FALSE)
			{
				// See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL

				char DestCall[10] = "NOCALL  ";
				char * ptr1 = strstr(&Msg[7], "to ");
				int i;
				APPLCALLS * APPL;
				char Appl[11];
				char Status[80];

				if (ptr1) memcpy(DestCall, &ptr1[3], 10);
				
				ptr1 = strchr(DestCall, ' ');
				if (ptr1) *(ptr1) = 0;					// Null Terminate

				Debugprintf("RP SABM Received for %s" , DestCall);

				if (strcmp(TNC->NodeCall, DestCall) != 0)
				{
					// Not Calling NodeCall/Portcall

					if (strcmp(NodeCall, DestCall) == 0)
						goto SetThisCall;

					// See if to one of our ApplCalls

					for (i = 0; i < 32; i++)
					{
						APPL=&APPLCALLTABLE[i];

						if (APPL->APPLCALL_TEXT[0] > ' ')
						{
							char * ptr;
							memcpy(Appl, APPL->APPLCALL_TEXT, 10);
							ptr=strchr(Appl, ' ');

							if (ptr) *ptr = 0;

							if (strcmp(Appl, DestCall) == 0)
							{
						SetThisCall:
								Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall);

								sprintf(Status, "%d SCANSTART 30", TNC->Port);
								Rig_Command((TRANSPORTENTRY *) -1, Status);
								TNC->SwitchToPactor = 0;		// Stay in RP
				
								strcpy(STREAM->MyCall, DestCall);
								STREAM->CmdSet = STREAM->CmdSave = malloc(100);
								sprintf(STREAM->CmdSet, "I%s\r", DestCall);
								TNC->InternalCmd = TRUE;
								
								break;
							}
						}
					}
				}
			}

			DoMonitor(TNC, &Msg[3], framelen - 3);
			return;

		}

		// 1, 2, 4, 5 - pass to Appl

		buffptr = GetBuff();

		if (buffptr == NULL) return;			// No buffers, so ignore

		buffptr->Len = sprintf(buffptr->Data,"Pactor} %s", &Msg[4]);

		OpenLogFile(TNC->Port);
		WriteLogLine(TNC->Port, &Msg[4], (int)strlen(&Msg[4]));
		CloseLogFile(TNC->Port);

		C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);

		return;
	}

	if (Msg[3] == 6)
	{
		// Monitor Data With length)

		DoMonitor(TNC, &Msg[3], framelen - 3);
		return;
	}

	if (Msg[3] == 7)
	{
		char StatusMsg[60];
		int Status, ISS, Offset;
		
		if (Msg[2] == 0xfe)						// Status Poll Response
		{
			int PactorLevel = Msg[6] & 7;		// Pactor Level 1-4
			
			if (TNC->MinLevelTimer)
			{
				if (PactorLevel >= TNC->MinLevel)
				{
					Debugprintf("Reached MIN Pactor Level");
					TNC->MinLevelTimer = 0;
				}
				else
					Debugprintf("Still waiting for Min Level Now %d Need %d", PactorLevel, TNC->MinLevel);
			}

			Status = Msg[5];
			
			TNC->Streams[0].PTCStatus0 = Status;
			TNC->Streams[0].PTCStatus1 = PactorLevel;		// Pactor Level 1-4
			TNC->Streams[0].PTCStatus2 = Msg[7];			// Speed Level
			Offset = Msg[8];

			if (Offset > 128)
				Offset -= 128;

			TNC->Streams[0].PTCStatus3 = Offset; 

			TNC->Mode = (Status >> 4) & 7;
			ISS = Status & 8;
			Status &= 7;

			if (TNC->PTCStatus != Status)		// Changed
			{
				Debugprintf("SCS status changed, now %s", status[Status]);

				if (Status == 6)		// SYNCH
				{
					// New Sync

					if (TNC->RIG->RIG_DEBUG)
						Debugprintf("SCS New SYNC Detected");
	
					TNC->TimeEnteredSYNCMode = time(NULL);
					TNC->SyncSupported = TRUE;
				}
				else
				{
					if (TNC->PTCStatus == 6)
					{
						if (TNC->RIG->RIG_DEBUG)
							Debugprintf("SCS left SYNC, now %s", status[Status]);
	
						TNC->TimeEnteredSYNCMode = 0;
					}
				}
				TNC->PTCStatus = Status;
			}
			sprintf(StatusMsg, "%x %x %x %x", TNC->Streams[0].PTCStatus0,
				TNC->Streams[0].PTCStatus1, TNC->Streams[0].PTCStatus2, TNC->Streams[0].PTCStatus3);
			
			if (ISS)
			{
				SetWindowText(TNC->xIDC_TXRX, "Sender");
				strcpy(TNC->WEB_TXRX, "Sender"); 
			}
			else
			{
				SetWindowText(TNC->xIDC_TXRX, "Receiver");
				strcpy(TNC->WEB_TXRX, "Receiver"); 
			}

			SetWindowText(TNC->xIDC_STATE, status[Status]);
			strcpy(TNC->WEB_STATE, status[Status]);
			SetWindowText(TNC->xIDC_MODE, ModeText[TNC->Mode]);
			strcpy(TNC->WEB_MODE, ModeText[TNC->Mode]);

			if (TNC->Mode == 7)
				TNC->Busy = TNC->BusyHold * 10;				// BusyHold  delay

			if (Offset == 128)		// Undefined
				sprintf(StatusMsg, "Mode %s Speed Level %d Freq Offset Unknown",
					PactorLevelText[TNC->Streams[0].PTCStatus1], Msg[7]);
			else
				sprintf(StatusMsg, "Mode %s Speed Level %d Freq Offset %d",
					PactorLevelText[TNC->Streams[0].PTCStatus1], Msg[7], Offset);

			strcpy(TNC->WEB_PACTORLEVEL, StatusMsg);
			SetWindowText(TNC->xIDC_PACTORLEVEL, StatusMsg);

			return;
		}
	
		if (Msg[2] == 248)	// Log Message
		{
			// Monitor Data - Length format
			// first 4 bytes contain a 32 bits long timestamp.
			// That timestamp holds the number of seconds that elapsed since date 01.01.2000 at 00:00:00.
			// The MS byte is sent first. The timestamp can be corrected to the usual C timestamp (seconds
			//since 01.01.1970, 00:00:00) simply by adding 946684800 (seconds) to it.
			// Teminated with LF

			int datalen = Msg[4] + 1;
			unsigned int timestamp = (Msg[5] << 24) + (Msg[6] << 16)
				+ (Msg[6] << 8) + Msg[7] + 946684800;

			Msg[5 + datalen] = 0;
			Debugprintf("SCS Debug %s", &Msg[9]);
			return;
		}

		if (Msg[2] == 253)						// Rig Port Response
		{
			// Queue for Rig Control Driver
			
			int datalen = Msg[4] + 1;
			PMSGWITHLEN buffptr;

			// if not configured to use PTC Rig Control, Ignore

			if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL)
				return;
			
			buffptr = GetBuff();

			if (buffptr)
			{
				buffptr->Len = datalen;
				memcpy(buffptr->Data, &Msg[5], datalen);
				C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr);
				if (TNC->RIG->RIG_DEBUG)
				{
					Debugprintf("SCS RIG frame received, len %d", datalen);
					Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x",
						Msg[5], Msg[6], Msg[7], Msg[8], Msg[9], Msg[10], Msg[11], Msg[12],
						Msg[13], Msg[14], Msg[15], Msg[16], Msg[17], Msg[18], Msg[19], Msg[20]);
		
				}
			}
			return;
		}

		// Connected Data
		
		buffptr = GetBuff();

		if (buffptr == NULL) return;			// No buffers, so ignore
			
		buffptr->Len = Msg[4] + 1;				// Length
		TNC->Streams[Stream].bytesRXed += (int)buffptr->Len;
		memcpy(buffptr->Data, &Msg[5], buffptr->Len);

		WritetoTrace(TNC, &Msg[5], (int)buffptr->Len);

		C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);

		return;
	}
}

int GetPTCRadioCommand(struct TNCINFO * TNC, char * Block)
{
	PMSGWITHLEN buffptr;
	int Length;

	if (TNC->RadiotoBPQ_Q == 0)
		return 0;

	buffptr = Q_REM(&TNC->RadiotoBPQ_Q);

	Length = (int)buffptr->Len;
		
	memcpy(Block, buffptr->Data, Length);
		
	ReleaseBuffer(buffptr);

//	Debugprintf("SCS Rig Command Queued");

	return Length;;
}

int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length)
{
	PMSGWITHLEN buffptr;

	if (TNC->TNCOK || (TNC->Hardware == H_ARDOP && TNC->ARDOPCommsMode == 'T'))
	{
	}
	else
		return 0;

	// Queue for TNC

	buffptr = GetBuff();

	if (buffptr == 0) return (0);			// No buffers, so ignore

	buffptr->Len = Length;
		
	memcpy(buffptr->Data, Block, Length);
		
	C_Q_ADD(&TNC->BPQtoRadio_Q, buffptr);

   return 0;

}

static MESSAGE Monframe;		// I frames come in two parts.

#define TIMESTAMP 352

MESSAGE * AdjMsg = &Monframe;	// Adjusted for digis

static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len)
{
	// Convert to ax.25 form and pass to monitor

	UCHAR * ptr, * starptr;
	char * context;
	char * MHCall = Monframe.ORIGIN;


	if (Msg[0] == 6)		// Second part of I or UI
	{
		int len = Msg[1] +1;

		memcpy(AdjMsg->L2DATA, &Msg[2], len);
		Monframe.LENGTH += len;

		time(&Monframe.Timestamp);

		BPQTRACE((MESSAGE *)&Monframe, TRUE);
		return;
	}

	Monframe.LENGTH = MSGHDDRLEN + 16;			// Control Frame

	Monframe.PORT = TNC->Port;
	
	AdjMsg = &Monframe;					// Adjusted for digis
	ptr = strstr(Msg, "fm ");

	ConvToAX25(&ptr[3], Monframe.ORIGIN);

	ptr = strstr(ptr, "to ");

	ConvToAX25(&ptr[3], Monframe.DEST);

	ptr = strstr(ptr, "via ");

	if (ptr)
	{
		// We have digis

		char Save[100];
		char * fiddle;

		memcpy(Save, &ptr[4], 60);

		ptr = strtok_s(Save, " ", &context);
DigiLoop:
		fiddle = (char *)AdjMsg;
		fiddle += 7;
		AdjMsg = (MESSAGE *)fiddle;

		Monframe.LENGTH += 7;

		starptr = strchr(ptr, '*');
		if (starptr)
			*(starptr) = 0;

		ConvToAX25(ptr, AdjMsg->ORIGIN);

		if (starptr)
			AdjMsg->ORIGIN[6] |= 0x80;				// Set end of address

		ptr = strtok_s(NULL, " ", &context);

		if (memcmp(ptr, "ctl", 3))
			goto DigiLoop;
	}

	AdjMsg->ORIGIN[6] |= 1;				// Set end of address

	ptr = strstr(Msg, "ctl ");

	if (memcmp(&ptr[4], "SABM", 4) == 0)
	{
		AdjMsg->CTL = 0x2f;
		UpdateMHwithDigis(TNC, MHCall, '.', 0);
	}
	else  
	if (memcmp(&ptr[4], "DISC", 4) == 0)
		AdjMsg->CTL = 0x43;
	else 
	if (memcmp(&ptr[4], "UA", 2) == 0)
	{
		AdjMsg->CTL = 0x63;
		UpdateMHwithDigis(TNC, MHCall, '.', 0);
	}
	else  
	if (memcmp(&ptr[4], "DM", 2) == 0)
		AdjMsg->CTL = 0x0f;
	else 
	if (memcmp(&ptr[4], "UI", 2) == 0)
	{
		AdjMsg->CTL = 0x03;
		UpdateMHwithDigis(TNC, MHCall, '.', 0);
	}
	else 
	if (memcmp(&ptr[4], "RR", 2) == 0)
		AdjMsg->CTL = 0x1 | (ptr[6] << 5);
	else 
	if (memcmp(&ptr[4], "RNR", 3) == 0)
		AdjMsg->CTL = 0x5 | (ptr[7] << 5);
	else 
	if (memcmp(&ptr[4], "REJ", 3) == 0)
		AdjMsg->CTL = 0x9 | (ptr[7] << 5);
	else 
	if (memcmp(&ptr[4], "FRMR", 4) == 0)
		AdjMsg->CTL = 0x87;
	else  
	if (ptr[4] == 'I')
	{
		AdjMsg->CTL = (ptr[5] << 5) | (ptr[6] & 7) << 1 ;
	}

	if (strchr(&ptr[4], '+'))
	{
		AdjMsg->CTL |= 0x10;
		Monframe.DEST[6] |= 0x80;				// SET COMMAND
	}

	if (strchr(&ptr[4], '-'))	
	{
		AdjMsg->CTL |= 0x10;
		Monframe.ORIGIN[6] |= 0x80;				// SET COMMAND
	}

	if (Msg[0] == 5)							// More to come
	{
		ptr = strstr(ptr, "pid ");	
		sscanf(&ptr[3], "%x", (int *)&AdjMsg->PID);
		return;	
	}

	time(&Monframe.Timestamp);

	BPQTRACE((MESSAGE *)&Monframe, TRUE);

}
//1:fm G8BPQ to KD6PGI-1 ctl I11^ pid F0
//fm KD6PGI-1 to G8BPQ ctl DISC+

VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
	// Queue it as we may have just sent data

	TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = malloc(100);
	sprintf(TNC->Streams[Stream].CmdSet, "D\r");
}


VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
	// Sending D twice should do a "Dirty Disconnect"

	// Try thst first. If it still doesn't disconnect maybe try restart

	unsigned char Resp[500] = "";
	char * Poll = &TNC->TXBuffer[0]; 

	Debugprintf("Failed to disconnect TNC - trying a forced disconnect");

	// Try Normal Mode DD (Dirty Disconnect)
					
	// Uses "Hidden" feature where you can send any normal mode command
	// in host mode by preceeding with a #

	Poll[2] = 31;
	Poll[3] = 0x1;
	Poll[4] = 2;
	sprintf(&Poll[5], "#DD\r");		// Node \r isn't sent but is there for log
	CRCStuffAndSend(TNC, Poll, 8);
			
	// It looks like there isn't a response

	TNC->Timeout = 0;	

	OpenLogFile(TNC->Port);
	WriteLogLine(TNC->Port, &Poll[5], 4);
	CloseLogFile(TNC->Port);

/*
	Poll[2] = 31;
	Poll[3] = 1;
	Poll[4] = 0;
	Poll[5] = 'D';

	CRCStuffAndSend(TNC, Poll, 6);

	// Wait for response before sending another

	n = 0;
	while (CheckRXHost(TNC, Resp) == FALSE)
	{
		Sleep(5);
		n++;
		if (n > 100) break;
	}

	Poll[2] = 31;
	Poll[3] = 1;
	Poll[4] = 0;
	Poll[5] = 'D';

	CRCStuffAndSend(TNC, Poll, 6);

	n = 0;
	while (CheckRXHost(TNC, Resp) == FALSE)
	{
		Sleep(5);
		n++;
		if (n > 100) break;
	}

	// See if it worked

	Poll[2] = 254;			// Channel
	Poll[3] = 0x1;			// Command
	Poll[4] = 1;			// Len-1
	Poll[5] = 'G';			// Extended Status Poll
	Poll[6] = '3';

	CRCStuffAndSend(TNC, Poll, 7);
	
	n = 0;
	while (CheckRXHost(TNC, Resp) == FALSE)
	{
		Sleep(5);
		n++;
		if (n > 100) break;
	}

	Debugprintf("PTC Status Now %x %x %x %x %x %x %x %x",
		Resp[0], Resp[1], Resp[2], Resp[3], Resp[4], Resp[5], Resp[6], Resp[7]); 

	TNC->Timeout = 0;

	return;

	// Maybe best just to restart the TNC

	if (TNC->PacketChannels == 0)		// Not using packet
	{
		Debugprintf("Forced Disconnect Failed - restarting TNC");

		// Ensure in Pactor

		if(TNC->Dragon == 0)
		{
			TNC->TXBuffer[2] = 31;
			TNC->TXBuffer[3] = 0x1;
			TNC->TXBuffer[4] = 0x1;
			memcpy(&TNC->TXBuffer[5], "PT", 2);

			CRCStuffAndSend(TNC, TNC->TXBuffer, 7);

			n = 0;
			while (CheckRXHost(TNC, Resp) == FALSE)
			{
				Sleep(5);
				n++;
				if (n > 100) break;
			}
		}

		Sleep(50);
		ExitHost(TNC);
		Sleep(50);

		n = 0;
		while (CheckRXHost(TNC, Resp) == FALSE)
		{
			Sleep(5);
			n++;
			if (n > 100) break;
		}

		TNC->Timeout = 0;
		TNC->HostMode = FALSE;
		TNC->ReinitState = 0;

		return;
	}
*/
}

VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
	char Status[80];
	struct STREAMINFO * STREAM = &TNC->Streams[Stream];

	Debugprintf("SCS Pactor Close Complete - Stream = %d", Stream);

	STREAM->CmdSet = STREAM->CmdSave = malloc(100);

	strcpy(STREAM->MyCall, TNC->NodeCall);

	if (Stream == 0 || TNC->HFPacket)
	{
		SetWindowText(TNC->xIDC_TNCSTATE, "Free");
		strcpy(TNC->WEB_TNCSTATE, "Free");
		sprintf(Status, "%d SCANSTART 15", TNC->Port);
		Rig_Command((TRANSPORTENTRY *) -1, Status);

		if (TNC->Dragon)
		{
			sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall);
			TNC->Streams[0].DEDStream = 31;		// Pactor Channel
		}
		else
		{
			if (TNC->HFPacket)
			{
				sprintf(STREAM->CmdSet, "I%s\rPR\r", TNC->NodeCall);
				TNC->Streams[0].DEDStream = 30;		// Packet Channel
				Debugprintf("BPQ32 Session Closed - switch to Packet");
			}
			else
			{
				sprintf(STREAM->CmdSet, "I%s\rPT\r", TNC->NodeCall);
				TNC->Streams[0].DEDStream = 31;		// Pactor Channel
				Debugprintf("BPQ32 Session Closed - switch to Pactor");
			}
		}
		ReleaseOtherPorts(TNC);
	}
	else
		sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall);

	Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet);

}

VOID PTCSuspendPort(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

//	Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet);
}

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

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

	STREAM->CmdSet = STREAM->CmdSave = zalloc(100);

	if (TNC->UseAPPLCallsforPactor && TNC->RIG && TNC->RIG != &TNC->DummyRig 
			&& TNC->RIG->FreqPtr[0]->APPLCALL[0])
		sprintf(STREAM->CmdSet, "I%s\r", TNC->RIG->FreqPtr[0]->APPLCALL);
	else
		sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall);

//	Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet);
}



