/* Copyright 2001-2022 John Wiseman G8BPQ This file is part of LinBPQ/BPQ32. LinBPQ/BPQ32 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. LinBPQ/BPQ32 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses */ // // DLL to inteface DED Host Mode TNCs to BPQ32 switch // // Uses BPQ EXTERNAL interface #define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #include #include #include "time.h" #define MaxStreams 1 #include "CHeaders.h" extern int (WINAPI FAR *GetModuleFileNameExPtr)(); extern int (WINAPI FAR *EnumProcessesPtr)(); #include "tncinfo.h" #define SD_RECEIVE 0x00 #define SD_SEND 0x01 #define SD_BOTH 0x02 //#include "bpq32.h" static char ClassName[]="TRACKERSTATUS"; static char WindowTitle[] = "WinRPR"; static int RigControlRow = 140; #define NARROWMODE 30 #define WIDEMODE 30 // Robust extern UCHAR LogDirectory[]; extern APPLCALLS APPLCALLTABLE[]; extern char LOC[]; static RECT Rect; VOID __cdecl Debugprintf(const char * format, ...); char * strlop(char * buf, char delim); char NodeCall[11]; // Nodecall, Null Terminated struct TNCINFO * CreateTTYInfo(int port, int speed); BOOL OpenConnection(int); BOOL SetupConnection(int); BOOL CloseConnection(struct TNCINFO * conn); static BOOL WriteCommBlock(struct TNCINFO * TNC); BOOL DestroyTTYInfo(int port); static void DEDCheckRX(struct TNCINFO * TNC); VOID DEDPoll(int Port); VOID StuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len); unsigned short int compute_crc(unsigned char *buf,int len); int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len); VOID TrkProcessDEDFrame(struct TNCINFO * TNC); VOID TrkProcessTermModeResponse(struct TNCINFO * TNC); static VOID ExitHost(struct TNCINFO * TNC); VOID DoTNCReinit(struct TNCINFO * TNC); VOID DoTermModeTimeout(struct TNCINFO * TNC); VOID DoMonitorHddr(struct TNCINFO * TNC, UCHAR * Msg, int Len, int Type); VOID DoMonitorData(struct TNCINFO * TNC, UCHAR * Msg, int Len); int Switchmode(struct TNCINFO * TNC, int Mode); VOID SwitchToRPacket(struct TNCINFO * TNC, char * Baud); VOID SwitchToNormPacket(struct TNCINFO * TNC, char * Baud); VOID SendRPBeacon(struct TNCINFO * TNC); BOOL APIENTRY Send_AX(PMESSAGE Block, DWORD Len, UCHAR Port); 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); BOOL KAMStartPort(struct PORTCONTROL * PORT); BOOL KAMStopPort(struct PORTCONTROL * PORT); extern VOID TRKSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC); extern VOID TRKReleasePort(struct TNCINFO * TNC); extern VOID CloseDebugLogFile(int Port); extern BOOL OpenDebugLogFile(int Port); extern void WriteDebugLogLine(int Port, char Dirn, char * Msg, int MsgLen); extern VOID TrkTidyClose(struct TNCINFO * TNC, int Stream); extern VOID TrkForcedClose(struct TNCINFO * TNC, int Stream); extern VOID TrkCloseComplete(struct TNCINFO * TNC, int Stream); VOID TrkExitHost(struct TNCINFO * TNC); int ConnecttoWinRPR(int port); BOOL KillOldTNC(char * Path); int KillTNC(struct TNCINFO * TNC); static BOOL RestartTNC(struct TNCINFO * TNC) { if (TNC->ProgramPath == NULL) return 0; 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 TNC %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 TNC - sendto returned %d", n); Sleep(100); closesocket(sock); return 1; // Cant tell if it worked, but assume ok } // Not Remote // Extract any parameters from command string #ifndef WIN32 { char * arg_list[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}; pid_t child_pid; char * Copy, * Context; signal(SIGCHLD, SIG_IGN); // Silently (and portably) reap children. Copy = _strdup(TNC->ProgramPath); // Save as strtok mangles it arg_list[0] = strtok_s(Copy, " \n\r", &Context); if (arg_list[0]) arg_list[1] = strtok_s(NULL, " \n\r", &Context); if (arg_list[1]) arg_list[2] = strtok_s(NULL, " \n\r", &Context); if (arg_list[2]) arg_list[3] = strtok_s(NULL, " \n\r", &Context); if (arg_list[3]) arg_list[4] = strtok_s(NULL, " \n\r", &Context); if (arg_list[4]) arg_list[5] = strtok_s(NULL, " \n\r", &Context); if (arg_list[5]) arg_list[6] = strtok_s(NULL, " \n\r", &Context); if (arg_list[6]) arg_list[7] = strtok_s(NULL, " \n\r", &Context); // Fork and Exec TNC printf("Trying to start %s\n", TNC->ProgramPath); /* Duplicate this process. */ child_pid = fork (); if (child_pid == -1) { printf ("StartTNC fork() Failed\n"); free(Copy); return 0; } if (child_pid == 0) { execvp (arg_list[0], arg_list); /* The execvp function returns only if an error occurs. */ printf ("Failed to start TNC\n"); exit(0); // Kill the new process } printf("Started TNC\n"); free(Copy); return TRUE; } #else { int n = 0; STARTUPINFO SInfo; // pointer to STARTUPINFO PROCESS_INFORMATION PInfo; // pointer to PROCESS_INFORMATION char workingDirectory[256]; int i = strlen(TNC->ProgramPath); SInfo.cb=sizeof(SInfo); SInfo.lpReserved=NULL; SInfo.lpDesktop=NULL; SInfo.lpTitle=NULL; SInfo.dwFlags=0; SInfo.cbReserved2=0; SInfo.lpReserved2=NULL; Debugprintf("RestartTNC Called for %s", TNC->ProgramPath); strcpy(workingDirectory, TNC->ProgramPath); while (i--) { if (workingDirectory[i] == '\\' || workingDirectory[i] == '/') { workingDirectory[i] = 0; break; } } while (KillOldTNC(TNC->ProgramPath) && n++ < 100) { Sleep(100); } if (CreateProcess(NULL, TNC->ProgramPath, NULL, NULL, FALSE,0, NULL, workingDirectory, &SInfo, &PInfo)) { Debugprintf("Restart TNC OK"); TNC->PID = PInfo.dwProcessId; return TRUE; } else { Debugprintf("Restart TNC Failed %d ", GetLastError()); return FALSE; } } #endif return 0; } static int ProcessLine(char * buf, int Port) { UCHAR * ptr,* p_cmd; char * p_port = 0; int BPQport; int len=510; struct TNCINFO * TNC; char errbuf[256]; char * p_ipad = 0; unsigned short WINMORport = 0; 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] = malloc(sizeof(struct TNCINFO)); memset(TNC, 0, sizeof(struct TNCINFO)); strcpy(TNC->NormSpeed, "300"); // HF Packet strcpy(TNC->RobustSpeed, "R600"); TNC->DefaultMode = TNC->WL2KMode = 0; // Packet 1200 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); WINMORport = atoi(p_port); TNC->destaddr.sin_family = AF_INET; TNC->destaddr.sin_port = htons(WINMORport); TNC->Datadestaddr.sin_family = AF_INET; TNC->Datadestaddr.sin_port = htons(WINMORport+1); 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 (_stricmp(ptr, "PTT") == 0) { ptr = strtok(NULL, " \t\n\r"); if (ptr) { DecodePTTString(TNC, ptr); 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); } } 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, "APPL", 4) == 0) { p_cmd = strtok(&buf[4], " \t\n\r"); if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#') TNC->ApplCmd=_strdup(_strupr(p_cmd)); } else // if (_mem`icmp(buf, "PACKETCHANNELS", 14) == 0) // // Packet Channels // TNC->PacketChannels = atoi(&buf[14]); // else if (_memicmp(buf, "SWITCHMODES", 11) == 0) { // Switch between Normal and Robust Packet double Robust = atof(&buf[12]); #pragma warning(push) #pragma warning(disable : 4244) TNC->RobustTime = Robust * 10; #pragma warning(pop) } if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log TNC->WRITELOG = atoi(&buf[9]); else if (_memicmp(buf, "BEACONAFTERSESSION", 18) == 0) // Send Beacon after each session TNC->RPBEACON = TRUE; else if (_memicmp(buf, "USEAPPLCALLS", 12) == 0) TNC->UseAPPLCalls = TRUE; else if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) TNC->RobustDefault = TRUE; else if (_memicmp(buf, "FORCE ROBUST", 12) == 0) TNC->ForceRobust = TNC->RobustDefault = TRUE; else if (_memicmp(buf, "UPDATEMAP", 9) == 0) TNC->PktUpdateMap = TRUE; else if (_memicmp(buf, "WL2KREPORT", 10) == 0) TNC->WL2K = DecodeWL2KReportLine(buf); else if (standardParams(TNC, buf) == FALSE) { strcat (TNC->InitScript, buf); // If %B param,and not R300 or R600 extract speed if (_memicmp(buf, "%B", 2) == 0) { ptr = strchr(buf, '\r'); if (ptr) *ptr = 0; if (strchr(buf, 'R') == 0) strcpy(TNC->NormSpeed, &buf[3]); else strcpy(TNC->RobustSpeed, &buf[3]); } } } return (TRUE); } static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { PMSGWITHLEN buffptr; int txlen = 0; struct TNCINFO * TNC = TNCInfo[port]; int Param; int Stream = 0; struct STREAMINFO * STREAM; int TNCOK; struct ScanEntry * Scan; int NewMode; if (TNC == NULL) return 0; if (TNC->CONNECTED == 0 && TNC->CONNECTING == 0) { // Clear anything from UI_Q while (TNC->PortRecord->UI_Q) { buffptr = Q_REM(&TNC->PortRecord->UI_Q); ReleaseBuffer(buffptr); } if (fn > 3 && fn < 6) goto ok; // Try to reopen every 15 secs if (fn > 3 && fn < 7) goto ok; TNC->ReopenTimer++; if (TNC->ReopenTimer < 150) return 0; TNC->ReopenTimer = 0; ConnecttoWinRPR(TNC->Port); return 0; } ok: if (TNC->CONNECTED == 0) return 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 connect after changing MYCALL if (TNC->SlowTimer) { TNC->SlowTimer--; if (TNC->SlowTimer == 0) { // Not connected in 45 secs, set back to Port Call Debugprintf("RP No response after changing call - setting MYCALL back to %s", TNC->NodeCall); TRKReleasePort(TNC); } } return 0; case 1: // poll for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->Streams[Stream].ReportDISC) { TNC->Streams[Stream].ReportDISC = FALSE; buff->PORT = Stream; return -1; } } DEDCheckRX(TNC); DEDPoll(port); DEDCheckRX(TNC); for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) { size_t datalen; buffptr = Q_REM(&TNC->Streams[Stream].PACTORtoBPQ_Q); datalen = buffptr->Len; buff->PORT = Stream; // Compatibility with Kam Driver buff->PID = 0xf0; memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte datalen += sizeof(void *) + 4; PutLengthinBuffer(buff, (int)datalen); ReleaseBuffer(buffptr); return (1); } } return 0; case 2: // send buffptr = GetBuff(); if (buffptr == 0) return (0); // No buffers, so ignore Stream = buff->PORT; if (!TNC->TNCOK) { // Send Error Response buffptr->Len = 22; memcpy(buffptr->Data, "No Connection to TNC\r", 22); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return 0; } txlen = GetLengthfromBuffer(buff) - (sizeof(void *) + 4); buffptr->Len = txlen; memcpy(&buffptr->Data[0], &buff->L2DATA[0], txlen); C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); TNC->Streams[Stream].FramesOutstanding++; return (0); case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding Stream = (int)(size_t)buff; TNCOK = (TNC->HostMode == 1 && TNC->ReinitState != 10); STREAM = &TNC->Streams[Stream]; if (Stream == 0) { if (STREAM->FramesOutstanding > 4) return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); } else { if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) return (1 | TNCOK << 8 | STREAM->Disconnecting << 15); } return TNCOK << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting case 4: // reinit TrkExitHost(TNC); Sleep(50); TNC->ReopenTimer = 250; TNC->HostMode = FALSE; shutdown(TNC->TCPSock, SD_BOTH); Sleep(100); closesocket(TNC->TCPSock); if (TNC->WeStartedTNC) { KillTNC(TNC); RestartTNC(TNC); } return 0; case 5: // Close // Ensure in Pactor TrkExitHost(TNC); Sleep(25); shutdown(TNC->TCPSock, SD_BOTH); Sleep(100); closesocket(TNC->TCPSock); if (TNC->WeStartedTNC) KillTNC(TNC); return (0); case 6: // Scan Interface Param = (int)(size_t)buff; switch (Param) { case 1: // Request Permission if (TNC->TNCOK) { TNC->WantToChangeFreq = TRUE; TNC->OKToChangeFreq = TRUE; return 0; } return 0; // Don't lock scan if TNC isn't reponding case 2: // Check Permission return TNC->OKToChangeFreq; case 3: // Release Permission TNC->WantToChangeFreq = FALSE; TNC->DontWantToChangeFreq = TRUE; return 0; default: // Change Mode. Param is Address of a struct ScanEntry Scan = (struct ScanEntry *)buff; // If no change, just return NewMode = Scan->RPacketMode | (Scan->HFPacketMode << 8); if (TNC->CurrentMode == NewMode) return 0; TNC->CurrentMode = NewMode; if (Scan->RPacketMode == '1') { SwitchToRPacket(TNC, "R300"); return 0; } if (Scan->RPacketMode == '2') { SwitchToRPacket(TNC, "R600"); return 0; } if (Scan->HFPacketMode == '1') { SwitchToNormPacket(TNC, "300"); return 0; } if (Scan->HFPacketMode == '2') { SwitchToNormPacket(TNC, "1200"); return 0; } if (Scan->HFPacketMode == '3') { SwitchToNormPacket(TNC, "9600"); return 0; } } } return 0; } int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) { int Interval = 15; int Len; if (LOCAL) { if (TNC->WEB_CHANGED) Interval = 1; else Interval = 4; } else { if (TNC->WEB_CHANGED) Interval = 4; else Interval = 15; } if (TNC->WEB_CHANGED) { TNC->WEB_CHANGED -= Interval; if (TNC->WEB_CHANGED < 0) TNC->WEB_CHANGED = 0; } Len = sprintf(Buff, "" "WinRPR Status

