/*
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 HAL Communications Corp Clover/Pacor controllers to BPQ32 switch 
//
//	Uses BPQ EXTERNAL interface
//

#define _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_DEPRECATE

#include "time.h"

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

#include "bpq32.h"

#define HAL 1

#define SetMYCALL 0x13
#define ConnectEnable 0x52
#define ConnectDisable 0x42
#define SetEAS 0x59				// Echo as Sent
#define SetTones 0xec
#define ClearOnDisc 0x57

static char ClassName[]="HALSTATUS";

static char WindowTitle[] = "HAL";
static int RigControlRow = 185;

#define	SOH	0x01	// CONTROL CODES 
#define	ETB	0x17
#define	DLE	0x10

//int MaxStreams = 0;

#ifndef LINBPQ
extern HFONT hFont;
#endif

static char status[23][50] = {"IDLE", "TFC", "RQ", "ERR", "PHS", "OVER", "FSK TX",
		"FSK RX", "P-MODE100", "P-MODE200", "HUFMAN ON", "HUFMAN OFF", "P-MODE SBY(LISTEN ON)",
		"P-MODE SBY(LISTEN OFF)", "ISS", "IRS",
		"AMTOR SBY(LISTEN ON)", "AMTOR SBY(LISTEN OFF)", "AMTOR FEC TX", "AMTOR FEC RX",  "P-MODE FEC TX", 
		"FREE SIGNAL TX (AMTOR)", "FREE SIGNAL TX TIMED OUT (AMTOR)"};

struct TNCINFO * CreateTTYInfo(int port, int speed);
BOOL SetupConnection(int);
static BOOL WriteCommBlock(struct TNCINFO * TNC);
static void CheckRX(struct TNCINFO * TNC);
VOID HALPoll(int Port);
VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len);
VOID ProcessTermModeResponse(struct TNCINFO * TNC);
static VOID DoTNCReinit(struct TNCINFO * TNC);
VOID DoTermModeTimeout(struct TNCINFO * TNC);
VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length);
VOID ProcessHALCmd(struct TNCINFO * TNC);
VOID ProcessHALData(struct TNCINFO * TNC);
VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer);
VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len);
VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len);

BOOL HALConnected(struct TNCINFO * TNC, char * Call);
VOID HALDisconnected(struct TNCINFO * TNC);

static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len);
VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len);
int	DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
int	DLEDecode(UCHAR * inbuff, UCHAR * outbuff, int len);

VOID COMClearDTR(HANDLE fd);
VOID COMClearRTS(HANDLE fd);
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);



//static HANDLE LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};

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

//char BaseDir[]="c:";

static VOID CloseLogfile(int Flags)
{
//	CloseHandle(LogHandle[Flags]);
//	LogHandle[Flags] = INVALID_HANDLE_VALUE;
}

static VOID OpenLogfile(int Flags)
{
/*
UCHAR FN[MAX_PATH];
	time_t T;
	struct tm * tm;

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

	sprintf(FN,"%s\\HALLog_%02d%02d%02d_%s.bin", BaseDir, tm->tm_mday, tm->tm_hour, tm->tm_min, Logs[Flags]);

	LogHandle[Flags] = CreateFile(FN,
					GENERIC_WRITE,
					FILE_SHARE_READ,
					NULL,
					OPEN_ALWAYS,
					FILE_ATTRIBUTE_NORMAL,
					NULL);

	SetFilePointer(LogHandle[Flags], 0, 0, FILE_END);

	return (LogHandle[Flags] != INVALID_HANDLE_VALUE);
*/
}

static void WriteLogLine(int Flags, char * Msg, int MsgLen)
{
//	int cnt;
//	WriteFile(LogHandle[Flags] ,Msg , MsgLen, &cnt, NULL);
}



int ProcessLine(char * buf, int Port)
{
	UCHAR * ptr,* p_cmd;
	char * p_ipad = 0;
	char * p_port = 0;
	unsigned short WINMORport = 0;
	int BPQport;
	int len=510;
	struct TNCINFO * TNC;
	char errbuf[256];

	strcpy(errbuf, buf);

	ptr = strtok(buf, " \t\n\r");

	if(ptr == NULL) return (TRUE);

	if(*ptr =='#') return (TRUE);			// comment

	if(*ptr ==';') return (TRUE);			// comment

	ptr = strtok(NULL, " \t\n\r");

	if (_stricmp(buf, "APPL") == 0)			// Using BPQ32 COnfig
	{
		BPQport = Port;
		p_cmd = ptr;
	}
	else
	if (_stricmp(buf, "PORT") != 0)			// Using Old Config
	{
		// New config without a PORT or APPL  - this is a Config Command

		strcpy(buf, errbuf);
		strcat(buf, "\r");

		BPQport = Port;

		TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO));

		TNC->InitScript = malloc(1000);
		TNC->InitScript[0] = 0;
		goto ConfigLine;
	}
	else

	{

		// Old Config from file

	BPQport=0;
	BPQport = atoi(ptr);
	
	p_cmd = strtok(NULL, " \t\n\r");

	if (Port && Port != BPQport)
	{
		// Want a particular port, and this isn't it

		while(TRUE)
		{
			if (GetLine(buf) == 0)
				return TRUE;

			if (memcmp(buf, "****", 4) == 0)
				return TRUE;

		}
	}
	}
	if(BPQport > 0 && BPQport < 33)
	{
		TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO));
		TNC->InitScript = malloc(1000);
		TNC->InitScript[0] = 0;

		if (p_cmd != NULL)
		{
			if (p_cmd[0] != ';' && p_cmd[0] != '#')
				TNC->ApplCmd=_strdup(p_cmd);
		}

		// 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, "WL2KREPORT", 10) == 0)
			{
				TNC->WL2K = DecodeWL2KReportLine(buf);
				continue;
			}
			if (_memicmp(buf, "NEEDXONXOFF", 10) == 0)
			{
				TNC->XONXOFF = TRUE;
				continue;
			}

			if (_memicmp(buf, "TONES", 5) == 0)
			{
				int tone1 = 0, tone2 = 0;

				ptr = strtok(&buf[6], " ,/\t\n\r");
				if (ptr)
				{
					tone1 = atoi(ptr);
					ptr = strtok(NULL, " ,/\t\n\r");
					if (ptr)
					{
						tone2 = atoi(ptr);
						ptr = &TNC->InitScript[TNC->InitScriptLen];

						// Try putting into FSK mode first

						*(ptr++) = 0x84;
						*(ptr++) = SetTones;		// Set Tones (Mark, Space HI byte first)
						*(ptr++) = tone1 >> 8;
						*(ptr++) = tone1 & 0xff;
						*(ptr++) = tone2 >> 8;
						*(ptr++) = tone2 & 0xff;

						TNC->InitScriptLen += 6;	

						continue;
					}
				}
				goto BadLine;
			}
			if (_memicmp(buf, "DEFAULTMODE ", 12) == 0)
			{
					
				ptr = strtok(&buf[12], " ,\t\n\r");
				if (ptr)
				{
					if (_stricmp(ptr, "CLOVER") == 0)
						TNC->DefaultMode = Clover;
					else if (_stricmp(ptr, "PACTOR") == 0)
						TNC->DefaultMode = Pactor;
					else if (_stricmp(ptr, "AMTOR") == 0)
						TNC->DefaultMode = AMTOR;
					else goto BadLine;
					
					continue;
				}
				goto BadLine;
			}
		}
		BadLine:
			WritetoConsole(" Bad config record ");
			WritetoConsole(errbuf);
			WritetoConsole("\r\n");
	}
	
	return (TRUE);	
}

