3967 lines
88 KiB
C
3967 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;
|
|
|
|
void ConnecttoFLDigiThread(void * portptr);
|
|
|
|
void CreateMHWindow();
|
|
int Update_MH_List(struct in_addr ipad, char * call, char proto);
|
|
|
|
int ConnecttoFLDigi(int port);
|
|
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 SendXMLCommandInt(struct TNCINFO * TNC, char * Command, int 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);
|
|
|
|
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( <ime );
|
|
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
|
|
{
|
|
SendXMLCommandInt(TNC, "modem.set_carrier", 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, ¶m);
|
|
|
|
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);
|
|
}
|
|
|
|
int ConnecttoFLDigi(int port)
|
|
{
|
|
_beginthread(ConnecttoFLDigiThread, 0, (void *)(size_t)port);
|
|
|
|
return 0;
|
|
}
|
|
|
|
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,¶m); // 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(struct 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)
|
|
{
|
|
hookL4SessionDeleted(TNC, STREAM);
|
|
}
|
|
|
|
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');
|
|
SendXMLCommandInt(TNC, "modem.set_carrier", 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 SendXMLCommandInt(struct TNCINFO * TNC, char * Command, int 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;
|
|
|
|
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
|
|
|
|
|