/*
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, ""
"
HAL StatusHAL Status
");
Len += sprintf(&Buff[Len], "");
Len += sprintf(&Buff[Len], "Comms State | %s |
", TNC->WEB_COMMSSTATE);
Len += sprintf(&Buff[Len], "TNC State | %s |
", TNC->WEB_TNCSTATE);
Len += sprintf(&Buff[Len], "Mode | %s |
", TNC->WEB_MODE);
Len += sprintf(&Buff[Len], "Status | %s |
", TNC->WEB_STATE);
Len += sprintf(&Buff[Len], "TX/RX State | %s |
", TNC->WEB_TXRX);
Len += sprintf(&Buff[Len], "Traffic | %s |
", TNC->WEB_TRAFFIC);
Len += sprintf(&Buff[Len], "LEDS | STBY CALL LINK ERROR TX RX |
");
Len += sprintf(&Buff[Len], " | %s |
", TNC->WEB_LEDS);
Len += sprintf(&Buff[Len], "
");
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[80];
#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->Hardware = H_HAL;
if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
PortEntry->MAXHOSTMODESESSIONS = 1; // Default
TNC->PortRecord = PortEntry;
if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
{
memcpy(TNC->NodeCall, MYNODECALL, 10);
}
else
{
ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
}
PortEntry->PORTCONTROL.PROTOCOL = 10;
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(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_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 .
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
case 0x10: // Robust Link to using MYCALL
case 0x11: // Normal Link to 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; iTXLen = 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;iNeedPACTOR = 30;
}