static size_t ExtProc(int fn, int port , PDATAMESSAGE buff)
{
	int txlen = 0;
	PMSGWITHLEN buffptr;
	struct TNCINFO * TNC = TNCInfo[port];
	struct STREAMINFO * STREAM;
	int Stream;

	if (TNC == NULL)
		return 0;
	
	if (fn < 4 || fn > 5)
		if (TNC->hDevice == 0)
			return 0;					// Port not open

	STREAM = &TNC->Streams[0];

	switch (fn)
	{
	case 1:				// poll

		while (TNC->PortRecord->UI_Q)			// Release anything accidentally put on UI_Q
		{
			buffptr = Q_REM(&TNC->PortRecord->UI_Q);
			ReleaseBuffer(buffptr);
		}

		//for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			if (STREAM->ReportDISC)
			{
				STREAM->ReportDISC = FALSE;
				buff->PORT = 0;

				return -1;
			}
		}

		CheckRX(TNC);
		HALPoll(port);

		//for (Stream = 0; Stream <= MaxStreams; Stream++)
		{
			if (STREAM->PACTORtoBPQ_Q !=0)
			{
				int datalen;
			
				buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q);

				datalen = (int)buffptr->Len;

				buff->PORT = 0;						// 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

		// Find TNC Record

		Stream = buff->PORT;

		if (!TNC->TNCOK)
		{
			// Send Error Response
			
			PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();

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

			buffptr->Len = 27;
			memcpy(&buffptr->Data[0], "No Connection to PACTOR TNC\r", 27);

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

		txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4);

		buffptr->Len = txlen;
		memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen);
		
		C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr);

		STREAM->FramesQueued++;

		return (0);


	case 3:				// CHECK IF OK TO SEND. Also used to check if TNC is responding
		
		Stream = (int)(size_t)buff;
			
		if (STREAM->FramesQueued  > 4)
			return (1 | TNC->HostMode << 8);
	
		return TNC->HostMode << 8 | STREAM->Disconnecting << 15;		// OK, but lock attach if disconnecting

	case 4:				// reinit

		return (0);

	case 5:				// Close

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

	case 6:				// Scan Control

		return 0;		// None Yet

	}
	return 0;

}

static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
	int Len = sprintf(Buff, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
	"<head><title>HAL Status</title></head><body><h3>HAL 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>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
	Len += sprintf(&Buff[Len], "<tr><td>LEDS</td><td>STBY CALL LINK ERROR TX RX</td></tr>");
	Len += sprintf(&Buff[Len], "<tr><td> </td><td>%s</td></tr>", TNC->WEB_LEDS);
	Len += sprintf(&Buff[Len], "</table>");

	Len = DoScanLine(TNC, Buff, Len);

	return Len;
}


VOID * HALExtInit(EXTPORTDATA *  PortEntry)
{
	char msg[500];
	struct TNCINFO * TNC;
	int port;
	char * ptr;
	int len;
	char Msg[512];
#ifndef LINBPQ
	HWND x;
#endif
	//
	//	Will be called once for each Pactor Port
	//	The COM port number is in IOBASE
	//

	sprintf(msg,"HAL Driver %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");
		WritetoConsole(msg);

		return ExtProc;
	}
	
	TNC->Port = port;
	TNC->PortRecord = PortEntry;

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

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

	PortEntry->MAXHOSTMODESESSIONS = 1;		// Default


	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;

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

	if (TNC->DefaultMode)
		TNC->CurrentMode = TNC->DefaultMode;
	else
		TNC->CurrentMode = Clover;

	TNC->PollDelay = 999999999;

	// Set Disable +?, ExpandedStatus , Channel Stats Off, ClearOnDisc, EAS and MYCALL

	len = sprintf(Msg, "%c%c%c%c%c%c%s", 0xcc, 0x56, 0x41, ClearOnDisc, SetEAS, SetMYCALL, TNC->NodeCall);
	len++;					// We include the NULL

	memcpy(&TNC->InitScript[TNC->InitScriptLen], Msg, len); 
	TNC->InitScriptLen += len;

	PortEntry->PORTCONTROL.TNC = TNC;

	TNC->WebWindowProc = WebProc;
	TNC->WebWinX = 510;
	TNC->WebWinY = 280;

	TNC->WEB_COMMSSTATE = zalloc(512);
	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_LEDS = zalloc(100);
	strcpy(TNC->WEB_LEDS, "  X   X     X    X   X  X");

