/* 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 KAM TNC in Pactor Mode to BPQ32 switch // // Uses BPQ EXTERNAL interface // // Version 1.2.1.2 July 2010 // Send Change to ISS before each transmission // Support up to 32 BPQ Ports // Version 1.2.1.3 August 2010 // Drop RTS as well as DTR on close // Version 1.2.1.4 August 2010 // Save Minimized State // Version 1.2.1.5 September 2010 // Fix Freq Display after Node reconfig // Only use AutoConnect APPL for Pactor Connects // Version 1.2.2.1 September 2010 // Add option to get config from bpq32.cfg //#define WIN32_LEAN_AND_MEAN #define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #include #include #include "time.h" #include "cheaders.h" #include "tncinfo.h" #include "bpq32.h" #define NARROWMODE Report_P1 #define WIDEMODE Report_P1 // Only supports PI static char ClassName[]="KAMPACTORSTATUS"; static char WindowTitle[] = "KAM Pactor"; static int RigControlRow = 165; extern UCHAR LogDirectory[]; static RECT Rect; int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); static FILE * LogHandle[32] = {0}; //char * Logs[4] = {"1", "2", "3", "4"}; static char BaseDir[MAX_PATH]="c:\\"; static BOOL WRITELOG = FALSE; BOOL KAMStopPort(struct PORTCONTROL * PORT) { // Disable Port - close TCP Sockets or Serial Port struct TNCINFO * TNC = PORT->TNC; TNC->CONNECTED = FALSE; TNC->Alerted = FALSE; if (TNC->Streams[0].Attached) TNC->Streams[0].ReportDISC = TRUE; if (TNC->hDevice) { CloseCOMPort(TNC->hDevice); TNC->hDevice = 0; } TNC->HostMode = FALSE; sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Stopped"); MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); return TRUE; } BOOL KAMStartPort(struct PORTCONTROL * PORT) { // Restart Port - Open Sockets or Serial Port struct TNCINFO * TNC = PORT->TNC; TNC->ReopenTimer = 999; TNC->ReinitState = 0; sprintf(PORT->TNC->WEB_COMMSSTATE, "%s", "Port Restarted"); MySetWindowText(PORT->TNC->xIDC_COMMSSTATE, PORT->TNC->WEB_COMMSSTATE); return TRUE; } static VOID CloseLogFile(int Flags) { if (WRITELOG) { fclose(LogHandle[Flags]); LogHandle[Flags] = NULL; } } static BOOL OpenLogFile(int Flags) { if (WRITELOG) { UCHAR FN[MAX_PATH]; time_t T; struct tm * tm; T = time(NULL); tm = gmtime(&T); sprintf(FN,"%s/logs/KAMLog_%02d%02d_%d.txt", LogDirectory, tm->tm_mon + 1, tm->tm_mday, Flags); LogHandle[Flags] = fopen(FN, "ab"); return (LogHandle[Flags] != NULL); } return 0; } static void WriteLogLine(int Port, char * Msg, int MsgLen) { if (WRITELOG) { OpenLogFile(Port); if (LogHandle[Port]) { fwrite(Msg, 1, MsgLen, LogHandle[Port]); fwrite("\r\n", 1, 2, LogHandle[Port]); } CloseLogFile(Port); } } int ProcessLine(char * buf, int Port) { UCHAR * ptr,* p_cmd; char * p_ipad = 0; char * p_port = 0; unsigned short WINMORport = 0; int BPQport; int len=510; struct TNCINFO * TNC; char errbuf[256]; strcpy(errbuf, buf); BPQport = Port; TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO)); memset(TNC, 0, sizeof(struct TNCINFO)); TNC->InitScript = malloc(1000); TNC->InitScript[0] = 0; goto ConfigLine; // Read Initialisation lines while(TRUE) { if (GetLine(buf) == 0) return TRUE; ConfigLine: strcpy(errbuf, buf); if (memcmp(buf, "****", 4) == 0) return TRUE; ptr = strchr(buf, ';'); if (ptr) { *ptr++ = 13; *ptr = 0; } if (_memicmp(buf, "DEBUGLOG", 8) == 0) // Write Debug Log WRITELOG = atoi(&buf[9]); else if (_memicmp(buf, "APPL", 4) == 0) { p_cmd = strtok(&buf[5], " \t\n\r"); if (p_cmd && p_cmd[0] != ';' && p_cmd[0] != '#') TNC->ApplCmd=_strdup(_strupr(p_cmd)); } else if (_memicmp(buf, "OLDMODE", 7) == 0) TNC->OldMode = TRUE; else if (_memicmp(buf, "VERYOLDMODE", 7) == 0) { TNC->OldMode = TRUE; TNC->VeryOldMode = TRUE; } else if (_memicmp(buf, "BUSYWAIT", 8) == 0) // Wait time beofre failing connect if busy TNC->BusyWait = atoi(&buf[8]); else if (_memicmp(buf, "WL2KREPORT", 10) == 0) TNC->WL2K = DecodeWL2KReportLine(buf); else strcat (TNC->InitScript, buf); } return (TRUE); } #define FEND 0xC0 // KISS CONTROL CODES #define FESC 0xDB #define TFEND 0xDC #define TFESC 0xDD static int MaxStreams = 26; BOOL CloseConnection(struct TNCINFO * conn); static BOOL WriteCommBlock(struct TNCINFO * TNC); BOOL DestroyTTYInfo(int port); void CheckRXKAM(struct TNCINFO * TNC); VOID KAMPoll(int Port); VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); static VOID ProcessTermModeResponse(struct TNCINFO * TNC); static VOID DoTNCReinit(struct TNCINFO * TNC); static VOID DoTermModeTimeout(struct TNCINFO * TNC); VOID ProcessPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); VOID ProcessKPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); VOID ProcessKNormCommand(struct TNCINFO * TNC, UCHAR * rxbuffer); VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len); static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); // Note that Kantronics host Mode uses KISS format Packets (without a KISS COntrol Byte) VOID EncodeAndSend(struct TNCINFO * TNC, UCHAR * txbuffer, int Len); static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { int txlen; PMSGWITHLEN buffptr; struct TNCINFO * TNC = TNCInfo[port]; struct STREAMINFO * STREAM; int Stream; size_t Param; struct ScanEntry * Scan; if (TNC == NULL) return 0; if (TNC->hDevice == 0) { // Clear anything from UI_Q while (TNC->PortRecord->UI_Q) { buffptr = Q_REM(&TNC->PortRecord->UI_Q); ReleaseBuffer(buffptr); } // Try to reopen every 30 secs if (fn > 3 && fn < 7) goto ok; TNC->ReopenTimer++; if (TNC->ReopenTimer < 300) return 0; TNC->ReopenTimer = 0; if (TNC->PortRecord->PORTCONTROL.PortStopped == 0) OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); if (TNC->hDevice == 0) return 0; } ok: switch (fn) { case 7: // 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances // 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; CheckRXKAM(TNC); KAMPoll(port); return 0; case 1: // poll while (TNC->PortRecord->UI_Q) // Release anything accidentally put on UI_Q { buffptr = Q_REM(&TNC->PortRecord->UI_Q); ReleaseBuffer(buffptr); } for (Stream = 0; Stream <= MaxStreams; Stream++) { STREAM = &TNC->Streams[Stream]; if (STREAM->ReportDISC) { STREAM->ReportDISC = FALSE; buff->PORT = Stream; return -1; } } for (Stream = 0; Stream <= MaxStreams; Stream++) { STREAM = &TNC->Streams[Stream]; if (STREAM->PACTORtoBPQ_Q !=0) { int datalen; buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); datalen = (int)buffptr->Len; buff->PORT = Stream; buff->PID= 0xf0; memcpy(buff->L2DATA, buffptr->Data, datalen); // Data goes to +7, but we have an extra byte datalen += (MSGHDDRLEN + 1); PutLengthinBuffer(buff, datalen); ReleaseBuffer(buffptr); return (1); } } return 0; case 2: // send buffptr = GetBuff(); if (buffptr == 0) return (0); // No buffers, so ignore // Find TNC Record Stream = buff->PORT; STREAM = &TNC->Streams[Stream]; if (!TNC->TNCOK) { // Send Error Response buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r"); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); return 0; } txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); buffptr->Len = txlen; memcpy(buffptr->Data, buff->L2DATA, txlen); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); if(STREAM->Connected) { STREAM->FramesOutstanding++; STREAM->FramesQueued++; STREAM->BytesOutstanding += txlen; } return (0); case 3: // CHECK IF OK TO SEND. Also used to check if TNC is responding Stream = (int)(size_t)buff; STREAM = &TNC->Streams[Stream]; if (Stream == 0) { if (TNC->HFPacket) { if (TNC->Mem1 < 2000 || TNC->Streams[0].FramesOutstanding > 4) return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } else { if (TNC->Streams[0].FramesQueued > 4) return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } } else { if (STREAM->FramesOutstanding > 3 || STREAM->BytesOutstanding > 500 || TNC->Mem1 < 500) return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting case 4: // reinit return (0); case 5: // Close EncodeAndSend(TNC, "Q", 1); // Exit Host Mode Sleep(50); CloseCOMPort(TNCInfo[port]->hDevice); return (0); case 6: // Scan Control // Use P0 to Disable Pactor, P1 to enable Param = (size_t)buff; if (Param == 2) // Check Permission (shouldn't happen) { return 1; // OK to change } if (!TNC->HostMode) return 0; // No connection so no interlock if (Param == 1) // Request Permission return 0; // OK to Change // Dont want to mess with disabling it if (Param == 3) // Release Permission return 0; // Param is Address of a struct ScanEntry Scan = (struct ScanEntry *)buff; if (Scan->PMaxLevel >= '0' && Scan->PMaxLevel < '5') // 1 - 4 { if (TNC->Bandwidth != Scan->PMaxLevel) { // Enable or disable by setting mycall TNC->Bandwidth = Scan->PMaxLevel; if (Scan->PMaxLevel == '0') { EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? return 0; } if (TNC->OldMode) EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen else EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen TNC->InternalCmd = 'T'; } } } return 0; } VOID KAMSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; strcpy(TNC->WEB_TNCSTATE, "Interlocked"); MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); // STREAM->CmdSet = STREAM->CmdSave = zalloc(100); // sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects } VOID KAMReleasePort(struct TNCINFO * TNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; strcpy(TNC->WEB_TNCSTATE, "Free"); MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) { int Len = sprintf(Buff, "" "KAM Pactor Status

KAM Pactor Status

"); 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_STATE); Len += sprintf(&Buff[Len], "", TNC->WEB_TXRX); 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
Status%s
TX/RX State%s
Free Space%s
Traffic%s
"); Len += sprintf(&Buff[Len], "", TNC->WebBuffer); Len = DoScanLine(TNC, Buff, Len); return Len; } void * KAMExtInit(EXTPORTDATA * PortEntry) { char msg[500]; struct TNCINFO * TNC; int port; char * ptr; char * TempScript; port=PortEntry->PORTCONTROL.PORTNUMBER; sprintf(msg,"KAM Pactor %s", PortEntry->PORTCONTROL.SerialPortName); WritetoConsole(msg); 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; TNC->PortRecord->PORTCONTROL.HWType = TNC->Hardware = H_KAM; if (TNC->BusyWait == 0) TNC->BusyWait = 10; PortEntry->MAXHOSTMODESESSIONS = 11; // Default if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) memcpy(TNC->NodeCall, MYNODECALL, 10); else ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); PortEntry->PORTCONTROL.PROTOCOL = 10; // WINMOR/Pactor PortEntry->PORTCONTROL.PORTQUALITY = 0; PortEntry->SCANCAPABILITIES = NONE; // No Scan Control if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; if (PortEntry->PORTCONTROL.PORTPACLEN == 0) PortEntry->PORTCONTROL.PORTPACLEN = 100; PortEntry->PORTCONTROL.PORTSTARTCODE = KAMStartPort; PortEntry->PORTCONTROL.PORTSTOPCODE = KAMStopPort; // TNC->SuspendPortProc = KAMSuspendPort; // TNC->ReleasePortProc = KAMReleasePort; ptr=strchr(TNC->NodeCall, ' '); if (ptr) *(ptr) = 0; // Null Terminate // Set Essential Params and MYCALL TempScript = malloc(4000); strcpy(TempScript, "MARK 1400\r"); strcat(TempScript, "SPACE 1600\r"); strcat(TempScript, "SHIFT MODEM\r"); strcat(TempScript, "INV ON\r"); strcat(TempScript, "PTERRS 30\r"); // Default Retries strcat(TempScript, "MAXUSERS 1/10\r"); strcat(TempScript, TNC->InitScript); free(TNC->InitScript); TNC->InitScript = TempScript; // Others go on end so they can't be overriden strcat(TNC->InitScript, "ECHO OFF\r"); strcat(TNC->InitScript, "XMITECHO ON\r"); strcat(TNC->InitScript, "TXFLOW OFF\r"); strcat(TNC->InitScript, "XFLOW OFF\r"); strcat(TNC->InitScript, "TRFLOW OFF\r"); strcat(TNC->InitScript, "AUTOCR 0\r"); strcat(TNC->InitScript, "AUTOLF OFF\r"); strcat(TNC->InitScript, "CRADD OFF\r"); strcat(TNC->InitScript, "CRSUP OFF\r"); strcat(TNC->InitScript, "CRSUP OFF/OFF\r"); strcat(TNC->InitScript, "LFADD OFF/OFF\r"); strcat(TNC->InitScript, "LFADD OFF\r"); strcat(TNC->InitScript, "LFSUP OFF/OFF\r"); strcat(TNC->InitScript, "LFSUP OFF\r"); strcat(TNC->InitScript, "RING OFF\r"); strcat(TNC->InitScript, "ARQBBS OFF\r"); // Set the ax.25 MYCALL sprintf(msg, "MYCALL %s/%s\r", TNC->NodeCall, TNC->NodeCall); strcat(TNC->InitScript, msg); // look for the MAXUSERS config line, and get the limits TNC->InitScript = _strupr(TNC->InitScript); ptr = strstr(TNC->InitScript, "MAXUSERS"); if (ptr) { ptr = strchr(ptr,'/'); // to the separator if (ptr) PortEntry->MAXHOSTMODESESSIONS = atoi(++ptr) + 1; } if (PortEntry->MAXHOSTMODESESSIONS > 26) PortEntry->MAXHOSTMODESESSIONS = 26; PortEntry->PORTCONTROL.TNC = TNC; TNC->WebWindowProc = WebProc; TNC->WebWinX = 510; TNC->WebWinY = 280; TNC->WEB_COMMSSTATE = zalloc(100); TNC->WEB_TNCSTATE = zalloc(100); strcpy(TNC->WEB_TNCSTATE, "Free"); TNC->WEB_MODE = zalloc(100); TNC->WEB_TRAFFIC = zalloc(100); TNC->WEB_BUFFERS = zalloc(100); TNC->WEB_STATE = zalloc(100); TNC->WEB_TXRX = zalloc(100); TNC->WebBuffer = zalloc(5000); #ifndef LINBPQ CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 500, 500, 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", 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", "Status", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_STATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "TX/RX State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_TXRX = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "Free Space", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_BUFFERS = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL); CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,138,80,20, TNC->hDlg, NULL, hInstance, NULL); TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "RX 0 TX 0 ACKED 0", WS_CHILD | WS_VISIBLE,116,138,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,RigControlRow + 44,250,300, TNC->hDlg, NULL, hInstance, NULL); TNC->ClientHeight = 500; TNC->ClientWidth = 500; MoveWindows(TNC); #endif OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); WritetoConsole("\n"); return ExtProc; } void CheckRXKAM(struct TNCINFO * TNC) { int Length, Len; char debug[512] = "RX: "; // only try to read number of bytes in queue if (TNC->RXLen == 500) TNC->RXLen = 0; Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); if (Len == 0) return; TNC->RXLen += Len; memcpy(&debug[4], TNC->RXBuffer, TNC->RXLen); debug[TNC->RXLen + 4] = 0; WriteLogLine(TNC->Port, debug, TNC->RXLen + 4); Length = TNC->RXLen; // If first char != FEND, then probably a Terminal Mode Frame. Wait for CR on end if (TNC->RXBuffer[0] != FEND) { // Char Mode Frame I think we need to see cmd: on end // If we think we are in host mode, then to could be noise - just discard. if (TNC->HostMode) { TNC->RXLen = 0; // Ready for next frame return; } TNC->RXBuffer[TNC->RXLen] = 0; // if (TNC->RXBuffer[TNC->RXLen-2] != ':') if (strstr(TNC->RXBuffer, "cmd:") == 0) return; // Wait for rest of frame // Complete Char Mode Frame TNC->RXLen = 0; // Ready for next frame if (TNC->HostMode == 0) { // We think TNC is in Terminal Mode ProcessTermModeResponse(TNC); return; } // We thought it was in Host Mode, but are wrong. TNC->HostMode = FALSE; return; } // Receiving a Host Mode frame if (TNC->HostMode == 0) // If we are in Term Mode, discard it. Probably in recovery { TNC->RXLen = 0; // Ready for next frame return; } if (Length < 3) // Minimum Frame Sise return; if (TNC->RXBuffer[Length-1] != FEND) return; // Wait till we have a full frame ProcessHostFrame(TNC, TNC->RXBuffer, Length); // Could have multiple packets in buffer TNC->RXLen = 0; // Ready for next frame return; } VOID ProcessHostFrame(struct TNCINFO * TNC, UCHAR * rxbuffer, int Len) { UCHAR * FendPtr; int NewLen; // Split into KISS Packets. By far the most likely is a single KISS frame // so treat as special case if (rxbuffer[1] == FEND) // Two FENDS - probably got out of sync { rxbuffer++; Len--; } FendPtr = memchr(&rxbuffer[1], FEND, Len-1); if (FendPtr == &rxbuffer[Len-1]) { ProcessKHOSTPacket(TNC, &rxbuffer[1], Len - 2); return; } // Process the first Packet in the buffer NewLen = (int)(FendPtr - rxbuffer - 1); ProcessKHOSTPacket(TNC, &rxbuffer[1], NewLen); // Loop Back ProcessHostFrame(TNC, FendPtr+1, Len - NewLen - 2); return; } static BOOL WriteCommBlock(struct TNCINFO * TNC) { char debug[512] = "TX: "; memcpy(&debug[4], TNC->TXBuffer, TNC->TXLen); debug[TNC->TXLen + 4] = 0; WriteLogLine(TNC->Port, debug, TNC->TXLen + 4); WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); return TRUE; } VOID KAMPoll(int Port) { struct TNCINFO * TNC = TNCInfo[Port]; struct STREAMINFO * STREAM; UCHAR * Poll = TNC->TXBuffer; char Status[80]; int Stream; if (TNC->PortRecord == 0) Stream = 0; // If Pactor Session has just been attached, drop back to cmd mode and set Pactor Call to // the connecting user's callsign for (Stream = 0; Stream <= MaxStreams; Stream++) { STREAM = &TNC->Streams[Stream]; if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0) { // New Attach STREAM->Attached = TRUE; if (Stream == 0) // HF Port { int calllen; UCHAR TXMsg[1000] = "D20"; int datalen; char Msg[80]; TNC->HFPacket = FALSE; TNC->TimeInRX = 0; calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4USER, TNC->Streams[0].MyCall); TNC->Streams[0].MyCall[calllen] = 0; EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? if (TNC->VeryOldMode) { datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->Streams[0].MyCall); EncodeAndSend(TNC, TXMsg, datalen); } else { datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->Streams[0].MyCall); EncodeAndSend(TNC, TXMsg, datalen); if (TNC->OldMode == 0) { EncodeAndSend(TNC, TXMsg, datalen); datalen = sprintf(TXMsg, "C20MYGTCALL %s", TNC->Streams[0].MyCall); } } TNC->InternalCmd = 'M'; TNC->NeedPACTOR = 0; // Cancel enter Pactor SuspendOtherPorts(TNC); sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); // Stop Scanning sprintf(Msg, "%d SCANSTOP", TNC->Port); Rig_Command( (TRANSPORTENTRY *) -1, Msg); } } } if (TNC->Timeout) { TNC->Timeout--; if (TNC->Timeout) // Still waiting return; // Timed Out if (TNC->HostMode == 0) { DoTermModeTimeout(TNC); return; } // Timed out in host mode - Clear any connection and reinit the TNC Debugprintf("KAM PACTOR - Link to TNC Lost"); TNC->TNCOK = FALSE; TNC->HostMode = 0; TNC->ReinitState = 0; sprintf(TNC->WEB_COMMSSTATE, "%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); for (Stream = 0; Stream <= MaxStreams; Stream++) { PMSGWITHLEN buffptr; STREAM = &TNC->Streams[Stream]; if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected { STREAM->Connected = FALSE; // Back to Command Mode STREAM->ReportDISC = TRUE; // Tell Node } STREAM->FramesQueued = 0; while(STREAM->BPQtoPACTOR_Q) { buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); ReleaseBuffer(buffptr); } while(STREAM->PACTORtoBPQ_Q) { buffptr=Q_REM(&STREAM->PACTORtoBPQ_Q); ReleaseBuffer(buffptr); } } } for (Stream = 0; Stream <= MaxStreams; Stream++) { STREAM = &TNC->Streams[Stream]; if (STREAM->Attached) CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); } // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence if (!TNC->HostMode) { DoTNCReinit(TNC); return; } if (TNC->BusyDelay) // Waiting to send connect { // Still Busy? if (InterlockedCheckBusy(TNC) == 0) { // No, so send EncodeAndSend(TNC, TNC->ConnectCmd, (int)strlen(TNC->ConnectCmd)); free(TNC->ConnectCmd); TNC->Timeout = 50; TNC->InternalCmd = 'C'; // So we dont send the reply to the user. STREAM->Connecting = TRUE; TNC->Streams[0].Connecting = TRUE; sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); TNC->BusyDelay = 0; return; } else { // Wait Longer TNC->BusyDelay--; if (TNC->BusyDelay == 0) { // Timed out - Send Error Response PMSGWITHLEN buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "Sorry, Can't Connect - Channel is busy\r"); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); free(TNC->ConnectCmd); } } } if (TNC->NeedPACTOR) { TNC->NeedPACTOR--; if (TNC->NeedPACTOR == 0) { int datalen; UCHAR TXMsg[80] = "D20"; if (TNC->VeryOldMode) { datalen = sprintf(TXMsg, "C20MYCALL %s", TNC->NodeCall); EncodeAndSend(TNC, TXMsg, datalen); } else { datalen = sprintf(TXMsg, "C20MYPTCALL %s", TNC->NodeCall); EncodeAndSend(TNC, TXMsg, datalen); if (TNC->OldMode == 0) { datalen = sprintf(TXMsg, "C20MYGTCALL %s", TNC->NodeCall); EncodeAndSend(TNC, TXMsg, datalen); } } EncodeAndSend(TNC, TXMsg, datalen); if (TNC->OldMode) EncodeAndSend(TNC, "C20PACTOR", 9); // Back to Listen else EncodeAndSend(TNC, "C20TOR", 6); // Back to Listen TNC->InternalCmd = 'T'; TNC->Timeout = 50; TNC->IntCmdDelay--; // Restart Scanning sprintf(Status, "%d SCANSTART 15", TNC->Port); Rig_Command( (TRANSPORTENTRY *) -1, Status); return; } } for (Stream = 0; Stream <= MaxStreams; Stream++) { STREAM = &TNC->Streams[Stream]; // If in HF Packet mode, normal flow control doesn't seem to work // If more that 4 packets sent, send a status poll. and use response to // reset frames outstanding if ((Stream == 0) && (TNC->HFPacket) && (TNC->Streams[0].FramesOutstanding > 4)) { EncodeAndSend(TNC, "C10S", 4); TNC->InternalCmd = 'S'; TNC->Timeout = 50; return; } if (TNC->TNCOK && STREAM->BPQtoPACTOR_Q) { int datalen; UCHAR TXMsg[1000] = "D20"; PMSGWITHLEN buffptr; UCHAR * MsgPtr; char Status[80]; if (STREAM->Connected) { int Next; if (Stream > 0) sprintf(TXMsg, "D1%c", Stream + '@'); else if (TNC->HFPacket) memcpy(TXMsg, "D2A", 3); else { // Pactor // Limit amount in TX, so we keep some on the TX Q and don't send turnround too early if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked > 200) continue; // Dont send if IRS State // If in IRS state for too long, force turnround if (TNC->TXRXState == 'R') { if (TNC->TimeInRX++ > 15) EncodeAndSend(TNC, "T", 1); // Changeover to ISS else goto Poll; } TNC->TimeInRX = 0; } buffptr=Q_REM(&STREAM->BPQtoPACTOR_Q); STREAM->FramesQueued--; datalen = buffptr->Len; MsgPtr = buffptr->Data; if (TNC->SwallowSignon && Stream == 0) { TNC->SwallowSignon = FALSE; if (strstr(MsgPtr, "Connected")) // Discard *** connected { ReleaseBuffer(buffptr); return; } } Next = 0; STREAM->bytesTXed += datalen; if (Stream == 0) { while (datalen > 100) // Limit Pactor Sends { memcpy(&TXMsg[3], &MsgPtr[Next], 100); EncodeAndSend(TNC, TXMsg, 103); Next += 100; datalen -= 100; WritetoTrace(TNC, &TXMsg[3], 100); } } memcpy(&TXMsg[3], &MsgPtr[Next], datalen); EncodeAndSend(TNC, TXMsg, datalen + 3); WritetoTrace(TNC, &TXMsg[3], datalen); ReleaseBuffer(buffptr); if (Stream == 0) { sprintf(Status, "RX %d TX %d ACKED %d ", TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); SetWindowText(TNC->xIDC_TRAFFIC, Status); if ((TNC->HFPacket == 0) && (TNC->Streams[0].BPQtoPACTOR_Q == 0)) // Nothing following { EncodeAndSend(TNC, "E", 1); // Changeover when all sent } } if (STREAM->Disconnecting) { Debugprintf("Send with Disc Pending, Q = %x", STREAM->BPQtoPACTOR_Q); if (STREAM->BPQtoPACTOR_Q == 0) // All Sent // KAM doesnt have a tidy close! STREAM->DisconnectingTimeout = 100; // Give 5 secs to get to other end } return; } else // Not Connected { buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); datalen = buffptr->Len; MsgPtr = buffptr->Data; // Command. Do some sanity checking and look for things to process locally datalen--; // Exclude CR MsgPtr[datalen] = 0; // Null Terminate _strupr(MsgPtr); if ((Stream == 0) && memcmp(MsgPtr, "RADIO ", 6) == 0) { sprintf(&MsgPtr[40], "%d %s", TNC->Port, &MsgPtr[6]); if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &MsgPtr[40])) { ReleaseBuffer(buffptr); } else { buffptr->Len = sprintf(buffptr->Data, "%s", &MsgPtr[40]); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); } return; } if (_memicmp(MsgPtr, "D\r", 2) == 0) { STREAM->ReportDISC = TRUE; // Tell Node return; } if ((Stream == 0) && memcmp(MsgPtr, "HFPACKET", 8) == 0) { TNC->HFPacket = TRUE; buffptr->Len = sprintf(buffptr->Data, "KAM} OK\r"); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); return; } if (MsgPtr[0] == 'C' && MsgPtr[1] == ' ' && datalen > 2) // Connect { memcpy(STREAM->RemoteCall, &MsgPtr[2], 9); STREAM->Connecting = TRUE; // If Stream 0, Convert C CALL to PACTOR CALL if (Stream == 0) { if (TNC->HFPacket) datalen = sprintf(TXMsg, "C2AC %s", TNC->Streams[0].RemoteCall); else datalen = sprintf(TXMsg, "C20PACTOR %s", TNC->Streams[0].RemoteCall); // If Pactor, check busy detecters on any interlocked ports if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) { // Channel Busy. Wait TNC->ConnectCmd = _strdup(TXMsg); sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); TNC->BusyDelay = TNC->BusyWait * 10; return; } TNC->OverrideBusy = FALSE; sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } else datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); EncodeAndSend(TNC, TXMsg, datalen); TNC->Timeout = 50; TNC->InternalCmd = 'C'; // So we dont send the reply to the user. ReleaseBuffer(buffptr); STREAM->Connecting = TRUE; return; } if (memcmp(MsgPtr, "GTOR ", 5) == 0) // GTOR Connect { memcpy(STREAM->RemoteCall, &MsgPtr[5], 9); STREAM->Connecting = TRUE; // If Stream 0, Convert C CALL to PACTOR CALL if (Stream == 0) { datalen = sprintf(TXMsg, "C20GTOR %s", TNC->Streams[0].RemoteCall); // If Pactor, check busy detecters on any interlocked ports if (TNC->HFPacket == 0 && InterlockedCheckBusy(TNC) && TNC->OverrideBusy == 0) { // Channel Busy. Wait TNC->ConnectCmd = _strdup(TXMsg); sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); TNC->BusyDelay = TNC->BusyWait * 10; return; } TNC->OverrideBusy = FALSE; sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } else datalen = sprintf(TXMsg, "C1%cC %s", Stream + '@', STREAM->RemoteCall); EncodeAndSend(TNC, TXMsg, datalen); TNC->Timeout = 50; TNC->InternalCmd = 'C'; // So we dont send the reply to the user. ReleaseBuffer(buffptr); STREAM->Connecting = TRUE; return; } if (memcmp(MsgPtr, "DISCONNECT", datalen) == 0) // Disconnect { if (Stream == 0) { if (TNC->HFPacket) EncodeAndSend(TNC, "C2AD", 4); // ??Return to packet mode?? else EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? TNC->NeedPACTOR = 50; } else { sprintf(TXMsg, "C1%cD", Stream + '@'); EncodeAndSend(TNC, TXMsg, 4); TNC->CmdStream = Stream; TNC->Timeout = 50; } TNC->Timeout = 0; // Don't expect a response STREAM->Connecting = FALSE; STREAM->ReportDISC = TRUE; ReleaseBuffer(buffptr); return; } // Other Command ?? if (Stream > 0) datalen = sprintf(TXMsg, "C1%c%s", Stream + '@', MsgPtr); else datalen = sprintf(TXMsg, "C20%s", MsgPtr); EncodeAndSend(TNC, TXMsg, datalen); ReleaseBuffer(buffptr); TNC->Timeout = 50; TNC->InternalCmd = 0; TNC->CmdStream = Stream; } } } Poll: // Need to poll data and control channel (for responses to commands) // Also check status if we have data buffered (for flow control) if (TNC->TNCOK) { if (TNC->IntCmdDelay == 50) { EncodeAndSend(TNC, "C10S", 4); TNC->InternalCmd = 'S'; TNC->Timeout = 50; TNC->IntCmdDelay--; return; } if (TNC->IntCmdDelay <=0) { if (TNC->VeryOldMode == FALSE) { EncodeAndSend(TNC, "?", 1); TNC->InternalCmd = '?'; TNC->Timeout = 50; } TNC->IntCmdDelay = 100; // Every 30 return; } else TNC->IntCmdDelay--; } return; } static VOID DoTNCReinit(struct TNCINFO * TNC) { UCHAR * Poll = TNC->TXBuffer; if (TNC->ReinitState == 1) // Forcing back to Term TNC->ReinitState = 0; // Got Response, so must be back in term mode if (TNC->ReinitState == 0) { // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); Poll[0] = 13; TNC->TXLen = 1; WriteCommBlock(TNC); TNC->Timeout = 50; return; } if (TNC->ReinitState == 2) // In Term State, Sending Initialisation Commands { char * start, * end; int len; start = TNC->InitPtr; if (*(start) == 0) // End of Script { // Put into Host Mode memcpy(Poll, "INTFACE HOST\r", 13); TNC->TXLen = 13; WriteCommBlock(TNC); TNC->Timeout = 50; TNC->ReinitState = 4; // Need Reset return; } end = strchr(start, 13); len = (int)(++end - start); TNC->InitPtr = end; memcpy(Poll, start, len); TNC->TXLen = len; WriteCommBlock(TNC); TNC->Timeout = 50; return; } } static VOID DoTermModeTimeout(struct TNCINFO * TNC) { UCHAR * Poll = TNC->TXBuffer; if (TNC->ReinitState == 0) { //Checking if in Terminal Mode - Try to set back to Term Mode TNC->ReinitState = 1; Poll[0] = 3; Poll[1] = 0x58; // ?? Back to cmd: mode ?? TNC->TXLen = 2; Poll[0] = 0xc0; Poll[1] = 'Q'; // ?? Back to cmd: mode ?? Poll[2] = 0xc0; TNC->TXLen = 3; WriteCommBlock(TNC); return; } if (TNC->ReinitState == 1) { // Forcing back to Term Mode TNC->ReinitState = 0; DoTNCReinit(TNC); // See if worked return; } if (TNC->ReinitState == 3) { // Entering Host Mode // Assume ok TNC->HostMode = TRUE; return; } } static VOID ProcessTermModeResponse(struct TNCINFO * TNC) { UCHAR * Poll = TNC->TXBuffer; if (TNC->ReinitState == 0 || TNC->ReinitState == 1) { // Testing if in Term Mode. It is, so can now send Init Commands TNC->InitPtr = TNC->InitScript; TNC->ReinitState = 2; DoTNCReinit(TNC); // Send First Command return; } if (TNC->ReinitState == 2) { // Sending Init Commands DoTNCReinit(TNC); // Send Next Command return; } if (TNC->ReinitState == 4) // Send INTFACE, Need RESET { TNC->ReinitState = 5; memcpy(Poll, "RESET\r", 6); TNC->TXLen = 6; WriteCommBlock(TNC); TNC->Timeout = 50; TNC->HostMode = TRUE; // Should now be in Host Mode TNC->NeedPACTOR = 50; // Need to Send PACTOR command after 5 secs return; } if (TNC->ReinitState == 5) // RESET sent { TNC->ReinitState = 5; return; } } VOID ProcessKHOSTPacket(struct TNCINFO * TNC, UCHAR * Msg, int Len) { PMSGWITHLEN buffptr; char * Buffer = &Msg[3]; // Data portion of frame char * Call; char Status[80]; int Stream = 0; struct STREAMINFO * STREAM; // Any valid frame is an ACK TNC->TNCOK = TRUE; Len = KissDecode(Msg, Msg, Len); // Remove KISS transparency if (Msg[1] == '0' && Msg[2] == '0') Stream = 0; else if (Msg[1] == '2') Stream = 0; else Stream = Msg[2] - '@'; STREAM = &TNC->Streams[Stream]; // See if Poll Reply or Data Msg[Len] = 0; // Terminate if (Msg[0] == 'M') // Monitor { DoMonitor(TNC, Msg, Len); return; } if (Msg[0] == 'E') // Data Echo { if (Msg[1] == '2') // HF Port { if (TNC->Streams[0].bytesTXed) TNC->Streams[0].BytesAcked += Len - 3; // We get an ack before the first send sprintf(Status, "RX %d TX %d ACKED %d ", TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); SetWindowText(TNC->xIDC_TRAFFIC, Status); if (TNC->Streams[0].bytesTXed - TNC->Streams[0].BytesAcked < 500) TNC->Streams[0].FramesOutstanding = 0; } return; } if (Msg[0] == 'D') // Data { // Pass to Appl buffptr = GetBuff(); if (buffptr == NULL) return; // No buffers, so ignore Len-=3; // Remove Header buffptr->Len = Len; // Length STREAM->bytesRXed += Len; memcpy(buffptr->Data, Buffer, Len); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); if (Stream == 0) { sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); } WritetoTrace(TNC, Buffer, Len); return; } if (Msg[0] == 'C') // Command Reponse { TNC->Timeout = 0; // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc // See if a response to internal command if (TNC->InternalCmd) { // Process it if (TNC->InternalCmd == 'S') // Status { char * Line; char * ptr; // Message is line giving free bytes, followed by a line for each active (packet) stream // FREE BYTES 1366/5094 // A/2 #1145(12) CONNECTED to KE7XO-3 // S/2 CONNECTED to NLV // each line is teminated by CR, and by the time it gets here it is null terminated //FREE BYTES 2628 //A/H #80(1) CONNECTED to DK0MNL.. if (TNC->HFPacket) TNC->Streams[0].FramesOutstanding = 0; Line = strchr(&Msg[3], 13); if (Line == 0) return; *(Line) = 0; ptr = strchr(&Msg[13], '/'); TNC->Mem1 = atoi(&Msg[13]); if (ptr) TNC->Mem2 = atoi(++ptr); else TNC->Mem2 = 0; SetWindowText(TNC->xIDC_BUFFERS, &Msg[14]); strcpy(TNC->WEB_BUFFERS, &Msg[14]); while (Line[1] != 0) // End of stream { Stream = Line[1] - '@'; STREAM = &TNC->Streams[Stream]; if (Line[5] == '#') { STREAM->BytesOutstanding = atoi(&Line[6]); ptr = strchr(&Line[6], '('); if (ptr) STREAM->FramesOutstanding = atoi(++ptr); } else { STREAM->BytesOutstanding = 0; STREAM->FramesOutstanding = 0; } Line = strchr(&Line[1], 13); } return; } return; } WritetoTrace(TNC, Buffer, Len); // Pass to Appl Stream = TNC->CmdStream; buffptr = GetBuff(); if (buffptr == NULL) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data,"KAM} %s", Buffer); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); return; } if (Msg[0] == 'I') // ISS/IRS State { if (Msg[2] == '1') { strcpy(TNC->WEB_TXRX, "Sender"); SetWindowText(TNC->xIDC_TXRX, "Sender"); TNC->TXRXState = 'S'; } else { strcpy(TNC->WEB_TXRX, "Receiver"); SetWindowText(TNC->xIDC_TXRX, "Receiver"); TNC->TXRXState = 'R'; } return; } if (Msg[0] == '?') // Status { TNC->Timeout = 0; /* The response frame from the TNC will be: ?0MSXY - where MSXY are coded as follows: M One byte indicating the current mode of operation. A=Packet B=RTTY C=ASCII D=AMTOR E=FEC F=SELFEC G=LAMTOR H=PACTOR I=PTLISTEN J=GTOR K=NAVTEX L=CW M=TOR Standby N=GMON O=PSK31 S One byte indicating a sub-mode of operation. The byte contains an ASCII character as follows: 0=Standby 1=Phasing 2=Connected 3=Disconnecting 4=FEC 5=SELFEC 6=PTFEC X One byte (called status byte X). This byte is bit- encoded to indicate specific conditions as follows: Bit 0 = (IDLE) set to 1 when receiving IDLE characters in a frame. Bit 1 = (ERR) set to 1 to indicate the received frame failed CRC check, or was not a valid CS response frame. Bit 2 = (Combined receive) set to 1 to indicate that the data was constructed through the use of error correction (i.e. Golay error correction for G-TOR or Memory ARQ for Pactor). Bit 3 = (RQ) set to 1 to indicate an RQ frame. If you are the ISS, it indicates that the receiving station has asked for a repeat of the last data due to received errors. When you are the IRS, it indicates that the transmitting station has sent the same data that you have already received. This means that the sending station did not properly copy your acknowledgement (CS code). Bit 4 = (Huffman) set to 1 to indicate that this frame contains data which uses Huffman compression. Bit 5 = (ISS) set to 1 to indicate that your station is currently the ISS. Bit 6&7 = (Speed) these two bits indicate the current speed of an ARQ link or the PSK31 mode. The coding of the bits is: 00 = 100 baud or BPSK31 01 = 200 baud or QPSK31 10 = 300 baud Y One byte (called status byte Y). This byte is bit- encoded to indicate specific conditions as follows: Bit 0 = reserved (set to 0). Bit 1 = (PTT) PTT is active. Bit 2 = (Changeover) changeover in progress Bits 3-7 = reserved (set to 0). */ return; } if (Msg[0] == 'S') // Status { if (Len < 4) { // Reset Response FEND FEND S00 FEND TNC->Timeout = 0; sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); return; } // Pass to Appl if (strstr(Buffer, "STANDBY>") || strstr(Buffer, "*** DISCONNECTED")) { if ((STREAM->Connecting | STREAM->Connected) == 0) { // Not connected or Connecting. Probably response to going into Pactor Listen Mode return; } if (STREAM->Connecting && STREAM->Disconnecting == FALSE) { // Connect Failed buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "*** Failure with %s\r", STREAM->RemoteCall); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); STREAM->Connecting = FALSE; STREAM->Connected = FALSE; // In case! STREAM->FramesOutstanding = 0; return; } // Must Have been connected or disconnecting - Release Session STREAM->Connecting = FALSE; STREAM->Connected = FALSE; // Back to Command Mode STREAM->FramesOutstanding = 0; if (STREAM->Disconnecting == FALSE) STREAM->ReportDISC = TRUE; // Tell Node STREAM->Disconnecting = FALSE; if (Stream == 0) { // Need to reset Pactor Call in case it was changed EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? TNC->NeedPACTOR = 20; } return; } if (Msg[2] == '0') Call = strstr(Buffer, "HFPacket == 0) { Buffer[Len-4] = 0; } STREAM->bytesRXed = STREAM->bytesTXed = STREAM->BytesAcked = 0; STREAM->ConnectTime = time(NULL); if (Stream == 0) { // Stop Scanner char Msg[80]; sprintf(Msg, "%d SCANSTOP", TNC->Port); Rig_Command( (TRANSPORTENTRY *) -1, Msg); sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %d ", TNC->Streams[0].bytesRXed, TNC->Streams[0].bytesTXed, TNC->Streams[0].BytesAcked); SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); } if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) { // Incoming Connect TRANSPORTENTRY * SESS; if (Msg[1] == '2' && Msg[2] == 'A') TNC->HFPacket = TRUE; // Stop other ports in same group SuspendOtherPorts(TNC); ProcessIncommingConnect(TNC, Call, Stream, TRUE); SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; if (Stream == 0) { struct WL2KInfo * WL2K = TNC->WL2K; char FreqAppl[10] = ""; // Frequecy-specific application if (TNC->RIG && TNC->RIG != &TNC->DummyRig) { sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", TNC->Streams[0].RemoteCall, TNC->NodeCall, TNC->RIG->Valchar); SESS->Frequency = (atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq // If Scan Entry has a Appl, save it if (TNC->RIG->FreqPtr && TNC->RIG->FreqPtr[0]->APPL[0]) strcpy(FreqAppl, &TNC->RIG->FreqPtr[0]->APPL[0]); } else { sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", TNC->Streams[0].RemoteCall, TNC->NodeCall); if (WL2K) { SESS->Frequency = WL2K->Freq; } } SESS->Mode = 11; // P1 if (WL2K) strcpy(SESS->RMSCall, WL2K->RMSCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); EncodeAndSend(TNC, "T", 1); // Changeover to ISS // If an autoconnect APPL is defined, send it if (FreqAppl[0]) // Frequency spcific APPL overrides TNC APPL { buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); TNC->SwallowSignon = TRUE; return; } if (TNC->ApplCmd) { buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "%s\r", TNC->ApplCmd); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); TNC->SwallowSignon = TRUE; return; } } if (FULL_CTEXT && HFCTEXTLEN == 0) { char CTBuff[300] = "D20"; int Len = CTEXTLEN, CTPaclen = 50; int Next = 0; if (Stream > 0) sprintf(CTBuff, "D1%c", Stream + '@'); while (Len > CTPaclen) // CTEXT Paclen { memcpy(&CTBuff[3], &CTEXTMSG[Next], CTPaclen); EncodeAndSend(TNC, CTBuff, CTPaclen + 3); Next += CTPaclen; Len -= CTPaclen; } memcpy(&CTBuff[3], &CTEXTMSG[Next], Len); EncodeAndSend(TNC, CTBuff, Len + 3); EncodeAndSend(TNC, "E", 1); // Changeover when all sent TNC->Streams[0].bytesTXed += CTEXTLEN; } return; } else { // Connect Complete buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "*** Connected to %s\r", Call);; C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); STREAM->Connecting = FALSE; STREAM->Connected = TRUE; // Subsequent data to data channel if (Stream == 0) { if (TNC->RIG) sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall, TNC->RIG->Valchar); else sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); UpdateMH(TNC, Call, '+', 'O'); } return; } } } } static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) { int i,txptr=0; UCHAR c; for (i=0;iTXLen = KissEncode(txbuffer, TNC->TXBuffer, Len); WriteCommBlock(TNC); } static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len) { int i,txptr=0; UCHAR c; outbuff[0]=FEND; txptr=1; for (i=0;i'); if (ptr) *(ptr) = 0; UpdateMH(TNC, &Msg[3], ' ', 0); } VOID TidyClose(struct TNCINFO * TNC, int Stream) { if (Stream == 0) // Pactor Stream { TNC->TimeInRX = 0; if (TNC->HFPacket) EncodeAndSend(TNC, "C2AD", 4); // Disconnect else EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? // EncodeAndSend(TNC, "C20TOR", 6); // Disconnect TNC->HFPacket = FALSE; } else { UCHAR TXMsg[10]; sprintf(TXMsg, "C1%cD", Stream + '@'); EncodeAndSend(TNC, TXMsg, 4); TNC->Timeout = 50; } } VOID ForcedClose(struct TNCINFO * TNC, int Stream) { if (Stream == 0) // Pactor Stream { TNC->TimeInRX = 0; if (TNC->HFPacket) EncodeAndSend(TNC, "C2AD", 4); // Disconnect else EncodeAndSend(TNC, "X", 1); // ??Return to packet mode?? TNC->HFPacket = FALSE; } else { UCHAR TXMsg[10]; sprintf(TXMsg, "C1%cD", Stream + '@'); EncodeAndSend(TNC, TXMsg, 4); // Send twice - must force a disconnect TNC->Timeout = 50; }} VOID CloseComplete(struct TNCINFO * TNC, int Stream) { sprintf(TNC->WEB_TNCSTATE, "Free"); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); ReleaseOtherPorts(TNC); TNC->NeedPACTOR = 50; } /* MARK 1400 SPACE 1600 SHIFT MODEM INV ON MAXUSERS 1/10 PTERRS 30 // Others go on end so they can't be overriden ECHO OFF XMITECHO ON TXFLOW OFF XFLOW OFF TRFLOW OFF AUTOCR 0 AUTOLF OFF CRADD OFF CRSUP OFF CRSUP OFF/OFF LFADD OFF/OFF LFADD OFF LFSUP OFF/OFF LFSUP OFF RING OFF ARQBBS OFF MYCALL */