WinRPR Status

", Interval); Len += sprintf(&Buff[Len], ""); Len += sprintf(&Buff[Len], "", TNC->WEB_COMMSSTATE); Len += sprintf(&Buff[Len], "", TNC->WEB_TNCSTATE); Len += sprintf(&Buff[Len], "", TNC->WEB_MODE); Len += sprintf(&Buff[Len], "", TNC->WEB_BUFFERS); Len += sprintf(&Buff[Len], "", TNC->WEB_TRAFFIC); Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Buffers%s
Traffic%s
"); Len += sprintf(&Buff[Len], "", TNC->WebBuffer); Len = DoScanLine(TNC, Buff, Len); return Len; } void * WinRPRExtInit(EXTPORTDATA * PortEntry) { char msg[500]; struct TNCINFO * TNC; int port; char * ptr; int Stream = 0; char * TempScript; 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"); WritetoConsoleLocal(msg); return ExtProc; } sprintf(msg,"WinRPR Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port)); WritetoConsoleLocal(msg); TNC->Port = port; TNC->Hardware = H_WINRPR; if (TNC->ProgramPath) TNC->WeStartedTNC = RestartTNC(TNC); // Set up DED addresses for streams for (Stream = 0; Stream <= MaxStreams; Stream++) { TNC->Streams[Stream].DEDStream = Stream + 1; // DED Stream = BPQ Stream + 1 } if (TNC->PacketChannels > MaxStreams) TNC->PacketChannels = MaxStreams; PortEntry->MAXHOSTMODESESSIONS = 1; //TNC->PacketChannels + 1; PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream PortEntry->SCANCAPABILITIES = NONE; // Scan Control 3 stage/conlock TNC->PortRecord = PortEntry; if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) memcpy(TNC->NodeCall, MYNODECALL, 10); else ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); PortEntry->PORTCONTROL.PROTOCOL = 10; PortEntry->PORTCONTROL.UICAPABLE = 1; PortEntry->PORTCONTROL.PORTQUALITY = 0; if (PortEntry->PORTCONTROL.PORTPACLEN == 0) PortEntry->PORTCONTROL.PORTPACLEN = 100; if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; TNC->SuspendPortProc = TRKSuspendPort; TNC->ReleasePortProc = TRKReleasePort; // PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; // PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; TNC->PortRecord->PORTCONTROL.SerialPortName = _strdup(TNC->HostName); ptr=strchr(TNC->NodeCall, ' '); if (ptr) *(ptr) = 0; // Null Terminate // get NODECALL for RP tests memcpy(NodeCall, MYNODECALL, 10); ptr=strchr(NodeCall, ' '); if (ptr) *(ptr) = 0; // Null Terminate TempScript = malloc(1000); strcpy(TempScript, "M UISC\r"); strcat(TempScript, "F 200\r"); // Sets SABM retry time to about 5 secs strcat(TempScript, "%F 1500\r"); // Tones may be changed but I want this as standard strcat(TempScript, TNC->InitScript); free(TNC->InitScript); TNC->InitScript = TempScript; // Others go on end so they can't be overriden strcat(TNC->InitScript, "Z 0\r"); // No Flow Control strcat(TNC->InitScript, "Y 1\r"); // One Channel strcat(TNC->InitScript, "E 1\r"); // Echo - Restart process needs echo sprintf(msg, "I %s\r", TNC->NodeCall); strcat(TNC->InitScript, msg); strcpy(TNC->Streams[0].MyCall, TNC->NodeCall); // For 1st Connected Test PortEntry->PORTCONTROL.TNC = TNC; if (TNC->WL2K == NULL) if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded as part of main config section TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo; 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_MODE = zalloc(20); TNC->WEB_BUFFERS = zalloc(100); TNC->WEB_TRAFFIC = zalloc(100); #ifndef LINBPQ CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 450, TrkForcedClose); CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,10,120,24, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,10,386,26, TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,36,106,24, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,36,520,24, TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,62,80,24, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,62,200,24, TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "Buffers", WS_CHILD | WS_VISIBLE, 10,88,80,24, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE, 116,88,144,24, TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,114,80,24, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0", WS_CHILD | WS_VISIBLE,116,114,374,24 , 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; MoveWindows(TNC); #endif if (TNC->RobustDefault) { TNC->Robust = TRUE; strcat(TNC->InitScript, "%B R300\r"); SetWindowText(TNC->xIDC_MODE, "Robust Packet"); strcpy(TNC->WEB_MODE, "Robust Packet"); TNC->WEB_CHANGED = TRUE; } else { char Cmd[40]; sprintf(Cmd, "%%B %s\r", TNC->NormSpeed); strcat(TNC->InitScript, Cmd); SetWindowText(TNC->xIDC_MODE, "HF Packet"); strcpy(TNC->WEB_MODE, "HF Packet"); TNC->WEB_CHANGED = TRUE; } strcpy(TNC->WEB_TNCSTATE, "Idle"); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); TNC->InitPtr = TNC->InitScript; if (TNC->RIG == &TNC->DummyRig || TNC->RIG == NULL) TNC->SwitchToPactor = TNC->RobustTime; // Don't alternate Modes if using Rig Control WritetoConsoleLocal("\n"); ConnecttoWinRPR(TNC->Port); return ExtProc; } static void DEDCheckRX(struct TNCINFO * TNC) { int Length, Len; UCHAR * ptr; UCHAR character; UCHAR * CURSOR; Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); if (Len == 0) return; // Nothing doing TNC->RXLen += Len; Length = TNC->RXLen; ptr = TNC->RXBuffer; CURSOR = &TNC->DEDBuffer[TNC->InputLen]; if ((TNC->HostMode == 0 || TNC->ReinitState == 10) && Length > 80) { // Probably Signon Message if (TNC->WRITELOG) WriteDebugLogLine(TNC->Port, 'R', ptr, Length); ptr[Length] = 0; Debugprintf("TRK %s", ptr); TNC->RXLen = 0; return; } if (TNC->HostMode == 0) { // If we are just restarting, and TNC is in host mode, we may get "Invalid Channel" Back if (memcmp(ptr, "\x18\x02INVALID", 9) == 0) { if (TNC->WRITELOG) WriteDebugLogLine(TNC->Port, 'R', ptr, Length); TNC->HostMode = TRUE; TNC->HOSTSTATE = 0; TNC->Timeout = 0; TNC->RXLen = 0; return; } // Command is echoed as * command * if (strstr(ptr, "*") || TNC->ReinitState == 5) // 5 is waiting for reponse to JHOST1 { TrkProcessTermModeResponse(TNC); TNC->RXLen = 0; TNC->HOSTSTATE = 0; return; } } if (TNC->ReinitState == 10) { if (Length == 1 && *(ptr) == '.') // 01 echoed as . { // TNC is in Term Mode if (TNC->WRITELOG) WriteDebugLogLine(TNC->Port, 'R', ptr, Length); TNC->ReinitState = 0; TNC->HostMode = 0; return; } } while (Length--) { character = *(ptr++); if (TNC->HostMode) { // n 0 Success (nothing follows) // n 1 Success (message follows, null terminated) // n 2 Failure (message follows, null terminated) // n 3 Link Status (null terminated) // n 4 Monitor Header (null terminated) // n 5 Monitor Header (null terminated) // n 6 Monitor Information (preceeded by length-1) // n 7 Connect Information (preceeded by length-1) switch(TNC->HOSTSTATE) { case 0: // SETCHANNEL TNC->MSGCHANNEL = character; TNC->HOSTSTATE++; break; case 1: // SETMSGTYPE TNC->MSGTYPE = character; if (character == 0) { // Success, no more info TrkProcessDEDFrame(TNC); TNC->HOSTSTATE = 0; break; } if (character > 0 && character < 6) { // Null Terminated Response) TNC->HOSTSTATE = 5; CURSOR = &TNC->DEDBuffer[0]; break; } if (character > 5 && character < 8) { TNC->HOSTSTATE = 2; // Get Length break; } // Invalid Debugprintf("TRK - Invalid MsgType %d %x %x %x", character, *(ptr), *(ptr+1), *(ptr+2)); break; case 2: // Get Length TNC->MSGCOUNT = character; TNC->MSGCOUNT++; // Param is len - 1 TNC->MSGLENGTH = TNC->MSGCOUNT; CURSOR = &TNC->DEDBuffer[0]; TNC->HOSTSTATE = 3; // Get Data break; case 5: // Collecting Null Terminated Response *(CURSOR++) = character; if (character) continue; // MORE TO COME TrkProcessDEDFrame(TNC); TNC->HOSTSTATE = 0; TNC->InputLen = 0; break; default: // RECEIVING Counted Response *(CURSOR++) = character; TNC->MSGCOUNT--; if (TNC->MSGCOUNT) continue; // MORE TO COME TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); TrkProcessDEDFrame(TNC); TNC->HOSTSTATE = 0; TNC->InputLen = 0; } } } // End of Input - Save buffer position TNC->InputLen = (int)(CURSOR - TNC->DEDBuffer); TNC->RXLen = 0; } //#include "Mmsystem.h" VOID RPRWriteCOMBlock(struct TNCINFO * TNC, UCHAR * Msg, int Len) { int txlen = send(TNC->TCPSock, Msg, Len, 0); } //1:fm G8BPQ to KD6PGI-1 ctl I11^ pid F0 //fm KD6PGI-1 to G8BPQ ctl DISC+ //VOID SwitchToRPacket(struct TNCINFO * TNC, char * Baud); //VOID SwitchToNormPacket(struct TNCINFO * TNC, char * Baud); //VOID SendRPBeacon(struct TNCINFO * TNC); VOID WinRPRProcessReceivedPacket(struct TNCINFO * TNC) { int Length, Len; UCHAR * ptr; UCHAR character; UCHAR * CURSOR; Len = recv(TNC->TCPSock, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen, 0); if (Len == 0 || Len == SOCKET_ERROR) { // Does this mean closed? int err = GetLastError(); closesocket(TNC->TCPSock); TNC->TCPSock = 0; TNC->CONNECTED = FALSE; TNC->Streams[0].ReportDISC = TRUE; sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); return; } TNC->RXLen += Len; Length = TNC->RXLen; ptr = TNC->RXBuffer; CURSOR = &TNC->DEDBuffer[TNC->InputLen]; if ((TNC->HostMode == 0 || TNC->ReinitState == 10) && Length > 80) { // Probably Signon Message if (TNC->WRITELOG) WriteDebugLogLine(TNC->Port, 'R', ptr, Length); ptr[Length] = 0; Debugprintf("TRK %s", ptr); TNC->RXLen = 0; return; } if (TNC->HostMode == 0) { // If we are just restarting, and TNC is in host mode, we may get "Invalid Channel" Back if (memcmp(ptr, "\x18\x02INVALID", 9) == 0) { if (TNC->WRITELOG) WriteDebugLogLine(TNC->Port, 'R', ptr, Length); TNC->HostMode = TRUE; TNC->HOSTSTATE = 0; TNC->Timeout = 0; TNC->RXLen = 0; return; } // Command is echoed as * command * if (strstr(ptr, "*") || TNC->ReinitState == 5) // 5 is waiting for reponse to JHOST1 { TrkProcessTermModeResponse(TNC); TNC->RXLen = 0; TNC->HOSTSTATE = 0; return; } } if (TNC->ReinitState == 10) { if (Length == 1 && *(ptr) == '.') // 01 echoed as . { // TNC is in Term Mode if (TNC->WRITELOG) WriteDebugLogLine(TNC->Port, 'R', ptr, Length); TNC->ReinitState = 0; TNC->HostMode = 0; return; } } while (Length--) { character = *(ptr++); if (TNC->HostMode) { // n 0 Success (nothing follows) // n 1 Success (message follows, null terminated) // n 2 Failure (message follows, null terminated) // n 3 Link Status (null terminated) // n 4 Monitor Header (null terminated) // n 5 Monitor Header (null terminated) // n 6 Monitor Information (preceeded by length-1) // n 7 Connect Information (preceeded by length-1) switch(TNC->HOSTSTATE) { case 0: // SETCHANNEL TNC->MSGCHANNEL = character; TNC->HOSTSTATE++; break; case 1: // SETMSGTYPE TNC->MSGTYPE = character; if (character == 0) { // Success, no more info TrkProcessDEDFrame(TNC); TNC->HOSTSTATE = 0; break; } if (character > 0 && character < 6) { // Null Terminated Response) TNC->HOSTSTATE = 5; CURSOR = &TNC->DEDBuffer[0]; break; } if (character > 5 && character < 8) { TNC->HOSTSTATE = 2; // Get Length break; } // Invalid Debugprintf("TRK - Invalid MsgType %d %x %x %x", character, *(ptr), *(ptr+1), *(ptr+2)); break; case 2: // Get Length TNC->MSGCOUNT = character; TNC->MSGCOUNT++; // Param is len - 1 TNC->MSGLENGTH = TNC->MSGCOUNT; CURSOR = &TNC->DEDBuffer[0]; TNC->HOSTSTATE = 3; // Get Data break; case 5: // Collecting Null Terminated Response *(CURSOR++) = character; if (character) continue; // MORE TO COME TrkProcessDEDFrame(TNC); TNC->HOSTSTATE = 0; TNC->InputLen = 0; break; default: // RECEIVING Counted Response *(CURSOR++) = character; TNC->MSGCOUNT--; if (TNC->MSGCOUNT) continue; // MORE TO COME TNC->InputLen = CURSOR - TNC->DEDBuffer; TrkProcessDEDFrame(TNC); TNC->HOSTSTATE = 0; TNC->InputLen = 0; } } } // End of Input - Save buffer position TNC->InputLen = CURSOR - TNC->DEDBuffer; TNC->RXLen = 0; } #ifndef LINBPQ static BOOL CALLBACK EnumTNCWindowsProc(HWND hwnd, LPARAM lParam) { char wtext[100]; struct TNCINFO * TNC = (struct TNCINFO *)lParam; UINT ProcessId; char FN[MAX_PATH] = ""; HANDLE hProc; if (TNC->ProgramPath == NULL) return FALSE; GetWindowText(hwnd,wtext,99); if (strstr(wtext,"WinRPR")) { GetWindowThreadProcessId(hwnd, &ProcessId); if (TNC->PID == ProcessId) { // Our Process hProc = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessId); if (hProc && GetModuleFileNameExPtr) { GetModuleFileNameExPtr(hProc, NULL, FN, MAX_PATH); // Make sure this is the right copy CloseHandle(hProc); if (_stricmp(FN, TNC->ProgramPath)) return TRUE; //Wrong Copy } TNC->PID = ProcessId; sprintf (wtext, "WinRPR - BPQ %s", TNC->PortRecord->PORTCONTROL.PORTDESCRIPTION); SetWindowText(hwnd, wtext); return FALSE; } } return (TRUE); } #endif VOID WinRPRThread(void * portptr); int ConnecttoWinRPR(int port) { _beginthread(WinRPRThread, 0, (void *)(size_t)port); return 0; } VOID WinRPRThread(void * portptr) { // Opens socket, connects and looks for data int port = (int)(size_t)portptr; char Msg[255]; int i, ret; u_long param=1; BOOL bcopt=TRUE; struct hostent * HostEnt; struct TNCINFO * TNC = TNCInfo[port]; fd_set readfs; fd_set errorfs; struct timeval timeout; char * ptr1; char * ptr2; UINT * buffptr; if (TNC->HostName == NULL) return; TNC->BusyFlags = 0; TNC->CONNECTING = TRUE; Sleep(3000); // Allow init to complete #ifdef WIN32 if (strcmp(TNC->HostName, "127.0.0.1") == 0) { // can only check if running on local host TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port); if (TNC->PID == 0) { TNC->CONNECTING = FALSE; sprintf(TNC->WEB_COMMSSTATE, "Waiting for TNC"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); return; // Not listening so no point trying to connect } // Get the File Name in case we want to restart it. if (TNC->ProgramPath == NULL) { if (GetModuleFileNameExPtr) { HANDLE hProc; char ExeName[256] = ""; hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID); if (hProc) { GetModuleFileNameExPtr(hProc, 0, ExeName, 255); CloseHandle(hProc); TNC->ProgramPath = _strdup(ExeName); } } } } #endif // // If we started the TNC make sure it is still running. // if (!IsProcess(TNC->PID)) // { // RestartTNC(TNC); // Sleep(3000); // } 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) { TNC->CONNECTING = FALSE; 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); } // closesocket(TNC->TCPSock); // closesocket(TNC->TCPDataSock); TNC->TCPSock = socket(AF_INET,SOCK_STREAM,0); if (TNC->TCPSock == INVALID_SOCKET) { i=sprintf(Msg, "Socket Failed for WinRPR socket - error code = %d\r\n", WSAGetLastError()); WritetoConsoleLocal(Msg); TNC->CONNECTING = FALSE; 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; sprintf(TNC->WEB_COMMSSTATE, "Connecting to TNC"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0) { // // Connected successful // #ifndef LINBPQ EnumWindows(EnumTNCWindowsProc, (LPARAM)TNC); #endif } else { if (TNC->Alerted == FALSE) { sprintf(Msg, "Connect Failed for WinRPR socket - error code = %d Port %d\n", WSAGetLastError(), htons(TNC->destaddr.sin_port)); WritetoConsoleLocal(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; } Sleep(1000); TNC->LastFreq = 0; // so V4 display will be updated TNC->CONNECTING = FALSE; TNC->CONNECTED = TRUE; TNC->BusyFlags = 0; TNC->InputLen = 0; // Send INIT script // VARA needs each command in a separate send ptr1 = &TNC->InitScript[0]; GetSemaphore(&Semaphore, 52); while(TNC->BPQtoWINMOR_Q) { buffptr = Q_REM(&TNC->BPQtoWINMOR_Q); if (buffptr) ReleaseBuffer(buffptr); } while (ptr1 && ptr1[0]) { unsigned char c; ptr2 = strchr(ptr1, 13); if (ptr2) { c = *(ptr2 + 1); // Save next char *(ptr2 + 1) = 0; // Terminate string } // VARASendCommand(TNC, ptr1, TRUE); if (ptr2) *(1 + ptr2++) = c; // Put char back ptr1 = ptr2; } TNC->Alerted = TRUE; sprintf(TNC->WEB_COMMSSTATE, "Connected to WinRPR TNC"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); FreeSemaphore(&Semaphore); sprintf(Msg, "Connected to WinRPR TNC Port %d\r\n", TNC->Port); WritetoConsoleLocal(Msg); #ifndef LINBPQ // FreeSemaphore(&Semaphore); Sleep(1000); // Give VARA time to update Window title // EnumWindows(EnumVARAWindowsProc, (LPARAM)TNC); // GetSemaphore(&Semaphore, 52); #endif while (TNC->CONNECTED) { FD_ZERO(&readfs); FD_ZERO(&errorfs); FD_SET(TNC->TCPSock,&readfs); FD_SET(TNC->TCPSock,&errorfs); timeout.tv_sec = 600; timeout.tv_usec = 0; // We should get messages more frequently that this ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout); if (ret == SOCKET_ERROR) { Debugprintf("WinRPR Select failed %d ", WSAGetLastError()); goto Lost; } if (ret > 0) { // See what happened if (FD_ISSET(TNC->TCPSock, &readfs)) { GetSemaphore(&Semaphore, 52); WinRPRProcessReceivedPacket(TNC); FreeSemaphore(&Semaphore); } if (FD_ISSET(TNC->TCPSock, &errorfs)) { Lost: sprintf(Msg, "WinRPR Connection lost for Port %d\r\n", TNC->Port); WritetoConsoleLocal(Msg); sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost"); MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); TNC->CONNECTED = FALSE; TNC->Alerted = FALSE; if (TNC->PTTMode) Rig_PTT(TNC, FALSE); // Make sure PTT is down if (TNC->Streams[0].Attached) TNC->Streams[0].ReportDISC = TRUE; TNC->TCPSock = 0; return; } } } sprintf(Msg, "WinRPR Thread Terminated Port %d\r\n", TNC->Port); WritetoConsoleLocal(Msg); }