#ifndef LINBPQ

	CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 233, ForcedClose);

	x = CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL);
	x = TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL);
	
	x = CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL);
	x = TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL);

	x = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL);
	x = TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL);

	x = CreateWindowEx(0, "STATIC", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
	x = TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);

 	x = CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
	x = TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);

	x = CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
	x = TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);

	x = CreateWindowEx(0, "STATIC", "LEDS", WS_CHILD | WS_VISIBLE,10,138,60,20, TNC->hDlg, NULL, hInstance, NULL);
	SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0);
	x = CreateWindowEx(0, "STATIC",                  "STBY CALL LINK ERROR TX RX", WS_CHILD | WS_VISIBLE,116,138,280,20, TNC->hDlg, NULL, hInstance, NULL);
	SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0);
	x = TNC->xIDC_LEDS = CreateWindowEx(0, "STATIC", "  X   X     X    X   X  X", WS_CHILD | WS_VISIBLE,116,158,280,20 , TNC->hDlg, NULL, hInstance, NULL);
	SendMessage(x, WM_SETFONT, (WPARAM)hFont, 0);
	
	TNC->ClientHeight = 233;
	TNC->ClientWidth = 500;
	
	MoveWindows(TNC);
#endif	

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

	SendCmd(TNC, "\x09" , 1);		// Reset

	WritetoConsole("\n");

	return ExtProc;
}


 
static VOID KISSCLOSE(int Port)
{ 
	struct TNCINFO * conn = TNCInfo[Port];

	// drop DTR and RTS

	COMClearDTR(conn->hDevice);
	COMClearRTS(conn->hDevice);

   // purge any outstanding reads/writes and close device handle

	CloseCOMPort(conn->hDevice);		
	
	return;
}


static void CheckRX(struct TNCINFO * TNC)
{
	int Length, Len;
	UCHAR * Xptr;

	// only try to read number of bytes in queue 

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

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

	if (Len == 0)
		return;

	TNC->RXLen += Len;

	Length = TNC->RXLen;

	// We need to konw whether data is received or echoed, so we can't split commands and data here.
	// Pass everything to the Command Handler. It will check that there are enough bytes for the command,
	// and wait for more if not.

	// The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91

	// If USB version, we might get unescaped xon and xoff, which we must ignore

	if (TNC->XONXOFF)
	{
		Xptr = memchr(&TNC->RXBuffer, 0x11, Length);
	
		while(Xptr)
		{
			Debugprintf("XON Port %d", TNC->Port);
			memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr));
			Xptr = memchr(&TNC->RXBuffer, 0x11, Length);
		}

		Xptr = memchr(&TNC->RXBuffer, 0x13, Length);
	
		while(Xptr)
		{
			Debugprintf("XOFF Port %d", TNC->Port);
			memmove(Xptr, Xptr + 1, Length-- - (TNC->RXBuffer - Xptr));
			Xptr = memchr(&TNC->RXBuffer, 0x13, Length);
		}
	
		Xptr = memchr(&TNC->RXBuffer, 0x91, Length);			// See if packet contains 0x91 escape

		if (Xptr)
	
			// Make sure we have the escaped char as well
	
			if ((Xptr - &TNC->RXBuffer[0]) == Length - 1)		// x91 is last char
				return;
	}

	ProcessHALBuffer(TNC, Length);

	TNC->RXLen = 0;
	
	return;

}



static BOOL WriteCommBlock(struct TNCINFO * TNC)
{
	WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen);
	return TRUE;
}

VOID HALPoll(int Port)
{
	struct TNCINFO * TNC = TNCInfo[Port];
	struct STREAMINFO * STREAM = &TNC->Streams[0];

	UCHAR * Poll = TNC->TXBuffer;
	char Status[80];
	UCHAR TXMsg[1000];
	int datalen;

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

		// Timed Out

		TNC->TNCOK = FALSE;
		TNC->HostMode = 0;
				
		sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName);
		MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

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

	}

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

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

	if (TNC->PortRecord->ATTACHEDSESSIONS[0] && STREAM->Attached == 0)
	{
		// New Attach

		int calllen;
		char Msg[80];

		STREAM->Attached = TRUE;

		STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0;

		calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, STREAM->MyCall);
		STREAM->MyCall[calllen] = 0;
		
		datalen = sprintf(TXMsg, "%c%s", SetMYCALL, STREAM->MyCall);
		SendCmd(TNC, TXMsg, datalen + 1);			// Send the NULL

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

		// Stop Scanning

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

		SendCmd(TNC, "\x42", 1);		// Connect Enable off

		return;

	}

	//for (Stream = 0; Stream <= MaxStreams; Stream++)

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

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

		if (TNC->NeedPACTOR == 0)
		{
			int datalen;

			UCHAR TXMsg[80];

			datalen = sprintf(TXMsg, "%c%s", SetMYCALL, TNC->NodeCall);
			SendCmd(TNC, TXMsg, datalen + 1);			// Send the NULL

			// Set Listen Mode

			switch (TNC->CurrentMode)
			{
			case Pactor:

				SendCmd(TNC, "\x84", 1);		// FSK
				SendCmd(TNC, "\x83", 1);		// Select P-MODE Standby
				SendCmd(TNC, "\x58", 1);		// Listen

				break;

			case Clover:

				SendCmd(TNC, "\x80", 1);		// Clover
				SendCmd(TNC, "\x54", 1);		// Enable adaptive Clover format
				SendCmd(TNC, "\x41", 1);		// No Statistics
				SendCmd(TNC, "\x60\x09", 2);	// Robust Retries
				SendCmd(TNC, "\x61\x09", 2);	// Normal Retries

				break;			
			}

			SendCmd(TNC, "\x52", 1);			// ConnectEnable

			// Restart Scanning

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

			return;
		}
	}

