linbpq/FLDigi.c

3962 lines
88 KiB
C

/*
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
*/
//
// FLARQ Emulator/FLDIGI Interface for BPQ32
//
#define _CRT_SECURE_NO_DEPRECATE
#include "CHeaders.h"
extern int (WINAPI FAR *GetModuleFileNameExPtr)();
extern int (WINAPI FAR *EnumProcessesPtr)();
#include <stdio.h>
#include <time.h>
#include "tncinfo.h"
#include "bpq32.h"
#define VERSION_MAJOR 2
#define VERSION_MINOR 0
#define SD_RECEIVE 0x00
#define SD_SEND 0x01
#define SD_BOTH 0x02
#define DLE 0x10
#define SOH 1
#define STX 2
#define EOT 4
#define FEND 0xC0
#define FESC 0xDB
#define TFEND 0xDC
#define TFESC 0xDD
#define TIMESTAMP 352
#define CONTIMEOUT 1200
#define AGWHDDRLEN sizeof(struct AGWHEADER)
extern int (WINAPI FAR *GetModuleFileNameExPtr)();
//int ResetExtDriver(int num);
;
int SemHeldByAPI;
static void ConnecttoFLDigiThread(void * portptr);
void CreateMHWindow();
int Update_MH_List(struct in_addr ipad, char * call, char proto);
static int ConnecttoFLDigi();
static int ProcessReceivedData(int bpqport);
static int ProcessLine(char * buf, int Port);
int KillTNC(struct TNCINFO * TNC);
static int RestartTNC(struct TNCINFO * TNC);
VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len);
VOID ProcessFLDigiKISSPacket(struct TNCINFO * TNC, char * Message, int Len);
struct TNCINFO * GetSessionKey(char * key, struct TNCINFO * TNC);
VOID SendARQData(struct TNCINFO * TNC, PMSGWITHLEN Buffer);
static VOID DoMonitorHddr(struct TNCINFO * TNC, struct AGWHEADER * RXHeader, UCHAR * Msg);
VOID SendRPBeacon(struct TNCINFO * TNC);
VOID FLReleaseTNC(struct TNCINFO * TNC);
unsigned int CalcCRC(UCHAR * ptr, int Len);
VOID ARQTimer(struct TNCINFO * TNC);
VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen);
VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen);
VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char *Input);
VOID SendXMLPoll(struct TNCINFO * TNC);
static int ProcessXMLData(int port);
VOID CheckFLDigiData(struct TNCINFO * TNC);
VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen);
int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType);
VOID FLSlowTimer(struct TNCINFO * TNC);
VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg);
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
char * strlop(char * buf, char delim);
extern UCHAR BPQDirectory[];
#define MAXMPSKPORTS 16
//LOGFONT LFTTYFONT ;
//HFONT hFont ;
static int MPSKChannel[MAXBPQPORTS+1]; // BPQ Port to MPSK Port
static int BPQPort[MAXMPSKPORTS][MAXBPQPORTS+1]; // MPSK Port and Connection to BPQ Port
//static int MPSKtoBPQ_Q[MAXBPQPORTS+1]; // Frames for BPQ, indexed by BPQ Port
//static int BPQtoMPSK_Q[MAXBPQPORTS+1]; // Frames for MPSK. indexed by MPSK port. Only used it TCP session is blocked
// Each port may be on a different machine. We only open one connection to each MPSK instance
static char * MPSKSignon[MAXBPQPORTS+1]; // Pointer to message for secure signin
static unsigned int MPSKInst = 0;
static int AttachedProcesses=0;
static HWND hResWnd,hMHWnd;
static BOOL GotMsg;
static HANDLE STDOUT=0;
//SOCKET sock;
static SOCKADDR_IN sinx;
static SOCKADDR_IN rxaddr;
static SOCKADDR_IN destaddr[MAXBPQPORTS+1];
static int addrlen=sizeof(sinx);
//static short MPSKPort=0;
static time_t ltime,lasttime[MAXBPQPORTS+1];
static BOOL CONNECTING[MAXBPQPORTS+1];
static BOOL CONNECTED[MAXBPQPORTS+1];
//HANDLE hInstance;
static char WindowTitle[] = "FLDIGI";
static char ClassName[] = "FLDIGISTATUS";
static int RigControlRow = 165;
static fd_set readfs;
static fd_set writefs;
static fd_set errorfs;
static struct timeval timeout;
int Blocksizes[10] = {0,2,4,8,16,32,64,128,256,512};
static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
PMSGWITHLEN buffptr;
char txbuff[500];
unsigned int txlen=0;
struct TNCINFO * TNC = TNCInfo[port];
int Stream = 0;
struct STREAMINFO * STREAM;
int TNCOK;
size_t Param;
if (TNC == NULL)
return 0; // Port not defined
// Look for attach on any call
// for (Stream = 0; Stream <= 1; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0)
{
char Cmd[80];
// New Attach
int calllen;
STREAM->Attached = TRUE;
TNC->FLInfo->RAW = FALSE;
calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall);
STREAM->MyCall[calllen] = 0;
STREAM->FramesOutstanding = 0;
SuspendOtherPorts(TNC); // Dont allow connects on interlocked ports
// Stop Scanning
sprintf(Cmd, "%d SCANSTOP", TNC->Port);
Rig_Command( (TRANSPORTENTRY *) -1, Cmd);
sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
/* len = sprintf(Cmd, "%cSTOP_BEACON_ARQ_FAE\x1b", '\x1a');
if (TNC->MPSKInfo->TX)
TNC->CmdSet = TNC->CmdSave = _strdup(Cmd); // Savde till not transmitting
else
SendPacket(TNC->TCPDataSock, Cmd, len, 0);
*/
}
}
switch (fn)
{
case 7:
// 100 mS Timer.
// 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;
// See if waiting for busy to clear before sending a connect
if (TNC->BusyDelay)
{
// Still Busy?
if (InterlockedCheckBusy(TNC) == FALSE)
{
// No, so send connect
struct ARQINFO * ARQ = TNC->ARQInfo;
int SendLen;
char Reply[80];
SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10",
STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream);
strcpy(TNC->WEB_PROTOSTATE, "Connecting");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
ARQ->ARQState = ARQ_ACTIVE;
ARQ->ARQTimerState = ARQ_CONNECTING;
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
STREAM->Connecting = TRUE;
sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
strcpy(TNC->WEB_PROTOSTATE, "Connecting");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
TNC->BusyDelay = 0;
}
else
{
// Wait Longer
TNC->BusyDelay--;
if (TNC->BusyDelay == 0)
{
// Timed out - Send Error Response
PMSGWITHLEN buffptr = GetBuff();
if (buffptr == 0) return (0); // No buffers, so ignore
buffptr->Len=39;
memcpy(buffptr+2,"Sorry, Can't Connect - Channel is busy\r", 39);
C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
free(TNC->ConnectCmd);
sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
}
}
}
if (STREAM->NeedDisc)
{
STREAM->NeedDisc--;
if (STREAM->NeedDisc == 0)
{
// Send the DISCONNECT
TidyClose(TNC, 0);
}
}
ARQTimer(TNC);
SendXMLPoll(TNC);
TNC->SlowTimer--;
if (TNC->SlowTimer < 0)
{
TNC->SlowTimer = 100;
FLSlowTimer(TNC); // 10 Secs
}
return 0;
case 1: // poll
if (TNC->CONNECTED == FALSE && TNC->CONNECTING == FALSE && TNC->FLInfo->KISSMODE == FALSE)
{
// See if time to reconnect
time( &ltime );
if (ltime-lasttime[port] >9 )
{
ConnecttoFLDigi(port);
lasttime[port]=ltime;
}
}
pollloop:
FD_ZERO(&readfs);
if (TNC->CONNECTED)
if (TNC->TCPSock)
FD_SET(TNC->TCPSock,&readfs);
if (TNC->CONNECTED || TNC->FLInfo->KISSMODE)
FD_SET(TNC->TCPDataSock,&readfs);
// FD_ZERO(&writefs);
// if (TNC->BPQtoWINMOR_Q) FD_SET(TNC->TCPDataSock,&writefs); // Need notification of busy clearing
FD_ZERO(&errorfs);
if (TNC->CONNECTED)
if (TNC->TCPSock)
FD_SET(TNC->TCPSock,&errorfs);
if (TNC->CONNECTED || TNC->FLInfo->KISSMODE)
FD_SET(TNC->TCPDataSock,&errorfs);
if (select((int)TNC->TCPDataSock + 1, &readfs, &writefs, &errorfs, &timeout) > 0)
{
// See what happened
if (FD_ISSET(TNC->TCPDataSock,&readfs))
{
// data available
ProcessReceivedData(port);
goto pollloop;
}
if (FD_ISSET(TNC->TCPSock,&readfs))
{
// data available
ProcessXMLData(port);
}
if (FD_ISSET(TNC->TCPDataSock,&writefs))
{
// Connect success
TNC->CONNECTED = TRUE;
TNC->CONNECTING = FALSE;
sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI");
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
// If required, send signon
// SendPacket(TNC->TCPDataSock,"\x1a", 1, 0);
// SendPacket(TNC->TCPDataSock,"DIGITAL MODE ?", 14, 0);
// SendPacket(TNC->TCPDataSock,"\x1b", 1, 0);
// EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC);
}
if (FD_ISSET(TNC->TCPDataSock,&errorfs) || FD_ISSET(TNC->TCPSock,&errorfs))
{
// if connecting, then failed, if connected then has just disconnected
// if (CONNECTED[port])
// if (!CONNECTING[port])
// {
// i=sprintf(ErrMsg, "MPSK Connection lost for BPQ Port %d\r\n", port);
// WritetoConsole(ErrMsg);
// }
CONNECTING[port]=FALSE;
CONNECTED[port]=FALSE;
}
}
// See if any frames for this port
for (Stream = 0; Stream <= 1; Stream++)
{
STREAM = &TNC->Streams[Stream];
if (STREAM->Attached)
CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);
if (STREAM->ReportDISC)
{
STREAM->ReportDISC = FALSE;
buff->PORT = Stream;
return -1;
}
// if Busy, send buffer status poll
if (STREAM->PACTORtoBPQ_Q == 0)
{
if (STREAM->DiscWhenAllSent)
{
STREAM->DiscWhenAllSent--;
if (STREAM->DiscWhenAllSent == 0)
STREAM->ReportDISC = TRUE; // Dont want to leave session attached. Causes too much confusion
}
}
else
{
int datalen;
buffptr=Q_REM(&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 += (MSGHDDRLEN + 1);
PutLengthinBuffer(buff, datalen);
WritetoTrace(TNC, &buffptr->Data[0], datalen - (MSGHDDRLEN + 1));
ReleaseBuffer(buffptr);
return (1);
}
}
if (TNC->PortRecord->UI_Q)
{
struct _MESSAGE * buffptr;
int SendLen;
char Reply[256];
int UILen;
char * UIMsg;
buffptr = Q_REM(&TNC->PortRecord->UI_Q);
UILen = buffptr->LENGTH;
UILen -= 23;
UIMsg = buffptr->L2DATA;
UIMsg[UILen] = 0;
if (UILen < 129 && TNC->Streams[0].Attached == FALSE) // Be sensible!
{
// >00uG8BPQ:72 TestA
SendLen = sprintf(Reply, "u%s:72 %s", TNC->NodeCall, UIMsg);
SendPacket(TNC, Reply, SendLen);
}
ReleaseBuffer(buffptr);
}
return (0);
case 2: // send
if (!TNC->CONNECTED) return 0; // Don't try if not connected to TNC
Stream = buff->PORT;
STREAM = &TNC->Streams[Stream];
// txlen=(buff[6]<<8) + buff[5] - 8;
txlen = GetLengthfromBuffer((PDATAMESSAGE)buff) - (MSGHDDRLEN + 1); // 1 as no PID;
if (STREAM->Connected)
{
buffptr = GetBuff();
if (buffptr == 0) return (0); // No buffers, so ignore
buffptr->Len = txlen;
memcpy(buffptr->Data, buff->L2DATA, txlen);
WritetoTrace(TNC, buffptr->Data, txlen);
C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);
return (0);
}
else
{
buff->L2DATA[txlen] = 0;
_strupr(&buff->L2DATA[0]);
if (_memicmp(&buff->L2DATA[0], "D\r", 2) == 0)
{
if (STREAM->Connected)
TidyClose(TNC, buff->PORT);
STREAM->ReportDISC = TRUE; // Tell Node
TNC->FLInfo->MCASTMODE = FALSE;
return 0;
}
// See if Local command (eg RADIO)
if (_memicmp(&buff->L2DATA[0], "RADIO ", 6) == 0)
{
sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &buff->L2DATA[6]);
if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0]))
{
}
else
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr == 0) return 1; // No buffers, so ignore
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]);
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
}
return 1;
}
if (_memicmp(&buff->L2DATA[0], "MODEM ", 6) == 0)
{
buff->L2DATA[txlen -1] = 0;
_strupr(&buff->L2DATA[0]);
// If in KISS mode, send as a KISS command Frame
if (TNC->FLInfo->KISSMODE)
{
sprintf(txbuff, "MODEM:%s MODEM:", &buff->L2DATA[6]);
SendKISSCommand(TNC, txbuff);
}
else
{
SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[6], 'S');
}
TNC->InternalCmd = TRUE;
return 1;
}
if (_memicmp(buff->L2DATA, "FREQ ", 5) == 0)
{
buff->L2DATA[txlen - 1] = 0;
_strupr(&buff->L2DATA[0]);
// If in KISS mode, send as a KISS command Frame
if (TNC->FLInfo->KISSMODE)
{
sprintf(txbuff, "WFF:%s WFF:", &buff->L2DATA[5]);
SendKISSCommand(TNC, txbuff);
}
else
{
SendXMLCommand(TNC, "modem.set_carrier", (char *)atoi(&buff->L2DATA[5]), 'I');
}
TNC->InternalCmd = TRUE;
return 1;
}
if (_memicmp(buff->L2DATA, "SQUELCH ", 8) == 0)
{
buff->L2DATA[txlen - 1] = 0;
_strupr(&buff->L2DATA[0]);
// Only works in KISS
if (TNC->FLInfo->KISSMODE)
{
if (_memicmp(&buff->L2DATA[8], "ON", 2) == 0)
sprintf(txbuff, "KPSQL:ON KPSQL:");
else if (_memicmp(&buff->L2DATA[8], "OFF", 3) == 0)
sprintf(txbuff, "KPSQL:OFF KPSQL:");
else
txlen = sprintf(txbuff, "KPSQLS:%s KPSQLS:", &buff->L2DATA[8]);
SendKISSCommand(TNC, txbuff);
TNC->InternalCmd = TRUE;
}
return 1;
}
if (_memicmp(buff->L2DATA, "KPSATT ", 7) == 0)
{
buff->L2DATA[txlen - 1] = 0;
_strupr(&buff->L2DATA[0]);
// If in KISS mode, send as a KISS command Frame
if (TNC->FLInfo->KISSMODE)
{
sprintf(txbuff, "KPSATT:%s KPSATT:", &buff->L2DATA[7]);
SendKISSCommand(TNC, txbuff);
TNC->InternalCmd = TRUE;
}
return 1;
}
if (STREAM->Connecting && _memicmp(buff->L2DATA, "ABORT", 5) == 0)
{
// len = sprintf(Command,"%cSTOP_SELECTIVE_CALL_ARQ_FAE\x1b", '\x1a');
// if (TNC->MPSKInfo->TX)
// TNC->CmdSet = TNC->CmdSave = _strdup(Command); // Save till not transmitting
// else
// SendPacket(TNC->TCPDataSock, Command, len, 0);
// TNC->InternalCmd = TRUE;
return (0);
}
if (_memicmp(&buff->L2DATA[0], "MODE", 4) == 0)
{
PMSGWITHLEN buffptr = GetBuff();
buff->L2DATA[txlen - 1] = 0; // Remove CR
if (strstr(&buff->L2DATA[0], "RAW"))
TNC->FLInfo->RAW = TRUE;
else if (strstr(&buff->L2DATA[0], "KISS"))
TNC->FLInfo->RAW = FALSE;
else
{
buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Error - Invalid Mode\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return 1;
}
buffptr->Len = sprintf(&buffptr->Data[0], "FLDigi} Ok - Mode is %s\r",
(TNC->FLInfo->RAW)?"RAW":"KISS");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return 1;
}
if (_memicmp(&buff->L2DATA[0], "MCAST", 5) == 0)
{
PMSGWITHLEN buffptr = GetBuff();
TNC->FLInfo->MCASTMODE = TRUE;
buffptr->Len = sprintf(buffptr->Data, "FLDigi} Ok\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return 1;
}
if (_memicmp(&buff->L2DATA[0], "INUSE?", 6) == 0)
{
// Return Error if in use, OK if not
PMSGWITHLEN buffptr = GetBuff();
int s = 0;
while(s <= 1)
{
if (s != Stream)
{
if (TNC->PortRecord->ATTACHEDSESSIONS[s])
{
buffptr->Len = sprintf(buffptr->Data, "FLDig} Error - In use\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return 1; // Busy
}
}
s++;
}
buffptr->Len = sprintf(buffptr->Data, "FLDigi} Ok - Not in use\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
return 1;
}
// See if a Connect Command.
if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect
{
char * ptr;
char * context;
struct ARQINFO * ARQ = TNC->ARQInfo;
int SendLen;
char Reply[80];
buff->L2DATA[txlen] = 0;
_strupr(&buff->L2DATA[0]);
memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State
ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent
ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received
ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel
ARQ->FarStream = 48; // Not yet defined
TNC->FLInfo->FLARQ = FALSE;
memset(STREAM->RemoteCall, 0, 10);
ptr = strtok_s(&buff->L2DATA[2], " ,\r", &context);
if (ptr == 0)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr)
{
buffptr->Len = sprintf(buffptr->Data,
"FLDigi} Error - Call missing from C command\r");
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
STREAM->DiscWhenAllSent = 10;
return 0;
}
strcpy(STREAM->RemoteCall, ptr);
// See if Busy
if (InterlockedCheckBusy(TNC))
{
// Channel Busy. Unless override set, wait
if (TNC->OverrideBusy == 0)
{
// Save Command, and wait up to 10 secs
sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel");
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
TNC->BusyDelay = TNC->BusyWait * 10; // BusyWait secs
return 0;
}
}
TNC->OverrideBusy = FALSE;
//<SOH>00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36<EOT>
SendLen = sprintf(Reply, "c%s:42 %s:24 %c 7 T60R5W10",
STREAM->MyCall, STREAM->RemoteCall, ARQ->OurStream);
strcpy(TNC->WEB_PROTOSTATE, "Connecting");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
ARQ->ARQState = ARQ_ACTIVE;
ARQ->ARQTimerState = ARQ_CONNECTING;
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
Debugprintf("FLDIGI Connection %s", Reply);
STREAM->Connecting = TRUE;
sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", STREAM->MyCall, STREAM->RemoteCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
strcpy(TNC->WEB_PROTOSTATE, "Connecting");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
return 0;
}
// Send any other command to FLDIGI
buff->L2DATA[txlen - 1] = 0;
_strupr(&buff->L2DATA[0]);
// If in KISS mode, send as a KISS command Frame
if (TNC->FLInfo->KISSMODE)
{
char outbuff[1000];
int newlen;
buff->L2DATA[-1] = 6; // KISS Control
newlen = KissEncode(&buff->L2DATA[-1], outbuff, txlen);
sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr));
}
else
{
SendXMLCommand(TNC, "modem.set_by_name", &buff->L2DATA[0], 'S');
}
TNC->InternalCmd = TRUE;
}
return (0);
case 3:
Stream = (int)(size_t)buff;
TNCOK = TNC->CONNECTED;
STREAM = &TNC->Streams[Stream];
{
// Busy if TX Window reached
struct ARQINFO * ARQ = TNC->ARQInfo;
int Outstanding;
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&TNC->Streams[0].BPQtoPACTOR_Q); // Save for Appl Level Queued Frames
if (Outstanding > ARQ->TXWindow)
return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); // 3rd Nibble is frames unacked
else
return TNCOK << 8 | STREAM->Disconnecting << 15;
}
return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting
case 4: // reinit
shutdown(TNC->TCPSock, SD_BOTH);
shutdown(TNC->TCPDataSock, SD_BOTH);
Sleep(100);
closesocket(TNC->TCPSock);
closesocket(TNC->TCPDataSock);
TNC->CONNECTED = FALSE;
if (TNC->WeStartedTNC)
{
KillTNC(TNC);
RestartTNC(TNC);
}
return (0);
case 5: // Close
shutdown(TNC->TCPSock, SD_BOTH);
shutdown(TNC->TCPDataSock, SD_BOTH);
Sleep(100);
closesocket(TNC->TCPSock);
closesocket(TNC->TCPDataSock);
if (TNC->WeStartedTNC)
{
KillTNC(TNC);
}
return 0;
case 6: // Scan Stop Interface
Param = (size_t)buff;
if (Param == 2) // Check Permission (shouldn't happen)
{
Debugprintf("Scan Check Permission called on FLDIGI");
return 1; // OK to change
}
if (!TNC->CONNECTED)
return 0; // No connection so no interlock
if (Param == 1) // Request Permission
{
if (TNC->ConnectPending == 0)
{
TNC->FLInfo->CONOK = FALSE;
TNC->GavePermission = TRUE;
return 0; // OK to Change
}
if (TNC->ConnectPending)
TNC->ConnectPending--; // Time out if set too long
if (!TNC->ConnectPending)
return 0; // OK to Change
return TRUE;
}
if (Param == 3) // Release Permission
{
if (TNC->GavePermission)
{
TNC->FLInfo->CONOK = TRUE;
TNC->GavePermission = FALSE;
}
return 0;
}
}
return 0;
}
#ifndef LINBPQ
int FindFLDIGI(char * Path)
{
HANDLE hProc;
char ExeName[256] = "";
char FLDIGIName[256];
DWORD Pid = 0;
DWORD Processes[1024], Needed, Count;
unsigned int i;
if (EnumProcessesPtr == NULL)
return 0; // Cant get PID
if (!EnumProcessesPtr(Processes, sizeof(Processes), &Needed))
return TRUE;
// Path is to .bat, so need to strip extension of both names
strcpy(FLDIGIName, Path);
strlop(FLDIGIName, '.');
// Calculate how many process identifiers were returned.
Count = Needed / sizeof(DWORD);
for (i = 0; i < Count; i++)
{
if (Processes[i] != 0)
{
hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, Processes[i]);
if (hProc)
{
GetModuleFileNameExPtr(hProc, 0, ExeName, 255);
CloseHandle(hProc);
strlop(ExeName, '.');
if (_stricmp(ExeName, FLDIGIName) == 0)
return Processes[i];
}
}
}
return 0;
}
static KillTNC(struct TNCINFO * TNC)
{
HANDLE hProc;
if (TNC->PTTMode)
Rig_PTT(TNC, FALSE); // Make sure PTT is down
if (TNC->ProgramPath)
TNC->PID = FindFLDIGI(TNC->ProgramPath);
if (TNC->PID == 0) return 0;
hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, TNC->PID);
if (hProc)
{
TerminateProcess(hProc, 0);
CloseHandle(hProc);
}
TNC->WeStartedTNC = 0; // So we don't try again
return 0;
}
#endif
static int RestartTNC(struct TNCINFO * TNC)
{
if (TNC->ProgramPath == NULL)
return 0;
_strlwr(TNC->ProgramPath);
if (_memicmp(TNC->ProgramPath, "REMOTE:", 7) == 0)
{
int n;
// Try to start TNC on a remote host
SOCKET sock = socket(AF_INET,SOCK_DGRAM,0);
struct sockaddr_in destaddr;
Debugprintf("trying to restart FLDIGI %s", TNC->ProgramPath);
if (sock == INVALID_SOCKET)
return 0;
destaddr.sin_family = AF_INET;
destaddr.sin_addr.s_addr = inet_addr(TNC->HostName);
destaddr.sin_port = htons(8500);
if (destaddr.sin_addr.s_addr == INADDR_NONE)
{
// Resolve name to address
struct hostent * HostEnt = gethostbyname (TNC->HostName);
if (!HostEnt)
return 0; // Resolve failed
memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
}
n = sendto(sock, TNC->ProgramPath, (int)strlen(TNC->ProgramPath), 0, (struct sockaddr *)&destaddr, sizeof(destaddr));
Debugprintf("Restart FLDIGI - sento returned %d", n);
Sleep(100);
closesocket(sock);
return 1; // Cant tell if it worked, but assume ok
}
#ifndef LINBPQ
{
STARTUPINFO SInfo; // pointer to STARTUPINFO
PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION
char HomeDir[MAX_PATH];
int i, ret;
SInfo.cb=sizeof(SInfo);
SInfo.lpReserved=NULL;
SInfo.lpDesktop=NULL;
SInfo.lpTitle=NULL;
SInfo.dwFlags=0;
SInfo.cbReserved2=0;
SInfo.lpReserved2=NULL;
if (TNC->ProgramPath)
{
strcpy(HomeDir, TNC->ProgramPath);
i = strlen(HomeDir);
while(--i)
{
if (HomeDir[i] == '/' || HomeDir[i] == '\\')
{
HomeDir[i] = 0;
break;
}
}
// for some reason the program name must be lower case
_strlwr(TNC->ProgramPath);
ret = CreateProcess(TNC->ProgramPath, NULL, NULL, NULL, FALSE,0 ,NULL , NULL, &SInfo, &PInfo);
return ret;
}
}
#endif
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>"
"<script type=\"text/javascript\">\r\n"
"function ScrollOutput()\r\n"
"{var textarea = document.getElementById('textarea');"
"textarea.scrollTop = textarea.scrollHeight;}</script>"
"</head><title>FLDigi Status</title></head><body id=Text onload=\"ScrollOutput()\">"
"<h2>FLDIGI Status</h2>");
Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 500px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");
Len += sprintf(&Buff[Len], "<tr><td width=110px>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>Channel State</td><td>%s</td></tr>", TNC->WEB_CHANSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Proto State</td><td>%s</td></tr>", TNC->WEB_PROTOSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
// Len += sprintf(&Buff[Len], "<tr><td>TNC Restarts</td><td></td></tr>", TNC->WEB_RESTARTS);
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 FLDIGISuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC)
{
TNC->FLInfo->CONOK = FALSE;
}
VOID FLDIGIReleasePort(struct TNCINFO * TNC)
{
TNC->FLInfo->CONOK = TRUE;
}
VOID SendKISSCommand(struct TNCINFO * TNC, char * Msg)
{
int txlen, rc;
char txbuff[256];
char outbuff[256];
txlen = sprintf(txbuff, "%c%s", 6, Msg);
txlen = KissEncode(txbuff, outbuff, txlen);
rc = sendto(TNC->TCPDataSock, outbuff, txlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr));
}
VOID * FLDigiExtInit(EXTPORTDATA * PortEntry)
{
int i, port;
char Msg[255];
struct TNCINFO * TNC;
char * ptr;
//
// The Socket to connect to is in IOBASE
//
srand((unsigned int)time(NULL));
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;
if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
memcpy(TNC->NodeCall, MYNODECALL, 10);
else
ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
PortEntry->PORTCONTROL.PROTOCOL = 10;
PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream
PortEntry->PORTCONTROL.UICAPABLE = 1; // Can send beacons
PortEntry->PORTCONTROL.PORTQUALITY = 0;
PortEntry->SCANCAPABILITIES = SIMPLE;
// Scan Control - None
TNC->FLInfo->CONOK = TRUE;
if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
PortEntry->PORTCONTROL.PORTPACLEN = 250;
TNC->SuspendPortProc = FLDIGISuspendPort;
TNC->ReleasePortProc = FLDIGIReleasePort;
ptr=strchr(TNC->NodeCall, ' ');
if (ptr) *(ptr) = 0; // Null Terminate
TNC->Hardware = H_FLDIGI;
if (TNC->BusyWait == 0)
TNC->BusyWait = 10;
MPSKChannel[port] = PortEntry->PORTCONTROL.CHANNELNUM-65;
PortEntry->MAXHOSTMODESESSIONS = 1;
i=sprintf(Msg,"FLDigi Host %s Port %d \n",
TNC->HostName, TNC->TCPPort);
WritetoConsole(Msg);
#ifndef LINBPQ
if (TNC->ProgramPath)
TNC->PID = FindFLDIGI(TNC->ProgramPath);
if (TNC->PID == 0) // Not running
#endif
TNC->WeStartedTNC = RestartTNC(TNC); // Always try if Linux
if (TNC->FLInfo->KISSMODE)
{
// Open Datagram port
SOCKET sock;
u_long param=1;
BOOL bcopt=TRUE;
struct sockaddr_in sinx;
struct hostent * HostEnt = NULL;
TNC->FLInfo->CmdControl = 5; //Send params immediately
TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName);
if (TNC->Datadestaddr.sin_addr.s_addr == INADDR_NONE)
{
// Resolve name to address
HostEnt = gethostbyname (TNC->HostName);
if (HostEnt)
{
memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4);
}
}
TNC->TCPDataSock = sock = socket(AF_INET,SOCK_DGRAM,0);
ioctl(sock, FIONBIO, &param);
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = htons(TNC->TCPPort + 1);
if (bind(sock, (struct sockaddr *) &sinx, sizeof(sinx)) != 0 )
{
// Bind Failed
int err = WSAGetLastError();
Consoleprintf("Bind Failed for UDP port %d - error code = %d", TNC->TCPPort, err);
}
TNC->Datadestaddr.sin_family = AF_INET;
TNC->Datadestaddr.sin_port = htons(TNC->TCPPort);
}
else
ConnecttoFLDigi(port);
time(&lasttime[port]); // Get initial time value
PortEntry->PORTCONTROL.TNC = TNC;
TNC->WebWindowProc = WebProc;
TNC->WebWinX = 520;
TNC->WebWinY = 500;
TNC->WebBuffer = zalloc(5000);
TNC->WEB_COMMSSTATE = zalloc(100);
TNC->WEB_TNCSTATE = zalloc(100);
TNC->WEB_CHANSTATE = zalloc(100);
TNC->WEB_BUFFERS = zalloc(100);
TNC->WEB_PROTOSTATE = zalloc(100);
TNC->WEB_RESTARTTIME = zalloc(100);
TNC->WEB_RESTARTS = zalloc(100);
TNC->WEB_MODE = zalloc(50);
TNC->WEB_TRAFFIC = zalloc(100);
#ifndef LINBPQ
CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, 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/CF", 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", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0 Resent 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);
TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
0,170,250,300, TNC->hDlg, NULL, hInstance, NULL);
TNC->ClientHeight = 450;
TNC->ClientWidth = 500;
TNC->hMenu = CreatePopupMenu();
AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill FLDigi");
AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart FLDigi");
AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session");
MoveWindows(TNC);
#endif
return ExtProc;
}
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;
struct ARQINFO * ARQ;
struct FLINFO * FL;
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
if (_stricmp(buf, "ADDR"))
return FALSE; // Must start with ADDR
ptr = strtok(NULL, " \t\n\r");
BPQport = Port;
p_ipad = ptr;
TNC = TNCInfo[BPQport] = zalloc(sizeof(struct TNCINFO));
ARQ = TNC->ARQInfo = zalloc(sizeof(struct ARQINFO));
FL = TNC->FLInfo = zalloc(sizeof(struct FLINFO));
TNC->Timeout = 50; // Default retry = 5 seconds
TNC->Retries = 6; // Default Retries
TNC->Window = 16;
TNC->FLInfo->KISSMODE = TRUE; // Default to KISS
TNC->InitScript = malloc(1000);
TNC->InitScript[0] = 0;
if (p_ipad == NULL)
p_ipad = strtok(NULL, " \t\n\r");
if (p_ipad == NULL) return (FALSE);
p_port = strtok(NULL, " \t\n\r");
if (p_port == NULL) return (FALSE);
TNC->TCPPort = atoi(p_port);
TNC->destaddr.sin_family = AF_INET;
TNC->destaddr.sin_port = htons(TNC->TCPPort + 40); // Defaults XML 7362 ARQ 7322
TNC->Datadestaddr.sin_family = AF_INET;
TNC->Datadestaddr.sin_port = htons(TNC->TCPPort);
TNC->HostName = malloc(strlen(p_ipad)+1);
if (TNC->HostName == NULL) return TRUE;
strcpy(TNC->HostName,p_ipad);
ptr = strtok(NULL, " \t\n\r");
if (ptr)
{
if (_memicmp(ptr, "PATH", 4) == 0)
{
p_cmd = strtok(NULL, "\n\r");
if (p_cmd) TNC->ProgramPath = _strdup(p_cmd);
}
}
// Read Initialisation lines
while(TRUE)
{
if (GetLine(buf) == 0)
return TRUE;
strcpy(errbuf, buf);
if (memcmp(buf, "****", 4) == 0)
return TRUE;
ptr = strchr(buf, ';');
if (ptr)
{
*ptr++ = 13;
*ptr = 0;
}
if (_memicmp(buf, "TIMEOUT", 7) == 0)
TNC->Timeout = atoi(&buf[8]) * 10;
else
if (_memicmp(buf, "RETRIES", 7) == 0)
TNC->Retries = atoi(&buf[8]);
else
if (_memicmp(buf, "WINDOW", 6) == 0)
TNC->Window = atoi(&buf[7]);
else
if (_memicmp(buf, "ARQMODE", 7) == 0)
TNC->FLInfo->KISSMODE = FALSE;
else
if (_memicmp(buf, "DEFAULTMODEM", 12) == 0) // Send Beacon after each session
{
// Check that freq is also specified
char * Freq = strchr(&buf[13], '/');
if (Freq)
{
*(Freq++) = 0;
strcpy(TNC->FLInfo->DefaultMode, &buf[13]);
TNC->FLInfo->DefaultFreq = atoi(Freq);
}
}
else
strcat (TNC->InitScript, buf);
}
return (TRUE);
}
static int ConnecttoFLDigi(int port)
{
_beginthread(ConnecttoFLDigiThread, 0, (void *)(size_t)port);
return 0;
}
static VOID ConnecttoFLDigiThread(void * portptr)
{
int port = (int)(size_t)portptr;
char Msg[255];
int err,i;
u_long param=1;
BOOL bcopt=TRUE;
struct hostent * HostEnt = NULL;
struct TNCINFO * TNC = TNCInfo[port];
Sleep(5000); // Allow init to complete
TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName);
TNC->Datadestaddr.sin_addr.s_addr = inet_addr(TNC->HostName);
if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE)
{
// Resolve name to address
HostEnt = gethostbyname (TNC->HostName);
if (!HostEnt) return; // Resolve failed
memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4);
}
if (TNC->TCPSock)
{
Debugprintf("FLDIGI Closing Sock %d", TNC->TCPSock);
closesocket(TNC->TCPSock);
}
TNC->TCPSock = 0;
TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0);
if (TNC->TCPSock == INVALID_SOCKET)
{
i=sprintf(Msg, "Socket Failed for FLDigi Control socket - error code = %d\n", WSAGetLastError());
WritetoConsole(Msg);
return;
}
setsockopt (TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = 0;
TNC->CONNECTING = TRUE;
if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0)
{
//
// Connected successful
//
}
else
{
if (TNC->Alerted == FALSE)
{
err=WSAGetLastError();
i=sprintf(Msg, "Connect Failed for FLDigi Control socket - error code = %d\n", err);
WritetoConsole(Msg);
sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed");
MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
TNC->Alerted = TRUE;
}
closesocket(TNC->TCPSock);
TNC->TCPSock = 0;
TNC->CONNECTING = FALSE;
return;
}
TNC->LastFreq = 0;
if (TNC->TCPDataSock)
closesocket(TNC->TCPDataSock);
TNC->TCPDataSock = 0;
TNC->TCPDataSock=socket(AF_INET,SOCK_STREAM,0);
setsockopt (TNC->TCPDataSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4);
if (TNC->TCPDataSock == INVALID_SOCKET)
{
i=sprintf(Msg, "Socket Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError());
WritetoConsole(Msg);
closesocket(TNC->TCPSock);
closesocket(TNC->TCPDataSock);
TNC->TCPSock = 0;
TNC->CONNECTING = FALSE;
return;
}
if (bind(TNC->TCPDataSock, (LPSOCKADDR) &sinx, addrlen) != 0 )
{
//
// Bind Failed
//
i=sprintf(Msg, "Bind Failed for FLDigi Data socket - error code = %d\r\n", WSAGetLastError());
WritetoConsole(Msg);
closesocket(TNC->TCPSock);
closesocket(TNC->TCPDataSock);
TNC->TCPSock = 0;
TNC->TCPDataSock = 0;
TNC->CONNECTING = FALSE;
return;
}
if (connect(TNC->TCPDataSock,(LPSOCKADDR) &TNC->Datadestaddr,sizeof(TNC->Datadestaddr)) == 0)
{
ioctlsocket (TNC->TCPDataSock,FIONBIO,&param); // Set nonblocking
TNC->CONNECTED = TRUE;
TNC->CONNECTING = FALSE;
TNC->Alerted = TRUE;
sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI");
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
}
else
{
sprintf(Msg, "Connect Failed for FLDigi Data socket Port %d - error code = %d\r\n", port, WSAGetLastError());
WritetoConsole(Msg);
closesocket(TNC->TCPSock);
closesocket(TNC->TCPDataSock);
TNC->TCPSock = 0;
TNC->TCPDataSock = 0;
TNC->CONNECTING = FALSE;
}
return;
}
VOID UpdateStatsLine(struct TNCINFO * TNC, struct STREAMINFO * STREAM)
{
sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d Resent %d Queued %d",
STREAM->BytesRXed, STREAM->BytesTXed, STREAM->BytesAcked, STREAM->BytesResent, STREAM->BytesOutstanding);
SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC);
}
VOID SendPacket(struct TNCINFO * TNC, UCHAR * Msg, int MsgLen)
{
if (TNC->FLInfo->KISSMODE)
{
char KissMsg[1000];
char outbuff[1000];
int newlen;
if (TNC->FLInfo->RAW)
{
// KISS RAW
// Add CRC and Send
unsigned short CRC;
char crcstring[6];
KissMsg[0] = 7; // KISS Raw
KissMsg[1] = 1; // SOH
KissMsg[2] = '0'; // Version
KissMsg[3] = TNC->ARQInfo->FarStream;
Msg[MsgLen] = 0;
memcpy(&KissMsg[4], Msg, MsgLen +1 ); // Get terminating NULL
CRC = CalcCRC(KissMsg + 1, MsgLen + 3);
sprintf(crcstring, "%04X%c", CRC, 4);
strcat(KissMsg, crcstring);
MsgLen += 9;
}
else
{
// Normal KISS
KissMsg[0] = 0; // KISS Control
KissMsg[1] = TNC->ARQInfo->FarStream;
memcpy(&KissMsg[2], Msg, MsgLen);
MsgLen += 2;
}
newlen = KissEncode(KissMsg, outbuff, MsgLen);
sendto(TNC->TCPDataSock, outbuff, newlen, 0, (struct sockaddr *)&TNC->Datadestaddr, sizeof(struct sockaddr));
SendKISSCommand(TNC, "TXBUF:");
}
else
{
// ARQ Scoket
// Add Header, CRC and Send
unsigned short CRC;
char crcstring[6];
char outbuff[1000];
outbuff[0] = 1; // SOH
outbuff[1] = '0'; // Version
outbuff[2] = TNC->ARQInfo->FarStream;
Msg[MsgLen] = 0;
memcpy(&outbuff[3], Msg, MsgLen + 1);
CRC = CalcCRC(outbuff , MsgLen + 3);
sprintf(crcstring, "%04X%c", CRC, 4);
strcat(outbuff, crcstring);
MsgLen += 8;
send(TNC->TCPDataSock, outbuff, MsgLen, 0);
}
}
VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW);
static int ProcessReceivedData(int port)
{
int bytes, used, bytesleft;
int i;
char ErrMsg[255];
unsigned char MessageBuff[1500];
unsigned char * Message = MessageBuff;
unsigned char * MessageBase = MessageBuff;
struct TNCINFO * TNC = TNCInfo[port];
struct FLINFO * FL = TNC->FLInfo;
struct STREAMINFO * STREAM = &TNC->Streams[0];
// If using KISS/UDP interface use recvfrom
if (FL->KISSMODE)
{
struct sockaddr_in rxaddr;
int addrlen = sizeof(struct sockaddr_in);
unsigned char * KissEnd;
bytesleft = recvfrom(TNC->TCPDataSock, Message, 1500, 0, (struct sockaddr *)&rxaddr, &addrlen);
if (bytesleft < 0)
{
int err = WSAGetLastError();
// if (err != 11)
// printf("KISS Error %d %d\n", nLength, err);
bytes = 0;
}
while (bytesleft > 0)
{
unsigned char * in;
unsigned char * out;
unsigned char c;
if (bytesleft < 3)
return 0;
if (Message[0] != FEND)
return 0; // Duff
Message = MessageBase;
in = out = &Message[2];
// We may have more than one KISS message in a packet
KissEnd = memchr(&Message[2], FEND, bytesleft );
if (KissEnd == 0)
return 0; // Duff
*(KissEnd) = 0;
used = (int)(KissEnd - Message + 1);
bytesleft -= used;
bytes = used;
MessageBase += used;
if (Message[1] == 6) // KISS Command
{
UCHAR * ptr = strchr(&Message[2], FEND);
if (ptr) *ptr = 0; // Null Terminate
if (bytes > 250)
Message[250] = 0;
FL->Responding = 5;
if (TNC->TNCOK == 0)
{
TNC->TNCOK = TRUE;
TNC->CONNECTED = TRUE;
sprintf(TNC->WEB_COMMSSTATE, "Connected to FLDIGI");
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
}
// Trap BUSY fiest - there are lots of them, and they are likely to be confused
// with tesponses to Interactive commands
if (memcmp(&Message[2], "BUSY", 4) == 0)
{
BOOL Changed = FALSE;
if (Message[7] == 'T' && FL->Busy == FALSE)
{
TNC->Busy = FL->Busy = TRUE;
Changed = TRUE;
}
else
{
if (Message[7] == 'F' && FL->Busy == TRUE)
{
TNC->Busy = FL->Busy = FALSE;
Changed = TRUE;
}
}
if (Changed)
{
if (FL->TX)
strcpy(TNC->WEB_CHANSTATE, "TX");
else
if (FL->Busy)
strcpy(TNC->WEB_CHANSTATE, "Busy");
else
strcpy(TNC->WEB_CHANSTATE, "Idle");
SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE);
}
continue;
}
if (TNC->InternalCmd)
{
PMSGWITHLEN buffptr = GetBuff();
TNC->InternalCmd = FALSE;
if (buffptr)
{
buffptr->Len = sprintf(buffptr->Data, "FLDIGI} Ok %s\r", &Message[2]);
C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
}
// Drop through in case need to extract info from command
}
// Auto Command
// Debugprintf("%d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]);
if (memcmp(&Message[2], "FLSTAT", 4) == 0)
{
if (strstr(&Message[2], "FLSTAT:INIT"))
{
// FLDIGI Reloaded - set parmas
SendKISSCommand(TNC, "RSIDBCAST:ON TRXSBCAST:ON TXBEBCAST:ON KISSRAW:ON");
}
continue;
}
if (memcmp(&Message[2], "TRXS", 4) == 0)
{
char * ptr1, * context;
BOOL Changed = FALSE;
ptr1 = strtok_s(&Message[7], ",", &context);
if (strstr(ptr1, "TX"))
{
if (TNC->FLInfo->TX == FALSE)
{
TNC->FLInfo->TX = TRUE;
Changed = TRUE;
}
}
else
{
if (TNC->FLInfo->TX)
{
TNC->FLInfo->TX = FALSE;
Changed = TRUE;
}
}
if (Changed)
{
if (FL->TX)
strcpy(TNC->WEB_CHANSTATE, "TX");
else
if (FL->Busy)
strcpy(TNC->WEB_CHANSTATE, "Busy");
else
strcpy(TNC->WEB_CHANSTATE, "Idle");
SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE);
}
continue;
}
if (memcmp(&Message[2], "TXBUF:", 6) == 0)
{
char * ptr1, * context;
ptr1 = strtok_s(&Message[8], ",", &context);
STREAM->BytesOutstanding = atoi(ptr1);
UpdateStatsLine(TNC, STREAM);
continue;
}
if (memcmp(&Message[2], "TXBE:", 5) == 0)
{
STREAM->BytesOutstanding = 0;
UpdateStatsLine(TNC, STREAM);
continue;
}
if (memcmp(&Message[2], "RSIDN:", 6) == 0)
{
char * ptr1, * context;
ptr1 = strtok_s(&Message[8], ",", &context);
TNC->FLInfo->CenterFreq = atoi(ptr1);
ptr1 = strtok_s(NULL, ",", &context);
if (strlen(ptr1) > 19)
ptr1[19] = 0;
strcpy(TNC->FLInfo->CurrentMode, ptr1);
TNC->ConnectPending = 3; // Lock Scan for 3
}
if (memcmp(&Message[2], "MODEM:", 6) == 0)
{
char * ptr1, * context;
ptr1 = strtok_s(&Message[8], ",", &context);
if (strlen(ptr1) > 19)
ptr1[19] = 0;
strcpy(TNC->FLInfo->CurrentMode, ptr1);
}
if (memcmp(&Message[2], "WFF:", 4) == 0)
{
char * ptr1, * context;
ptr1 = strtok_s(&Message[6], ",", &context);
TNC->FLInfo->CenterFreq = atoi(ptr1);
}
sprintf(TNC->WEB_MODE, "%s/%d", TNC->FLInfo->CurrentMode, TNC->FLInfo->CenterFreq);
SetWindowText(TNC->xIDC_MODE, TNC->WEB_MODE);
continue;
}
if (Message[1] == 7) // Not Normal Data
{
// "RAW" Mode. Just process as if received from TCP Socket Interface
// Debugprintf("7 %d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]);
ProcessFLDigiPacket(TNC, &Message[2] , bytes - 3); // Data may be for another port
continue;
}
bytes -= 3; // Two FEND and Control
// Debugprintf("0 %d %s", TNC->PortRecord->PORTCONTROL.PORTNUMBER, &Message[2]);
// Undo KISS
while (bytes)
{
bytes--;
c = *(in++);
if (c == FESC)
{
c = *(in++);
bytes--;
if (c == TFESC)
c = FESC;
else if (c == TFEND)
c = FEND;
}
*(out++) = c;
}
ProcessFLDigiData(TNC, &Message[3], (int)(out - &Message[3]), Message[2], FALSE); // KISS not RAW
}
return 0;
}
// Need to extract messages from byte stream
bytes = recv(TNC->TCPDataSock, Message, 500, 0);
if (bytes == SOCKET_ERROR)
{
// i=sprintf(ErrMsg, "Read Failed for MPSK socket - error code = %d\r\n", WSAGetLastError());
// WritetoConsole(ErrMsg);
closesocket(TNC->TCPDataSock);
TNC->CONNECTED = FALSE;
if (TNC->Streams[0].Attached)
TNC->Streams[0].ReportDISC = TRUE;
return (0);
}
if (bytes == 0)
{
// zero bytes means connection closed
i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port);
WritetoConsole(ErrMsg);
TNC->CONNECTED = FALSE;
if (TNC->Streams[0].Attached)
TNC->Streams[0].ReportDISC = TRUE;
return (0);
}
// Have some data
ProcessFLDigiPacket(TNC, Message, bytes); // Data may be for another port
return (0);
}
VOID ProcessFLDigiPacket(struct TNCINFO * TNC, char * Message, int Len)
{
char * MPTR = Message;
char c;
struct FLINFO * FL = TNC->FLInfo;
if (TNC->FLInfo->MCASTMODE)
{
if (TNC->Streams[0].Attached == 0)
return;
while(Len)
{
c = *(MPTR++);
if (TNC->InPacket)
{
TNC->DataBuffer[TNC->DataLen++] = c;
// Sanity Check
if (TNC->DataLen == 6)
{
char * ptr = &TNC->DataBuffer[1];
if (memcmp(ptr, "DATA ", 5) == 0 ||
memcmp(ptr, "PROG ", 5) == 0 ||
memcmp(ptr, "FILE ", 5) == 0 ||
memcmp(ptr, "SIZE ", 5) == 0 ||
memcmp(ptr, "DESC ", 5) == 0 ||
memcmp(ptr, "CNTL ", 5) == 0 ||
memcmp(ptr, "ID ", 3) == 0)
{
}
else
{
// False Trigger, try again
TNC->InPacket = FALSE;
}
}
else
{
if (TNC->InData)
{
if (--TNC->MCASTLen == 0)
{
// Got a packet
PMSGWITHLEN buffptr;
int Stream = 0;
struct STREAMINFO * STREAM = &TNC->Streams[0];
buffptr = GetBuff();
if (buffptr)
{
TNC->DataBuffer[TNC->DataLen++] = 13; // Keep Tidy
buffptr->Len = TNC->DataLen;
memcpy(&buffptr[2], &TNC->DataBuffer[0], TNC->DataLen);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
TNC->InPacket = FALSE;
}
}
else
{
// Looking for >
if (TNC->DataLen == 16)
{
// Not found it
TNC->InPacket = FALSE;
}
else
{
if (c == '>')
{
// Got Header - extract Length
char * ptr;
int len;
ptr = strchr(TNC->DataBuffer, ' ');
if (ptr)
{
len = atoi(ptr);
if (len)
{
TNC->InData = TRUE;
TNC->MCASTLen = len;
}
}
}
}
}
}
if (TNC->DataLen > 520)
TNC->DataLen--; // Protect Buffer
}
else
{
// Look for '<'
if (c == '<')
{
TNC->DataBuffer[0] = c;
TNC->DataLen = 1;
TNC->InPacket = TRUE;
TNC->InData = FALSE;
}
}
Len--;
}
return;
}
// Look for SOH/EOT delimiters. May Have several SOH before EOTTNC->FL
while(Len)
{
c = *(MPTR++);
switch (c)
{
case 01: // New Packet
if (TNC->InPacket)
CheckFLDigiData(TNC);
TNC->DataBuffer[0] = 1;
TNC->DataLen = 1;
TNC->InPacket = TRUE;
break;
case 04:
if (TNC->InPacket)
CheckFLDigiData(TNC);
TNC->DataLen = 0;
TNC->InPacket = FALSE;
break;
default:
if (TNC->InPacket)
{
if (TNC->DataLen == 1)
{
if (c != '0' && c != '1')
{
// Drop if not Protocol '0' or '1' - this should eliminate almost all noise packets
TNC->InPacket = 0;
break;
}
}
TNC->DataBuffer[TNC->DataLen++] = c;
}
if (TNC->DataLen > 520)
TNC->DataLen--; // Protect Buffer
}
Len--;
}
}
VOID CheckFLDigiData(struct TNCINFO * TNC)
{
UCHAR * Input = &TNC->DataBuffer[0];
int Len = TNC->DataLen - 4; // Not including CRC
unsigned short CRC;
char crcstring[6];
if (Len < 0)
return;
TNC->DataBuffer[TNC->DataLen] = 0;
// RAW format message, either from ARQ Scoket or RAW KISS
// Check Checksum
CRC = CalcCRC(Input , Len);
sprintf(crcstring, "%04X", CRC);
if (memcmp(&Input[Len], crcstring, 4) !=0)
{
// CRC Error - could just be noise
// Debugprintf("%s %s", crcstring, Input);
return;
}
ProcessFLDigiData(TNC, &Input[3], Len - 3, Input[2], TRUE); // From RAW
}
/*
VOID ProcessARQPacket(struct PORTCONTROL * PORT, MESSAGE * Buffer)
{
// ARQ Packet from KISS-Like Hardware
struct TNCINFO * TNC = TNCInfo[PORT->PORTNUMBER];
UCHAR * Input;
int Len;
if (TNC == NULL)
{
// Set up TNC info
TNC = TNCInfo[PORT->PORTNUMBER] = zalloc(sizeof(struct TNCINFO));
TNC->ARQInfo = zalloc(sizeof(struct ARQINFO));
TNC->FLInfo = zalloc(sizeof(struct FLINFO));
TNC->Timeout = 50; // Default retry = 10 seconds
TNC->Retries = 6; // Default Retries
TNC->Window = 16;
}
Input = &Buffer->DEST[0];
Len = Buffer->LENGTH - 7; // Not including CRC
// Look for attach on any call
ProcessFLDigiData(TNC, Input, Len);
}
*/
static int Stuff(UCHAR * inbuff, UCHAR * outbuff, int len)
{
int i, txptr = 0;
UCHAR c;
UCHAR * ptr = inbuff;
// DLE Escape DLE, SOH, EOT
for (i = 0; i < len; i++)
{
c = *(ptr++);
// if (c == 0 || c == DLE || c == SOH || c == EOT)
if (c < 32 && c != 10 && c != 13 && c != 8)
{
outbuff[txptr++] = DLE;
// if between 0 and 0x1F, Add 40,
// if > x80 and less than 0xa0 subtract 20
c += 0x40;
}
outbuff[txptr++]=c;
}
return txptr;
}
static int UnStuff(UCHAR * inbuff, int len)
{
int i, txptr = 0;
UCHAR c;
UCHAR * outbuff = inbuff;
UCHAR * ptr = inbuff;
// This unstuffs into the input buffer
for (i = 0; i < len; i++)
{
c = *(ptr++);
if (c == DLE)
{
c = *(ptr++);
i++;
// if between 0x40 and 0x5F, subtract 0x40,
// else add 0x20 (so we can send chars 80-9f without a double DLE)
if (c < 0x60)
c -= 0x40;
else
c += 0x20;
}
outbuff[txptr++] = c;
}
return txptr;
}
unsigned int crcval = 0xFFFF;
void update(char c)
{
int i;
crcval ^= c & 255;
for (i = 0; i < 8; ++i)
{
if (crcval & 1)
crcval = (crcval >> 1) ^ 0xA001;
else
crcval = (crcval >> 1);
}
}
unsigned int CalcCRC(UCHAR * ptr, int Len)
{
int i;
crcval = 0xFFFF;
for (i = 0; i < Len; i++)
{
update(*ptr++);
}
return crcval;
}
/*
<SOH>00cG8BPQ:1025 G8BPQ:24 0 8 T60R6W108E06<EOT>
<SOH>00kG8BPQ:24 G8BPQ 4 85F9B<EOT>
<SOH>00cG8BPQ:1025 GM8BPQ:24 0 7 T60R5W1051D5<EOT> (128, 5)
,<SOH>00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36<EOT>
<SOH>00kG8BPQ:24 G8BPQ 5 89FCA<EOT>
First no sees to be a connection counter. Next may be stream
<SOH>08s___ABFC<EOT>
<SOH>08tG8BPQ:73 xxx 33FA<EOT>
<SOH>00tG8BPQ:73 yyy 99A3<EOT>
<SOH>08dG8BPQ:90986C<EOT>
<SOH>00bG8BPQ:911207<EOT>
call:90 for dis 91 for dis ack 73<sp> for chat)
<SOH>08pG8BPQ<SUB>?__645E<EOT>
<SOH>00s_??4235<EOT>
<SOH>08pG8BPQ<SUB>?__645E<EOT>
<SOH>00s_??4235<EOT>
i Ident
c Connect
k Connect Ack
r Connect NAK
d Disconnect req
s Data Ack/ Retransmit Req )status)
p Poll
f Format Fail
b dis ack
t talk
a Abort
o Abort ACK
<SOH>00cG8BPQ:1025 G8BPQ:24 0 7 T60R5W10FA36<EOT>
<SOH>00kG8BPQ:24 G8BPQ 6 49A3A<EOT>
<SOH>08s___ABFC<EOT>
<SOH>08 ARQ:FILE::flarqmail-1.eml
ARQ:EMAIL::
ARQ:SIZE::90
ARQ::STX
//FLARQ COMPOSER
Date: 09/01/2014 23:24:42
To: gm8bpq
From:
SubjectA0E0<SOH>
<SOH>08!: Test
Test Message
ARQ::ETX
F0F2<SOH>
<SOH>08pG8BPQ<SUB>!__623E<EOT>
<SOH>08pG8BPQ<SUB>!__623E<EOT>
<SOH>08pG8BPQ<SUB>!__623E<EOT>
*/
VOID ProcessFLDigiData(struct TNCINFO * TNC, UCHAR * Input, int Len, char Channel, BOOL RAW)
{
PMSGWITHLEN buffptr;
int Stream = 0;
struct STREAMINFO * STREAM = &TNC->Streams[0];
char CTRL = Input[0];
struct ARQINFO * ARQ = TNC->ARQInfo;
struct FLINFO * FL = TNC->FLInfo;
int SendLen;
char Reply[80];
// Process Message
// This processes eitrher message from the KISS or RAW interfaces.
// Headers and RAW checksum have been removed, so packet starts with Control Byte
// Only a connect request is allowed with no session, so check first
if (CTRL == 'c')
{
// Connect Request
char * call1;
char * call2;
char * port1;
char * port2;
char * ptr;
char * context;
char FarStream = 0;
int BlockSize = 6; // 64 default
int Window = TNC->Window;
APPLCALLS * APPL;
char * ApplPtr = APPLS;
int App;
char Appl[10];
struct WL2KInfo * WL2K = TNC->WL2K;
TRANSPORTENTRY * SESS;
TNC->ConnectPending = 0;
if (FL->CONOK == FALSE)
return;
call1 = strtok_s(&Input[1], " ", &context);
call2 = strtok_s(NULL, " ", &context);
port1 = strlop(call1, ':');
port2 = strlop(call2, ':');
// See if for us
for (App = 0; App < 32; App++)
{
APPL=&APPLCALLTABLE[App];
memcpy(Appl, APPL->APPLCALL_TEXT, 10);
ptr=strchr(Appl, ' ');
if (ptr) *ptr = 0;
if (_stricmp(call2, Appl) == 0)
break;
}
if (App > 31)
if (strcmp(TNC->NodeCall, call2) !=0)
return; // Not Appl or Port/Node Call
ptr = strtok_s(NULL, " ", &context);
FarStream = *ptr;
ptr = strtok_s(NULL, " ", &context);
BlockSize = atoi(ptr);
if (ARQ->ARQState)
{
// We have already received a connect request - just ACK it
goto AckConnectRequest;
}
// Get a Session
SuspendOtherPorts(TNC);
ProcessIncommingConnect(TNC, call1, 0, TRUE);
SESS = TNC->PortRecord->ATTACHEDSESSIONS[0];
strcpy(STREAM->MyCall, call2);
STREAM->ConnectTime = time(NULL);
STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0;
if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT"))
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, call2, TNC->RIG->Valchar);
SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq
SESS->Mode = TNC->WL2KMode;
}
else
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, call2);
if (WL2K)
{
SESS->Frequency = WL2K->Freq;
SESS->Mode = WL2K->mode;
}
}
if (WL2K)
strcpy(SESS->RMSCall, WL2K->RMSCall);
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
strcpy(TNC->WEB_PROTOSTATE, "Connect Pending");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
memset(ARQ, 0, sizeof(struct ARQINFO)); // Reset ARQ State
ARQ->FarStream = FarStream;
ARQ->TXSeq = ARQ->TXLastACK = 63; // Last Sent
ARQ->RXHighest = ARQ->RXNoGaps = 63; // Last Received
ARQ->ARQState = ARQ_ACTIVE;
ARQ->OurStream = (rand() % 78) + 49; // To give some protection against other stuff on channel
ARQ->FarStream = FarStream; // Not Yet defined
if (strcmp(port1, "1025") == 0)
{
FL->FLARQ = TRUE; // From FLARQ
ARQ->OurStream = '8'; // FLARQ Ignores what we send
}
else
FL->FLARQ = FALSE; // From other app (eg BPQ)
FL->RAW = RAW;
STREAM->NeedDisc = 0;
if (App < 32)
{
char AppName[13];
memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12);
AppName[12] = 0;
// Make sure app is available
if (CheckAppl(TNC, AppName))
{
char Buffer[32];
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;
// Save Appl Call in case needed for
}
else
{
STREAM->NeedDisc = 50; // 1 sec
}
}
ARQ->TXWindow = Window;
if (BlockSize < 4) BlockSize = 4;
if (BlockSize < 9) BlockSize = 9;
ARQ->MaxBlock = Blocksizes[BlockSize];
ARQ->ARQTimer = 10; // To force CTEXT to be Queued
if (STREAM->NeedDisc)
{
// Send Not Avail
buffptr = GetBuff();
if (buffptr)
{
buffptr->Len = sprintf(buffptr->Data, "Application Not Available\n");
SendARQData(TNC, buffptr);
}
}
AckConnectRequest:
SendLen = sprintf(Reply, "k%s:24 %s %c 7", call2, call1, ARQ->OurStream);
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
ARQ->ARQTimerState = ARQ_CONNECTACK;
return;
}
// All others need a session
// if (!STREAM->Connected && !STREAM->Connecting)
// return;
if (CTRL == 'k')
{
// Connect ACK
char * call1;
char * call2;
char * port1;
char * port2;
char * ptr;
char * context;
char FarStream = 0;
int BlockSize = 6; // 64 default
int Window = 16;
char Reply[80];
int ReplyLen;
call1 = strtok_s(&Input[1], " ", &context);
call2 = strtok_s(NULL, " ", &context);
port1 = strlop(call1, ':');
port2 = strlop(call2, ':');
if (strcmp(call1, STREAM->RemoteCall) != 0)
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
ptr = strtok_s(NULL, " ", &context);
if (ptr)
FarStream = *ptr;
ptr = strtok_s(NULL, " ", &context);
if (ptr)
BlockSize = atoi(ptr);
if (STREAM->Connected)
goto SendKReply; // Repeated ACK
STREAM->ConnectTime = time(NULL);
STREAM->BytesRXed = STREAM->BytesTXed = STREAM->BytesAcked = STREAM->BytesResent = 0;
STREAM->Connected = TRUE;
ARQ->ARQTimerState = 0;
ARQ->ARQTimer = 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);
UpdateMH(TNC, STREAM->RemoteCall, '+', 'Z');
ARQ->ARQTimerState = 0;
ARQ->FarStream = FarStream;
ARQ->TXWindow = TNC->Window;
ARQ->MaxBlock = Blocksizes[BlockSize];
ARQ->ARQState = ARQ_ACTIVE;
STREAM->NeedDisc = 0;
buffptr = GetBuff();
if (buffptr)
{
ReplyLen = sprintf(Reply, "*** Connected to %s\r", STREAM->RemoteCall);
buffptr->Len = ReplyLen;
memcpy(buffptr->Data, Reply, ReplyLen);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
strcpy(TNC->WEB_PROTOSTATE, "Connected");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
SendKReply:
// Reply with status
SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32);
if (ARQ->RXHighest != ARQ->RXNoGaps)
{
int n = ARQ->RXNoGaps + 1;
n &= 63;
while (n != ARQ->RXHighest)
{
if (ARQ->RXHOLDQ[n] == 0) // Dont have it
SendLen += sprintf(&Reply[SendLen], "%c", n + 32);
n++;
n &= 63;
}
}
QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
return;
}
// All others need a session
//if (!STREAM->Connected)
// return;
if (CTRL == 's')
{
// Status
if (Channel != ARQ->OurStream)
return; // Wrong Session
ARQ->ARQTimer = 0; // Stop retry timer
Input[Len] = 0;
ProcessARQStatus(TNC, ARQ, &Input[1]);
return;
}
if (CTRL == 'p')
{
// Poll
char * call1;
char * context;
call1 = strtok_s(&Input[1], " \x1A", &context);
if (strcmp(call1, STREAM->RemoteCall) != 0)
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
SendLen = sprintf(Reply, "s%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32);
if (ARQ->RXHighest != ARQ->RXNoGaps)
{
int n = ARQ->RXNoGaps + 1;
n &= 63;
while (n != ARQ->RXHighest)
{
if (ARQ->RXHOLDQ[n] == 0) // Dont have it
SendLen += sprintf(&Reply[SendLen], "%c", n + 32);
n++;
n &= 63;
}
}
else
ARQ->TurnroundTimer = 15; // Allow us to send it all acked
QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
return;
}
if (CTRL == 'a')
{
// Abort. Send Abort ACK - same as
char * call1;
char * context;
call1 = strtok_s(&Input[1], " :", &context);
if (strcmp(call1, STREAM->RemoteCall) != 0)
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
SendLen = sprintf(Reply, "o%c%c%c", ARQ->TXSeq + 32, ARQ->RXNoGaps + 32, ARQ->RXHighest + 32);
if (ARQ->RXHighest != ARQ->RXNoGaps)
{
int n = ARQ->RXNoGaps + 1;
n &= 63;
while (n != ARQ->RXHighest)
{
if (ARQ->RXHOLDQ[n] == 0) // Dont have it
SendLen += sprintf(&Reply[SendLen], "%c", n + 32);
n++;
n &= 63;
}
}
QueueAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
return;
}
if (CTRL == 'i')
{
// Ident
return;
}
if (CTRL == 't')
{
// Talk - pass to node
char * call1;
char * context;
char * ptr;
PMSGWITHLEN buffptr;
Input[Len] = 0; // Removes checksum
call1 = strtok_s(&Input[1], " ", &context);
strlop(call1, ':');
if (strcmp(STREAM->RemoteCall, call1))
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
while (ptr = strchr(context, 10))
*ptr = 13;
buffptr->Len = strlen(context);
strcpy(buffptr->Data, context);
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
return;
}
if (CTRL == 'd')
{
// Disconnect Request
char * call1;
char * context;
call1 = strtok_s(&Input[1], " ", &context);
strlop(call1, ':');
if (strcmp(STREAM->RemoteCall, call1))
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
// As the Disc ACK isn't repeated, we have to clear session now
STREAM->Connected = FALSE;
STREAM->Connecting = FALSE;
STREAM->ReportDISC = TRUE;
strcpy(TNC->WEB_PROTOSTATE, "Disconnected");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
ARQ->ARQState = 0;
SendLen = sprintf(Reply, "b%s:91", STREAM->MyCall);
ARQ->ARQTimerState = ARQ_WAITACK;
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
ARQ->Retries = 2;
return;
}
if (CTRL == 'b')
{
// Disconnect ACK
char * call1;
char * context;
call1 = strtok_s(&Input[1], " ", &context);
strlop(call1, ':');
if (strcmp(STREAM->RemoteCall, call1))
return;
if (Channel != ARQ->OurStream)
return; // Wrong Session
ARQ->ARQTimer = 0;
ARQ->ARQTimerState = 0;
ARQ->ARQState = 0;
if (STREAM->Connected)
{
// Create a traffic record
char logmsg[120];
time_t Duration;
Duration = time(NULL) - STREAM->ConnectTime;
if (Duration == 0)
Duration = 1;
sprintf(logmsg,"Port %2d %9s Bytes Sent %d BPS %d Bytes Received %d BPS %d Time %d Seconds",
TNC->Port, STREAM->RemoteCall,
STREAM->BytesTXed, (int)(STREAM->BytesTXed/Duration),
STREAM->BytesRXed, (int)(STREAM->BytesRXed/Duration), (int)Duration);
Debugprintf(logmsg);
}
STREAM->Connecting = FALSE;
STREAM->Connected = FALSE; // Back to Command Mode
STREAM->ReportDISC = TRUE; // Tell Node
if (STREAM->Disconnecting) //
FLReleaseTNC(TNC);
STREAM->Disconnecting = FALSE;
strcpy(TNC->WEB_PROTOSTATE, "Disconnected");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
return;
}
if (CTRL == 'u')
{
// Beacon
//>00uGM8BPQ:72 GM8BPQ TestingAD67
char * Call = &Input[1];
strlop(Call, ':');
UpdateMH(TNC, Call, '!', 0);
return;
}
if (STREAM->Connected)
{
if (Channel != ARQ->OurStream)
return; // Wrong Session
if (CTRL >= ' ' && CTRL < 96)
{
// ARQ Data
int Seq = CTRL - 32;
int Work;
// if (rand() % 5 == 2)
// {
// Debugprintf("Dropping %d", Seq);
// return;
// }
buffptr = GetBuff();
if (buffptr == NULL)
return; // Should never run out, but cant do much else
// Remove any DLE transparency
if (TNC->FLInfo->KISSMODE)
Len -= 1;
else
Len = UnStuff(&Input[1], Len - 1);
buffptr->Len = Len;
memcpy(buffptr->Data, &Input[1], Len);
STREAM->BytesRXed += Len;
UpdateStatsLine(TNC, STREAM);
// Safest always to save, then see what we can process
if (ARQ->RXHOLDQ[Seq])
{
// Wot! Shouldn't happen
ReleaseBuffer(ARQ->RXHOLDQ[Seq]);
// Debugprintf("ARQ Seq %d Duplicate");
}
ARQ->RXHOLDQ[Seq] = buffptr;
// Debugprintf("ARQ saving %d", Seq);
// If this is higher that highest received, save. But beware of wrap'
// Hi = 2, Seq = 60 dont save s=h = 58
// Hi = 10 Seq = 12 save s-h = 2
// Hi = 14 Seq = 10 dont save s-h = -4
// Hi = 60 Seq = 2 save s-h = -58
Work = Seq - ARQ->RXHighest;
if ((Work > 0 && Work < 32) || Work < -32)
ARQ->RXHighest = Seq;
// We may now be able to process some
Work = (ARQ->RXNoGaps + 1) & 63; // The next one we need
while (ARQ->RXHOLDQ[Work])
{
// We have it
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, ARQ->RXHOLDQ[Work]);
ARQ->RXHOLDQ[Work] = NULL;
// Debugprintf("Processing %d from Q", Work);
ARQ->RXNoGaps = Work;
Work = (Work + 1) & 63; // The next one we need
}
ARQ->TurnroundTimer = 200; // Delay before allowing reply. Will normally be reset by the poll following data
return;
}
}
}
VOID SendARQData(struct TNCINFO * TNC, PMSGWITHLEN Buffer)
{
// Send Data, saving a copy until acked.
struct ARQINFO * ARQ = TNC->ARQInfo;
struct FLINFO * FL = TNC->FLInfo;
struct STREAMINFO * STREAM = &TNC->Streams[0];
UCHAR TXBuffer[300];
SOCKET sock = TNC->TCPDataSock;
int SendLen;
UCHAR * ptr;
int Origlen = Buffer->Len;
int Stuffedlen;
ARQ->TXSeq++;
ARQ->TXSeq &= 63;
SendLen = sprintf(TXBuffer, "%c", ARQ->TXSeq + 32);
ptr = (UCHAR *)&Buffer->Data; // Start of data;
ptr[Buffer->Len] = 0;
if (memcmp(ptr, "ARQ:", 4) == 0)
{
// FLARQ Mail/FIle transfer. Turn off CR > LF translate (used for terminal mode)
FL->FLARQ = FALSE;
}
if (FL->FLARQ)
{
// Terminal Mode. Need to convert CR to LF so it displays in FLARQ Window
ptr = strchr(ptr, 13);
while (ptr)
{
*(ptr++) = 10; // Replace CR with LF
ptr = strchr(ptr, 13);
}
}
if (TNC->FLInfo->KISSMODE)
{
memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer->Data, Origlen);
SendLen += Origlen;
}
else
{
Stuffedlen = Stuff((UCHAR *)&Buffer->Data, &TXBuffer[SendLen], Origlen);
SendLen += Stuffedlen;
}
TXBuffer[SendLen] = 0;
// if (rand() % 5 == 2)
// Debugprintf("Dropping %d", ARQ->TXSeq);
// else
ARQ->TXHOLDQ[ARQ->TXSeq] = Buffer;
STREAM->BytesTXed += Origlen;
UpdateStatsLine(TNC, STREAM);
// if waiting for ack, don't send, just queue. Will be sent when ack received
if (ARQ->ARQTimer == 0 || ARQ->ARQTimerState == ARQ_WAITDATA)
{
SendPacket(TNC, TXBuffer, SendLen);
ARQ->ARQTimer = 15; // wait up to 1.5 sec for more data before polling
ARQ->Retries = 1;
ARQ->ARQTimerState = ARQ_WAITDATA;
}
else
STREAM->BytesResent -= Origlen; // So wont be included in resent bytes
}
VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
char Reply[80];
int SendLen;
struct ARQINFO * ARQ = TNC->ARQInfo;
SendLen = sprintf(Reply, "d%s:90", TNC->Streams[0].MyCall);
SaveAndSend(TNC, ARQ, TNC->TCPDataSock, Reply, SendLen);
ARQ->ARQTimerState = ARQ_DISC;
strcpy(TNC->WEB_PROTOSTATE, "Disconnecting");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
}
VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
TidyClose(TNC, Stream); // I don't think Hostmode has a DD
}
VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
FLReleaseTNC(TNC);
}
VOID FLReleaseTNC(struct TNCINFO * TNC)
{
// Set mycall back to Node or Port Call, and Start Scanner
UCHAR TXMsg[1000];
strcpy(TNC->WEB_TNCSTATE, "Free");
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
// if a default Modem is defined, select it
if (TNC->FLInfo->DefaultMode[0])
{
char txbuff[80];
if (TNC->FLInfo->KISSMODE)
{
sprintf(txbuff, "WFF:%d MODEM:%s MODEM: WFF:", TNC->FLInfo->DefaultFreq, TNC->FLInfo->DefaultMode);
SendKISSCommand(TNC, txbuff);
}
else
{
SendXMLCommand(TNC, "modem.set_by_name", TNC->FLInfo->DefaultMode, 'S');
SendXMLCommand(TNC, "modem.set_carrier", (char *)TNC->FLInfo->DefaultFreq, 'I');
}
}
// Start Scanner
sprintf(TXMsg, "%d SCANSTART 15", TNC->Port);
Rig_Command( (TRANSPORTENTRY *) -1, TXMsg);
ReleaseOtherPorts(TNC);
}
VOID QueueAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen)
{
// Queue to be sent after TXDELAY
memcpy(ARQ->TXMsg, Msg, MsgLen + 1);
ARQ->TXLen = MsgLen;
ARQ->TXDelay = 15; // Try 1500 ms
}
VOID SaveAndSend(struct TNCINFO * TNC, struct ARQINFO * ARQ, SOCKET sock, char * Msg, int MsgLen)
{
// Used for Messages that need a reply. Save, send and set timeout
memcpy(ARQ->LastMsg, Msg, MsgLen + 1); // Include Null
ARQ->LastLen = MsgLen;
// Delay the send for a short while Just use the timeout code
// SendPacket(sock, Msg, MsgLen, 0);
ARQ->ARQTimer = 1; // Try 500 ms
ARQ->Retries = TNC->Retries + 1; // First timout is rthe real send
return;
}
VOID ARQTimer(struct TNCINFO * TNC)
{
struct ARQINFO * ARQ = TNC->ARQInfo;
PMSGWITHLEN buffptr;
struct STREAMINFO * STREAM = &TNC->Streams[0];
int SendLen;
char Reply[80];
struct FLINFO * FL = TNC->FLInfo;
//Send frames, unless held by TurnroundTimer or Window
int Outstanding;
// Use new BUSY: poll to detect busy state
if (FL->TX == FALSE)
if (TNC->FLInfo->KISSMODE)
SendKISSCommand(TNC, "BUSY:"); // Send every poll for now - may need to optimize later
/*
// Use Received chars as a rough channel active indicator
FL->BusyTimer++;
if (FL->BusyTimer > 4)
{
FL->BusyTimer = 0;
if (FL->BusyCounter > 2) // 2 chars in last .3 secs
FL->Busy = TRUE;
else
FL->Busy = FALSE;
if (FL->TX)
strcpy(TNC->WEB_CHANSTATE, "TX");
else
if (FL->Busy)
strcpy(TNC->WEB_CHANSTATE, "Busy");
else
strcpy(TNC->WEB_CHANSTATE, "Idle");
FL->BusyCounter = 0;
SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE);
}
*/ // TXDelay is used as a turn round delay for frames that don't have to be retried. It doesn't
// need to check for busy (or anything else (I think!)
if (ARQ->TXDelay)
{
ARQ->TXDelay--;
if (ARQ->TXDelay)
return;
SendPacket(TNC, ARQ->TXMsg, ARQ->TXLen);
}
// if We are alredy sending (State = ARQ_WAITDATA) we should allow it to send more (and the Poll at end)
if (ARQ->ARQTimerState == ARQ_WAITDATA)
{
while (STREAM->BPQtoPACTOR_Q)
{
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames
if (Outstanding > ARQ->TXWindow)
break;
buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
SendARQData(TNC, buffptr);
}
ARQ->ARQTimer--;
if (ARQ->ARQTimer > 0)
return; // Timer Still Running
// No more data available - send poll
SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall);
ARQ->ARQTimerState = ARQ_WAITACK;
// This is one message that should not be queued so it is sent straiget after data
// Debugprintf("Sending Poll");
memcpy(ARQ->LastMsg, Reply, SendLen + 1);
ARQ->LastLen = SendLen;
SendPacket(TNC, Reply, SendLen);
ARQ->ARQTimer = TNC->Timeout;
ARQ->Retries = TNC->Retries;
strcpy(TNC->WEB_PROTOSTATE, "Wait ACK");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
return;
}
// TrunroundTimer is used to allow time for far end to revert to RX
if (ARQ->TurnroundTimer && !FL->Busy)
ARQ->TurnroundTimer--;
if (ARQ->TurnroundTimer == 0)
{
while (STREAM->BPQtoPACTOR_Q)
{
if (ARQ->ARQState != ARQ_ACTIVE)
break;
if (ARQ->ARQTimerState == ARQ_CONNECTACK)
break;
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q) + 1; // Make sure busy is reported to BBS
if (Outstanding > ARQ->TXWindow)
break;
buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q);
SendARQData(TNC, buffptr);
}
}
if (ARQ->ARQTimer)
{
if (FL->TX || FL->Busy)
{
// Only decrement if running send poll timer
if (ARQ->ARQTimerState != ARQ_WAITDATA)
return;
}
ARQ->ARQTimer--;
{
if (ARQ->ARQTimer)
return; // Timer Still Running
}
ARQ->Retries--;
if (ARQ->Retries)
{
// Retry Current Message
SendPacket(TNC, ARQ->LastMsg, ARQ->LastLen);
ARQ->ARQTimer = TNC->Timeout + (rand() % 30);
return;
}
// Retried out.
switch (ARQ->ARQTimerState)
{
case ARQ_WAITDATA:
// No more data available - send poll
SendLen = sprintf(Reply, "p%s", TNC->Streams[0].MyCall);
ARQ->ARQTimerState = ARQ_WAITACK;
// This is one message that should not be queued so it is sent straiget after data
memcpy(ARQ->LastMsg, Reply, SendLen + 1);
ARQ->LastLen = SendLen;
SendPacket(TNC, Reply, SendLen);
ARQ->ARQTimer = TNC->Timeout;
ARQ->Retries = TNC->Retries;
strcpy(TNC->WEB_PROTOSTATE, "Wait ACK");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
return;
case ARQ_CONNECTING:
// Report Connect Failed, and drop back to command mode
buffptr = GetBuff();
if (buffptr)
{
buffptr->Len = sprintf(buffptr->Data, "FLDigi} Failure with %s\r", STREAM->RemoteCall);
C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr);
}
// Send Disc to TNC in case it got the Connects, but we missed the ACKs
TidyClose(TNC, 0);
ARQ->Retries = 2; // First timout is the real send, only send once
STREAM->Connecting = FALSE; // Back to Command Mode
ARQ->ARQState = FALSE;
break;
case ARQ_WAITACK:
case ARQ_CONNECTACK:
case ARQ_DISC:
STREAM->Connected = FALSE; // Back to Command Mode
STREAM->ReportDISC = TRUE;
ARQ->ARQState = FALSE;
while (STREAM->PACTORtoBPQ_Q)
ReleaseBuffer(Q_REM(&STREAM->PACTORtoBPQ_Q));
while (STREAM->BPQtoPACTOR_Q)
ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q));
strcpy(TNC->WEB_TNCSTATE, "Free");
SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
strcpy(TNC->WEB_PROTOSTATE, "Disconncted");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
break;
}
}
}
VOID ProcessARQStatus(struct TNCINFO * TNC, struct ARQINFO * ARQ, char * Input)
{
// Release any acked frames and resend any outstanding
int LastInSeq = Input[1] - 32;
int LastRXed = Input[2] - 32;
int FirstUnAcked = ARQ->TXLastACK;
int n = (int)strlen(Input) - 3;
char * ptr;
int NexttoResend;
int First, Last, Outstanding;
PMSGWITHLEN Buffer;
struct STREAMINFO * STREAM = &TNC->Streams[0];
int Acked = 0;
// First status is an ack of Connect ACK
if (ARQ->ARQTimerState == ARQ_CONNECTACK)
{
ARQ->Retries = 0;
ARQ->ARQTimer = 0;
ARQ->ARQTimerState = 0;
strcpy(TNC->WEB_PROTOSTATE, "Connected");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
}
// Release all up to LastInSeq
while (FirstUnAcked != LastInSeq)
{
FirstUnAcked++;
FirstUnAcked &= 63;
Buffer = ARQ->TXHOLDQ[FirstUnAcked];
if (Buffer)
{
// Debugprintf("Acked %d", FirstUnAcked);
STREAM->BytesAcked += Buffer->Len;
ReleaseBuffer(Buffer);
ARQ->TXHOLDQ[FirstUnAcked] = NULL;
Acked++;
}
}
ARQ->TXLastACK = FirstUnAcked;
Outstanding = ARQ->TXSeq - ARQ->TXLastACK;
if (Outstanding < 0)
Outstanding += 64;
TNC->PortRecord->FramesQueued = Outstanding + C_Q_COUNT(&STREAM->BPQtoPACTOR_Q); // Save for Appl Level Queued Frames
if (FirstUnAcked == ARQ->TXSeq)
{
UpdateStatsLine(TNC, STREAM);
ARQ->NoAckRetries = 0;
strcpy(TNC->WEB_PROTOSTATE, "Connected");
SetWindowText(TNC->xIDC_PROTOSTATE, TNC->WEB_PROTOSTATE);
return; // All Acked
}
// Release any not in retry list up to LastRXed.
ptr = &Input[3];
while (n)
{
NexttoResend = *(ptr++) - 32;
FirstUnAcked++;
FirstUnAcked &= 63;
while (FirstUnAcked != NexttoResend)
{
Buffer = ARQ->TXHOLDQ[FirstUnAcked];
if (Buffer)
{
// Debugprintf("Acked %d", FirstUnAcked);
STREAM->BytesAcked += Buffer->Len;
ReleaseBuffer(Buffer);
ARQ->TXHOLDQ[FirstUnAcked] = NULL;
Acked++;
}
FirstUnAcked++;
FirstUnAcked &= 63;
}
// We don't ACK this one. Process any more resend values, then release up to LastRXed.
n--;
}
// Release rest up to LastRXed
while (FirstUnAcked != LastRXed)
{
FirstUnAcked++;
FirstUnAcked &= 63;
Buffer = ARQ->TXHOLDQ[FirstUnAcked];
if (Buffer)
{
// Debugprintf("Acked %d", FirstUnAcked);
STREAM->BytesAcked += Buffer->Len;
ReleaseBuffer(Buffer);
ARQ->TXHOLDQ[FirstUnAcked] = NULL;
Acked++;
}
}
// Resend anything in TX Buffer (From LastACK to TXSeq
Last = ARQ->TXSeq + 1;
Last &= 63;
First = LastInSeq;
while (First != Last)
{
First++;
First &= 63;
if(ARQ->TXHOLDQ[First])
{
PMSGWITHLEN Buffer = ARQ->TXHOLDQ[First];
UCHAR TXBuffer[300];
SOCKET sock = TNC->TCPDataSock;
int SendLen;
// Debugprintf("Resend %d", First);
STREAM->BytesResent += Buffer->Len;
SendLen = sprintf(TXBuffer, "%c", First + 32);
if (TNC->FLInfo->KISSMODE)
{
memcpy(&TXBuffer[SendLen], (UCHAR *)&Buffer->Data, Buffer->Len);
SendLen += Buffer->Len;
}
else
SendLen += Stuff((UCHAR *)&Buffer->Data, &TXBuffer[SendLen], Buffer->Len);
TXBuffer[SendLen] = 0;
SendPacket(TNC, TXBuffer, SendLen);
ARQ->ARQTimer = 10; // wait up to 1 sec for more data before polling
ARQ->Retries = 1;
ARQ->ARQTimerState = ARQ_WAITDATA;
if (Acked == 0)
{
// Nothing acked by this statis message
Acked = 0; // Dont count more thna once
ARQ->NoAckRetries++;
if (ARQ->NoAckRetries > TNC->Retries)
{
// Too many retries - just disconnect
TidyClose(TNC, 0);
return;
}
}
}
}
UpdateStatsLine(TNC, STREAM);
}
VOID FLSlowTimer(struct TNCINFO * TNC)
{
struct FLINFO * FL = TNC->FLInfo;
// Entered every 10 secs
// if in MCAST mode, clear KILL timer (MCAST RX can run for a long time
if (TNC->FLInfo->MCASTMODE)
{
TRANSPORTENTRY * SESS = TNC->PortRecord->ATTACHEDSESSIONS[0];
if (SESS)
SESS->L4KILLTIMER = 0;
}
if (FL->KISSMODE)
{
if (FL->Responding)
FL->Responding--;
if (FL->Responding == 0)
{
TNC->TNCOK = FALSE;
TNC->CONNECTED = FALSE;
sprintf(TNC->WEB_COMMSSTATE, "Connection to FLDIGI lost");
SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
// Set basic params till it responds
}
FL->CmdControl++;
if (FL->CmdControl > 5) // Every Minute
{
FL->CmdControl = 0;
SendKISSCommand(TNC, "FLSTAT: MODEM: WFF:");
}
SendKISSCommand(TNC, "TRXS: TXBUF:"); // In case TX/RX report is missed
}
}
static int ProcessXMLData(int port)
{
unsigned int bytes;
int i;
char ErrMsg[255];
char Message[500];
struct TNCINFO * TNC = TNCInfo[port];
struct FLINFO * FL = TNC->FLInfo;
char * ptr1, * ptr2, *ptr3;
// Need to extract messages from byte stream
bytes = recv(TNC->TCPSock,(char *)&Message, 500, 0);
if (bytes == SOCKET_ERROR)
{
// i=sprintf(ErrMsg, "Read Failed for FLDigi socket - error code = %d\r\n", WSAGetLastError());
// WritetoConsole(ErrMsg);
closesocket(TNC->TCPSock);
TNC->CONNECTED = FALSE;
if (TNC->Streams[0].Attached)
TNC->Streams[0].ReportDISC = TRUE;
return (0);
}
if (bytes == 0)
{
// zero bytes means connection closed
i=sprintf(ErrMsg, "FlDigi Connection closed for BPQ Port %d\n", port);
WritetoConsole(ErrMsg);
TNC->CONNECTED = FALSE;
if (TNC->Streams[0].Attached)
TNC->Streams[0].ReportDISC = TRUE;
return (0);
}
// Have some data. Assume for now we get a whole packet
if (TNC->InternalCmd)
{
PMSGWITHLEN buffptr = GetBuff();
TNC->InternalCmd = FALSE;
ptr1 = strstr(Message, "<value>");
if (ptr1)
{
ptr1 += 7;
ptr2 = strstr(ptr1, "</value>");
if (ptr2) *ptr2 = 0;
ptr3 = strstr(ptr1, "<i4>");
if (ptr3)
{
ptr1 = ptr3 + 4;
ptr2 = strstr(ptr1, "</i4>");
if (ptr2) *ptr2 = 0;
}
if (buffptr)
{
buffptr->Len = sprintf(buffptr->Data, "FLDIGI} Ok Was %s\r", ptr1);
C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr);
}
}
return 0;
}
ptr1 = strstr(Message, "<value>");
if (ptr1)
{
ptr1 += 7;
ptr2 = strstr(ptr1, "</value>");
if (ptr2) *ptr2 = 0;
ptr2 = strstr(ptr1, "<string>");
if (ptr2)
{
ptr2 += 8;
ptr1 = ptr2;
ptr2 = strstr(ptr1, "</string>");
if (ptr2) *ptr2 = 0;
}
if (strcmp(FL->LastXML, "modem.get_name") == 0)
{
strcpy(TNC->WEB_MODE, ptr1);
SetWindowText(TNC->xIDC_MODE, ptr1);
}
else if (strcmp(FL->LastXML, "main.get_trx_state") == 0)
{
if (strcmp(ptr1, "TX") == 0)
FL->TX = TRUE;
else
FL->TX = FALSE;
if (FL->TX)
strcpy(TNC->WEB_CHANSTATE, "TX");
else
if (FL->Busy)
strcpy(TNC->WEB_CHANSTATE, "Busy");
else
strcpy(TNC->WEB_CHANSTATE, "Idle");
SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE);
}
else if (strcmp(FL->LastXML, "main.get_squelch") == 0)
{
/*
if (_memicmp(Buffer, "BUSY TRUE", 9) == 0)
{
TNC->BusyFlags |= CDBusy;
TNC->Busy = TNC->BusyHold * 10; // BusyHold delay
SetWindowText(TNC->xIDC_CHANSTATE, "Busy");
strcpy(TNC->WEB_CHANSTATE, "Busy");
TNC->WinmorRestartCodecTimer = time(NULL);
*/
return 0;
}
/*
if (_memicmp(Buffer, "BUSY FALSE", 10) == 0)
{
TNC->BusyFlags &= ~CDBusy;
if (TNC->BusyHold)
strcpy(TNC->WEB_CHANSTATE, "BusyHold");
else
strcpy(TNC->WEB_CHANSTATE, "Clear");
SetWindowText(TNC->xIDC_CHANSTATE, TNC->WEB_CHANSTATE);
TNC->WinmorRestartCodecTimer = time(NULL);
return;
}
*/
}
return (0);
}
char MsgHddr[] = "POST /RPC2 HTTP/1.1\r\n"
"User-Agent: XMLRPC++ 0.8\r\n"
"Host: 127.0.0.1:7362\r\n"
"Content-Type: text/xml\r\n"
"Content-length: %d\r\n"
"\r\n%s";
char Req[] = "<?xml version=\"1.0\"?>\r\n"
"<methodCall><methodName>%s</methodName>\r\n"
"%s"
"</methodCall>\r\n";
VOID SendXMLCommand(struct TNCINFO * TNC, char * Command, char * Value, char ParamType)
{
int Len;
char ReqBuf[512];
char SendBuff[512];
struct FLINFO * FL = TNC->FLInfo;
struct ARQINFO * ARQ = TNC->ARQInfo;
char ValueString[256] ="";
if (!TNC->CONNECTED || TNC->FLInfo->KISSMODE)
return;
if (Value)
if (ParamType == 'S')
sprintf(ValueString, "<params><param><value><string>%s</string></value></param></params\r\n>", Value);
else
sprintf(ValueString, "<params><param><value><i4>%d</i4></value></param></params\r\n>", Value);
strcpy(FL->LastXML, Command);
Len = sprintf(ReqBuf, Req, FL->LastXML, ValueString);
Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf);
send(TNC->TCPSock, SendBuff, Len, 0);
return;
}
VOID SendXMLPoll(struct TNCINFO * TNC)
{
int Len;
char ReqBuf[256];
char SendBuff[256];
struct FLINFO * FL = TNC->FLInfo;
struct ARQINFO * ARQ = TNC->ARQInfo;
if (!TNC->CONNECTED)
return;
if (TNC->FLInfo->KISSMODE)
return;
if (ARQ->ARQTimer)
{
// if timer is running, poll fot TX State
strcpy(FL->LastXML, "main.get_trx_state");
Len = sprintf(ReqBuf, Req, FL->LastXML, "");
Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf);
send(TNC->TCPSock, SendBuff, Len, 0);
return;
}
FL->XMLControl++;
if (FL->XMLControl > 9)
{
FL->XMLControl = 0;
strcpy(FL->LastXML, "modem.get_name");
}
else
{
if (FL->XMLControl == 5)
strcpy(FL->LastXML, "main.get_trx_state");
else
return;
}
Len = sprintf(ReqBuf, Req, FL->LastXML, "");
Len = sprintf(SendBuff, MsgHddr, Len, ReqBuf);
send(TNC->TCPSock, SendBuff, Len, 0);
}
// sudo add-apt-repository ppa:kamalmostafa/fldigi