/* 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 SCS TNC in Pactor Mode to BPQ32 switch // // Uses BPQ EXTERNAL interface // Dec 29 2009 // Add Scan Control using %W Hostmode Command // Map Rig control port to a Virtual Serial Port. // Add Support for packet port(s). // July 2010 // Support up to 32 BPQ Ports // Version 1.1.1.14 August 2010 // Drop RTS as well as DTR on close // Version 1.2.1.1 August 2010 // Save Minimized State // Version 1.2.1.2 August 2010 // Implement scan bandwidth change // Version 1.2.1.3 September 2010 // Don't connect if channel is busy // Add WL2K reporting // Add PACKETCHANNELS config command // And Port Selector (P1 or P2) for Packet Ports // Version 1.2.1.4 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 // October 2011 // Changes for P4Dragon #define _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_DEPRECATE #include #include #include "time.h" //#include //#include #define MaxStreams 10 // First is used for Pactor, even though Pactor uses channel 31 #include "CHeaders.h" #include "tncinfo.h" #include "bpq32.h" #ifndef WIN32 #ifndef MACBPQ #ifndef FREEBSD #include #include #endif #endif #endif static char ClassName[]="PACTORSTATUS"; static char WindowTitle[] = "SCS Pactor"; static int RigControlRow = 185; #define NARROWMODE 12 // PI/II #define WIDEMODE 16 // PIII only extern UCHAR LogDirectory[]; static RECT Rect; VOID __cdecl Debugprintf(const char * format, ...); char NodeCall[11]; // Nodecall, Null Terminated int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len); VOID SuspendOtherPorts(struct TNCINFO * ThisTNC); VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC); VOID PTCSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC); VOID PTCReleasePort(struct TNCINFO * TNC); int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); int CheckMode(struct TNCINFO * TNC); VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len); void SCSTryToSendDATA(struct TNCINFO * TNC, int Stream); VOID UpdateMHwithDigis(struct TNCINFO * TNC, UCHAR * Call, char Mode, char Direction); int standardParams(struct TNCINFO * TNC, char * buf); int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length); #define FEND 0xC0 // KISS CONTROL CODES #define FESC 0xDB #define TFEND 0xDC #define TFESC 0xDD static FILE * LogHandle[32] = {0}; //char * Logs[4] = {"1", "2", "3", "4"}; static char BaseDir[MAX_PATH]="c:\\"; static BOOL WRITELOG = FALSE; BOOL SCSStopPort(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 SCSStartPort(struct PORTCONTROL * PORT) { // Restart Port - Open Sockets or Serial Port struct TNCINFO * TNC = PORT->TNC; TNC->ReopenTimer = 999; // Reopen immediately 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/SCSLog_%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 Flags, char * Msg, int MsgLen) { if (WRITELOG) { if (LogHandle[Flags]) { fwrite(Msg, 1, MsgLen, LogHandle[Flags]); fwrite("\r\n", 1, 2, LogHandle[Flags]); } } } static int DontAddPDUPLEX = 0; 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; char errbuf[256]; 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, "PACKETCHANNELS", 14) == 0) // Packet Channels TNC->PacketChannels = atoi(&buf[14]); else if (_memicmp(buf, "SCANFORROBUSTPACKET", 19) == 0) { // Spend a percentage of scan time in Robust Packet Mode double Robust = atof(&buf[20]); #pragma warning(push) #pragma warning(disable : 4244) TNC->RobustTime = Robust * 10; #pragma warning(pop) } else if (_memicmp(buf, "USEAPPLCALLS", 12) == 0 && buf[12] != 'F' && buf[12] != 'f') TNC->UseAPPLCalls = TRUE; else if (_memicmp(buf, "USEAPPLCALLSFORPACTOR", 21) == 0) TNC->UseAPPLCallsforPactor = TRUE; else if (_memicmp(buf, "DRAGON", 6) == 0) { TNC->Dragon = TRUE; if (_memicmp(&buf[7], "SINGLE", 6) == 0) TNC->DragonSingle = TRUE; if (_memicmp(&buf[7], "KISS", 4) == 0) TNC->DragonKISS = TRUE; } else if (_memicmp(buf, "DEFAULT ROBUST", 14) == 0) TNC->RobustDefault = TRUE; else if (_memicmp(buf, "DontAddPDUPLEX", 14) == 0) DontAddPDUPLEX = TRUE; else if (_memicmp(buf, "FORCE ROBUST", 12) == 0) TNC->ForceRobust = TNC->RobustDefault = TRUE; else if (_memicmp(buf, "MAXLEVEL", 8) == 0) // Maximum Pactor Level to use. TNC->MaxLevel = atoi(&buf[8]); else if (_memicmp(buf, "DATE", 4) == 0) { char Cmd[32]; time_t T; struct tm * tm; T = time(NULL); tm = gmtime(&T); sprintf(Cmd,"DATE %02d%02d%02d\r", tm->tm_mday, tm->tm_mon + 1, tm->tm_year - 100); strcat (TNC->InitScript, Cmd); } else if (_memicmp(buf, "TIME", 4) == 0) { char Cmd[32]; time_t T; struct tm * tm; T = time(NULL); tm = gmtime(&T); sprintf(Cmd,"TIME %02d%02d%02d\r", tm->tm_hour, tm->tm_min, tm->tm_sec); strcat (TNC->InitScript, Cmd); } else if (standardParams(TNC, buf) == FALSE) strcat (TNC->InitScript, buf); } return (TRUE); } struct TNCINFO * CreateTTYInfo(int port, int speed); BOOL CloseConnection(struct TNCINFO * conn); BOOL WriteCommBlock(struct TNCINFO * TNC); BOOL DestroyTTYInfo(int port); void SCSCheckRX(struct TNCINFO * TNC); VOID SCSPoll(int Port); VOID CRCStuffAndSend(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 ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * rxbuff, int len); VOID ProcessTermModeResponse(struct TNCINFO * TNC); static VOID ExitHost(struct TNCINFO * TNC); static VOID DoTNCReinit(struct TNCINFO * TNC); static VOID DoTermModeTimeout(struct TNCINFO * TNC); static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len); int Switchmode(struct TNCINFO * TNC, int Mode); VOID SwitchToPactor(struct TNCINFO * TNC); VOID SwitchToPacket(struct TNCINFO * TNC); char status[8][8] = {"ERROR", "REQUEST", "TRAFFIC", "IDLE", "OVER", "PHASE", "SYNCH", ""}; char ModeText[8][14] = {"STANDBY", "AMTOR-ARQ", "PACTOR-ARQ", "AMTOR-FEC", "PACTOR-FEC", "RTTY / CW", "LISTEN", "Channel-Busy"}; char PactorLevelText[5][14] = {"Not Connected", "PACTOR-I", "PACTOR-II", "PACTOR-III", "PACTOR-IV"}; char PleveltoMode[5] = {30, 11, 14, 16, 20}; // WL2K Reporting Modes - RP, P1, P2, P3, P4 static size_t ExtProc(int fn, int port, PDATAMESSAGE buff) { int txlen = 0; PMSGWITHLEN buffptr; struct TNCINFO * TNC = TNCInfo[port]; size_t Param; int Stream = 0; struct STREAMINFO * STREAM = &TNC->Streams[0]; char PLevel; 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; #ifndef WIN32 #ifndef MACBPQ #ifndef FREEBSD if (TNC->Dragon) { struct serial_struct sstruct; // Need to set custom baud rate if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) { Debugprintf("Error: Dragon could not get comm ioctl\n"); } else { // set custom divisor to get 829440 baud sstruct.custom_divisor = 29; sstruct.flags |= ASYNC_SPD_CUST; // set serial_struct if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); else Debugprintf("Dragon custom baud rate set\n"); } } #endif #endif #endif } 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; SCSCheckRX(TNC); SCSPoll(port); return 0; case 1: // poll // Check session limit timer if ((STREAM->Connecting || STREAM->Connected) && !STREAM->Disconnecting) { if (TNC->SessionTimeLimit && STREAM->ConnectTime && time(NULL) > (TNC->SessionTimeLimit + STREAM->ConnectTime)) { STREAM->CmdSet = STREAM->CmdSave = malloc(100); sprintf(STREAM->CmdSet, "D\r"); STREAM->Disconnecting = TRUE; } } for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->Streams[Stream].ReportDISC) { TNC->Streams[Stream].ReportDISC = FALSE; buff->PORT = Stream; return -1; } } if (TNC->EnterExit) return 0; // Switching to Term mode to change bandwidth for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->Streams[Stream].PACTORtoBPQ_Q !=0) { int datalen; buffptr = Q_REM(&TNC->Streams[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 += sizeof(void *) + 4; PutLengthinBuffer(buff, datalen); ReleaseBuffer(buffptr); return (1); } } return 0; case 2: // send buffptr = GetBuff(); if (buffptr == 0) return 0; // No buffers, so ignore Stream = buff->PORT; if (!TNC->TNCOK) { // Send Error Response buffptr->Len = sprintf(buffptr->Data, "No Connection to PACTOR TNC\r"); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return 0; } txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN + 1); // 1 as no PID if (txlen == 1 && buff->L2DATA[0] == 0) // Keepalive Packet { ReleaseBuffer(buffptr); return 0; } buffptr->Len = txlen; memcpy(buffptr->Data, buff->L2DATA, txlen); C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr); TNC->Streams[Stream].FramesOutstanding++; // See if possible to send immediately SCSTryToSendDATA(TNC, Stream); 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->Dragon) { if (STREAM->FramesOutstanding > 25) return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } else { if (STREAM->FramesOutstanding > 10) return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } } else { if (STREAM->FramesOutstanding > 3 || TNC->Buffers < 200) return (1 | TNC->HostMode << 8 | STREAM->Disconnecting << 15); } return TNC->HostMode << 8 | STREAM->Disconnecting << 15; // OK, but lock attach if disconnecting case 4: // reinit // Ensure in Pactor TNC->TXBuffer[2] = 31; TNC->TXBuffer[3] = 0x1; TNC->TXBuffer[4] = 0x1; memcpy(&TNC->TXBuffer[5], "PT", 2); CRCStuffAndSend(TNC, TNC->TXBuffer, 7); Sleep(25); ExitHost(TNC); Sleep(50); CloseCOMPort(TNC->hDevice); TNC->hDevice =(HANDLE)0; TNC->ReopenTimer = 250; TNC->HostMode = FALSE; return (0); case 5: // Close // Ensure in Pactor TNC->TXBuffer[2] = 31; TNC->TXBuffer[3] = 0x1; TNC->TXBuffer[4] = 0x1; memcpy(&TNC->TXBuffer[5], "PT", 2); CRCStuffAndSend(TNC, TNC->TXBuffer, 7); Sleep(25); ExitHost(TNC); Sleep(25); CloseCOMPort(TNCInfo[port]->hDevice); return (0); case 6: // Scan Interface Param = (size_t)buff; switch (Param) { case 1: // Request Permission if (TNC->TNCOK) { // If been in Sync a long time, or if using applcalls and // Scan had been locked too long just let it change if (TNC->UseAPPLCallsforPactor) { if (TNC->PTCStatus == 6) // Sync { int insync = (int)(time(NULL) - TNC->TimeEnteredSYNCMode); if (insync > 4) { Debugprintf("SCS Scan - in SYNC for %d Secs - allow change regardless", insync); return 0; } } else if (TNC->TimeScanLocked) { time_t timeLocked = time(NULL) - TNC->TimeScanLocked; if (timeLocked > 4) { Debugprintf("SCS Scan - Scan Locked for %d Secs - allow change regardless", timeLocked); TNC->TimeScanLocked = 0; return 0; } } } TNC->WantToChangeFreq = TRUE; TNC->OKToChangeFreq = FALSE; return TRUE; } return 0; // Don't lock scan if TNC isn't responding case 2: // Check Permission return TNC->OKToChangeFreq; case 3: // Release Permission TNC->WantToChangeFreq = FALSE; if (TNC->DontReleasePermission) // Disable connects during this interval? { TNC->DontReleasePermission = FALSE; if (TNC->SyncSupported == FALSE) TNC->TimeScanLocked = time(NULL) + 100; // Make sure doesnt time out return 0; } TNC->DontWantToChangeFreq = TRUE; return 0; default: // Param is Address of a struct ScanEntry Scan = (struct ScanEntry *)buff; PLevel = Scan->PMaxLevel; if (PLevel == 0 && (Scan->HFPacketMode || Scan->RPacketMode)) { // Switch to Packet for this Interval if (TNC->RIG->RIG_DEBUG) Debugprintf("SCS Switching to Packet, %d", TNC->HFPacket); if (TNC->HFPacket == FALSE) SwitchToPacket(TNC); return 0; } if (PLevel > '0' && PLevel < '5') // 1 - 4 { if (TNC->Bandwidth != PLevel || TNC->MinLevel != (Scan->PMinLevel - '0')) { TNC->Bandwidth = PLevel; TNC->MinLevel = Scan->PMinLevel - '0'; Switchmode(TNC, PLevel - '0'); } if (TNC->UseAPPLCallsforPactor && Scan->APPLCALL[0]) { // Switch callsign STREAM = &TNC->Streams[0]; STREAM->CmdSet = STREAM->CmdSave = malloc(100); strcpy(STREAM->MyCall, Scan->APPLCALL); sprintf(STREAM->CmdSet, "I%s\rI\r", STREAM->MyCall); if (TNC->RIG->RIG_DEBUG) Debugprintf("SCS Pactor APPLCALL Set to %s", STREAM->MyCall); } else { if (TNC->HFPacket) SwitchToPactor(TNC); } } if (Scan->RPacketMode) if (TNC->RobustTime) SwitchToPacket(TNC); // Always start in packet, switch to pactor after RobustTime ticks if (PLevel == '0') TNC->DontReleasePermission = TRUE; // Dont allow connects in this interval else TNC->DontReleasePermission = FALSE; return 0; } } return 0; } int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len) { struct RIGINFO * RIG = TNC->RIG; if (RIG && RIG->WEB_Label) { Len += sprintf(&Buff[Len], ""); Len += sprintf(&Buff[Len], "", RIG->WEB_Label); Len += sprintf(&Buff[Len], "", RIG->WEB_FREQ); Len += sprintf(&Buff[Len], "", RIG->WEB_MODE); Len += sprintf(&Buff[Len], "", RIG->WEB_SCAN); Len += sprintf(&Buff[Len], "", RIG->WEB_PTT); if (TNC->TXRIG && TNC->TXRIG != TNC->RIG) { struct RIGINFO * RIG = TNC->TXRIG; Len += sprintf(&Buff[Len], "", RIG->WEB_Label); Len += sprintf(&Buff[Len], "", RIG->WEB_FREQ); Len += sprintf(&Buff[Len], "", RIG->WEB_MODE); Len += sprintf(&Buff[Len], "", RIG->WEB_SCAN); Len += sprintf(&Buff[Len], "
%s%s%s%c%c
%s%s%s%c%c
", RIG->WEB_PTT); } Len += sprintf(&Buff[Len], ""); } return Len; } static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL) { int Len = sprintf(Buff, "" "SCS Pactor Status

SCS 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], "", TNC->WEB_PACTORLEVEL); Len += sprintf(&Buff[Len], "
Comms State%s
TNC State%s
Mode%s
Status%s
TX/RX State%s
Buffers%s
Traffic%s
Mode%s
"); Len += sprintf(&Buff[Len], "", TNC->WebBuffer); Len = DoScanLine(TNC, Buff, Len); return Len; } void * SCSExtInit(EXTPORTDATA * PortEntry) { char msg[500]; struct TNCINFO * TNC; int port; char * ptr; int Stream = 0; char * TempScript; // // Will be called once for each Pactor Port // The COM port number is in IOBASE // DontAddPDUPLEX = 0; sprintf(msg,"SCS Pactor %s", PortEntry->PORTCONTROL.SerialPortName); WritetoConsole(msg); port=PortEntry->PORTCONTROL.PORTNUMBER; ReadConfigFile(port, ProcessLine); TNC = TNCInfo[port]; if (TNC == NULL) { // Not defined in Config file sprintf(msg," ** Error - no info in BPQ32.cfg for this port\n"); WritetoConsole(msg); return ExtProc; } TNC->Port = port; TNC->Hardware = H_SCS; OpenLogFile(TNC->Port); CloseLogFile(TNC->Port); if (TNC->BusyHold == 0) TNC->BusyHold = 3; if (TNC->BusyWait == 0) TNC->BusyWait = 10; if (TNC->MaxLevel == 0) TNC->MaxLevel = 3; // Set up DED addresses for streams (first stream (Pactor) = DED 31 TNC->Streams[0].DEDStream = 31; for (Stream = 1; Stream <= MaxStreams; Stream++) { TNC->Streams[Stream].DEDStream = Stream; } if (TNC->PacketChannels > MaxStreams) TNC->PacketChannels = MaxStreams; PortEntry->MAXHOSTMODESESSIONS = TNC->PacketChannels + 1; PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream PortEntry->SCANCAPABILITIES = CONLOCK; // Scan Control 3 stage/conlock TNC->PortRecord = PortEntry; if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0) TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK; if (PortEntry->PORTCONTROL.PORTCALL[0] == 0) memcpy(TNC->NodeCall, MYNODECALL, 10); else ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall); PortEntry->PORTCONTROL.PROTOCOL = 10; PortEntry->PORTCONTROL.PORTQUALITY = 0; if (PortEntry->PORTCONTROL.PORTPACLEN == 0) PortEntry->PORTCONTROL.PORTPACLEN = 100; TNC->SuspendPortProc = PTCSuspendPort; TNC->ReleasePortProc = PTCReleasePort; PortEntry->PORTCONTROL.PORTSTARTCODE = SCSStartPort; PortEntry->PORTCONTROL.PORTSTOPCODE = SCSStopPort; PortEntry->PORTCONTROL.UICAPABLE = TRUE; 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 // Set TONES to 4 TempScript = malloc(1000); strcpy(TempScript, "QUIT\r"); // In case in pac: mode strcat(TempScript, "TONES 4\r"); // Tones may be changed but I want this as standard strcat(TempScript, "MAXERR 30\r"); // Max retries strcat(TempScript, "MODE 0\r"); // ASCII mode, no PTC II compression (Forwarding will use FBB Compression) strcat(TempScript, "MAXSUM 20\r"); // Max count for memory ARQ strcat(TempScript, "CWID 0 2\r"); // CW ID disabled strcat(TempScript, "PTCC 0\r"); // Dragon out of PTC Compatibility Mode strcat(TempScript, "VER\r"); // Try to determine Controller Type sprintf(msg, "MYLEVEL %d\r", TNC->MaxLevel); strcat(TempScript, msg); // Default Level to MAXLEVEL strcat(TempScript, TNC->InitScript); free(TNC->InitScript); TNC->InitScript = TempScript; // Others go on end so they can't be overriden strcat(TNC->InitScript, "ADDLF 0\r"); // Auto Line Feed disabled strcat(TNC->InitScript, "ARX 0\r"); // Amtor Phasing disabled strcat(TNC->InitScript, "BELL 0\r"); // Disable Bell strcat(TNC->InitScript, "BC 0\r"); // FEC reception is disabled strcat(TNC->InitScript, "BKCHR 2\r"); // Breakin Char = 2 strcat(TNC->InitScript, "CHOBELL 0\r"); // Changeover Bell off strcat(TNC->InitScript, "CMSG 0\r"); // Connect Message Off strcat(TNC->InitScript, "LFIGNORE 0\r"); // No insertion of Line feed strcat(TNC->InitScript, "LISTEN 0\r"); // Pactor Listen disabled strcat(TNC->InitScript, "MAIL 0\r"); // Disable internal mailbox reporting strcat(TNC->InitScript, "REMOTE 0\r"); // Disable remote control strcat(TNC->InitScript, "PAC CBELL 0\r"); // strcat(TNC->InitScript, "PAC CMSG 0\r"); // strcat(TNC->InitScript, "PAC PRBOX 0\r"); // Turn off Packet Radio Mailbox // Automatic Status must be enabled for BPQ32 // Pactor must use Host Mode Chanel 31 // PDuplex must be set. The Node code relies on automatic IRS/ISS changeover // 5 second duplex timer strcat(TNC->InitScript, "STATUS 2\rPTCHN 31\rPDTIMER 5\r"); if (DontAddPDUPLEX == 0) strcat(TNC->InitScript, "PDUPLEX 1\r"); sprintf(msg, "MYCALL %s\rPAC MYCALL %s\r", TNC->NodeCall, TNC->NodeCall); strcat(TNC->InitScript, msg); PortEntry->PORTCONTROL.TNC = TNC; TNC->WebWindowProc = WebProc; TNC->WebWinX = 510; TNC->WebWinY = 280; TNC->WEB_COMMSSTATE = zalloc(100); TNC->WEB_TNCSTATE = zalloc(100); strcpy(TNC->WEB_TNCSTATE, "Free"); TNC->WEB_MODE = zalloc(100); TNC->WEB_TRAFFIC = zalloc(100); TNC->WEB_BUFFERS = zalloc(100); TNC->WEB_STATE = zalloc(100); TNC->WEB_TXRX = zalloc(100); TNC->WEB_PACTORLEVEL = 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", "Buffers", 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->xIDC_PACTORLEVEL = CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE,10,160,430,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; sprintf(TNC->WEB_BUFFERS, "%05d Queued %05d", TNC->Buffers, TNC->Streams[0].FramesOutstanding); SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS); MoveWindows(TNC); #endif OpenCOMMPort(TNC, PortEntry->PORTCONTROL.SerialPortName, PortEntry->PORTCONTROL.BAUDRATE, FALSE); #ifndef WIN32 #ifndef MACBPQ #ifndef FREEBSD if (TNC->Dragon) { struct serial_struct sstruct; // Need to set custom baud rate if (ioctl(TNC->hDevice, TIOCGSERIAL, &sstruct) < 0) { printf("Error: Dragon could not get comm ioctl\n"); } else { // set custom divisor to get 829440 baud sstruct.custom_divisor = 29; sstruct.flags |= ASYNC_SPD_CUST; // set serial_struct if (ioctl(TNC->hDevice, TIOCSSERIAL, &sstruct) < 0) Debugprintf("Error: Dragon could not set custom comm baud divisor\n"); else Debugprintf("Dragon custom baud rate set\n"); } } #endif #endif #endif if (TNC->RobustDefault) SwitchToPacket(TNC); WritetoConsole("\n"); return ExtProc; } void SCSCheckRX(struct TNCINFO * TNC) { int Length, Len; unsigned short crc; char UnstuffBuffer[500]; if (TNC->RXLen == 500) TNC->RXLen = 0; Len = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); if (Len == 0) return; TNC->RXLen += Len; Length = TNC->RXLen; // DED mode doesn't have an end of frame delimiter. We need to know if we have a full frame // Fortunately this is a polled protocol, so we only get one frame at a time // If first char != 170, then probably a Terminal Mode Frame. Wait for CR on end // If first char is 170, we could check rhe length field, but that could be corrupt, as // we haen't checked CRC. All I can think of is to check the CRC and if it is ok, assume frame is // complete. If CRC is duff, we will eventually time out and get a retry. The retry code // can clear the RC buffer if (TNC->UsingTermMode) { // Send response to Host if connected PMSGWITHLEN buffptr; int Len = TNC->RXLen; int Posn = 0; // First message is probably ACK of JHO4T - AA AA 1F 00 1E 19 if (TNC->RXBuffer[0] == 0xaa && Len > 6) { memmove(TNC->RXBuffer, &TNC->RXBuffer[6], Len - 6); Len -= 6; } // TNC seems to send 1e f7 or 1e 87 regularly while (TNC->RXBuffer[0] == 0x1e && Len > 1) { memmove(TNC->RXBuffer, &TNC->RXBuffer[2], Len - 2); Len -= 2; } if (Len == 0) { TNC->RXLen = 0; // Ready for next frame return; } while (Len > 250) { buffptr = GetBuff(); buffptr->Len = 250; memcpy(buffptr->Data, &TNC->RXBuffer[Posn], 250); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); Len -= 250; Posn += 250; } buffptr = GetBuff(); buffptr->Len = Len; memcpy(buffptr->Data, &TNC->RXBuffer[Posn], Len); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); TNC->RXLen = 0; // Ready for next frame return; } if (TNC->RXBuffer[0] != 170) { // 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->Streams[Stream].RXBuffer[TNC->Streams[Stream].RXLen-2] != ':') if (strlen(TNC->RXBuffer) < TNC->RXLen) TNC->RXLen = 0; if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) return; // Wait for rest of frame // Complete Char Mode Frame OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, TNC->RXBuffer, (int)strlen(TNC->RXBuffer)); CloseLogFile(TNC->Port); 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 (Length < 6) // Minimum Frame Sise return; if (TNC->RXBuffer[2] == 170) { // Retransmit Request TNC->RXLen = 0; return; // Ignore for now } // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); if (Length == -1) { // Unstuff returned an errors (170 not followed by 0) TNC->RXLen = 0; return; // Ignore for now } crc = compute_crc(&UnstuffBuffer[2], Length); if (crc == 0xf0b8) // Good CRC { TNC->RXLen = 0; // Ready for next frame ProcessDEDFrame(TNC, UnstuffBuffer, Length); // If there are more channels to poll (more than 1 entry in general poll response, // and link is not active, poll the next one if (TNC->Timeout == 0) { UCHAR * Poll = TNC->TXBuffer; if (TNC->NexttoPoll[0]) { UCHAR Chan = TNC->NexttoPoll[0] - 1; memmove(&TNC->NexttoPoll[0], &TNC->NexttoPoll[1], 19); Poll[2] = Chan; // Channel Poll[3] = 0x1; // Command if (Chan == 254) // Status - Send Extended Status (G3) { Poll[4] = 1; // Len-1 Poll[5] = 'G'; // Extended Status Poll Poll[6] = '3'; } else { Poll[4] = 0; // Len-1 Poll[5] = 'G'; // Poll } CRCStuffAndSend(TNC, Poll, Poll[4] + 6); TNC->InternalCmd = FALSE; return; } else { // if last message wasn't a general poll, send one now if (TNC->PollSent) return; TNC->PollSent = TRUE; // Use General Poll (255) Poll[2] = 255 ; // Channel Poll[3] = 0x1; // Command Poll[4] = 0; // Len-1 Poll[5] = 'G'; // Poll CRCStuffAndSend(TNC, Poll, 6); TNC->InternalCmd = FALSE; } } return; } // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. return; } BOOL WriteCommBlock(struct TNCINFO * TNC) { BOOL ret = WriteCOMBlock(TNC->hDevice, TNC->TXBuffer, TNC->TXLen); TNC->Timeout = 20; // 2 secs return ret; } VOID SCSPoll(int Port) { struct TNCINFO * TNC = TNCInfo[Port]; UCHAR * Poll = TNC->TXBuffer; char Status[80]; int Stream = 0; int nn; struct STREAMINFO * STREAM; if (TNC->UsingTermMode) { if (TNC->Streams[Stream].BPQtoPACTOR_Q) { PMSGWITHLEN buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); // See if enter host mode command if (_memicmp(buffptr->Data, "ENTERHOST\r", buffptr->Len) == 0) { TNC->UsingTermMode = FALSE; memcpy(Poll, "JHOST4\r", 7); TNC->TXLen = 7; WriteCommBlock(TNC); // No response expected Sleep(10); Poll[2] = 255; // Channel TNC->Toggle = 0; Poll[3] = 0x41; Poll[4] = 0; // Len-1 Poll[5] = 'G'; // Poll CRCStuffAndSend(TNC, Poll, 6); TNC->InternalCmd = FALSE; TNC->Timeout = 5; // 1/2 sec - In case missed } else { // Send to TNC memcpy(&Poll[0], buffptr->Data, buffptr->Len); TNC->TXLen = buffptr->Len;; WriteCommBlock(TNC); } ReleaseBuffer(buffptr); } return; } if (TNC->MinLevelTimer) { TNC->MinLevelTimer--; if (TNC->MinLevelTimer == 0) { // Failed to reach min level in 15 secs STREAM = &TNC->Streams[0]; if (STREAM->Connected) { PMSGWITHLEN buffptr; Debugprintf("Required Min Level not reached - disconnecting"); // Discard Queued Data, Send a Message, then a disconnect while (STREAM->BPQtoPACTOR_Q) ReleaseBuffer(Q_REM(&STREAM->BPQtoPACTOR_Q)); STREAM->NeedDisc = 15; // 1 secs buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data, "This port only allows Pactor Level %d or above - Disconnecting\r\n", TNC->MinLevel); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); } } } if (TNC->SwitchToPactor) { TNC->SwitchToPactor--; if (TNC->SwitchToPactor == 0) SwitchToPactor(TNC); } for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && TNC->Streams[Stream].Attached == 0) { // New Attach // If Pactor, stop scanning and take out of listen mode. // Set call to connecting user's call // If Stream 0 Put in Pactor Mode so Busy Detect will work int calllen=0; STREAM = &TNC->Streams[Stream]; Debugprintf("SCS New Attach Stream %d DEDStream %d", Stream, STREAM->DEDStream); if (Stream == 0) STREAM->DEDStream = 31; // Pactor STREAM->Attached = TRUE; calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, STREAM->MyCall); STREAM->MyCall[calllen] = 0; STREAM->CmdSet = STREAM->CmdSave = malloc(100); if (Stream == 0) { // Release Scan Lock if it is held TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit if (TNC->DontReleasePermission) { TNC->DontReleasePermission = FALSE; TNC->DontWantToChangeFreq = TRUE; } sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); // Stop Scanner sprintf(Status, "%d SCANSTOP", TNC->Port); TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch Rig_Command((TRANSPORTENTRY *) -1, Status); } else { sprintf(STREAM->CmdSet, "I%s\r", STREAM->MyCall); Debugprintf("SCS Pactor Attach CMDSet = %s", STREAM->CmdSet); } } } if (TNC->Timeout) { TNC->Timeout--; if (TNC->Timeout) // Still waiting return; TNC->Retries--; if(TNC->Retries) { WriteCommBlock(TNC); // Retransmit Block return; } // Retried out. if (TNC->HostMode == 0) { DoTermModeTimeout(TNC); return; } // Retried out in host mode - Clear any connection and reinit the TNC Debugprintf("PACTOR - Link to TNC Lost"); TNC->TNCOK = FALSE; sprintf(TNC->WEB_COMMSSTATE,"%s Open but TNC not responding", TNC->PortRecord->PORTCONTROL.SerialPortName); SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); // Clear anything from UI_Q while (TNC->PortRecord->UI_Q) { UINT * buffptr = Q_REM(&TNC->PortRecord->UI_Q); ReleaseBuffer(buffptr); } TNC->HostMode = 0; TNC->ReinitState = 0; for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->PortRecord->ATTACHEDSESSIONS[Stream]) // Connected { TNC->Streams[Stream].Connected = FALSE; // Back to Command Mode TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node } } } // We delay clearing busy for BusyHold secs if (TNC->Busy) if (TNC->Mode != 7) TNC->Busy--; if (TNC->BusyDelay) // Waiting to send connect { // Still Busy? if (InterlockedCheckBusy(TNC) == 0) { // No, so send TNC->Streams[0].CmdSet = TNC->ConnectCmd; TNC->Streams[0].Connecting = TRUE; TNC->Streams[0].ConnectTime = time(NULL); sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[0].MyCall, TNC->Streams[0].RemoteCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[0].CmdSet); 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 = 39; memcpy(buffptr->Data,"Sorry, Can't Connect - Channel is busy\r", 39); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); free(TNC->ConnectCmd); } } } for (Stream = 0; Stream <= MaxStreams; Stream++) { STREAM = &TNC->Streams[Stream]; if (STREAM->Attached) CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete); if (STREAM->NeedDisc) { STREAM->NeedDisc--; if (STREAM->NeedDisc == 0) STREAM->ReportDISC = TRUE; } if (TNC->Timeout) return; // We've sent something } // if we have just restarted or TNC appears to be in terminal mode, run Initialisation Sequence if (!TNC->HostMode) { DoTNCReinit(TNC); return; } TNC->PollSent = FALSE; //If sending internal command list, send next element for (Stream = 0; Stream <= MaxStreams; Stream++) { if (TNC->Streams[Stream].CmdSet) { unsigned char * start, * end; int len; start = TNC->Streams[Stream].CmdSet; if (*(start) == 250) // 2nd part of long KISS packet { len = start[1]; Poll[2] = 250; // KISS Channel Poll[3] = 0; // Data Poll[4] = len - 1; memcpy(&Poll[5], &start[2], len); CRCStuffAndSend(TNC, Poll, len + 5); free(TNC->Streams[Stream].CmdSave); TNC->Streams[Stream].CmdSet = NULL; return; } if (*(start) == 0) // End of Script { free(TNC->Streams[Stream].CmdSave); TNC->Streams[Stream].CmdSet = NULL; } else { end = strchr(start, 13); len = (int)(++end - start - 1); // exclude cr TNC->Streams[Stream].CmdSet = end; if (*(start) == 1) { // This is UI data, not a command. Send it to channel 0 len --; Poll[2] = 0; // UI Channel Poll[3] = 0; // Data Poll[4] = len - 1; memcpy(&Poll[5], &start[1], len); CRCStuffAndSend(TNC, Poll, len + 5); return; } if (*(start) == 2) { // This is a UI command Send it to channel 0 len--; Poll[2] = 0; // UI Channel Poll[3] = 1; // Command Poll[4] = len - 1; memcpy(&Poll[5], &start[1], len); CRCStuffAndSend(TNC, Poll, len + 5); return; } Poll[2] = TNC->Streams[Stream].DEDStream; // Channel Poll[3] = 1; // Command Poll[4] = len - 1; memcpy(&Poll[5], start, len); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], len); CloseLogFile(TNC->Port); CRCStuffAndSend(TNC, Poll, len + 5); return; } } } // if Freq Change needed, check if ok to do it. if (TNC->TNCOK) { if (TNC->WantToChangeFreq) { Poll[2] = 31; // Command Poll[3] = 1; // Command Poll[4] = 2; // Len -1 Poll[5] = '%'; Poll[6] = 'W'; Poll[7] = '0'; CRCStuffAndSend(TNC, Poll, 8); TNC->InternalCmd = TRUE; TNC->WantToChangeFreq = FALSE; if (TNC->RIG->RIG_DEBUG) Debugprintf("Scan Debug SCS Pactor Requesting permission from TNC"); return; } if (TNC->DontWantToChangeFreq) { Poll[2] = 31; // Command Poll[3] = 1; // Command Poll[4] = 2; // Len -1 Poll[5] = '%'; Poll[6] = 'W'; Poll[7] = '1'; CRCStuffAndSend(TNC, Poll, 8); TNC->InternalCmd = TRUE; TNC->DontWantToChangeFreq = FALSE; TNC->OKToChangeFreq = FALSE; if (TNC->RIG->RIG_DEBUG) Debugprintf("Scan Debug SCS Pactor Release Scan Lock sent to TNC"); return; } } // Send Radio Command if avail if (TNC->TNCOK && TNC->BPQtoRadio_Q) { int datalen; PMSGWITHLEN buffptr; buffptr = Q_REM(&TNC->BPQtoRadio_Q); datalen = (int)buffptr->Len; Poll[2] = 253; // Radio Channel Poll[3] = 0; // Data? Poll[4] = datalen - 1; memcpy(&Poll[5], buffptr->Data, datalen); ReleaseBuffer(buffptr); CRCStuffAndSend(TNC, Poll, datalen + 5); if (TNC->RIG->RIG_DEBUG) { Debugprintf("SCS Rig Command Queued, Len = %d", datalen ); Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", Poll[5], Poll[6], Poll[7], Poll[8], Poll[9], Poll[10], Poll[11], Poll[12], Poll[13], Poll[14], Poll[15], Poll[16], Poll[17], Poll[18], Poll[19], Poll[20]); } // Debugprintf("SCS Sending Rig Command"); return; } if (TNC->TNCOK && TNC->PortRecord->UI_Q) { int datalen; char * Buffer; char ICall[16]; char CCMD[80] = "C"; char Call[12] = " "; struct _MESSAGE * buffptr; buffptr = Q_REM(&TNC->PortRecord->UI_Q); datalen = buffptr->LENGTH - MSGHDDRLEN; Buffer = &buffptr->DEST[0]; // Raw Frame Buffer[datalen] = 0; // If a Dragon with KISS over Hostmade we can just send it if (TNC->DragonKISS) { int EncLen; Poll[2] = 250; // KISS Channel Poll[3] = 0; // CMD Poll[4] = datalen + 2; // 3 extrac chars, but need Len - 1 Buffer--; *(Buffer) = 0; // KISS Control on front EncLen = KissEncode(Buffer, &Poll[5], datalen + 1); // We can only send 256 bytes in HostMode, so if longer will // have to fragemt if (EncLen > 256) { //We have to wait for response before sending rest, so use CmdSet // We need to save the extra first, as CRC will overwrite the first two bytes UCHAR * ptr = TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400); (*ptr++) = 250; // KISS Channel (*ptr++) = EncLen - 256; memcpy(ptr, &Poll[5 + 256], EncLen - 256); // Send first 256 Poll[4] = 255; //need Len - 1 CRCStuffAndSend(TNC, Poll, 261); } else { Poll[4] = EncLen - 1; //need Len - 1 CRCStuffAndSend(TNC, Poll, EncLen + 5); } ReleaseBuffer((UINT *)buffptr); return; } // Not dragon KISS // Buffer has an ax.25 header, which we need to pick out and set as channel 0 Connect address // before sending the beacon // We also need to set Chan 0 Mycall so digi'ing can work, and put // it back after so incoming calls will work // But we cant set digipeated bit in call, so if we find one, skip message // This doesn't seem to work ConvFromAX25(Buffer + 7, ICall); // Origin strlop(ICall, ' '); ConvFromAX25(Buffer, &Call[1]); // Dest strlop(&Call[1], ' '); strcat(CCMD, Call); Buffer += 14; // Skip Origin datalen -= 7; while ((Buffer[-1] & 1) == 0) { if (Buffer[6] & 0x80) // Digied bit set? { ReleaseBuffer((UINT *)buffptr); return; } ConvFromAX25(Buffer, &Call[1]); strlop(&Call[1], ' '); strcat(CCMD, Call); Buffer += 7; // End of addr datalen -= 7; } if (Buffer[0] == 3) // UI { Buffer += 2; datalen -= 2; Poll[2] = 0; // UI Channel Poll[3] = 1; // CMD Poll[4] = (int)strlen(CCMD) - 1; strcpy(&Poll[5], CCMD); CRCStuffAndSend(TNC, Poll, Poll[4] + 6); // Set Dest and Path TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = zalloc(400); sprintf(TNC->Streams[0].CmdSet, "%cI%s\r%c%s\r%cI%s\r", 2, ICall, // Flag as Chan 0 Command 1, Buffer, // Flag CmdSet as Data 2, TNC->NodeCall); // Flag as Chan 0 Command } ReleaseBuffer((UINT *)buffptr); return; } // Check status Periodically if (TNC->TNCOK) { if (TNC->IntCmdDelay == 6) { Poll[2] = 254; // Channel Poll[3] = 0x1; // Command Poll[4] = 1; // Len-1 Poll[5] = 'G'; // Extended Status Poll Poll[6] = '3'; CRCStuffAndSend(TNC, Poll, 7); TNC->InternalCmd = TRUE; TNC->IntCmdDelay--; return; } if (TNC->IntCmdDelay == 4) { Poll[2] = 31; // Channel Poll[3] = 0x1; // Command Poll[4] = 1; // Len-1 Poll[5] = '%'; // Bytes acked Status Poll[6] = 'T'; CRCStuffAndSend(TNC, Poll, 7); TNC->InternalCmd = TRUE; TNC->IntCmdDelay--; return; } if (TNC->IntCmdDelay <=0) { Poll[2] = 31; // Channel Poll[3] = 0x1; // Command Poll[4] = 1; // Len-1 Poll[5] = '@'; // Buffer Status Poll[6] = 'B'; CRCStuffAndSend(TNC, Poll, 7); TNC->InternalCmd = TRUE; TNC->IntCmdDelay = 20; // Every 2 secs return; } else TNC->IntCmdDelay--; } // If busy, send status poll, send Data if avail // We need to start where we last left off, or a busy stream will lock out the others for (nn = 0; nn <= MaxStreams; nn++) { Stream = TNC->LastStream++; if (TNC->LastStream > MaxStreams) TNC->LastStream = 0; if (TNC->TNCOK && TNC->Streams[Stream].BPQtoPACTOR_Q) { int datalen; PMSGWITHLEN buffptr; char * Buffer; // Dont send to Pactor if waiting for Min Level to be reached if (TNC->MinLevelTimer && Stream == 0) continue; buffptr = Q_REM(&TNC->Streams[Stream].BPQtoPACTOR_Q); datalen = (int)buffptr->Len; Buffer = buffptr->Data; // Data portion of frame Poll[2] = TNC->Streams[Stream].DEDStream; // Channel if (TNC->Streams[Stream].Connected) { if (TNC->SwallowSignon && Stream == 0) { TNC->SwallowSignon = FALSE; if (strstr(Buffer, "Connected")) // Discard *** connected { ReleaseBuffer(buffptr); return; } } Poll[3] = 0; // Data? TNC->Streams[Stream].BytesTXed += datalen; Poll[4] = datalen - 1; memcpy(&Poll[5], Buffer, datalen); WritetoTrace(TNC, Buffer, datalen); ReleaseBuffer(buffptr); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], datalen); CloseLogFile(TNC->Port); CRCStuffAndSend(TNC, Poll, datalen + 5); TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; if (STREAM->Disconnecting && TNC->Streams[Stream].BPQtoPACTOR_Q == 0) TidyClose(TNC, 0); return; } // Command. Do some sanity checking and look for things to process locally Poll[3] = 1; // Command datalen--; // Exclude CR Buffer[datalen] = 0; // Null Terminate _strupr(Buffer); if (_memicmp(Buffer, "DD", 2) == 0) { // Send DD (Dirty Disconnect) // Uses "Hidden" feature where you can send any normal mode command // in host mode by preceeding with a # Poll[2] = 31; Poll[3] = 0x1; Poll[4] = 2; sprintf(&Poll[5], "#DD\r"); CRCStuffAndSend(TNC, Poll, 8); // It looks like there isn't a response TNC->Timeout = 0; OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], 4); CloseLogFile(TNC->Port); ReleaseBuffer(buffptr); return; } if (_memicmp(Buffer, "D", 1) == 0) { TNC->Streams[Stream].ReportDISC = TRUE; // Tell Node ReleaseBuffer(buffptr); return; } if (memcmp(Buffer, "RADIO ", 6) == 0) { sprintf(&Buffer[40], "%d %s", TNC->Port, &Buffer[6]); if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &Buffer[40])) { ReleaseBuffer(buffptr); } else { buffptr->Len = sprintf(buffptr->Data, "%s", &Buffer[40]); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); } return; } if (_memicmp(Buffer, "SessionTimeLimit", 16) == 0) { if (Buffer[16] != 13) { PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff(); TNC->SessionTimeLimit = atoi(&Buffer[16]) * 60; if (buffptr) { buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SCS} OK\r"); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); } return; } } if (memcmp(Buffer, "MYLEVEL ", 8) == 0) { Switchmode(TNC, Buffer[8] - '0'); TNC->Bandwidth = Buffer[8]; // so scanner knows where we are buffptr->Len = sprintf(buffptr->Data, "Ok\r"); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return; } if (memcmp(Buffer, "CHECKLEVEL", 10) == 0) { CheckMode(TNC); buffptr->Len = sprintf(buffptr->Data, "%s\r", &TNC->RXBuffer[2]); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return; } if (_memicmp(Buffer, "OVERRIDEBUSY", 12) == 0) { TNC->OverrideBusy = TRUE; buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return; } if ((Stream == 0) && memcmp(Buffer, "RPACKET", 7) == 0) { TNC->HFPacket = TRUE; buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); return; } if ((Stream == 0) && memcmp(Buffer, "PACTOR", 6) == 0) { TNC->HFPacket = FALSE; buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); return; } if ((Stream == 0) && memcmp(Buffer, "EXITHOST", 8) == 0) { UCHAR * Poll = TNC->TXBuffer; TNC->UsingTermMode = 1; ExitHost(TNC); // Send CR to get prompt from TNC Poll[0] = 13; TNC->TXLen = 1; WriteCommBlock(TNC); ReleaseBuffer(buffptr); return; } if (Stream == 0 && Buffer[0] == 'C' && datalen > 2) // Pactor Connect Poll[2] = TNC->Streams[0].DEDStream = 31; // Pactor Channel if (Stream == 0 && Buffer[0] == 'R' && Buffer[1] == 'C') // Robust Packet Connect { Poll[2] = TNC->Streams[0].DEDStream = 30; // Last Packet Channel memmove(Buffer, &Buffer[1], datalen--); } if (Buffer[0] == 'C' && datalen > 2) // Connect { if (*(++Buffer) == ' ') Buffer++; // Space isn't needed if ((memcmp(Buffer, "P1 ", 3) == 0) ||(memcmp(Buffer, "P2 ", 3) == 0)) { // Port Selector for Packet Connect convert to 2:CALL Buffer[0] = Buffer[1]; Buffer[1] = ':'; memmove(&Buffer[2], &Buffer[3], datalen--); //Buffer += 2; } memcpy(TNC->Streams[Stream].RemoteCall, Buffer, 9); TNC->Streams[Stream].Connecting = TRUE; if (Stream == 0) { // Send Call, Mode Command followed by connect TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); if (TNC->Dragon) sprintf(TNC->Streams[0].CmdSet, "I%s\r%s\r", TNC->Streams[0].MyCall, buffptr->Data); else { if (TNC->Streams[0].DEDStream == 31) sprintf(TNC->Streams[0].CmdSet, "I%s\rPT\r%s\r", TNC->Streams[0].MyCall, buffptr->Data); else sprintf(TNC->Streams[0].CmdSet, "I%s\rPR\r%s\r", TNC->Streams[0].MyCall, buffptr->Data); } ReleaseBuffer(buffptr); // See if Busy if (InterlockedCheckBusy(TNC)) { // Channel Busy. Unless override set, wait if (TNC->OverrideBusy == 0) { // Send Mode Command now, save command, and wait up to 10 secs // No, leave in Pactor, or Busy Detect won't work. Queue the whole conect sequence TNC->ConnectCmd = TNC->Streams[0].CmdSet; TNC->Streams[0].CmdSet = NULL; sprintf(TNC->WEB_TNCSTATE, "Waiting for clear channel"); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); TNC->BusyDelay = TNC->BusyWait * 10; TNC->Streams[Stream].Connecting = FALSE; // Not connecting Yet return; } } TNC->OverrideBusy = FALSE; sprintf(TNC->WEB_TNCSTATE, "%s Connecting to %s", TNC->Streams[Stream].MyCall, TNC->Streams[Stream].RemoteCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); Debugprintf("SCS Pactor CMDSet = %s", TNC->Streams[Stream].CmdSet); TNC->Streams[0].InternalCmd = FALSE; return; } } Poll[4] = datalen - 1; memcpy(&Poll[5], buffptr->Data, datalen); // if it starts with # the tnc won't respond, so send OK now. if (Buffer[0] == '#') { TNC->HFPacket = TRUE; buffptr->Len = sprintf(buffptr->Data, "SCS} OK\r"); C_Q_ADD(&TNC->Streams[0].PACTORtoBPQ_Q, buffptr); } else ReleaseBuffer(buffptr); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], datalen); CloseLogFile(TNC->Port); CRCStuffAndSend(TNC, Poll, datalen + 5); TNC->Streams[Stream].InternalCmd = TNC->Streams[Stream].Connected; return; } // if frames outstanding, issue a poll if (TNC->Streams[Stream].FramesOutstanding) { Poll[2] = TNC->Streams[Stream].DEDStream; Poll[3] = 0x1; // Command Poll[4] = 0; // Len-1 Poll[5] = 'L'; // Status CRCStuffAndSend(TNC, Poll, 6); TNC->InternalCmd = TRUE; TNC->IntCmdDelay--; return; } } TNC->PollSent = TRUE; // Use General Poll (255) Poll[2] = 255 ; // Channel Poll[3] = 0x1; // Command if (TNC->ReinitState == 3) { TNC->ReinitState = 0; Poll[3] = 0x41; } Poll[4] = 0; // Len-1 Poll[5] = 'G'; // Poll CRCStuffAndSend(TNC, Poll, 6); TNC->InternalCmd = FALSE; return; } void SCSTryToSendDATA(struct TNCINFO * TNC, int Stream) { // Used after queuing data to see if it can be sent immediately struct STREAMINFO * STREAM = &TNC->Streams[Stream]; int datalen; PMSGWITHLEN buffptr; char * Buffer; UCHAR * Poll = TNC->TXBuffer; if (TNC->TNCOK == 0 || STREAM->BPQtoPACTOR_Q == 0 || STREAM->Connected == 0) return; // Dont send to Pactor if waiting for Min Level to be reached if (TNC->MinLevelTimer && Stream == 0) return;; Sleep(10); // Give TNC time to respond SCSCheckRX(TNC); // See if anything received if (TNC->Timeout) return; // Link busy buffptr = Q_REM(&STREAM->BPQtoPACTOR_Q); datalen = (int)buffptr->Len; Buffer = buffptr->Data; // Data portion of frame Poll[2] = STREAM->DEDStream; // Channel if (TNC->SwallowSignon && Stream == 0) { TNC->SwallowSignon = FALSE; if (strstr(Buffer, "Connected")) // Discard *** connected { ReleaseBuffer(buffptr); return; } } Poll[3] = 0; // Data STREAM->BytesTXed += datalen; Poll[4] = datalen - 1; memcpy(&Poll[5], Buffer, datalen); WritetoTrace(TNC, Buffer, datalen); ReleaseBuffer(buffptr); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], datalen); CloseLogFile(TNC->Port); CRCStuffAndSend(TNC, Poll, datalen + 5); if (STREAM->Disconnecting && STREAM->BPQtoPACTOR_Q == 0) TidyClose(TNC, Stream); return; } VOID DoTNCReinit(struct TNCINFO * TNC) { UCHAR * Poll = TNC->TXBuffer; if (TNC->ReinitState == 0) { // Just Starting - Send a TNC Mode Command to see if in Terminal or Host Mode TNC->TNCOK = FALSE; sprintf(TNC->WEB_COMMSSTATE,"%s Initialising TNC", TNC->PortRecord->PORTCONTROL.SerialPortName); SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); Poll[0] = 13; Poll[1] = 0x1B; TNC->TXLen = 2; if (WriteCommBlock(TNC) == FALSE) { CloseCOMPort(TNC->hDevice); OpenCOMMPort(TNC, TNC->PortRecord->PORTCONTROL.SerialPortName, TNC->PortRecord->PORTCONTROL.BAUDRATE, TRUE); } TNC->Retries = 1; } if (TNC->ReinitState == 1) // Forcing back to Term TNC->ReinitState = 0; 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 Debugprintf("DOTNCReinit Complete - Entering Hostmode"); TNC->TXBuffer[2] = 0; TNC->Toggle = 0; memcpy(Poll, "JHOST4\r", 7); TNC->TXLen = 7; WriteCommBlock(TNC); // Timeout will enter host mode TNC->Timeout = 1; TNC->Retries = 1; TNC->Toggle = 0; TNC->ReinitState = 3; // Set toggle force bit TNC->OKToChangeFreq = 1; // In case failed whilst waiting for permission return; } end = strchr(start, 13); len = (int)(++end - start); TNC->InitPtr = end; memcpy(Poll, start, len); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, Poll, len); CloseLogFile(TNC->Port); TNC->TXLen = len; WriteCommBlock(TNC); TNC->Retries = 2; } } 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; ExitHost(TNC); TNC->Retries = 1; 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; TNC->IntCmdDelay = 10; return; } } BOOL CheckRXText(struct TNCINFO * TNC) { int Length; // only try to read number of bytes in queue if (TNC->RXLen == 500) TNC->RXLen = 0; Length = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); if (Length == 0) return FALSE; // Nothing doing TNC->RXLen += Length; Length = TNC->RXLen; TNC->RXBuffer[TNC->RXLen] = 0; if (strlen(TNC->RXBuffer) < TNC->RXLen) TNC->RXLen = 0; if ((strstr(TNC->RXBuffer, "cmd: ") == 0) && (strstr(TNC->RXBuffer, "pac: ") == 0)) return 0; // Wait for rest of frame // Complete Char Mode Frame OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, TNC->RXBuffer, TNC->RXLen); CloseLogFile(TNC->Port); TNC->RXBuffer[TNC->RXLen] = 0; if (TNC->RIG->RIG_DEBUG) Debugprintf(TNC->RXBuffer); TNC->RXLen = 0; // Ready for next frame return TRUE; } BOOL CheckRXHost(struct TNCINFO * TNC, char * UnstuffBuffer) { int Length; unsigned short crc; // only try to read number of bytes in queue if (TNC->RXLen == 500) TNC->RXLen = 0; Length = ReadCOMBlock(TNC->hDevice, &TNC->RXBuffer[TNC->RXLen], 500 - TNC->RXLen); if (Length == 0) return FALSE; // Nothing doing TNC->RXLen += Length; Length = TNC->RXLen; if (Length < 6) // Minimum Frame Sise return FALSE; if (TNC->RXBuffer[2] == 170) { // Retransmit Request TNC->RXLen = 0; return FALSE; // Ignore for now } // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice Length = Unstuff(&TNC->RXBuffer[2], &UnstuffBuffer[2], Length - 2); if (Length == -1) { // Unstuff returned an errors (170 not followed by 0) TNC->RXLen = 0; return FALSE; // Ignore for now } crc = compute_crc(&UnstuffBuffer[2], Length); if (crc == 0xf0b8) // Good CRC { TNC->RXLen = 0; // Ready for next frame return TRUE; } // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. return FALSE; } //#include "Mmsystem.h" int Sleeptime = 250; int CheckMode(struct TNCINFO * TNC) { int n; char UnstuffBuffer[500]; UCHAR * Poll = TNC->TXBuffer; if (TNC->HostMode == 0) return 0; // Don't try if initialising TNC->EnterExit = TRUE; Poll[2] = 31; Poll[3] = 0x41; Poll[4] = 0x5; memcpy(&Poll[5], "JHOST0", 6); CRCStuffAndSend(TNC, Poll, 11); n = 0; while (CheckRXHost(TNC, UnstuffBuffer) == FALSE) { Sleep(5); n++; if (n > 100) break; } sprintf(Poll, "MYL\r"); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, Poll, 3); CloseLogFile(TNC->Port); TNC->TXLen = 4; WriteCommBlock(TNC); n = 0; while (CheckRXText(TNC) == FALSE) { Sleep(5); n++; if (n > 100) break; } memcpy(Poll, "JHOST4\r", 7); TNC->TXLen = 7; WriteCommBlock(TNC); // No response expected Sleep(10); Poll[2] = 255; // Channel TNC->Toggle = 0; Poll[3] = 0x41; Poll[4] = 0; // Len-1 Poll[5] = 'G'; // Poll CRCStuffAndSend(TNC, Poll, 6); TNC->InternalCmd = FALSE; TNC->Timeout = 5; // 1/2 sec - In case missed TNC->EnterExit = FALSE; return 0; } int Switchmode(struct TNCINFO * TNC, int Mode) { int n; char UnstuffBuffer[500]; UCHAR * Poll = TNC->TXBuffer; if (TNC->HostMode == 0) return 0; // Don't try if initialising if (TNC->HFPacket) { Poll[2] = 31; Poll[3] = 0x1; Poll[4] = 0x1; memcpy(&Poll[5], "PT", 2); CRCStuffAndSend(TNC, Poll, 7); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, "SwitchModes - Setting Pactor", 28); CloseLogFile(TNC->Port); TNC->HFPacket = FALSE; TNC->Streams[0].DEDStream = 31; // Pactor Channel n = 0; while (CheckRXHost(TNC, UnstuffBuffer) == FALSE) { Sleep(5); n++; if (n > 100) break; } // Debugprintf("Set Pactor ACK received in %d mS, Sleeping for %d", 5 * n, Sleeptime); Sleep(Sleeptime); } // Uses "Hidden" feature where you can send any normal mode command // in host mode by preceeding with a # Poll[2] = 31; Poll[3] = 0x1; Poll[4] = 5; sprintf(&Poll[5], "#MYL %d\r", Mode); CRCStuffAndSend(TNC, Poll, 11); // It looks like there isn't a response TNC->Timeout = 0; OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], 6); CloseLogFile(TNC->Port); return 0; } VOID SwitchToPactor(struct TNCINFO * TNC) { if (TNC->ForceRobust) return; TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); sprintf(TNC->Streams[0].CmdSet, "PT\r"); TNC->HFPacket = FALSE; TNC->Streams[0].DEDStream = 31; // Pactor Channel if (TNC->RIG->RIG_DEBUG) Debugprintf("BPQ32 Scan - switch to Pactor"); } VOID SwitchToPacket(struct TNCINFO * TNC) { TNC->Streams[0].CmdSet = TNC->Streams[0].CmdSave = malloc(100); sprintf(TNC->Streams[0].CmdSet, "PR\r"); TNC->HFPacket = TRUE; TNC->Streams[0].DEDStream = 30; // Packet Channel TNC->SwitchToPactor = TNC->RobustTime; if (TNC->RIG->RIG_DEBUG) Debugprintf("BPQ32 Scan - switch to Packet"); } VOID ExitHost(struct TNCINFO * TNC) { UCHAR * Poll = TNC->TXBuffer; // Try to exit Host Mode TNC->TXBuffer[2] = 31; TNC->TXBuffer[3] = 0x41; TNC->TXBuffer[4] = 0x5; memcpy(&TNC->TXBuffer[5], "JHOST0", 6); CRCStuffAndSend(TNC, Poll, 11); return; } VOID CRCStuffAndSend(struct TNCINFO * TNC, UCHAR * Msg, int Len) { unsigned short int crc; UCHAR StuffedMsg[500]; int i, j; Msg[3] |= TNC->Toggle; TNC->Toggle ^= 0x80; crc = compute_crc(&Msg[2], Len-2); crc ^= 0xffff; Msg[Len++] = (crc&0xff); Msg[Len++] = (crc>>8); for (i = j = 2; i < Len; i++) { StuffedMsg[j++] = Msg[i]; if (Msg[i] == 170) { StuffedMsg[j++] = 0; } } if (j != i) { Len = j; memcpy(Msg, StuffedMsg, j); } TNC->TXLen = Len; Msg[0] = 170; Msg[1] = 170; WriteCommBlock(TNC); TNC->Retries = 5; } int Unstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len) { int i, j=0; for (i=0; iTXBuffer; if (TNC->ReinitState == 0) { // Testing if in Term Mode. It is, so can now send Init Commands TNC->InitPtr = TNC->InitScript; TNC->ReinitState = 2; // Send Restart to make sure PTC is in a known state strcpy(Poll, "RESTART\r"); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, Poll, 7); CloseLogFile(TNC->Port); TNC->TXLen = 8; WriteCommBlock(TNC); TNC->Timeout = 60; // 6 secs return; } if (TNC->ReinitState == 2) { // Sending Init Commands if (strstr(TNC->RXBuffer, "SCS P4dragon")) { TNC->Dragon = TRUE; Debugprintf("SCSPactor in P4dragon mode"); } DoTNCReinit(TNC); // Send Next Command return; } } VOID ProcessIncomingCall(struct TNCINFO * TNC, struct STREAMINFO * STREAM, int Stream) { APPLCALLS * APPL; char * ApplPtr = APPLS; int App; char Appl[10]; char FreqAppl[10] = ""; // Frequecy-specific application char DestCall[10]; TRANSPORTENTRY * SESS; struct WL2KInfo * WL2K = TNC->WL2K; UCHAR * ptr; UCHAR Buffer[80]; PMSGWITHLEN buffptr; BOOL PactorCall = FALSE; char * Call = STREAM->RemoteCall; if (Stream > 0 && Stream < 30) ProcessIncommingConnectEx(TNC, Call, Stream, FALSE, TRUE); // No CTEXT else ProcessIncommingConnectEx(TNC, Call, Stream, TRUE, TRUE); SESS = TNC->PortRecord->ATTACHEDSESSIONS[Stream]; if (SESS == NULL) return; // Cant do much without one if (Stream > 0 && Stream < 30) { // Packet Connect. Much safer to process here, even though it means // duplicating some code, or the Pactor/RP mode tests get very complicated int Len = 0; struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL; strcpy(DestCall, STREAM->MyCall); Debugprintf("PTC Packet Incoming Call - MYCALL = *%s*", DestCall); for (App = 0; App < 32; App++) { APPL=&APPLCALLTABLE[App]; memcpy(Appl, APPL->APPLCALL_TEXT, 10); ptr=strchr(Appl, ' '); if (ptr) *ptr = 0; if (_stricmp(DestCall, Appl) == 0) break; } if (App < 32) { char AppName[13]; memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); AppName[12] = 0; // Make sure app is available Debugprintf("Connect is to APPL %s", AppName); if (CheckAppl(TNC, AppName)) { 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; } else { char Msg[] = "Application not available\r\n"; // Send a Message, then a disconenct buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = (int)strlen(Msg); memcpy(buffptr->Data, Msg, strlen(Msg)); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); STREAM->NeedDisc = 100; // 10 secs } return; } // Not to a known appl - drop through to Node if (PORT->CTEXT) { Len = strlen(PORT->CTEXT); ptr = PORT->CTEXT; } else if (CTEXTLEN) { Len = CTEXTLEN; ptr = CTEXTMSG; } else return; while (Len > 0) { int sendLen = TNC->PortRecord->ATTACHEDSESSIONS[Stream]->SESSPACLEN; if (sendLen == 0) sendLen = 80; if (Len < sendLen) sendLen = Len; buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = sendLen; memcpy(buffptr->Data, ptr, sendLen); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); ptr += sendLen; Len -= sendLen; } return; } //Connect on HF port. May be Pactor or RP on some models if (STREAM->DEDStream == 31) PactorCall = TRUE; if (TNC->RIG && TNC->RIG != &TNC->DummyRig) { sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", STREAM->RemoteCall, TNC->NodeCall, TNC->RIG->Valchar); SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq // If Scan Entry has a Appl, save it if (PactorCall && 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", STREAM->RemoteCall, TNC->NodeCall); if (WL2K) SESS->Frequency = WL2K->Freq; } if (WL2K) strcpy(SESS->RMSCall, WL2K->RMSCall); SESS->Mode = PleveltoMode[TNC->Streams[Stream].PTCStatus1]; SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); if (PactorCall && TNC->MinLevel > 1) TNC->MinLevelTimer = 150; // Check we have reached right level // See which application the connect is for strcpy(DestCall, STREAM->MyCall); if (PactorCall) Debugprintf("Pactor Incoming Call - MYCALL = *%s*", DestCall); else Debugprintf("HF Packet/RP Incoming Call - MYCALL = *%s*", DestCall); if ((PactorCall && TNC->UseAPPLCallsforPactor) || (PactorCall == 0 && TNC->UseAPPLCalls)) // Test for Richard - Should drop through to Node if not to an APPLCALL //&& strcmp(DestCall, TNC->NodeCall) != 0) // Not Connect to Node Call { for (App = 0; App < 32; App++) { APPL=&APPLCALLTABLE[App]; memcpy(Appl, APPL->APPLCALL_TEXT, 10); ptr=strchr(Appl, ' '); if (ptr) *ptr = 0; if (_stricmp(DestCall, Appl) == 0) break; } if (App < 32) { char AppName[13]; memcpy(AppName, &ApplPtr[App * sizeof(CMDX)], 12); AppName[12] = 0; // if SendTandRtoRelay set and Appl is RMS change to RELAY if (TNC->SendTandRtoRelay && memcmp(AppName, "RMS ", 4) == 0 && (strstr(Call, "-T" ) || strstr(Call, "-R"))) strcpy(AppName, "RELAY "); // Make sure app is available Debugprintf("Connect is to APPL %s", AppName); if (CheckAppl(TNC, AppName)) { 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; } else { char Msg[] = "Application not available\r\n"; // Send a Message, then a disconenct buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = strlen(Msg); memcpy(buffptr->Data, Msg, strlen(Msg)); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); STREAM->NeedDisc = 100; // 10 secs } return; } // Not to a known appl - drop through to Node } if (!PactorCall && TNC->UseAPPLCalls) goto DontUseAPPLCmd; // Don't use APPL= for Packet Calls // if SendTandRtoRelay set and Appl is RMS change to RELAY if (TNC->SendTandRtoRelay && strcmp(FreqAppl, "RMS") == 0 && (strstr(Call, "-T" ) || strstr(Call, "-R"))) strcpy(FreqAppl, "RELAY"); Debugprintf("Pactor Call is %s Freq Specific Appl is %s Freq is %s", DestCall, FreqAppl, TNC->RIG->Valchar); if (FreqAppl[0]) // Frequency specific APPL overrides TNC APPL { buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore Debugprintf("Using Freq Specific Appl %s", FreqAppl); buffptr->Len = sprintf(buffptr->Data, "%s\r", FreqAppl); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); TNC->SwallowSignon = TRUE; return; } // If an autoconnect APPL is defined, send it if (TNC->ApplCmd) { char App[16]; buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore strcpy(App, TNC->ApplCmd); Debugprintf("Using Default Appl *%s*, connecting call is %s", App, Call); // if SendTandRtoRelay set and Appl is RMS change to RELAY if (TNC->SendTandRtoRelay && memcmp(App, "RMS", 3) == 0 && (strstr(Call, "-T" ) || strstr(Call, "-R"))) { strcpy(App, "RELAY"); Debugprintf("Radio Only Call - Connecting to RELAY"); } buffptr->Len = sprintf(buffptr->Data, "%s\r", App); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); TNC->SwallowSignon = TRUE; return; } DontUseAPPLCmd: if (FULL_CTEXT && CTEXTLEN && HFCTEXTLEN == 0) { int Len = CTEXTLEN, CTPaclen = 100; int Next = 0; while (Len > CTPaclen) // CTEXT Paclen { buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = CTPaclen; memcpy(buffptr->Data, &CTEXTMSG[Next], CTPaclen); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); Next += CTPaclen; Len -= CTPaclen; } buffptr = GetBuff(); if (buffptr == 0) return; // No buffers, so ignore buffptr->Len = Len; memcpy(buffptr->Data, &CTEXTMSG[Next], Len); C_Q_ADD(&STREAM->BPQtoPACTOR_Q, buffptr); } } VOID ProcessDEDFrame(struct TNCINFO * TNC, UCHAR * Msg, int framelen) { PMSGWITHLEN buffptr; UCHAR * Buffer; // Data portion of frame char Status[80]; unsigned int Stream = 0, RealStream; if (TNC->HostMode == 0) return; // Any valid frame is an ACK TNC->Timeout = 0; if (TNC->TNCOK == FALSE) { // Just come up struct RIGPORTINFO * PORT; TNC->TNCOK = TRUE; sprintf(TNC->WEB_COMMSSTATE,"%s TNC link OK", TNC->PortRecord->PORTCONTROL.SerialPortName); SetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE); // If using an FT847 on PTC Port it needa a "Cat On" Command. Send it here PORT = TNC->RIG->PORT; if (PORT->PTC && strcmp(PORT->Rigs[0].RigName, "FT847") == 0) { UCHAR CATON[6] = {0,0,0,0,0}; SendPTCRadioCommand(PORT->PTC, CATON, 5); } } Stream = RealStream = Msg[2]; if (Stream > 29) Stream = 0; // 31 = Pactor or 30 = Robust Packet Outgoing // if in Dragon Single Mode (Pactor and Packet on Same Port) // we only use stream 0, so if a packet frame, set DEDStream // Im not convinced this is the bast place to do this, but let's try if (TNC->DragonSingle && RealStream && RealStream < 31) // Not a Pactor or control frame { // must be packet TNC->Streams[0].DEDStream = RealStream; // Packet Channel Stream = 0; } if (TNC->TXBuffer[5] == '#') // Shouldnt happen! return; // See if Poll Reply or Data if (Msg[3] == 0) { // Success - Nothing Follows if (Stream < 32) if (TNC->Streams[Stream].CmdSet) return; // Response to Command Set if ((TNC->TXBuffer[3] & 1) == 0) // Data return; // If the response to a Command, then we should convert to a text "Ok" for forward scripts, etc if (TNC->TXBuffer[5] == 'G') // Poll return; if (TNC->TXBuffer[5] == 'C') // Connect - reply we need is async return; if (TNC->TXBuffer[5] == 'L') // Shouldnt happen! return; if (TNC->TXBuffer[5] == '#') // Shouldnt happen! return; if (TNC->TXBuffer[5] == '%' && TNC->TXBuffer[6] == 'W') // Scan Control - Response to W1 if (TNC->InternalCmd) return; // Just Ignore if (TNC->TXBuffer[5] == 'J') // JHOST { if (TNC->TXBuffer[10] == '0') // JHOST0 { TNC->Timeout = 1; // return; } } if (TNC->Streams[Stream].Connected) return; buffptr = GetBuff(); if (buffptr == NULL) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data,"Pactor} Ok\r"); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, buffptr->Data, (int)buffptr->Len); CloseLogFile(TNC->Port); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return; } if (Msg[3] > 0 && Msg[3] < 6) { // Success with message - null terminated UCHAR * ptr; int len; if (Msg[2] == 0xff) // General Poll Response { UCHAR * Poll = TNC->TXBuffer; UCHAR Chan = Msg[4] - 1; if (Chan == 255) // Nothing doing return; if (Msg[5] != 0) { // More than one to poll - save the list of channels to poll strcpy(TNC->NexttoPoll, &Msg[5]); } // Poll the channel that had data Poll[2] = Chan; // Channel Poll[3] = 0x1; // Command if (Chan == 254) // Status - Send Extended Status (G3) { Poll[4] = 1; // Len-1 Poll[5] = 'G'; // Extended Status Poll Poll[6] = '3'; } else { Poll[4] = 0; // Len-1 Poll[5] = 'G'; // Poll } CRCStuffAndSend(TNC, Poll, Poll[4] + 6); TNC->InternalCmd = FALSE; return; } Buffer = &Msg[4]; ptr = strchr(Buffer, 0); if (ptr == 0) return; *(ptr++) = 13; *(ptr) = 0; len = (int)(ptr - Buffer); if (len > 256) return; // See if we need to process locally (Response to our command, Incoming Call, Disconencted, etc if (Msg[3] < 3) // 1 or 2 - Success or Fail { char LastCmd = TNC->TXBuffer[5]; struct STREAMINFO * STREAM = &TNC->Streams[Stream]; // See if a response to internal command if (TNC->RIG->RIG_DEBUG) if (LastCmd == 'I') Debugprintf("SCS I Cmd Response %s", Buffer); if (LastCmd == 'I' && STREAM->CheckingCall == TRUE) { // We've received a connect and are checking MYCALL Debugprintf("SCS Incoming Call I Cmd Response %s Stream %d DED Stream %d", Buffer, Stream, RealStream); strlop(Buffer, 13); strcpy(STREAM->MyCall, Buffer); ProcessIncomingCall(TNC, STREAM, Stream); STREAM->CheckingCall = FALSE; return; } if (TNC->InternalCmd) { // Process it if (LastCmd == 'L') // Status { int s1, s2, s3, s4, s5, s6, num; num = sscanf(Buffer, "%d %d %d %d %d %d", &s1, &s2, &s3, &s4, &s5, &s6); TNC->Streams[Stream].FramesOutstanding = s3; // flow control debug sprintf(TNC->WEB_BUFFERS, "%d Q %d", TNC->Buffers, TNC->Streams[0].FramesOutstanding); SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS); return; } if (LastCmd == '@') // @ Commands { if (TNC->TXBuffer[6]== 'B') // Buffer Status { TNC->Buffers = atoi(Buffer); sprintf(TNC->WEB_BUFFERS, "%d Q %d", TNC->Buffers, TNC->Streams[0].FramesOutstanding); SetWindowText(TNC->xIDC_BUFFERS, TNC->WEB_BUFFERS); return; } } if (LastCmd == '%') // % Commands { if (TNC->TXBuffer[6]== 'T') // TX count Status { sprintf(TNC->WEB_TRAFFIC, "RX %d TX %d ACKED %s", TNC->Streams[Stream].BytesRXed, TNC->Streams[Stream].BytesTXed, Buffer); SetWindowText(TNC->xIDC_TRAFFIC, TNC->WEB_TRAFFIC); return; } if (TNC->TXBuffer[6] == 'W') // Scan Control { if (Msg[4] == '1') // Ok to Change { TNC->OKToChangeFreq = 1; TNC->TimeScanLocked = 0; if (TNC->RIG->RIG_DEBUG) Debugprintf("Scan Debug SCS Pactor TNC gave permission"); } else { TNC->OKToChangeFreq = -1; if (TNC->SyncSupported == FALSE && TNC->UseAPPLCallsforPactor && TNC->TimeScanLocked == 0) TNC->TimeScanLocked = time(NULL); if (TNC->RIG->RIG_DEBUG) Debugprintf("Scan Debug SCS Pactor TNC refused permission"); } } } return; } } if (Msg[3] == 3) // Status { struct STREAMINFO * STREAM = &TNC->Streams[Stream]; if (strstr(Buffer, "DISCONNECTED") || strstr(Buffer, "LINK FAILURE")) { if ((STREAM->Connecting | STREAM->Connected) == 0) 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", TNC->Streams[Stream].RemoteCall); C_Q_ADD(&STREAM->PACTORtoBPQ_Q, buffptr); STREAM->Connecting = FALSE; STREAM->Connected = FALSE; // In case! STREAM->FramesOutstanding = 0; if (Stream == 0) { sprintf(TNC->WEB_TNCSTATE, "In Use by %s", STREAM->MyCall); SetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE); } STREAM->ReportDISC = TRUE; // Tell Node 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; return; } if (strstr(Buffer, "CONNECTED")) { char * Call = strstr(Buffer, " to "); char * ptr; char MHCall[30]; // Do we need to protect against 2nd call in Dragon Single Mode??? Call += 4; if (Call[1] == ':') Call +=2; ptr = strchr(Call, ' '); if (ptr) *ptr = 0; ptr = strchr(Call, 13); if (ptr) *ptr = 0; STREAM->Connected = TRUE; // Subsequent data to data channel STREAM->Connecting = FALSE; STREAM->ConnectTime = time(NULL); STREAM->BytesRXed = STREAM->BytesTXed = 0; // Stop Scanner if (Stream == 0 || TNC->HFPacket) { TNC->SwitchToPactor = 0; // Cancel any RP to Pactor switch sprintf(Status, "%d SCANSTOP", TNC->Port); Rig_Command((TRANSPORTENTRY *) -1, Status); SuspendOtherPorts(TNC); // Prevent connects on other ports in same scan gruop memcpy(MHCall, Call, 9); MHCall[9] = 0; } if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] == 0) { // Incoming Connect TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit // Check for ExcludeList if (ExcludeList[0]) { UCHAR AXCALL[7]; ConvToAX25(MHCall, AXCALL); //Permitted calls are stored in ax.25 format if (CheckExcludeList(AXCALL) == FALSE) { TidyClose(TNC, Stream); sprintf(Status, "%d SCANSTART 15", TNC->Port); Rig_Command((TRANSPORTENTRY *) -1, Status); Debugprintf("SCS Call from %s rejected", MHCall); return; } } // IF WE HAVE A PERMITTED CALLS LIST, SEE IF HE IS IN IT if (TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS) { UCHAR * ptr = TNC->PortRecord->PORTCONTROL.PERMITTEDCALLS; UCHAR AXCALL[7]; ConvToAX25(MHCall, AXCALL); //Permitted calls are stored in ax.25 format while (TRUE) { if (memcmp(AXCALL, ptr, 6) == 0) // Ignore SSID break; ptr += 7; if ((*ptr) == 0) // Not in list { char Status[64]; TidyClose(TNC, 0); sprintf(Status, "%d SCANSTART 15", TNC->Port); Rig_Command((TRANSPORTENTRY *) -1, Status); Debugprintf("Pactor Call from %s not in ValidCalls - rejected", Call); return; } } } // Check that we think we are in the right mode if (Stream == 0 && TNC->Dragon == 0) // Dragon runs both at the same time { if (TNC->HFPacket && RealStream == 31) { Debugprintf("Incoming Pactor Call while in Packet Mode"); TNC->HFPacket = FALSE; STREAM->DEDStream = 31; } else if (TNC->HFPacket == 0 && RealStream == 30) { Debugprintf("Incoming Packet Call while in Pactor Mode"); TNC->HFPacket = TRUE; STREAM->DEDStream = 30; } } if (TNC->HFPacket) { char Save = TNC->RIG->CurrentBandWidth; TNC->RIG->CurrentBandWidth = 'R'; UpdateMH(TNC, MHCall, '+', 'I'); TNC->RIG->CurrentBandWidth = Save; } memcpy(STREAM->RemoteCall, Call, 9); // Save Text Callsign // We need to check what MYCALL is set to, either in case // Appl Scan has failed to change the callsign or if a // Packet Call to MYALIAS STREAM->CmdSet = STREAM->CmdSave = malloc(100); sprintf(STREAM->CmdSet, "I\r"); STREAM->CheckingCall = TRUE; 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); if (Stream == 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); if (STREAM->DEDStream == 30) // Robust Mode { char Save = TNC->RIG->CurrentBandWidth; TNC->RIG->CurrentBandWidth = 'R'; UpdateMH(TNC, Call, '+', 'O'); TNC->RIG->CurrentBandWidth = Save; } else { UpdateMH(TNC, Call, '+', 'O'); } } return; } } return; } if (Msg[3] == 4 || Msg[3] == 5) { struct STREAMINFO * STREAM = &TNC->Streams[1]; // RP Stream // Monitor if ((TNC->HFPacket || TNC->DragonSingle) && TNC->UseAPPLCalls && strstr(&Msg[4], "SABM") && STREAM->Connected == FALSE) { // See if a call to Nodecall or one of our APPLCALLS - if so, stop scan and switch MYCALL char DestCall[10] = "NOCALL "; char * ptr1 = strstr(&Msg[7], "to "); int i; APPLCALLS * APPL; char Appl[11]; char Status[80]; if (ptr1) memcpy(DestCall, &ptr1[3], 10); ptr1 = strchr(DestCall, ' '); if (ptr1) *(ptr1) = 0; // Null Terminate Debugprintf("RP SABM Received for %s" , DestCall); if (strcmp(TNC->NodeCall, DestCall) != 0) { // Not Calling NodeCall/Portcall if (strcmp(NodeCall, DestCall) == 0) goto SetThisCall; // See if to one of our ApplCalls for (i = 0; i < 32; i++) { APPL=&APPLCALLTABLE[i]; if (APPL->APPLCALL_TEXT[0] > ' ') { char * ptr; memcpy(Appl, APPL->APPLCALL_TEXT, 10); ptr=strchr(Appl, ' '); if (ptr) *ptr = 0; if (strcmp(Appl, DestCall) == 0) { SetThisCall: Debugprintf("RP SABM is for NODECALL or one of our APPLCalls - setting MYCALL to %s and pausing scan", DestCall); sprintf(Status, "%d SCANSTART 30", TNC->Port); Rig_Command((TRANSPORTENTRY *) -1, Status); TNC->SwitchToPactor = 0; // Stay in RP strcpy(STREAM->MyCall, DestCall); STREAM->CmdSet = STREAM->CmdSave = malloc(100); sprintf(STREAM->CmdSet, "I%s\r", DestCall); TNC->InternalCmd = TRUE; break; } } } } } DoMonitor(TNC, &Msg[3], framelen - 3); return; } // 1, 2, 4, 5 - pass to Appl buffptr = GetBuff(); if (buffptr == NULL) return; // No buffers, so ignore buffptr->Len = sprintf(buffptr->Data,"Pactor} %s", &Msg[4]); OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Msg[4], (int)strlen(&Msg[4])); CloseLogFile(TNC->Port); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return; } if (Msg[3] == 6) { // Monitor Data With length) DoMonitor(TNC, &Msg[3], framelen - 3); return; } if (Msg[3] == 7) { char StatusMsg[60]; int Status, ISS, Offset; if (Msg[2] == 0xfe) // Status Poll Response { int PactorLevel = Msg[6] & 7; // Pactor Level 1-4 if (TNC->MinLevelTimer) { if (PactorLevel >= TNC->MinLevel) { Debugprintf("Reached MIN Pactor Level"); TNC->MinLevelTimer = 0; } else Debugprintf("Still waiting for Min Level Now %d Need %d", PactorLevel, TNC->MinLevel); } Status = Msg[5]; TNC->Streams[0].PTCStatus0 = Status; TNC->Streams[0].PTCStatus1 = PactorLevel; // Pactor Level 1-4 TNC->Streams[0].PTCStatus2 = Msg[7]; // Speed Level Offset = Msg[8]; if (Offset > 128) Offset -= 128; TNC->Streams[0].PTCStatus3 = Offset; TNC->Mode = (Status >> 4) & 7; ISS = Status & 8; Status &= 7; if (TNC->PTCStatus != Status) // Changed { Debugprintf("SCS status changed, now %s", status[Status]); if (Status == 6) // SYNCH { // New Sync if (TNC->RIG->RIG_DEBUG) Debugprintf("SCS New SYNC Detected"); TNC->TimeEnteredSYNCMode = time(NULL); TNC->SyncSupported = TRUE; } else { if (TNC->PTCStatus == 6) { if (TNC->RIG->RIG_DEBUG) Debugprintf("SCS left SYNC, now %s", status[Status]); TNC->TimeEnteredSYNCMode = 0; } } TNC->PTCStatus = Status; } sprintf(StatusMsg, "%x %x %x %x", TNC->Streams[0].PTCStatus0, TNC->Streams[0].PTCStatus1, TNC->Streams[0].PTCStatus2, TNC->Streams[0].PTCStatus3); if (ISS) { SetWindowText(TNC->xIDC_TXRX, "Sender"); strcpy(TNC->WEB_TXRX, "Sender"); } else { SetWindowText(TNC->xIDC_TXRX, "Receiver"); strcpy(TNC->WEB_TXRX, "Receiver"); } SetWindowText(TNC->xIDC_STATE, status[Status]); strcpy(TNC->WEB_STATE, status[Status]); SetWindowText(TNC->xIDC_MODE, ModeText[TNC->Mode]); strcpy(TNC->WEB_MODE, ModeText[TNC->Mode]); if (TNC->Mode == 7) TNC->Busy = TNC->BusyHold * 10; // BusyHold delay if (Offset == 128) // Undefined sprintf(StatusMsg, "Mode %s Speed Level %d Freq Offset Unknown", PactorLevelText[TNC->Streams[0].PTCStatus1], Msg[7]); else sprintf(StatusMsg, "Mode %s Speed Level %d Freq Offset %d", PactorLevelText[TNC->Streams[0].PTCStatus1], Msg[7], Offset); strcpy(TNC->WEB_PACTORLEVEL, StatusMsg); SetWindowText(TNC->xIDC_PACTORLEVEL, StatusMsg); return; } if (Msg[2] == 248) // Log Message { // Monitor Data - Length format // first 4 bytes contain a 32 bits long timestamp. // That timestamp holds the number of seconds that elapsed since date 01.01.2000 at 00:00:00. // The MS byte is sent first. The timestamp can be corrected to the usual C timestamp (seconds //since 01.01.1970, 00:00:00) simply by adding 946684800 (seconds) to it. // Teminated with LF int datalen = Msg[4] + 1; unsigned int timestamp = (Msg[5] << 24) + (Msg[6] << 16) + (Msg[6] << 8) + Msg[7] + 946684800; Msg[5 + datalen] = 0; Debugprintf("SCS Debug %s", &Msg[9]); return; } if (Msg[2] == 253) // Rig Port Response { // Queue for Rig Control Driver int datalen = Msg[4] + 1; PMSGWITHLEN buffptr; // if not configured to use PTC Rig Control, Ignore if (TNC->RIG->PORT == NULL || TNC->RIG->PORT->PTC == NULL) return; buffptr = GetBuff(); if (buffptr) { buffptr->Len = datalen; memcpy(buffptr->Data, &Msg[5], datalen); C_Q_ADD(&TNC->RadiotoBPQ_Q, buffptr); if (TNC->RIG->RIG_DEBUG) { Debugprintf("SCS RIG frame received, len %d", datalen); Debugprintf("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", Msg[5], Msg[6], Msg[7], Msg[8], Msg[9], Msg[10], Msg[11], Msg[12], Msg[13], Msg[14], Msg[15], Msg[16], Msg[17], Msg[18], Msg[19], Msg[20]); } } return; } // Connected Data buffptr = GetBuff(); if (buffptr == NULL) return; // No buffers, so ignore buffptr->Len = Msg[4] + 1; // Length TNC->Streams[Stream].BytesRXed += (int)buffptr->Len; memcpy(buffptr->Data, &Msg[5], buffptr->Len); WritetoTrace(TNC, &Msg[5], (int)buffptr->Len); C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr); return; } } int GetPTCRadioCommand(struct TNCINFO * TNC, char * Block) { PMSGWITHLEN buffptr; int Length; if (TNC->RadiotoBPQ_Q == 0) return 0; buffptr = Q_REM(&TNC->RadiotoBPQ_Q); Length = (int)buffptr->Len; memcpy(Block, buffptr->Data, Length); ReleaseBuffer(buffptr); // Debugprintf("SCS Rig Command Queued"); return Length;; } int SendPTCRadioCommand(struct TNCINFO * TNC, char * Block, int Length) { PMSGWITHLEN buffptr; if (TNC->TNCOK || (TNC->Hardware == H_ARDOP && TNC->ARDOPCommsMode == 'T')) { } else return 0; // Queue for TNC buffptr = GetBuff(); if (buffptr == 0) return (0); // No buffers, so ignore buffptr->Len = Length; memcpy(buffptr->Data, Block, Length); C_Q_ADD(&TNC->BPQtoRadio_Q, buffptr); return 0; } static MESSAGE Monframe; // I frames come in two parts. #define TIMESTAMP 352 MESSAGE * AdjMsg = &Monframe; // Adjusted for digis static VOID DoMonitor(struct TNCINFO * TNC, UCHAR * Msg, int Len) { // Convert to ax.25 form and pass to monitor UCHAR * ptr, * starptr; char * context; char * MHCall = Monframe.ORIGIN; if (Msg[0] == 6) // Second part of I or UI { int len = Msg[1] +1; memcpy(AdjMsg->L2DATA, &Msg[2], len); Monframe.LENGTH += len; time(&Monframe.Timestamp); BPQTRACE((MESSAGE *)&Monframe, TRUE); return; } Monframe.LENGTH = MSGHDDRLEN + 16; // Control Frame Monframe.PORT = TNC->Port; AdjMsg = &Monframe; // Adjusted for digis ptr = strstr(Msg, "fm "); ConvToAX25(&ptr[3], Monframe.ORIGIN); ptr = strstr(ptr, "to "); ConvToAX25(&ptr[3], Monframe.DEST); ptr = strstr(ptr, "via "); if (ptr) { // We have digis char Save[100]; char * fiddle; memcpy(Save, &ptr[4], 60); ptr = strtok_s(Save, " ", &context); DigiLoop: fiddle = (char *)AdjMsg; fiddle += 7; AdjMsg = (MESSAGE *)fiddle; Monframe.LENGTH += 7; starptr = strchr(ptr, '*'); if (starptr) *(starptr) = 0; ConvToAX25(ptr, AdjMsg->ORIGIN); if (starptr) AdjMsg->ORIGIN[6] |= 0x80; // Set end of address ptr = strtok_s(NULL, " ", &context); if (memcmp(ptr, "ctl", 3)) goto DigiLoop; } AdjMsg->ORIGIN[6] |= 1; // Set end of address ptr = strstr(Msg, "ctl "); if (memcmp(&ptr[4], "SABM", 4) == 0) { AdjMsg->CTL = 0x2f; UpdateMHwithDigis(TNC, MHCall, '.', 0); } else if (memcmp(&ptr[4], "DISC", 4) == 0) AdjMsg->CTL = 0x43; else if (memcmp(&ptr[4], "UA", 2) == 0) { AdjMsg->CTL = 0x63; UpdateMHwithDigis(TNC, MHCall, '.', 0); } else if (memcmp(&ptr[4], "DM", 2) == 0) AdjMsg->CTL = 0x0f; else if (memcmp(&ptr[4], "UI", 2) == 0) { AdjMsg->CTL = 0x03; UpdateMHwithDigis(TNC, MHCall, '.', 0); } else if (memcmp(&ptr[4], "RR", 2) == 0) AdjMsg->CTL = 0x1 | (ptr[6] << 5); else if (memcmp(&ptr[4], "RNR", 3) == 0) AdjMsg->CTL = 0x5 | (ptr[7] << 5); else if (memcmp(&ptr[4], "REJ", 3) == 0) AdjMsg->CTL = 0x9 | (ptr[7] << 5); else if (memcmp(&ptr[4], "FRMR", 4) == 0) AdjMsg->CTL = 0x87; else if (ptr[4] == 'I') { AdjMsg->CTL = (ptr[5] << 5) | (ptr[6] & 7) << 1 ; } if (strchr(&ptr[4], '+')) { AdjMsg->CTL |= 0x10; Monframe.DEST[6] |= 0x80; // SET COMMAND } if (strchr(&ptr[4], '-')) { AdjMsg->CTL |= 0x10; Monframe.ORIGIN[6] |= 0x80; // SET COMMAND } if (Msg[0] == 5) // More to come { ptr = strstr(ptr, "pid "); sscanf(&ptr[3], "%x", (int *)&AdjMsg->PID); return; } time(&Monframe.Timestamp); BPQTRACE((MESSAGE *)&Monframe, TRUE); } //1:fm G8BPQ to KD6PGI-1 ctl I11^ pid F0 //fm KD6PGI-1 to G8BPQ ctl DISC+ VOID TidyClose(struct TNCINFO * TNC, int Stream) { // Queue it as we may have just sent data TNC->Streams[Stream].CmdSet = TNC->Streams[Stream].CmdSave = malloc(100); sprintf(TNC->Streams[Stream].CmdSet, "D\r"); } VOID ForcedClose(struct TNCINFO * TNC, int Stream) { // Sending D twice should do a "Dirty Disconnect" // Try thst first. If it still doesn't disconnect maybe try restart unsigned char Resp[500] = ""; char * Poll = &TNC->TXBuffer[0]; Debugprintf("Failed to disconnect TNC - trying a forced disconnect"); // Try Normal Mode DD (Dirty Disconnect) // Uses "Hidden" feature where you can send any normal mode command // in host mode by preceeding with a # Poll[2] = 31; Poll[3] = 0x1; Poll[4] = 2; sprintf(&Poll[5], "#DD\r"); // Node \r isn't sent but is there for log CRCStuffAndSend(TNC, Poll, 8); // It looks like there isn't a response TNC->Timeout = 0; OpenLogFile(TNC->Port); WriteLogLine(TNC->Port, &Poll[5], 4); CloseLogFile(TNC->Port); /* Poll[2] = 31; Poll[3] = 1; Poll[4] = 0; Poll[5] = 'D'; CRCStuffAndSend(TNC, Poll, 6); // Wait for response before sending another n = 0; while (CheckRXHost(TNC, Resp) == FALSE) { Sleep(5); n++; if (n > 100) break; } Poll[2] = 31; Poll[3] = 1; Poll[4] = 0; Poll[5] = 'D'; CRCStuffAndSend(TNC, Poll, 6); n = 0; while (CheckRXHost(TNC, Resp) == FALSE) { Sleep(5); n++; if (n > 100) break; } // See if it worked Poll[2] = 254; // Channel Poll[3] = 0x1; // Command Poll[4] = 1; // Len-1 Poll[5] = 'G'; // Extended Status Poll Poll[6] = '3'; CRCStuffAndSend(TNC, Poll, 7); n = 0; while (CheckRXHost(TNC, Resp) == FALSE) { Sleep(5); n++; if (n > 100) break; } Debugprintf("PTC Status Now %x %x %x %x %x %x %x %x", Resp[0], Resp[1], Resp[2], Resp[3], Resp[4], Resp[5], Resp[6], Resp[7]); TNC->Timeout = 0; return; // Maybe best just to restart the TNC if (TNC->PacketChannels == 0) // Not using packet { Debugprintf("Forced Disconnect Failed - restarting TNC"); // Ensure in Pactor if(TNC->Dragon == 0) { TNC->TXBuffer[2] = 31; TNC->TXBuffer[3] = 0x1; TNC->TXBuffer[4] = 0x1; memcpy(&TNC->TXBuffer[5], "PT", 2); CRCStuffAndSend(TNC, TNC->TXBuffer, 7); n = 0; while (CheckRXHost(TNC, Resp) == FALSE) { Sleep(5); n++; if (n > 100) break; } } Sleep(50); ExitHost(TNC); Sleep(50); n = 0; while (CheckRXHost(TNC, Resp) == FALSE) { Sleep(5); n++; if (n > 100) break; } TNC->Timeout = 0; TNC->HostMode = FALSE; TNC->ReinitState = 0; return; } */ } VOID CloseComplete(struct TNCINFO * TNC, int Stream) { char Status[80]; struct STREAMINFO * STREAM = &TNC->Streams[Stream]; Debugprintf("SCS Pactor Close Complete - Stream = %d", Stream); STREAM->CmdSet = STREAM->CmdSave = malloc(100); strcpy(STREAM->MyCall, TNC->NodeCall); if (Stream == 0 || TNC->HFPacket) { SetWindowText(TNC->xIDC_TNCSTATE, "Free"); strcpy(TNC->WEB_TNCSTATE, "Free"); sprintf(Status, "%d SCANSTART 15", TNC->Port); Rig_Command((TRANSPORTENTRY *) -1, Status); if (TNC->Dragon) { sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); TNC->Streams[0].DEDStream = 31; // Pactor Channel } else { if (TNC->HFPacket) { sprintf(STREAM->CmdSet, "I%s\rPR\r", TNC->NodeCall); TNC->Streams[0].DEDStream = 30; // Packet Channel Debugprintf("BPQ32 Session Closed - switch to Packet"); } else { sprintf(STREAM->CmdSet, "I%s\rPT\r", TNC->NodeCall); TNC->Streams[0].DEDStream = 31; // Pactor Channel Debugprintf("BPQ32 Session Closed - switch to Pactor"); } } ReleaseOtherPorts(TNC); } else sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); } VOID PTCSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; STREAM->CmdSet = STREAM->CmdSave = zalloc(100); sprintf(STREAM->CmdSet, "I%s\r", "SCSPTC"); // Should prevent connects Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); } VOID PTCReleasePort(struct TNCINFO * TNC) { struct STREAMINFO * STREAM = &TNC->Streams[0]; STREAM->CmdSet = STREAM->CmdSave = zalloc(100); if (TNC->UseAPPLCallsforPactor && TNC->RIG && TNC->RIG != &TNC->DummyRig && TNC->RIG->FreqPtr[0]->APPLCALL[0]) sprintf(STREAM->CmdSet, "I%s\r", TNC->RIG->FreqPtr[0]->APPLCALL); else sprintf(STREAM->CmdSet, "I%s\r", TNC->NodeCall); Debugprintf("SCS Pactor CMDSet = %s", STREAM->CmdSet); }