#define MAXHALTX 256

	//for (Stream = 0; Stream <= MaxStreams; Stream++)
	{
		if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q && (STREAM->bytesTXed - STREAM->BytesAcked < 600))
		{
			int datalen;
			PMSGWITHLEN buffptr;
			UCHAR * MsgPtr;
			unsigned char TXMsg[500];
			
			buffptr = (PMSGWITHLEN)STREAM->BPQtoPACTOR_Q;
			datalen = buffptr->Len;
			MsgPtr = buffptr->Data;

			if (STREAM->Connected)
			{
				if (TNC->SwallowSignon)
				{
					TNC->SwallowSignon = FALSE;	
					if (strstr(MsgPtr, "Connected"))	// Discard *** connected
					{
						ReleaseBuffer(buffptr);
						STREAM->FramesQueued--;
						return;
					}
				}

				// Must send data in small chunks - the Hal has limited buffer space

				// If in IRS force a turnround 

				if (TNC->TXRXState == 'R' && TNC->CurrentMode != Clover)
				{
					if (TNC->TimeInRX++ > 15)
						SendCmd(TNC, "\x87", 1);		// Changeover to ISS 
					else
						goto Poll;
				}

				TNC->TimeInRX = 0;
					
				EncodeAndSend(TNC, MsgPtr, datalen);
				buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
				ReleaseBuffer(buffptr);
				WriteLogLine(2, MsgPtr, datalen);

				STREAM->bytesTXed += datalen; 
				STREAM->FramesQueued--;

				ShowTraffic(TNC);

				return;
			}
			else
			{
				buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q);
				STREAM->FramesQueued--;

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

				datalen--;				// Exclude CR
				MsgPtr[datalen] = 0;	// Null Terminate
				_strupr(MsgPtr);

				if (memcmp(MsgPtr, "RADIO ", 6) == 0)
				{
					sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]);
					if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40]))
					{
						ReleaseBuffer(buffptr);
					}
					else
					{
						buffptr->Len = sprintf((UCHAR *)buffptr->Data, "%s", &MsgPtr[40]);
						C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
					}
					return;
				}

				if (memcmp(MsgPtr, "MODE CLOVER", 11) == 0)
				{
					TNC->CurrentMode = Clover;
					buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r");
					C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
			
					MySetWindowText(TNC->xIDC_MODE, "Clover");
					strcpy(TNC->WEB_MODE, "Clover");

					SendCmd(TNC, "\x80", 1);		// Clover
					SendCmd(TNC, "\x54", 1);		// Enable adaptive Clover format
					SendCmd(TNC, "\x41", 1);		// No Statistics

					return;
				}

				if (memcmp(MsgPtr, "MODE PACTOR", 11) == 0)
				{
					TNC->CurrentMode = Pactor;
					buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r");
					C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);

					SendCmd(TNC, "\x84", 1);		// FSK
					SendCmd(TNC, "\x83", 1);		// Select P-MODE Standby
					SendCmd(TNC, "\x48", 1);		// Listen Off

					return;
				}
				if (memcmp(MsgPtr, "MODE AMTOR", 11) == 0)
				{
					TNC->CurrentMode = AMTOR;
					buffptr->Len = sprintf((UCHAR *)buffptr->Data,"HAL} Ok\r");
					C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);

					return;
				}

				if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2)	// Connect
				{
					memcpy(STREAM->RemoteCall, &MsgPtr[2], 9);

					switch (TNC->CurrentMode)
					{
					case Pactor:

						SendCmd(TNC, "\x84", 1);		// FSK
						SendCmd(TNC, "\x83", 1);		// Select P-MODE Standby

						datalen = sprintf(TXMsg, "\x19%s", STREAM->RemoteCall);
					
						sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - PACTOR", STREAM->MyCall, STREAM->RemoteCall);

						// DOnt set connecting till we get the 19 response so we can trap listen as a fail
						break;

					case Clover:

						SendCmd(TNC, "\x54", 1);		// Enable adaptive Clover format
						SendCmd(TNC, "\x57", 1);		// Enable TX buffer clear on disconnect

						datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall);
					
						sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER", STREAM->MyCall, STREAM->RemoteCall);

						break;			
					}
					
					MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
					SendCmd(TNC, TXMsg, datalen + 1);	// Include NULL

					ReleaseBuffer(buffptr);

					return;
				}

				if (memcmp(MsgPtr, "CLOVER ", 7) == 0)
				{
					memcpy(STREAM->RemoteCall, &MsgPtr[2], 9);

					SendCmd(TNC, "\x54", 1);		// Enable adaptive Clover format
					SendCmd(TNC, "\x57", 1);		// Enable TX buffer clear on disconnect

					datalen = sprintf(TXMsg, "\x11%s", STREAM->RemoteCall);
					SendCmd(TNC, TXMsg, datalen + 1);	// Include NULL
					
					sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s - CLOVER",
					STREAM->MyCall, STREAM->RemoteCall);
					MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

					ReleaseBuffer(buffptr);

					return;
				}

				if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0)	// Disconnect
				{
					SendCmd(TNC, "\x07", 1);		// Normal Disconnect
					TNC->NeedPACTOR = 50;
	
					STREAM->Connecting = FALSE;
					STREAM->ReportDISC = TRUE;
					ReleaseBuffer(buffptr);

					return;
				}
	
				// Other Command ?? Treat as HEX string

				datalen = sscanf(MsgPtr, "%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X",
					(UINT *)&TXMsg[0], (UINT *)&TXMsg[1], (UINT *)&TXMsg[2], (UINT *)&TXMsg[3], (UINT *)&TXMsg[4],
					(UINT *)&TXMsg[5], (UINT *)&TXMsg[6], (UINT *)&TXMsg[7], (UINT *)&TXMsg[8], (UINT *)&TXMsg[9], 
					(UINT *)&TXMsg[10], (UINT *)&TXMsg[11], (UINT *)&TXMsg[12], (UINT *)&TXMsg[13], 
					(UINT *)&TXMsg[14], (UINT *)&TXMsg[15]);

//				SendCmd(TNC, TXMsg, datalen);
				ReleaseBuffer(buffptr);
				TNC->InternalCmd = 0;
			}
		}
	}
Poll:
	// Nothing doing - send Poll (but not too often)

	TNC->PollDelay++;

	if (TNC->PollDelay < 20)
		return;

	TNC->PollDelay = 0;

	if (TNC->TNCOK)
		SendCmd(TNC, "\x7d" , 1);			// Use Get LEDS as Poll
	else
		SendCmd(TNC, "\x09" , 1);			// Reset

	TNC->Timeout = 100;

	return;
}

static VOID DoTNCReinit(struct TNCINFO * TNC)
{
	// TNC Has Restarted, send init commands (can probably send all at once)

//	TNC->TXBuffer[0] = 0x1b;
//	TNC->TXLen = 1;

	WriteCommBlock(TNC);

	SendCmd(TNC, TNC->InitScript, TNC->InitScriptLen);

	TNC->HostMode = TRUE;		// Should now be in Host Mode
	TNC->NeedPACTOR = 20;		// Need to set Calls and start scan

	TNC->DataMode = RXDATA;		// Start with RX Data

	SendCmd(TNC, "\x7d" , 1);	// Use Get LEDS as Poll
//	SendCmd(TNC, "\xc9" , 1);	// Huffman Off
	SendCmd(TNC, "\x57", 1);	// Enable TX buffer clear on disconnect

	SendCmd(TNC, "\x60\x06", 2);	// Robust Mode Retries

//	SendCmd(TNC, "\x6f\x03" , 2);	// Undocumented XON/XOFF On - used to see if old or new style modem

	TNC->Timeout = 50;

	return;

}	

VOID ProcessHALData(struct TNCINFO * TNC)
{
	// Received Data just pass to Appl

	PMSGWITHLEN buffptr;
	int Len = TNC->DataLen;
	struct STREAMINFO * STREAM = &TNC->Streams[0];

	TNC->DataLen = 0;

	if (TNC->DataMode == TXDATA)
	{
		STREAM->BytesAcked += Len;
//		Debugprintf("Acked %d", Len);

		if (STREAM->BytesAcked > STREAM->bytesTXed)
			Debugprintf("Too Much Acked");

		if ((STREAM->BPQtoPACTOR_Q == 0) && STREAM->BytesAcked >= STREAM->bytesTXed)
		{
			// All sent 

			if (STREAM->Disconnecting)
				TidyClose(TNC, 0);
			else
				if (TNC->CurrentMode != Clover)
			
				// turn round link

					SendCmd(TNC, "\x0c" , 1);	// Turnround
			
		}
	}
	else
	{
		if (TNC->DataMode == RXDATA)
		{
//			Debugprintf("RXed %d", Len);
			buffptr = GetBuff();
			if (buffptr == NULL) 
				return;	// No buffers, so ignore

			buffptr->Len = Len;				// Length

			WriteLogLine(1, TNC->DataBuffer, Len);

			STREAM->bytesRXed += Len;

			memcpy(buffptr->Data, TNC->DataBuffer, Len);

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

	ShowTraffic(TNC);

	return;
}



VOID ProcessHALBuffer(struct TNCINFO * TNC, int Length)
{
	UCHAR Char;
	UCHAR * inptr;
	UCHAR * cmdptr;
	UCHAR * dataptr;
	BOOL CmdEsc, DataEsc;

	inptr = TNC->RXBuffer;

	cmdptr = &TNC->CmdBuffer[TNC->CmdLen];
	dataptr = &TNC->DataBuffer[TNC->DataLen];
	CmdEsc = TNC->CmdEsc;
	DataEsc = TNC->DataEsc;

	// HAL uses HEX 80 as a command escape, 81 to  ESCAPE 80 and 81

	// The USB version also uses 0x91 0x31 to eacape 0x11, 0x91 0x33 for 0x13 and 0x91 0xB1 for 0x91

	// Command Responses can be variable length

	// Command Handler will check for each command/response if it has enough - if not it will wait till more arrives

	while(Length--)
	{
		Char = *(inptr++);

		if (CmdEsc)
		{
			CmdEsc = FALSE;

			if (TNC->XONXOFF && Char == 0x91)
			{
				// XON/XOFF escape. We ensured above that data follows so we can process it inline

				Length--;
				Char = *(inptr++) - 0x20;
			}
			*(cmdptr++) = Char;
		}
		else if (DataEsc)
		{
			DataEsc = FALSE;
			goto DataChar;
		}
		else
NotData:
		if (Char == 0x80)				// Next Char is Command
			CmdEsc = TRUE;
		else if (Char == 0x81)				// Next Char is escaped data (80 or 81)
			DataEsc = TRUE;
		else
		{
			// This is a Data Char. We must process any Commands received so far, so we know the type of data

		DataChar:

			TNC->CmdLen = cmdptr - TNC->CmdBuffer;
			ProcessHALCmd(TNC);
			cmdptr = &TNC->CmdBuffer[TNC->CmdLen];
			dataptr = &TNC->DataBuffer[TNC->DataLen];

			*(dataptr++) = Char;			// Normal Data

			// Now process any other data chars

			while(Length--)
			{
				Char = *(inptr++);

				if (TNC->XONXOFF && Char == 0x91)
				{
					// XON/XOFF escape within data. We ensured above that data follows so we
					// can process it here

						Length--;
						Char = *(inptr++) - 0x20;
				}

				if (Char == 0x80 || Char == 0x81)
				{
					// Process any data we have, then loop back

					TNC->DataLen = dataptr - TNC->DataBuffer;
					ProcessHALData(TNC);

					goto NotData;
				}
				*(dataptr++) = Char;			// Normal Data
			}

			// Used all data

			TNC->DataLen = dataptr - TNC->DataBuffer;

			ProcessHALData(TNC);
			TNC->CmdEsc = CmdEsc;
			TNC->DataEsc = DataEsc;

			return;
		}	
	}

	// Save State

	TNC->CmdLen = cmdptr - TNC->CmdBuffer;

	TNC->CmdEsc = CmdEsc;
	TNC->DataEsc = DataEsc;

	if (TNC->DataLen)
		ProcessHALData(TNC);

	if (TNC->CmdLen)
		ProcessHALCmd(TNC);
}

VOID mySetWindowText(struct TNCINFO * TNC, char * Msg)
{
	MySetWindowText(TNC->xIDC_STATE, Msg);
	strcpy(TNC->WEB_STATE, Msg);
}

VOID ProcessHALCmd(struct TNCINFO * TNC)
{
	char * Call;
	int Stream = 0;
	int Opcode;
	int StatusByte;
	int Leds;
	int Len;
	int Used;
	struct STREAMINFO * STREAM = &TNC->Streams[0];

CmdLoop:

	Opcode = TNC->CmdBuffer[0];
	Len = TNC->CmdLen;

	if (Len == 0)
		return;

	TNC->TNCOK = TRUE;
	TNC->Timeout = 0;

	sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName);
	MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);

	// We may have more than one response in the buffer, and only each cmd/response decoder knows how many it needs

	switch(Opcode)
	{
	case 0x09:			//Hardware Reset - equivalent to power on reset

		// Hardware has reset - need to reinitialise

		TNC->HostMode = 0;			// Force Reinit

		Used = 1;
		break;

	case 0x7a:				// FSK Modes Status

		// Mixture of mode and state - eg listen huffman on/off irs/iss, so cant just display

		if (Len < 2) return;		// Wait for more
	
		StatusByte = TNC->CmdBuffer[1];

		switch (StatusByte)
		{
			case 0x06:		// FSK TX (RTTY)
			case 0x07:		// FSK RX (RTTY)
			case 0x10:		// AMTOR STANDBY (LISTEN ON)
			case 0x11:		// AMTOR STANDBY (LISTEN OFF)
			case 0x12:		// AMTOR FEC TX (AMTOR)
			case 0x13:		// AMTOR FEC RX (AMTOR)
			case 0x14:		// P-MODE FEC TX (P-MODE)
			case 0x15:		// FREE SIGNAL TX (AMTOR)
			case 0x16:		// FREE SIGNAL TX TIMED OUT (AMTOR)

			// Diaplay Linke Status

			MySetWindowText(TNC->xIDC_MODE, status[StatusByte]);
			strcpy(TNC->WEB_MODE, status[StatusByte]);

			break;

			case 0x0C:		// P-MODE STANDBY (LISTEN ON)
			case 0x0D:		// P-MODE STANDBY (LISTEN OFF)

			// if we were connecting, this means connect failed.

			MySetWindowText(TNC->xIDC_MODE, status[StatusByte]);
			strcpy(TNC->WEB_MODE, status[StatusByte]);

			if (STREAM->Connecting)
				HALDisconnected(TNC);

			break;

			case 0x0E:		// ISS (AMTOR/P-MODE)

				MySetWindowText(TNC->xIDC_TXRX,"ISS");
				strcpy(TNC->WEB_TXRX, "ISS");
				TNC->TXRXState = 'S';
				break;

			case 0x0F:		// IRS (AMTOR/P-MODE)

				MySetWindowText(TNC->xIDC_TXRX,"IRS");
				strcpy(TNC->WEB_TXRX, "IRS");
				TNC->TXRXState = 'R';
				break;

			case 0x00:		//  IDLE (AMTOR/P-MODE)
			case 0x01:		//  TFC (AMTOR/P-MODE)
			case 0x02:		//  RQ (AMTOR/P-MODE)
			case 0x03:		//  ERR (AMTOR/P-MODE)
			case 0x04:		//  PHS (AMTOR/P-MODE)
			case 0x05:		//  OVER (AMTOR/P-MODE) (not implemented)

				MySetWindowText(TNC->xIDC_STATE, status[StatusByte]);
				strcpy(TNC->WEB_MODE, status[StatusByte]);



//$807A $8008 P-MODE100 (P-MODE)
//$807A $8009 P-MODE200 (P-MODE)
//$807A $800A HUFFMAN ON (P-MODE)
//$807A $800B HUFFMAN OFF (P-MODE)
				;
		}
		Used = 2;
		break;
		

	case 0x7d:				// Get LED Status
		
		// We use Get LED Status as a Poll

		if (Len < 2) return;		// Wait for more
	
		Leds = TNC->CmdBuffer[1];
		sprintf(TNC->WEB_LEDS,"  %c   %c    %c    %c     %c %c ", 
			(Leds & 0x20)? 'X' : ' ',
			(Leds & 0x10)? 'X' : ' ',
			(Leds & 0x08)? 'X' : ' ',
			(Leds & 0x04)? 'X' : ' ',
			(Leds & 0x02)? 'X' : ' ',
			(Leds & 0x01)? 'X' : ' ');

//		STBY CALL LINK ERROR TX RX
		MySetWindowText(TNC->xIDC_LEDS, TNC->WEB_LEDS);

		Used = 2;
		break;

	case 0x21:				// Monitored FEC CCB
	case 0x22:				// Monitored ARQ CCB

		// As the reply is variable, make sure we have the terminating NULL

		if (memchr(TNC->CmdBuffer, 0, Len) == NULL)
			return;					// Wait for more

		Call = &TNC->CmdBuffer[1];
		Used = (int)strlen(Call) + 2;	// Opcode and Null

		UpdateMH(TNC, Call, '!', 0);

		break;

	case 0x27:						// Clover ARQ LINK REQUEST status message 
		
		//indicates an incoming link request to either MYCALL ($8027 $8000), or MYALTCALL ($8027 $8001).

		if (Len < 2) return;		// Wait for more

		// Don't need to do anything (but may eventally use ALTCALL as an APPLCALL
		Used = 2;
		break;

	case 0x2D:						// FSK ARQ Link Request status message
		
		// $802D $8001 $8000 CLOVER Link Request (not implemented)
		// $802D $8002 $8000 AMTOR CCIR-476 Link Request
		// $802D $8003 $8000 AMTOR CCIR-625 Link Request
		// $802D $8004 $8000 P-MODE Link Request

		if (Len < 3) return;		// Wait for more

		// Don't need to do anything (but may save Session type later

		Used = 3;
		break;


	case 0x28:				// Monitored Call

		// As the reply is variable, make sure we have the terminating NULL

		if (memchr(TNC->CmdBuffer, 0, Len) == NULL)
			return;					// Wait for more

		Call = &TNC->CmdBuffer[1];
		Used = strlen(Call) + 2;	// Opcode and Null

		// Could possibly be used for APPLCALLS by changing MYCALL when we see a call to one of our calls

		break;


	case 0x20:			// Clover Linked with - Call Connected
	case 0x29:			// The Linked 476 message indicates the start of a CCIR 476 linked session.
	case 0x2A:			// The Linked 625 message indicates the start of a CCIR 625 linked session to <CALL>.
	case 0x2B:			// P-MODE link to

		// As the reply is variable, make sure we have the terminating NULL

		if (memchr(TNC->CmdBuffer, 0, Len) == NULL)
			return;					// Wait for more

		Call = &TNC->CmdBuffer[1];
		Used = (int)strlen(Call) + 2;	// Opcode and Null

		HALConnected(TNC, Call);

		break;

	case 0x23:						// Normal Disconnected - followed by $8000
	case 0x24:						// Link failed (any of the link errors)
	case 0x25:						// Signal Lost (LOS)

		if (Len < 2) return;		// Wait for more

		HALDisconnected(TNC);

		Used = 2;
		break;


		// Stream Switch Reports - we will need to do something with these if Echo as Sent is set
		// or we do something with the secondary port

	case 0x30:						// Switch to Receive Data characters
	case 0x31:						// Switch to Transmit Data characters
	case 0x32:						// Switch to RX data from secondary port

		TNC->DataMode = Opcode; 
		Used = 1;
		break;

	case 0x33:						// Send TX data to modem
	case 0x34:						// Send TX data to secondary port

		TNC->TXMode = Opcode; 
		Used = 1;
		break;

	case 0x70:						// Channel Spectra Data 
									// $807F $80xx $8030 Invalid or unimplemented command code
		if (Len < 9) return;		// Wait for more

		Used = 9;
		break;
		
	case 0x71:						// SelCall On/Off

		if (Len < 2) return;		// Wait for more

		Used = 2;
		break;

	case 0x72:						// Channel Spectra Data 
									// $807F $80xx $8030 Invalid or unimplemented command code
		if (Len < 15) return;		// Wait for more

		Used = 15;
		break;

	case 0x73:						// Clover Link state

		if (Len < 2) return;		// Wait for more

		StatusByte = TNC->CmdBuffer[1];

		switch (StatusByte)
		{
		case 0x00:		mySetWindowText(TNC, "Channel idle"); break;
		case 0x01:		mySetWindowText(TNC, "Channel occupied with non-Clover signal"); break;
		case 0x42:		mySetWindowText(TNC, "Linked stations monitored"); break;
		case 0x64:		mySetWindowText(TNC, "Attempting normal link"); break;
		case 0x65:		mySetWindowText(TNC, "Attempting robust link"); break;
		case 0x66:		mySetWindowText(TNC, "Calling ARQ CQ"); break;
		case 0x78:		mySetWindowText(TNC, "Clover Control Block (CCB) send retry"); break;
		case 0x79:		mySetWindowText(TNC, "Clover Control Block (CCB) receive retry"); break;
		case 0x7D:		mySetWindowText(TNC, "Clover Control Block (CCB) received successfully"); break;
		case 0x8A:		mySetWindowText(TNC, "TX data block sent"); break;
		case 0x8B:		mySetWindowText(TNC, "RX data block received ok (precedes data block)"); break;
		case 0x8C:		mySetWindowText(TNC, "TX data block re-sent"); break;
		case 0x8D:		mySetWindowText(TNC, "RX data block decode failed (precedes data block)"); break;
		case 0x8E:		mySetWindowText(TNC, "TX idle"); break;
		case 0x8F:		mySetWindowText(TNC, "RX idle"); break;
		case 0x9C:		mySetWindowText(TNC, "Link failed: CCB send retries exceeded"); break;
		case 0x9D:		mySetWindowText(TNC, "Link failed: CCB receive retries exceeded"); break;
		case 0x9E:		mySetWindowText(TNC, "Link failed: protocol error"); break;
		case 0xA0:		mySetWindowText(TNC, "Receiving FEC SYNC sequence"); break;
		}

		Used = 2;
		break;

	case 0x75:		// Clover waveform format

		if (Len < 5) return;		// Wait for more

		Used = 5;
		break;

	case 0x7F:						// Error $80xx $80yy Error in command $80xx of type $80yy
									// $807F $80xx $8030 Invalid or unimplemented command code
									// $807F $80xx $8031 Invalid parameter value
									// $807F $80xx $8032 Not allowed when connected
									// $807F $80xx $8033 Not allowed when disconnected
									// $807F $80xx $8034 Not valid in this mode
									// $807F $80xx $8035 Not valid in this code
									// $807F $8096 $8036 EEPROM write error

		if (Len < 3) return;		// Wait for more

		if (TNC->CmdBuffer[1] == 0x6f && TNC->CmdBuffer[2] == 0x31)
		{
			// Reject of XON/XOFF enable

//			TNC->XONXOFF = FALSE;
//			Debugprintf("BPQ32 HAL Port %d - Disabling XON/XOFF mode", TNC->Port);
		}
		else
			Debugprintf("HAL Port %d Command Error Cmd %X Error %X", TNC->Port, TNC->CmdBuffer[1], TNC->CmdBuffer[2]);

		Used = 3;
		break;

		// Following are all immediate commands - response is echo of command

	case 0x6f:				// XON/XOFF on
		
//		TNC->XONXOFF = TRUE;	// And drop through
//		Debugprintf("BPQ32 HAL Port %d - Enabling XON/XOFF mode", TNC->Port);

	case 0x19:			// Call P-MODE to <CALL>
	case 0x10:			// Robust Link to <CALL> using MYCALL
	case 0x11:			// Normal Link to <CALL> using MYCALL

		STREAM->Connecting = TRUE;

	case 0x00:			// P Load LOD file
	case 0x01:			// P Load S28 file
	case 0x02:			//Check Unit Error Status
	case 0x03:			//F Check System Clock
	case 0x04:			//C Close PTT and transmit Clover waveform
	case 0x05:			//Open PTT and stop transmit test
	case 0x06:			//Immediate Abort (Panic Kill)
	case 0x07:			//Normal disconnect (wait for ACK)
	case 0x08:			//Software reset - restore all program defaults
	case 0x0A:			//Send CW ID
	case 0x0B:			//Close PTT and transmit Single Tone
	case 0x0C:			//F Normal OVER (AMTOR,P-MODE)
	case 0x0D:			//F Force RTTY TX (Baudot/ASCII)
	case 0x0E:			//F Go to RTTY RX (Baudot/ASCII)
	case 0x0F:			//Go to LOD/S28 file loader
	case SetMYCALL:		// Set MYCALL Response

	case 0x1E:			// Set MYALTCALL Response

	case 0x41:
	case 0x42:
	case 0x46:
	case 0x47:
	case 0x48:
	case 0x4d:
	case 0x52:			// Enable adaptive Clover format
	case 0x54:			// Enable adaptive Clover format

	case 0x56:			// Expanded Link State Reports OFF/ON
	case 0x57:			// Clear buffers on disc
	case 0x58:
	case 0x59:
	case 0x60:			// Robust Mode Retries
	case 0x61:			// Normal Mode Retries
	case 0x80:			//Switch to CLOVER mode
	case 0x81:			//Select AMTOR Standby
	case 0x82:			//Select AMTOR FEC
	case 0x83:			//Select P-MODE Standby
	case 0x84:			//Switch to FSK modes
	case 0x85:			//Select Baudot
	case 0x86:			//Select ASCII
	case 0x87:			//Forced OVER (AMTOR, P-MODE)
	case 0x88:			//Forced END (AMTOR, P-MODE)
	case 0x89:			//Force LTRS shift
	case 0x8A:			//Force FIGS shift
	case 0x8B:			//Send MARK tone
	case 0x8C:			//Send SPACE tone
	case 0x8D:			//Send MARK/SPACE tones
	case 0x8E:			//Received first character on line
	case 0x8F:			//Close PTT only (no tones)

	case 0xC9:			//Huffman Off/On
	case 0xCC:
	case 0xD9:			//Close PTT only (no tones)

	case SetTones:

		Used = 1;
		break;

	case 0x91:				// ????

//		if (Len < 2) return;		// Wait for more

		Used = 1;
		break;

	default:

		// We didn't recognise command, so don't know how long it is - disaster!

		Debugprintf("HAL Port %d Unrecognised Command %x", TNC->Port, Opcode);
		TNC->CmdLen = 0;

		return;
	}

	if (Used == Len)
	{
		// All used - most likely case

		TNC->CmdLen = 0;
		return;
	}

	// Move Command Down buffer, and reenter

	TNC->CmdLen -= Used;

	memmove(TNC->CmdBuffer, &TNC->CmdBuffer[Used], TNC->CmdLen);

	goto CmdLoop;


}

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

	CloseLogfile(0);
	CloseLogfile(1);
	CloseLogfile(2);

	if ((STREAM->Connecting | STREAM->Connected) == 0)
	{
		// Not connected or Connecting. Probably response to going into Pactor Listen Mode

		return;
	}

	if (STREAM->Connecting && STREAM->Disconnecting == FALSE)
	{
		PMSGWITHLEN buffptr;

		// Connect Failed - actually I think HAL uses another code for connect failed, but leave here for now
			
		buffptr = GetBuff();
	
		if (buffptr)
		{
			buffptr->Len  = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall);

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

		STREAM->Connecting = FALSE;
		STREAM->Connected = FALSE;				// In case!
		STREAM->FramesQueued = 0;

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

		return;
	}

	// Connected, or Disconnecting - Release Session

	STREAM->Connecting = FALSE;
	STREAM->Connected = FALSE;		// Back to Command Mode
	STREAM->FramesQueued = 0;

	if (STREAM->Disconnecting == FALSE)
		STREAM->ReportDISC = TRUE;		// Tell Node

	STREAM->Disconnecting = FALSE;

	// Need to reset Pactor Call in case it was changed

	TNC->NeedPACTOR = 20;
}

BOOL HALConnected(struct TNCINFO * TNC, char * Call)
{
	char Msg[80];
	PMSGWITHLEN buffptr;
	struct STREAMINFO * STREAM = &TNC->Streams[0];
	char CallCopy[80];

	strcpy(CallCopy, Call);
	strcat(CallCopy, "          ");			// Some routines expect 10 char calls

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

	// Stop Scanner

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

	ShowTraffic(TNC);

	TNC->DataMode = RXDATA; 

	OpenLogfile(0);
	OpenLogfile(1);
	OpenLogfile(2);

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

		ProcessIncommingConnect(TNC, CallCopy, 0, TRUE);
					
		sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", STREAM->RemoteCall, TNC->NodeCall);
		MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

		if (TNC->CurrentMode != Clover)
			SendCmd(TNC, "\x87", 1);		// Changeover to ISS 

		// If an autoconnect APPL is defined, send it

		if (TNC->ApplCmd)
		{
			buffptr = GetBuff();
			if (buffptr == 0) return TRUE;			// No buffers, so ignore

			buffptr->Len = sprintf((UCHAR *)buffptr->Data, "%s\r", TNC->ApplCmd);
			C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
			TNC->SwallowSignon = TRUE;

			return TRUE;
		}

		if (FULL_CTEXT && HFCTEXTLEN == 0)
		{
			EncodeAndSend(TNC, CTEXTMSG, CTEXTLEN);
			WriteLogLine(2, CTEXTMSG, CTEXTLEN);

			STREAM->bytesTXed += CTEXTLEN;
		}
		return TRUE;
	}

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

	buffptr->Len  = sprintf((UCHAR *)buffptr->Data, "*** Connected to %s\r", Call);;

	C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
	
	STREAM->Connecting = FALSE;
	STREAM->Connected = TRUE;			// Subsequent data to data channel

	sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->NodeCall, STREAM->RemoteCall);
	MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);

	UpdateMH(TNC, CallCopy, '+', 'O');


	return TRUE;
}	


static VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len)
{
	// Send A Packet With DLE Encoding Encoding

	TNC->TXLen = DLEEncode(txbuffer, TNC->TXBuffer, Len);

	WriteCommBlock(TNC);
}

VOID SendCmd(struct TNCINFO * TNC, UCHAR * txbuffer, int Len)
{
	// Send A Packet With Command Encoding (preceed each with 0x80

	int i,txptr=0;
	UCHAR * outbuff = TNC->TXBuffer;

	for (i=0; i<Len; i++)
	{
		outbuff[txptr++] = 0x80;
		outbuff[txptr++] = txbuffer[i];
	}

	TNC->TXLen = txptr;
	WriteCommBlock(TNC);
}

int	DLEEncode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
	int i, txptr = 0;
	UCHAR c;

	// Escape x80 and x81 with x81

//	outbuff[0] = 0x80;
//	outbuff[1] = 0x33;		// Send data to modem
	
	for (i=0;i<len;i++)
	{
		c=inbuff[i];
		
		switch (c)
		{
		case 0x80:
		case 0x81:

			outbuff[txptr++] = 0x81;
		}
		outbuff[txptr++] = c;
	}

	return txptr;

}

VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
		SendCmd(TNC, "\x07", 1);		// Tidy Disconnect
}

VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
		SendCmd(TNC, "\x06", 1);		// Hard Disconnect
}

VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
		TNC->NeedPACTOR = 30;	
}




