/* 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 */ // TNC Emulator Module for BPQ32 switch // Supports TNC2 and WA8DED Hostmode interfaces #define _CRT_SECURE_NO_DEPRECATE #include "CHeaders.h" typedef struct _TCMDX { char String[12]; // COMMAND STRING UCHAR CMDLEN; // SIGNIFICANT LENGTH VOID (* CMDPROC)(struct TNCDATA * TNC, char * Tail, struct _TCMDX * CMD);// COMMAND PROCESSOR size_t CMDFLAG; // FLAG/VALUE Offset } TCMDX; #define LF 10 #define CR 13 #define Connect(stream) SessionControl(stream,1,0) #define Disconnect(stream) SessionControl(stream,2,0) #define ReturntoNode(stream) SessionControl(stream,3,0) #define ConnectUsingAppl(stream, appl) SessionControl(stream, 0, appl) int APIENTRY SendMsg(int stream, char * msg, int len); VOID SENDPACKET(struct TNCDATA * TNC); VOID CHECKCTS(struct TNCDATA * TNC); VOID SETCOMMANDMODE(struct TNCDATA * TNC); VOID SETCOMM00(struct TNCDATA * TNC); VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len); VOID TNCCOMMAND(struct TNCDATA * TNC); VOID TNC2PutChar(struct TNCDATA * TNC, int Char); VOID KBECHO(struct TNCDATA * TNC, int Char); VOID KBNORM(struct TNCDATA * TNC, int Char); VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char); VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome); VOID CONNECTTONODE(struct TNCDATA * TNC); DllImport int APIENTRY ChangeSessionCallsign(int Stream, unsigned char * AXCall); DllImport int APIENTRY GetCallsign(int stream, char * callsign); VOID GETDATA(struct TNCDATA * TNC); VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream); VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream); VOID READCHANGE(int Stream); VOID DOMONITORING(int NeedTrace, struct TNCDATA * TNC); int APIENTRY DecodeFrame(MESSAGE * msg, char * buffer, time_t Stamp); time_t APIENTRY GetRaw(int stream, char * msg, int * len, int * count); BOOL TfPut(struct TNCDATA * TNC, UCHAR character); int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC); VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len);; int KANTConnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); int KANTDisconnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream); VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length); VOID TNCPoll(); VOID DisableAppl(struct TNCDATA * TNC); int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay); int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen); int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen); int SendHostOK(struct TNCDATA * TNC); extern struct TNCDATA * TNCCONFIGTABLE; struct TNCDATA * TNC2TABLE = NULL; // malloc'ed extern int NUMBEROFTNCPORTS; // MODEFLAG DEFINITIONS #define COMMAND 1 #define TRANS 2 #define CONV 4 // APPLFLAGS BITS //CMD_TO_APPL EQU 1B ; PASS COMMAND TO APPLICATION //MSG_TO_USER EQU 10B ; SEND "CONNECTED" TO USER //MSG_TO_APPL EQU 100B ; SEND "CONECTED" TO APPL extern char pgm[256]; int CloseDelay = 10; // Close after connect fail delay MESSAGE MONITORDATA; // RAW FRAME FROM NODE char NEWCALL[11]; //TABLELEN DW TYPE TNCDATA char LNKSTATEMSG[] = "Link state is: "; char CONNECTEDMSG[] = "CONNECTED to "; char WHATMSG[] = "Eh?\rcmd:"; char CMDMSG[] = "cmd:"; char DISCONNMSG[] = "\r*** DISCONNECTED\r"; char CONMSG1[] = "\r*** CONNECTED to "; char CONCALL[10]; char SIGNON[] = "\r\rG8BPQ TNC2 EMULATOR\r\r"; char CONMSG[] ="\r*** CONNECTED to "; char SWITCH[] = "SWITCH\r"; char SWITCHSP[] = "SWITCH "; char WAS[] = " was "; char VIA[] = " via "; char OFF[] = "OFF\r"; char ON[] = "ON \r"; // BPQ Serial Device Support // On W2K and above, BPQVIrtualCOM.sys provides a pair of cross-connected devices, and a control channel // to enumerate, add and delete devices. // On Win98 BPQVCOMM.VXD provides a single IOCTL interface, over which calls for each COM device are multiplexed #ifdef WIN32 #define IOCTL_SERIAL_SET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 1,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_QUEUE_SIZE CTL_CODE(FILE_DEVICE_SERIAL_PORT, 2,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT, 3,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_BREAK_ON CTL_CODE(FILE_DEVICE_SERIAL_PORT, 4,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_BREAK_OFF CTL_CODE(FILE_DEVICE_SERIAL_PORT, 5,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_IMMEDIATE_CHAR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 6,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 7,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_TIMEOUTS CTL_CODE(FILE_DEVICE_SERIAL_PORT, 8,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT, 9,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_CLR_DTR CTL_CODE(FILE_DEVICE_SERIAL_PORT,10,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_RESET_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,11,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,12,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_CLR_RTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,13,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_XOFF CTL_CODE(FILE_DEVICE_SERIAL_PORT,14,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_XON CTL_CODE(FILE_DEVICE_SERIAL_PORT,15,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,16,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_WAIT_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,17,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_WAIT_ON_MASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,18,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_PURGE CTL_CODE(FILE_DEVICE_SERIAL_PORT,19,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_BAUD_RATE CTL_CODE(FILE_DEVICE_SERIAL_PORT,20,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_LINE_CONTROL CTL_CODE(FILE_DEVICE_SERIAL_PORT,21,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,22,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_CHARS CTL_CODE(FILE_DEVICE_SERIAL_PORT,23,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,24,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_HANDFLOW CTL_CODE(FILE_DEVICE_SERIAL_PORT,25,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_MODEMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,26,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_COMMSTATUS CTL_CODE(FILE_DEVICE_SERIAL_PORT,27,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_XOFF_COUNTER CTL_CODE(FILE_DEVICE_SERIAL_PORT,28,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_PROPERTIES CTL_CODE(FILE_DEVICE_SERIAL_PORT,29,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GET_DTRRTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,30,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_IS_COM_OPEN CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_GETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x801,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SETDATA CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x802,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x803,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x804,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_SET_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x805,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_CLR_CTS CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x806,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_CLR_DSR CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x807,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_SERIAL_CLR_DCD CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x808,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_BPQ_ADD_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x809,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_BPQ_DELETE_DEVICE CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80a,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_BPQ_LIST_DEVICES CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80b,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_BPQ_SET_POLLDELAY CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80c,METHOD_BUFFERED,FILE_ANY_ACCESS) #define IOCTL_BPQ_SET_DEBUGMASK CTL_CODE(FILE_DEVICE_SERIAL_PORT,0x80d,METHOD_BUFFERED,FILE_ANY_ACCESS) #define W98_SERIAL_IS_COM_OPEN 0x800 #define W98_SERIAL_GETDATA 0x801 #define W98_SERIAL_SETDATA 0x802 #define W98_SERIAL_SET_CTS 0x803 #define W98_SERIAL_SET_DSR 0x804 #define W98_SERIAL_SET_DCD 0x805 #define W98_SERIAL_CLR_CTS 0x806 #define W98_SERIAL_CLR_DSR 0x807 #define W98_SERIAL_CLR_DCD 0x808 #define W98_BPQ_ADD_DEVICE 0x809 #define W98_BPQ_DELETE_DEVICE 0x80a #define W98_BPQ_LIST_DEVICES 0x80b #define W98_BPQ_SET_POLLDELAY 0x80c #define W98_BPQ_SET_DEBUGMASK 0x80d #define W98_SERIAL_GET_COMMSTATUS 27 #define W98_SERIAL_GET_DTRRTS 30 #define DebugModemStatus 1 #define DebugCOMStatus 2 #define DebugWaitCompletion 4 #define DebugReadCompletion 8 HANDLE hControl; BOOL Win98; typedef struct _SERIAL_STATUS { ULONG Errors; ULONG HoldReasons; ULONG AmountInInQueue; ULONG AmountInOutQueue; BOOL EofReceived; BOOL WaitForImmediate; } SERIAL_STATUS,*PSERIAL_STATUS; #endif #ifndef WIN32 #ifdef MACBPQ #include #endif extern int posix_openpt (int __oflag); extern int grantpt (int __fd); extern int unlockpt (int __fd); extern char *ptsname (int __fd); extern int ptsname_r (int __fd, char *__buf, size_t __buflen); extern int getpt (void); HANDLE LinuxOpenPTY(char * Name) { // Open a Virtual COM Port HANDLE hDevice, slave;; char slavedevice[80]; int ret; u_long param=1; struct termios term; #ifdef MACBPQ // Create a pty pair openpty(&hDevice, &slave, &slavedevice[0], NULL, NULL); close(slave); #else hDevice = posix_openpt(O_RDWR|O_NOCTTY); if (hDevice == -1) { perror("posix_openpt Create PTY pair failed"); return -1; } if (grantpt (hDevice) == -1) { perror("grantpt Create PTY pair failed"); return -1; } if (unlockpt (hDevice) == -1) { perror("unlockpt Create PTY pair failed"); return -1; } if (ptsname_r(hDevice, slavedevice, 80) != 0) { perror("ptsname_r Create PTY pair failed"); return -1; } #endif printf("slave device: %s. ", slavedevice); if (tcgetattr(hDevice, &term) == -1) { perror("tty_speed: tcgetattr"); return FALSE; } cfmakeraw(&term); if (tcsetattr(hDevice, TCSANOW, &term) == -1) { perror("tcsetattr"); return -1; } ioctl(hDevice, FIONBIO, ¶m); chmod(slavedevice, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP|S_IROTH|S_IWOTH); unlink (Name); ret = symlink (slavedevice, Name); if (ret == 0) printf ("symlink to %s created\n", Name); else printf ("symlink to %s failed\n", Name); return hDevice; } #else HANDLE BPQOpenSerialPort(struct TNCDATA * TNC, DWORD * lasterror) { // Open a Virtual COM Port int port = TNC->ComPort; char szPort[80]; HANDLE hDevice; int Err; *lasterror=0; if (Win98) { sprintf( szPort, "\\\\.\\COM%d",port) ; hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access NULL, // no security attrs OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hDevice == (HANDLE) -1 ) { // If In Use(5) ok, else fail if (GetLastError() == 5) return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device return (HANDLE)(ptrdiff_t) - 1; } CloseHandle(hDevice); return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device } // Try New Style VCOM firsrt sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", port ) ; hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access NULL, // no security attrs OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); Err = GetLastError(); if (hDevice != (HANDLE) -1) { TNC->NewVCOM = TRUE; TNC->PortEnabled = TRUE; Err = GetFileType(hDevice); } else { // Try old style sprintf(szPort, "\\\\.\\BPQ%d", port ) ; hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access NULL, // no security attrs OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (TNC->PollDelay) BPQSerialSetPollDelay(hDevice, TNC->PollDelay); } if (hDevice == (HANDLE) -1 ) { *lasterror=GetLastError(); } return hDevice; } #endif int BPQSerialSetCTS(HANDLE hDevice) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl,(DWORD)hDevice | W98_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL); #endif } int BPQSerialSetDSR(HANDLE hDevice) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DSR,NULL,0,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DSR, NULL,0,NULL,0, &bytesReturned,NULL); #endif } int BPQSerialSetDCD(HANDLE hDevice) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL); #endif } int BPQSerialClrCTS(HANDLE hDevice) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL); #endif } int BPQSerialClrDSR(HANDLE hDevice) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL); #endif } int BPQSerialClrDCD(HANDLE hDevice) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DCD,NULL,0,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DCD, NULL,0,NULL,0, &bytesReturned,NULL); #endif } static int SendDataToTNC(struct TNCDATA * TNC, UCHAR * TXMsg, int n) { // Used for all port types #ifdef WIN32 // WIN32 VCOM Used one of my Drivers if (TNC->VCOM) BPQSerialSendData(TNC, TXMsg, n); #endif // Linux VCOM uses SOCAT Pairs and normal write return WriteCOMBlock(TNC->hDevice, TXMsg, n); } int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen) { HANDLE hDevice = TNC->hDevice; ULONG bytesReturned; // Host Mode code calls BPQSerialSendData for all ports, so it a real port, pass on to real send routine if (!TNC->VCOM) return WriteCOMBlock(TNC->hDevice, Message, MsgLen); #ifndef WIN32 // Linux usies normal IO for all ports return WriteCOMBlock(TNC->hDevice, Message, MsgLen); #else if (MsgLen > 4096 ) return ERROR_INVALID_PARAMETER; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); else { if (TNC->NewVCOM) { // Have to escape all oxff chars, as these are used to get status info UCHAR NewMessage[1000]; UCHAR * ptr1 = Message; UCHAR * ptr2 = NewMessage; UCHAR c; int Length = MsgLen; while (Length != 0) { c = *(ptr1++); *(ptr2++) = c; if (c == 0xff) { *(ptr2++) = c; MsgLen++; } Length--; } return WriteFile(hDevice, NewMessage, MsgLen, &bytesReturned, NULL); } else return DeviceIoControl(hDevice,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL); } #endif } int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error); int GetDataFromTNC(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) { // Used for all port types #ifdef WIN32 // WIN32 VCOM Used one of my Drivers if (TNC->VCOM) return BPQSerialGetData(TNC, Message, BufLen, MsgLen); *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); return 0; #else int Error = 0; if (TNC->VCOM == 0) { *MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen); return 0; } // Linux VCOM uses SOCAT Pairs and normal Read // If the slave closes connection read returns 5. Need to trap and // close/reopen. So use ReadCOMBlockEx *MsgLen = ReadCOMBlockEx(TNC->hDevice, Message, BufLen, &Error); if (Error == 5) { printf("Read error on TNCPORT %s - Restarting\n", TNC->PORTNAME); close(TNC->hDevice); TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); } return 0; #endif } int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen) { #ifdef WIN32 DWORD dwLength = 0; DWORD Available = 0; HANDLE hDevice = TNC->hDevice; int Length, RealLen = 0; if (BufLen > 4096 ) return ERROR_INVALID_PARAMETER; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); if (TNC->NewVCOM) { int ret = PeekNamedPipe(hDevice, NULL, 0, NULL, &Available, NULL); if (ret == 0) { ret = GetLastError(); if (ret == ERROR_BROKEN_PIPE) { CloseHandle(hDevice); hDevice = INVALID_HANDLE_VALUE; return 0; } } if (Available > BufLen) Available = BufLen; if (Available) { UCHAR * ptr1 = Message; UCHAR * ptr2 = Message; UCHAR c; ReadFile(hDevice, Message, Available, &Length, NULL); // Have to look for FF escape chars RealLen = Length; while (Length != 0) { c = *(ptr1++); Length--; if (c == 0xff) { c = c = *(ptr1++); Length--; if (c == 0xff) // ff ff means ff { RealLen--; } else { // This is connection statua from other end RealLen -= 2; // TNC->PortEnabled = c; continue; } } *(ptr2++) = c; } } *MsgLen = RealLen; return 0; } return DeviceIoControl(hDevice,IOCTL_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL); } #else return 0; } #endif int BPQSerialGetQCounts(HANDLE hDevice,ULONG * RXCount, ULONG * TXCount) { #ifndef WIN32 return 0; #else SERIAL_STATUS Resp; int MsgLen; int ret; if (Win98) ret = DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); else ret = DeviceIoControl(hDevice,IOCTL_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL); *RXCount=Resp.AmountInInQueue; *TXCount=Resp.AmountInOutQueue; return ret; #endif } int BPQSerialGetDeviceList(HANDLE hDevice,ULONG * Slot,ULONG * Port) { #ifndef WIN32 return 0; #else ULONG bytesReturned; return DeviceIoControl (hDevice,IOCTL_BPQ_LIST_DEVICES,Slot,4,Port,4,&bytesReturned,NULL); #endif } int BPQSerialIsCOMOpen(HANDLE hDevice,ULONG * Count) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL); #endif } int BPQSerialGetDTRRTS(HANDLE hDevice, ULONG * Flags) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL); #endif } int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay) { #ifndef WIN32 return 0; #else ULONG bytesReturned; if (Win98) return DeviceIoControl(hControl, (UINT)hDevice | W98_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); else return DeviceIoControl(hDevice,IOCTL_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL); #endif } int BPQSerialSetDebugMask(HANDLE hDevice, int DebugMask) { #ifndef WIN32 return 0; #else ULONG bytesReturned; return DeviceIoControl(hDevice, IOCTL_BPQ_SET_DEBUGMASK, &DebugMask, 4, NULL, 0, &bytesReturned,NULL); #endif } void CheckForStreamChange(struct TNCDATA * TNC, int ToStream) { // Send Stream Switched Message if changed char Msg[80]; int Len; if (ToStream == TNC->RXStream) return; TNC->RXStream = ToStream; // Send Message // |B:WA7GXD: if (TNC->StreamCall) Len = sprintf(Msg, "%c%c:%s:", TNC->StreamSW, ToStream + 'A', TNC->TNC2Stream[ToStream]->RemoteCall); else Len = sprintf(Msg, "%c%c", TNC->StreamSW, ToStream + 'A'); SENDREPLY(TNC, Msg, Len); } int LocalSessionState(int stream, int * state, int * change, BOOL ACK) { // Get current Session State. Any state changed is ACK'ed // automatically. See BPQHOST functions 4 and 5. // Local version without semaphore or checktimer BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1 // CX = 0 if stream disconnected or CX = 1 if stream connected // DX = 0 if no change of state since last read, or DX = 1 if // the connected/disconnected state has changed since // last read (ie. delta-stream status). // HOSTFLAGS = Bit 80 = Allocated // Bit 40 = Disc Request // Bit 20 = Stay Flag // Bit 02 and 01 State Change Bits if ((HOST->HOSTFLAGS & 3) == 0) // No Chaange *change = 0; else *change = 1; if (HOST->HOSTSESSION) // LOCAL SESSION // Connected *state = 1; else *state = 0; if (ACK) HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits return 0; } VOID ONOFF(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { // PROCESS COMMANDS WITH ON/OFF PARAM char Param; UCHAR * valueptr; UCHAR oldvalue, newvalue = 0xff; char Response[80]; int len; _strupr(Tail); Param = *Tail; valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; oldvalue = (UCHAR)*valueptr; switch(Param) { case ' ': break; case 'Y': newvalue = 1; break; case 'N': newvalue = 0; break; case 'O': if (Tail[1] == 'N') newvalue = 1; else newvalue = 0; break; } if (newvalue == 255) { len = sprintf(Response, "%s %s\r", CMD->String, (oldvalue)?"ON":"OFF"); } else { len = sprintf(Response, "%s was %s\r", CMD->String, (oldvalue)?"ON":"OFF"); *valueptr = newvalue; } SENDREPLY(TNC, Response, len); } VOID ONOFF_CONOK(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; ONOFF(TNC, Tail, CMD); // UPDATE APPL FLAGS ON NODE PORT if (TNC->CONOK) SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); else SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); } VOID SETMYCALL(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { char Response[80]; int len; char Call[10] = " "; _strupr(Tail); if (*Tail == ' ') { // REQUEST FOR CURRENT STATE len = sprintf(Response, "MYCALL %s\r", TNC->MYCALL); } else { strlop(Tail,' ');; memcpy(Call, Tail, (int)strlen(Tail) + 1); len = sprintf(Response, "MYCALL was %s\r", TNC->MYCALL); memcpy(TNC->MYCALL, Call, 10); } SENDREPLY(TNC, Response, len); } VOID CTEXTCMD(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { char Response[256]; int len, n; if (*Tail == ' ') { // REQUEST FOR CURRENT STATE len = sprintf(Response, "CTEXT %s\r", TNC->CTEXT); } else { Tail[TNC->MSGLEN] = 0; n = strlen(Tail) - 1; while(n > 0 && Tail[n] == ' ') Tail[n--] = 0; if (strlen(Tail) > 119) Tail[119] = 0; len = sprintf(Response, "CTEXT was %s\r", TNC->CTEXT); strcpy(TNC->CTEXT, Tail); } SENDREPLY(TNC, Response, len); } VOID BTEXT(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { } VOID VALUE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { // PROCESS COMMANDS WITH decimal value char Param = *Tail; UCHAR * valueptr; int oldvalue, newvalue; char Response[80]; int len; valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; oldvalue = *valueptr; strlop(Tail, ' '); if (Tail[0]) { newvalue = atoi(Tail); len = sprintf(Response, "%s was %d\r", CMD->String, oldvalue); *valueptr = newvalue; } else { len = sprintf(Response, "%s %d\r", CMD->String, oldvalue); } SENDREPLY(TNC, Response, len); } VOID VALHEX(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { // PROCESS COMMANDS WITH decimal value char Param = *Tail; UCHAR * valueptr; UINT * intvalueptr; UINT oldvalue, newvalue; char Response[80]; int len; valueptr = (UCHAR *)TNC + CMD->CMDFLAG;; intvalueptr = (UINT *)valueptr; oldvalue = *intvalueptr; strlop(Tail, ' '); if (Tail[0]) { if (Tail[0] == '$') newvalue = (UINT)strtol(Tail + 1, NULL, 16); else newvalue = (UINT)strtol(Tail, NULL, 0); len = sprintf(Response, "%s was $%x\r", CMD->String, oldvalue); *intvalueptr = newvalue; } else { len = sprintf(Response, "%s $%x\r", CMD->String, oldvalue); } SENDREPLY(TNC, Response, len); } VOID APPL_VALHEX(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { int ApplNum = 1; UINT APPLMASK; VALHEX(TNC, Tail, CMD); // UPDATE APPL FLAGS ON NODE PORT if (TNC->CONOK) SetAppl(TNC->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); else SetAppl(TNC->BPQPort, TNC->APPLFLAGS, 0); // Set MYCALL to APPLCALL APPLMASK = TNC->APPLICATION; ApplNum = 1; while (APPLMASK && (APPLMASK & 1) == 0) { ApplNum++; APPLMASK >>= 1; } if (TNC->CONOK && TNC->APPLICATION) memcpy(TNC->MYCALL, GetApplCall(ApplNum), 10); } VOID CSWITCH(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { char Response[80]; int len; len = sprintf(Response, "%s", CMDMSG); SENDREPLY(TNC, Response, len); CONNECTTONODE(TNC); } VOID CONMODE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { SENDREPLY(TNC, CMDMSG, 4); } VOID TNCCONV(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; TNCStream->MODEFLAG |= CONV; TNCStream->MODEFLAG &= ~(COMMAND+TRANS); } VOID TNCNODE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { // CONNECT TO NODE struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; TNCStream->VMSR |= 0x88; // SET CONNECTED TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE TNCStream->MODEFLAG &= ~(COMMAND+TRANS); CONNECTTONODE(TNC); } VOID CStatus(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; char Response[80]; int i, len; char Call[10] = ""; char Selected[3] = " "; for (i = 0; i < TNC->HOSTSTREAMS; i++) { char Selected[3] = " "; TNCStream = TNC->TNC2Stream[i]; if (TNC->RXStream == i) Selected[0] = 'I'; if (TNC->TXStream == i) Selected[1] = 'O'; if (TNCStream->VMSR & 0x80) { GetCallsign(TNCStream->BPQPort, Call); strlop(Call, ' '); len = sprintf(Response, "%c stream - %s CONNECTED to %s\r", i + 'A', Selected, Call); } else { len = sprintf(Response, "%c stream - %s DISCONNECTED\r", i + 'A', Selected); } SENDREPLY(TNC, Response, len); } } VOID TNCCONNECT(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; char Response[80]; int len; char Call[10] = ""; _strupr(Tail); if (*Tail == ' ') { // REQUEST FOR CURRENT STATE len = sprintf(Response, "%s", LNKSTATEMSG); if (TNCStream->VMSR & 0x80) { GetCallsign(TNCStream->BPQPort, Call); strlop(Call, ' '); len = sprintf(Response, "%c Link state is: CONNECTED to %s\r", TNC->TXStream + 'A', Call); } else { len = sprintf(Response, "%c Link state is: DISCONNECTED\r", TNC->TXStream + 'A'); } SENDREPLY(TNC, Response, len); return; } // CONNECT, BUT NOT TO SWITCH - CONNECT TO NODE, THEN PASS TO IT FOR PROCESSING TNCNODE(TNC, Tail, CMD); READCHANGE(TNCStream->BPQPort); //CLEAR STATUS CHANGE (TO AVOID SUPURIOUS "CONNECTED TO") strcat(TNC->TONODEBUFFER, "\r"); TNC->MSGLEN = (int)strlen(TNC->TONODEBUFFER); SENDPACKET(TNC); // Will now go to node } VOID TNCDISC(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; Disconnect(TNCStream->BPQPort); SENDREPLY(TNC, CMDMSG, 4); } VOID READCHANGE(int Stream) { int dummy; LocalSessionState(Stream, &dummy, &dummy, TRUE); } VOID TNCRELEASE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { ReturntoNode(TNC->BPQPort); TNC->VMSR &= 0x7F; // DROP DCD TNC->VMSR |= 8; //DELTA DCD SENDREPLY(TNC, CMDMSG, 4); } VOID TNCTRANS(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; // MAKE PRETTY SURE THIS ISNT A BIT OF STRAY DATA if (TNC->MSGLEN > 6) return; TNCStream->MODEFLAG |= TRANS; TNCStream->MODEFLAG &= ~(COMMAND+CONV); } static VOID TNCRESTART(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { // REINITIALISE CHANNEL struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; TNC->RXCOUNT = 0; TNC->VLSR = 0x20; TNC->VMSR = 0x30; TNCStream->MODEFLAG = COMMAND; TNC->SENDPAC = 13; TNC->CRFLAG = 1; TNC->MALL = 1; TNC->MMASK = -1; // MONITOR MASK FOR PORTS TNC->TPACLEN = PACLEN; // TNC PACLEN TNC->COMCHAR = 3; TNC->CMDTIME = 10; // SYSTEM TIMER = 100MS TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START TNC->MSGLEN = 0; SENDREPLY(TNC, SIGNON, 23); } static VOID TNCUNPROTOCMD(struct TNCDATA * TNC, char * Tail, TCMDX * CMD) { } TCMDX TNCCOMMANDLIST[] = { "AUTOLF ",2, ONOFF, offsetof(struct TNCDATA, AUTOLF), "BBSMON ",6, ONOFF, offsetof(struct TNCDATA, BBSMON), "BTEXT ",2,BTEXT,0, "CONOK ",4,ONOFF_CONOK,offsetof(struct TNCDATA, CONOK), "C SWITCH",8,CSWITCH,0, "CBELL ",2,ONOFF,offsetof(struct TNCDATA, CBELL), "CMDTIME ",2,VALUE, offsetof(struct TNCDATA, CMDTIME), "CMSG ",4,ONOFF,offsetof(struct TNCDATA, CMSG), "COMMAND ",3,VALHEX, offsetof(struct TNCDATA, COMCHAR), "CONMODE ",4,CONMODE,0, "CPACTIME",2,ONOFF,offsetof(struct TNCDATA, CPACTIME), "CR ",2,ONOFF,offsetof(struct TNCDATA, CRFLAG), "CSTATUS ",2,CStatus,0, "CTEXT ",2,CTEXTCMD,0, "APPLFLAG",5,APPL_VALHEX, offsetof(struct TNCDATA, APPLFLAGS), "APPL ",4,APPL_VALHEX, offsetof(struct TNCDATA, APPLICATION), "CONVERS ",4,TNCCONV,0, "CONNECT ",1,TNCCONNECT,0, "DISCONNE",1,TNCDISC,0, "ECHO ",1,ONOFF,offsetof(struct TNCDATA, ECHOFLAG), "FLOW ",4,ONOFF,offsetof(struct TNCDATA, FLOWFLAG), "HEADERLN",2,ONOFF,offsetof(struct TNCDATA, HEADERLN), "K ",1,TNCNODE,0, "MTXFORCE",4,ONOFF,offsetof(struct TNCDATA, MTXFORCE), "LCSTREAM",8,ONOFF, offsetof(struct TNCDATA, LCStream), "LFIGNORE",3,ONOFF,offsetof(struct TNCDATA, LFIGNORE), "MTX ",3,ONOFF,offsetof(struct TNCDATA, MTX), "MALL ",2,ONOFF,offsetof(struct TNCDATA, MALL), "MCOM ",4,ONOFF,offsetof(struct TNCDATA, MCOM), "MCON ",2,ONOFF,offsetof(struct TNCDATA, MCON), "MMASK ",2,VALHEX, offsetof(struct TNCDATA, MMASK), "MONITOR ",3,ONOFF,offsetof(struct TNCDATA, TRACEFLAG), "MYCALL ",2,SETMYCALL,0, "NEWMODE ",2,ONOFF,offsetof(struct TNCDATA, NEWMODE), "NODE ",3,TNCNODE,0, "NOMODE ",2,ONOFF,offsetof(struct TNCDATA, NOMODE), "SENDPAC ",2,VALHEX, offsetof(struct TNCDATA, SENDPAC), "STREAMCA",8,ONOFF, offsetof(struct TNCDATA, StreamCall), "STREAMDBL",7,ONOFF, offsetof(struct TNCDATA, StreamDbl), "STREAMSW",3,VALHEX, offsetof(struct TNCDATA, StreamSW), "PACLEN ",1,VALUE, offsetof(struct TNCDATA, TPACLEN), "PASS ",3,VALHEX, offsetof(struct TNCDATA, PASSCHAR), "RELEASE ",3,TNCRELEASE,0, "RESTART ",7,TNCRESTART,0, "TRANS ",1,TNCTRANS,0, "UNPROTO ",1,TNCUNPROTOCMD,0, "USERS ",2,VALUE, offsetof(struct TNCDATA, Users), }; int NUMBEROFTNCCOMMANDS = sizeof(TNCCOMMANDLIST)/sizeof(TCMDX); /*NEWVALUE DW 0 HEXFLAG DB 0 NUMBER DB 4 DUP (0),CR NUMBERH "$0000",CR BADMSG "?bad parameter",CR,0 BTHDDR ,0 ; CHAIN DB 0 ; PORT DW 7 ; LENGTH DB 0F0H ; PID BTEXTFLD DB 0DH,256 DUP (0) CMDENDADDR,0 ; POINTER TO END OF COMMAND MBOPTIONBYTE DB 0 NORMCALL DB 10 DUP (0) AX25CALL DB 7 DUP (0) CONNECTCALL DB 10 DUP (0) ; CALLSIGN IN CONNECT MESSAGE DIGICALL DB 10 DUP (0) ; DIGI IN CONNECT COMMAND AX25STRING DB 64 DUP (0) ; DIGI STRING IN AX25 FORMAT DIGIPTR ,0 ; POINTER TO CURRENT DIGI IN STRING NORMLEN ,0 EVEN */ int TRANSDELAY = 10; // 1 SEC //UNPROTOCALL DB "UNPROTO",80 DUP (0) char MONBUFFER[1000]; VOID TNC2GetChar(struct TNCDATA * TNC, int * returnedchar, int * moretocome) { // Poll Node if (TNC->Mode == 0) GETDATA(TNC); *returnedchar = -1; *moretocome = 0; if (TNC->RXCOUNT == 0) return; *returnedchar = *(TNC->GETPTR++); if (TNC->GETPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) TNC->GETPTR = &TNC->TOUSERBUFFER[0]; *moretocome = --TNC->RXCOUNT; //ANY MORE? if (TNC->RXCOUNT < 128) // GETTING LOW? { if (TNC->RTSFLAG & 1) // RTS UP? { // RTS HAD BEEN DROPPED TO STOP OTHER END SENDING - RAISE IT AGAIN TNC->RTSFLAG &= 0xFE; } } } int TNCGetVMSR(struct TNCDATA * TNC, struct TNC2StreamInfo * TNCStream, BOOL ClearDeltas) { // On TNC2 Connected bit should come from current stream // flow control bits from TNC record int val = TNC->VMSR; if (TNC->Mode == TNC2) val |= TNCStream->VMSR; if (ClearDeltas) { TNC->VMSR &= 0xF0; // CLEAR DELTA BITS if (TNC->Mode == TNC2) TNCStream->VMSR &= 0xF0; } return val; } BOOL TNCRUNNING;; VOID TNCBGThread(void * unused) { TNCRUNNING = TRUE; Sleep(5000); while (TNCRUNNING) { TNCPoll(); Sleep(50); } } VOID AllocateDEDChannel(struct TNCDATA * TNC, int Num) { struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); char * PNptr; // Only show last element of name on Streams display PNptr = &TNC->PORTNAME[0]; while (strchr(PNptr, '/')) PNptr = strchr(PNptr, '/') + 1; sprintf(pgm, "DED %s", PNptr); TNC->Channels[Num] = Channel; Channel->BPQStream = FindFreeStream(); READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected Debugprintf("BPQ32 DED Stream %d BPQ Stream %d", Num, Channel->BPQStream ); if (TNC->MODE) // if host mode, set appl SetAppl(Channel->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); strcpy(pgm, "bpq32.exe"); } BOOL InitializeTNCEmulator() { int resp, i; ULONG OpenCount = 0; DWORD Errorval; int ApplNum = 1; UINT APPLMASK; struct TNC2StreamInfo * TNCStream; struct TNCDATA * TNC = TNCCONFIGTABLE; TNC2TABLE = TNCCONFIGTABLE; while (TNC) { // Com Port may be a hardware device (ie /dev/ttyUSB0) COMn or VCOMn (BPQ Virtual COM) char * Baud = strlop(TNC->PORTNAME, ':'); char * PNptr; if (Baud == 0) Baud = strlop(TNC->PORTNAME, ','); PNptr = &TNC->PORTNAME[0]; // Only show last element of name on Streams display while (strchr(PNptr, '/')) { PNptr = strchr(PNptr, '/') + 1; } switch (TNC->Mode) { case TNC2: sprintf(pgm, "TNC2 %s", PNptr); // Start with number of streams, can add or remove with USRES command if (TNC->Users == 0) TNC->Users = TNC->HOSTSTREAMS; if (TNC->Users == 0) TNC->Users = TNC->HOSTSTREAMS = 1; else TNC->HOSTSTREAMS = TNC->Users; Debugprintf("TNC2 USers = %d, HOSTSTREAMS = %d\n", TNC->Users, TNC->HOSTSTREAMS); for (i = 0; i < TNC->HOSTSTREAMS; i++) { TNCStream = TNC->TNC2Stream[i] = zalloc(sizeof(struct TNC2StreamInfo)); TNCStream->BPQPort = FindFreeStream(); if (TNCStream->BPQPort == 0) { Debugprintf("Insufficient free Streams for TNC2 Emulator"); return FALSE; } READCHANGE(TNCStream->BPQPort); // Prevent Initial *** disconnected TNCStream->MODEFLAG = COMMAND; if (TNC->CONOK) SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, TNC->APPLICATION); else SetAppl(TNCStream->BPQPort, TNC->APPLFLAGS, 0); } strcpy(pgm, "bpq32.exe"); if (TNC->TPACLEN == 0) TNC->TPACLEN = PACLEN; // TNC PACLEN break; case DED: if (TNC->HOSTSTREAMS == 0) TNC->HOSTSTREAMS = 4; // Default TNC->MALL = 1; TNC->MTX = 1; TNC->MCOM = 1; TNC->MMASK = -1; // MONITOR MASK FOR PORTS TNC->TPACLEN = PACLEN; // TNC PACLEN for (i = 1; i <= TNC->HOSTSTREAMS; i++) { AllocateDEDChannel(TNC, i); // Also used by Y command handler } TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring break; case KANTRONICS: sprintf(pgm, "KANT %s", PNptr); if (TNC->HOSTSTREAMS == 0) TNC->HOSTSTREAMS = 1; // Default for (i = 0; i <= TNC->HOSTSTREAMS; i++) { struct StreamInfo * Channel; // Use Stream zero for defaults TNC->Channels[i] = malloc(sizeof (struct StreamInfo)); memset(TNC->Channels[i], 0, sizeof (struct StreamInfo)); Channel = TNC->Channels[i]; Channel->BPQStream = FindFreeStream(); READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected Debugprintf("BPQ32 KANT Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); // channel->Chan_TXQ = 0; // channel->BPQStream = 0; // channel->Connected = FALSE; // channel->MYCall[0] = 0; } break; case SCS: TNC->ECHOFLAG = 1; if (TNC->HOSTSTREAMS == 0) TNC->HOSTSTREAMS = 1; // Default TNC->MALL = 1; TNC->MCOM = 1; TNC->MMASK = -1; // MONITOR MASK FOR PORTS TNC->TPACLEN = PACLEN; // TNC PACLEN sprintf(pgm, "SCS %s", PNptr); for (i = 1; i <= TNC->HOSTSTREAMS; i++) { struct StreamInfo * Channel = zalloc(sizeof(struct StreamInfo)); TNC->Channels[i] = Channel; Channel->BPQStream = FindFreeStream(); READCHANGE(Channel->BPQStream); // Prevent Initial *** disconnected Debugprintf("BPQ32 SCS Init Stream %d BPQ Stream %d", i, Channel->BPQStream ); } TNC->Channels[0] = zalloc(sizeof(struct StreamInfo)); memcpy(TNC->Channels[0], TNC->Channels[1], sizeof(struct StreamInfo)); // For monitoring strcpy(pgm, "bpq32.exe"); break; } if (Baud) TNC->Speed = atoi(Baud); else TNC->VCOM = TRUE; if (_memicmp(TNC->PORTNAME, "COM", 3) == 0) { TNC->VCOM = FALSE; } else { if (_memicmp(TNC->PORTNAME, "VCOM", 4) == 0) TNC->ComPort = atoi(&TNC->PORTNAME[4]); } if (TNC->VCOM == 0) { // Real port TNC->hDevice = OpenCOMPort(TNC->PORTNAME, TNC->Speed, TRUE, TRUE, FALSE, 0); TNC->PortEnabled = 1; TNC->RTS = 1; // TNC->DTR = 1; } else { // VCOM Port #ifdef WIN32 TNC->hDevice = BPQOpenSerialPort(TNC, &Errorval); #else TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME); #endif if (TNC->hDevice != (HANDLE) -1) { if (TNC->NewVCOM == 0) { resp = BPQSerialIsCOMOpen(TNC->hDevice, &OpenCount); TNC->PortEnabled = OpenCount; } resp = BPQSerialSetCTS(TNC->hDevice); resp = BPQSerialSetDSR(TNC->hDevice); TNC->CTS = 1; TNC->DSR = 1; } else { Consoleprintf("TNC - Open Failed for Port %s", TNC->PORTNAME); TNC->hDevice = 0; } } if (TNC->hDevice) { // Set up buffer pointers TNC->PUTPTR = TNC->GETPTR = &TNC->TOUSERBUFFER[0]; TNC->RXCOUNT = 0; TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START TNC->MSGLEN = 0; TNC->VLSR = 0x20; TNC->VMSR = 0x30; /* PUSH ECX MOV ESI,OFFSET UNPROTOCALL CALL DECODECALLSTRING LEA EDI,UNPROTO[EBX] MOV ECX,56 REP MOVSB ; UNPROTO ADDR POP ECX */ APPLMASK = TNC->APPLICATION; ApplNum = 1; while (APPLMASK && (APPLMASK & 1) == 0) { ApplNum++; APPLMASK >>= 1; } memcpy(TNC->MYCALL, &APPLCALLTABLE[ApplNum-1].APPLCALL_TEXT, 10); if (TNC->MYCALL[0] < '0') memcpy(TNC->MYCALL, MYNODECALL, 10); strlop(TNC->MYCALL, ' '); } TNC = TNC->Next; } #ifdef LINBPQ strcpy(pgm, "LinBPQ"); #else strcpy(pgm, "bpq32.exe"); #endif Consoleprintf("TNC Emulator Init Complete"); _beginthread(TNCBGThread,0,0); return TRUE; } VOID CloseTNCEmulator() { struct TNCDATA * TNC = TNC2TABLE; // malloc'ed int i, Stream; TNCRUNNING = FALSE; while (TNC) { if (TNC->Mode == TNC2) { Stream = TNC->BPQPort; SetAppl(Stream, 0, 0); Disconnect(Stream); READCHANGE(Stream); // Prevent Initial *** disconnected DeallocateStream(Stream); } else { for (i = 1; i <= TNC->HOSTSTREAMS; i++) { Stream = TNC->Channels[i]->BPQStream; SetAppl(Stream, TNC->APPLFLAGS, 0); Disconnect(Stream); READCHANGE(Stream); // Prevent Initial *** disconnected DeallocateStream(Stream); } } CloseCOMPort(TNC->hDevice); TNC = TNC->Next; } } VOID TNCTimer() { // 100 Ms Timer struct TNCDATA * TNC = TNC2TABLE; struct StreamInfo * channel; int n; int NeedTrace = 0; struct TNCDATA * TTNC = 0; // First TNC2 while (TNC) { if (TNC->LastDEDPollTime && (time(NULL) > TNC->LastDEDPollTime + 30)) // No polls for 30 secs { int Len = 0; int Count; TNC->LastDEDPollTime = 0; Debugprintf("DED Host Application Lost"); TNC->MODE = 0; TNC->HOSTSTATE = 0; DisableAppl(TNC); // Clear Monitor Q GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); while (Len) { GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); } continue; } if (TNC->Mode != TNC2) goto NotTNC2; if (TTNC == 0 && TNC->TNC2Stream[0]) // Save first TNC2 TNC TTNC = TNC; NeedTrace |= TNC->TRACEFLAG; //SEE IF ANY PORTS ARE MONITORING // CHECK FOR PACTIMER EXPIRY AND CMDTIME if (TNC->CMDTMR) { TNC->CMDTMR--; if (TNC->CMDTMR == 0) { // CMDTMR HAS EXPIRED - IF 3 COMM CHARS RECEIVED, ENTER COMMAND MODE if (TNC->COMCOUNT == 3) { // 3 ESCAPE CHARS RECEIVED WITH GUARDS - LEAVE TRAN MODE SETCOMM00(TNC); goto TIM100; //DONT RISK TRANSTIMER AND CMDTIME FIRING AT ONCE } TNC->CMDTMR = 0; // RESET COUNT goto TIM100; } } if (TNC->TRANSTIMER) { TNC->TRANSTIMER--; if (TNC->TRANSTIMER == 0) { if (TNC->MSGLEN) // ?MESSAGE ALREADY SENT SENDPACKET(TNC); } } TIM100: // CHECK FLOW CONTROL if ((TNC->VMSR & 0x20)) // ALREADY OFF? { CHECKCTS(TNC); // No, so check } goto NextTNC; NotTNC2: for (n = 1; n <= TNC->HOSTSTREAMS; n++) { channel = TNC->Channels[n]; if (channel->CloseTimer) { channel->CloseTimer--; if (channel->CloseTimer == 0) Disconnect(channel->BPQStream); } } NextTNC: TNC = TNC->Next; } if (TTNC) // Only if we have TNC2 Streams DOMONITORING(NeedTrace, TTNC); } /* #ifndef WIN32 int TNCReadCOMBlock(HANDLE fd, char * Block, int MaxLength, int * err) { int Length; *err = 0; Length = read(fd, Block, MaxLength); if (Length < 0) { if (errno != 11 && errno != 35) // Would Block *err = errno; return 0; } return Length; } #endif */ void CheckForConnectStatusChange(struct TNCDATA * TNC); void CheckForHostStatusChange(struct TNCDATA * TNC); void CheckForDataFromHost(struct TNCDATA * TNC); void CheckForDataFromTerminal(struct TNCDATA * TNC); int isTNCBusy(struct TNCDATA * TNC) { // if using old VCOM check Q #ifdef WIN32 if (TNC->VCOM) { if (TNC->NewVCOM == 0) { int TXCount, RXCount; BPQSerialGetQCounts(TNC->hDevice, &RXCount, &TXCount); if (TXCount > 4096) return TRUE; // Busy return FALSE; } // Windows New VCOM cant check (I think!) } #endif // return FALSE; } VOID TNCPoll() { struct TNCDATA * TNC = TNC2TABLE; // malloc'ed // This logic had got very convoluted. This Tries // to rationalize it while (TNC) { if (TNC->hDevice) { CheckForConnectStatusChange(TNC); CheckForHostStatusChange(TNC); CheckForDataFromHost(TNC); CheckForDataFromTerminal(TNC); } TNC = TNC->Next; } } void CheckForConnectStatusChange(struct TNCDATA * TNC) { #ifdef WIN32 if (TNC->VCOM && TNC->NewVCOM == 0) { // Can check if other end is connected int ConCount = 0; BPQSerialIsCOMOpen(TNC->hDevice, &ConCount); if (TNC->PortEnabled == 1 && ConCount == 0) // Connection has just closed - if connected, disconnect stream // This should close all streams on multistream port SessionControl(TNC->BPQPort, 2, 0); if (TNC->PortEnabled != ConCount) { TNC->PortEnabled = ConCount; } } #endif } void CheckForHostStatusChange(struct TNCDATA * TNC) { if (TNC->Mode == KANTRONICS) { // Have to poll for Data and State changes int n, state, change; struct StreamInfo * Channel; for (n = 1; n <= TNC->HOSTSTREAMS; n++) { Channel = TNC->Channels[n]; SessionState(Channel->BPQStream, &state, &change); if (change == 1) { if (state == 1) // Connected KANTConnected(TNC, Channel, n); else KANTDisconnected(TNC, Channel, n); } } return; } // ?? Should other modes check here ?? } void CheckForDataFromHost(struct TNCDATA * TNC) { unsigned int n; int retval, more; char TXMsg[1000]; ULONG Read = 0; // I think we should check for space in TNC to Terminal Buffer if (TNC->RXCOUNT + 500 > TNCBUFFLEN) return; // Only KANTRONICS and TNC2 check for data here // DED and SCS check when polled by Host Program if (TNC->Mode == KANTRONICS) { // Have to poll for Data and State changes int n, len, count; struct StreamInfo * Channel; UCHAR Buffer[400]; for (n = 1; n <= TNC->HOSTSTREAMS; n++) { Channel = TNC->Channels[n]; do { if (TNC->RXCOUNT + 500 > TNCBUFFLEN) return; GetMsg(Channel->BPQStream, &Buffer[3], &len, &count); if (len > 0) { // If a failure, set a close timer (for Airmail, etc) if (strstr(&Buffer[3], "} Downlink connect needs port number") || strstr(&Buffer[3], "} Failure with ") || strstr(&Buffer[3], "} Sorry, ")) Channel->CloseTimer = CloseDelay * 10; else Channel->CloseTimer = 0; // Cancel Timer if (TNC->MODE) { Buffer[0] = 'D'; Buffer[1] = '1'; Buffer[2] = n + '@'; SendKISSData(TNC, Buffer, len+3); } else SendDataToTNC(TNC, &Buffer[3], len); } } while (0); //(count > 0); } return; } if (TNC->Mode == SCS) return; n = 0; getloop: TNC2GetChar(TNC, &retval, &more); if (retval != -1) TXMsg[n++] = retval; if (more > 0 && n < 1000) goto getloop; if (n > 0) WriteCOMBlock(TNC->hDevice, TXMsg, n); return; } /* Where does this go ?? // We look for change on current RX Stream retval = TNCGetVMSR(TNC, TNC->TNC2Stream[TNC->RXStream], TRUE); if ((retval & 8) == 8) //' DCD (Connected) Changed { TNC->DCD = (retval & 128) / 128; if (TNC->DCD == 1) BPQSerialSetDCD(TNC->hDevice); else BPQSerialClrDCD(TNC->hDevice); } if ((retval & 1) == 1) //' CTS (Flow Control) Changed { TNC->CTS = (retval & 16) / 16; if (TNC->CTS == 1) BPQSerialSetCTS(TNC->hDevice); else BPQSerialClrCTS(TNC->hDevice); } BPQSerialGetDTRRTS(TNC->hDevice,&ModemStat); if ((ModemStat & 1) != TNC->DTR) { TNC->DTR=!TNC->DTR; } if ((ModemStat & 2) >> 1 != TNC->RTS) { TNC->RTS=!TNC->RTS; } TNC = TNC->Next; continue; } #endif { // Real Port or Linux Virtual int Read, n; int retval, more; char TXMsg[500]; #ifndef WIN32 int err; // We can tell if partner has gone on PTY Pair - read returns 5 if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) Read = TNCReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &err); else Read = TNCReadCOMBlock(TNC->hDevice, rxbuffer, 1000, &err); if (err) { if (TNC->PortEnabled) { TNC->PortEnabled = FALSE; DisableAppl(TNC); Debugprintf("Device %s closed", TNC->PORTNAME); } } else TNC->PortEnabled = TRUE; #else if (TNC->Mode == KANTRONICS || TNC->Mode == SCS) Read = ReadCOMBlock(TNC->hDevice, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr); else Read = ReadCOMBlock(TNC->hDevice, rxbuffer, 1000); #endif if (Read) { if (TNC->Mode == TNC2) { for (n = 0; n < Read; n++) TNC2PutChar(TNC, rxbuffer[n]); } else if (TNC->Mode == DED) { for (n = 0; n < Read; n++) TfPut(TNC, rxbuffer[n]); } else if (TNC->Mode == KANTRONICS) { TNC->RXBPtr += Read; ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); } else if (TNC->Mode == SCS) { TNC->RXBPtr += Read; ProcessSCSPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); } } n=0; getloopR: TNC2GetChar(TNC, &retval, &more); if (retval != -1) TXMsg[n++] = retval; if (more > 0 && n < 500) goto getloopR; if (n > 0) { resp = WriteCOMBlock(TNC->hDevice, TXMsg, n); } } TNC = TNC->Next; } } */ void CheckForDataFromTerminal(struct TNCDATA * TNC) { unsigned int n; char rxbuffer[1000]; ULONG Read = 0, resp; if (TNC->Mode == KANTRONICS) resp = GetDataFromTNC(TNC, &TNC->TOUSERBUFFER[TNC->RXBPtr], 1000 - TNC->RXBPtr, &Read); else if (TNC->Mode == SCS) resp = GetDataFromTNC(TNC, &TNC->FROMUSERBUFFER[TNC->FROMUSERLEN], TNCBUFFLEN - TNC->FROMUSERLEN, &Read); else resp = GetDataFromTNC(TNC, rxbuffer, 1000, &Read); if (Read) { if (TNC->Mode == TNC2) { for (n = 0; n < Read; n++) TNC2PutChar(TNC, rxbuffer[n]); } else if (TNC->Mode == DED) { for (n = 0; n < Read; n++) TfPut(TNC, rxbuffer[n]); } else if (TNC->Mode == KANTRONICS) { TNC->RXBPtr += Read; ProcessPacket(TNC, TNC->TOUSERBUFFER, TNC->RXBPtr); } else if (TNC->Mode == SCS) { TNC->FROMUSERLEN += Read; ProcessSCSPacket(TNC, TNC->FROMUSERBUFFER, TNC->FROMUSERLEN); } } } VOID DOMONITORING(int NeedTrace, struct TNCDATA * TTNC) { // IF ANY PORTS HAVE MONITOR ENABLED, SET MONITOR BIT ON FIRST PORT int Tracebit = 0, len, count, n; time_t Stamp; uint64_t SaveMMASK = MMASK; BOOL SaveMTX = MTX; BOOL SaveMCOM = MCOM; BOOL SaveMUI = MUIONLY; struct TNCDATA * TNC = TNC2TABLE; int BPQStream = TTNC->TNC2Stream[0]->BPQPort; if (NeedTrace) Tracebit = 0x80; if (BPQStream) { if (TTNC->CONOK && TTNC->Mode == TNC2) SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, TTNC->APPLICATION); else SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, 0); } Stamp = GetRaw(BPQStream, (char *)&MONITORDATA, &len, &count); if (len == 0) return; len = DecodeFrame(&MONITORDATA, MONBUFFER, Stamp); while (TNC) { if (TNC->Mode == TNC2 && TNC->TRACEFLAG) { IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0); len = IntDecodeFrame(&MONITORDATA, MONBUFFER, Stamp, TNC->MMASK, FALSE, FALSE); // printf("%d %d %d %d %d\n", len, MMASK, MTX, MCOM, MUIONLY); IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); if (len) { for (n = 0; n < len; n++) { PUTCHARINBUFFER(TNC, MONBUFFER[n]); } } } TNC=TNC->Next; } } VOID TNC2PutChar(struct TNCDATA * TNC, int Char) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; if (TNCStream->MODEFLAG & COMMAND) goto KEYB06C; // COMMAND MODE - SKIP TRANS TEST if (TNCStream->MODEFLAG & TRANS) goto KEYB06T; // TRANS MODE // CONV MODE - SEE IF CPACTIME ON if (TNC->CPACTIME) TNC->TRANSTIMER = TRANSDELAY; goto KEYB06; // PROCESS CHAR KEYB06T: // Transparent Mode - See if Escape Sequence Received (3 esc chars, with guard timer) // CHECK FOR COMMAND CHAR IF CMDTIME EXPIRED OR COMAND CHAR ALREADY RECEIVED if (TNC->COMCOUNT) goto KBTRN3; // ALREADY GOT AT LEAST 1 if (TNC->CMDTMR) goto KBTRN5; // LESS THAN CMDTIME SINCE LAST CHAR KBTRN3: if (Char != TNC->COMCHAR) { TNC->COMCOUNT = 0; goto KBTRN5; // NOT COMMAND CHAR } TNC->COMCOUNT++; KBTRN5: TNC->CMDTMR = TNC->CMDTIME; // REPRIME ESCAPE TIMER TNC->TRANSTIMER = TRANSDELAY; KBNORM(TNC, Char); return; // TRANSPARENT MODE KEYB06: // STILL JUST CONV MODE if (Char != TNC->SENDPAC) goto NOTSENDPAC; // SEND PACKET CHAR - SHOUD WE SEND IT? TNC->TRANSTIMER = 0; if (TNC->CRFLAG) KBNORM(TNC, Char); // PUT CR IN BUFFER SENDPACKET(TNC); return; NOTSENDPAC: KEYB06C: // COMMAND OR CONV MODE // Check for Escaped if (TNC->InEscape) { TNC->InEscape = 0; KBNORM(TNC, Char); // Process as normal chars return; } if (TNC->InStreamSW) { TNC->InStreamSW = 0; if (Char != TNC->StreamSW) { // Switch TX Stream if valid int n; if (TNC->ECHOFLAG) KBECHO(TNC, Char); if (TNC->LCStream) Char = toupper(Char); n = Char - 'A'; if (n >= 0 && TNC->TNC2Stream[n]) TNC->TXStream = n; return; } } if (Char == TNC->PASSCHAR) { TNC->InEscape = 1; return; } if (TNC->StreamSW && Char == TNC->StreamSW) { TNC->InStreamSW = 1; if (TNC->ECHOFLAG) KBECHO(TNC, Char); return; } if (Char < 32) // control { if (Char == 10 && TNC->LFIGNORE) return; if (Char == 8) { if (TNC->MSGLEN == 0) return; TNC->MSGLEN--; TNC->CURSOR--; if (TNC->ECHOFLAG) { KBECHO(TNC, Char); // Delete char from display KBECHO(TNC, ' '); KBECHO(TNC, Char); } return; } if (Char == 26) // Ctrl/Z { KBNORM(TNC, Char); // FOR MBX TERMINATOR return; } if (Char == TNC->COMCHAR) { SETCOMMANDMODE(TNC); return; } if (TNCStream->MODEFLAG & COMMAND) { if (Char == 0x14) // CTRL/T { TNC->TRACEFLAG ^= 1; return; } if (Char == 13) { KBNORM(TNC, 13); // PUT CR IN BUFFER SENDPACKET(TNC); return; } } KBNORM(TNC, Char); // Process others as normal chars } KBNORM(TNC, Char); } VOID KBNORM(struct TNCDATA * TNC, int Char) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; if (TNC->MSGLEN > 256) goto TOOLONG; // PROTECT BUFFER *(TNC->CURSOR++) = Char; TNC->MSGLEN++; TOOLONG: if (TNC->ECHOFLAG) KBECHO(TNC, Char); if (TNC->MSGLEN < TNCStream->TPACLEN) return; // DONT APPLY PACLEN IN COMMAND MODE if (TNCStream->MODEFLAG & COMMAND) return; SENDPACKET(TNC); // Send what we have } VOID SETCOMMANDMODE(struct TNCDATA * TNC) { if (TNC->MSGLEN) SENDPACKET(TNC); SETCOMM00(TNC); } VOID SETCOMM00(struct TNCDATA * TNC) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; TNCStream->MODEFLAG |= COMMAND; // BACK TO COMMAND MODE TNCStream->MODEFLAG &= ~(CONV+TRANS); TNC->TRANSTIMER = 0; // CANCEL TRANS MODE SEND TIMER TNC->AUTOSENDFLAG = 0; // IN CASE ALREADY SET CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed SENDREPLY(TNC, CMDMSG, 4); TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START TNC->MSGLEN = 0; } VOID SENDPACKET(struct TNCDATA * TNC) { // SEE IF COMMAND STATE struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; int Stream = 0; // Unprooto if (TNCStream->MODEFLAG & COMMAND) { TNCCOMMAND(TNC); // COMMAND TO TNC TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START TNC->MSGLEN = 0; return; } // IF CONNECTED, SEND TO L4 (COMMAND HANDLER OR DATA), // OTHERWISE SEND AS AN UNPROTO FRAME (TO ALL PORTS) if (TNCStream->VMSR & 0x80) // CONNECTED? Stream = TNCStream->BPQPort; SendMsg(Stream, TNC->TONODEBUFFER, TNC->MSGLEN); TNC->CURSOR = &TNC->TONODEBUFFER[0]; // RESET MESSAGE START TNC->MSGLEN = 0; CHECKCTS(TNC); // SEE IF NOW BUSY return; } VOID KBECHO(struct TNCDATA * TNC, int Char) { PUTCHARINBUFFER(TNC, Char); } VOID TNCCOMMAND(struct TNCDATA * TNC) { // PROCESS COMMAND TO TNC CODE char * ptr, * ptr1, * ptr2; int n; TCMDX * CMD; *(--TNC->CURSOR) = 0; strcat(TNC->TONODEBUFFER, " "); ptr = strchr(TNC->TONODEBUFFER, ' '); if (ptr) { // convert command to upper case, leave tail *ptr = 0; _strupr(TNC->TONODEBUFFER); *ptr = ' '; } if (_memicmp(ptr, " switch", 7) == 0) _strupr(ptr); // Special Case ptr1 = &TNC->TONODEBUFFER[0]; // n = 10; while ((*ptr1 == ' ' || *ptr1 == 0) && n--) ptr1++; // STRIP LEADING SPACES and nulls (from keepalive) if (n == -1) { // Null command SENDREPLY(TNC, CMDMSG, 4); return; } ptr2 = ptr1; // Save CMD = &TNCCOMMANDLIST[0]; n = 0; for (n = 0; n < NUMBEROFTNCCOMMANDS; n++) { int CL = CMD->CMDLEN; ptr1 = ptr2; // ptr1 is input command if (memcmp(CMD->String, ptr1, CL) == 0) { // Found match so far - check rest char * ptr2 = &CMD->String[CL]; ptr1 += CL; if (*(ptr1) != ' ') { while(*(ptr1) == *ptr2 && *(ptr1) != ' ') { ptr1++; ptr2++; } } if (*(ptr1) == ' ') { ptr1++; // Skip space CMD->CMDPROC(TNC, ptr1, CMD); SENDREPLY(TNC, CMDMSG, 4); return; } } CMD++; } SENDREPLY(TNC, WHATMSG, 8); } /* ; UNPROTOCMD: ; ; EXTRACT CALLSIGN STRING ; CMP BYTE PTR [ESI],20H JE UNPROTODIS CMP BYTE PTR [ESI],"*" JE CLEARUNPROTO CALL DECODECALLSTRING ; CONVERT TO AX25 FORMAT JZ UNPROTOOK JMP TNCDUFF CLEARUNPROTO: LEA EDI,UNPROTO[EBX] MOV AL,0 MOV ECX,63 REP STOSB ; COPY IN UNPROTODIS: MOV AL,1 CALL DISPLAYUNPROTO ; DISPLAY CURRENT SETTING JMP SENDOK UNPROTOOK: PUSH ESI MOV AL,0 CALL DISPLAYUNPROTO ; DISPLAY OLD STRRING POP ESI LEA EDI,UNPROTO[EBX] MOV ECX,63 REP MOVSB ; COPY IN CONMODE: JMP SENDOK ;CONMODE: JMP KBRET DISPLAYUNPROTO: ; PUSH EAX MOV ESI,OFFSET UNPROT MOV ECX,8 CALL PUTSTRINGINBUFFER MOV ESI,OFFSET WAS ; DISPLAY "was" MOV ECX,5 POP EAX OR AL,AL JZ DISPU00 ; NO MOV ECX,1 ; LEAVE OUT "WAS" DISPU00: CALL PUTSTRINGINBUFFER LEA ESI,UNPROTO[EBX] CMP BYTE PTR [ESI],40H JBE DISPUPRET CALL CONVFROMAX25 PUSH ESI MOV ESI,OFFSET NORMCALL CALL PUTSTRINGINBUFFER POP ESI CMP BYTE PTR [ESI],0 JE DISPUPRET PUSH ESI MOV ESI,OFFSET VIA MOV ECX,5 CALL PUTSTRINGINBUFFER POP ESI DISPUPLOOP: CALL CONVFROMAX25 PUSH ESI MOV ESI,OFFSET NORMCALL INC ECX CALL PUTSTRINGINBUFFER POP ESI CMP BYTE PTR [ESI],0 JNE DISPUPLOOP DISPUPRET: MOV AL,0DH CALL PUTCHARINBUFFER RET BTEXT: ; CMP BYTE PTR [ESI],20H JE BTDIS ; PUSH ESI MOV AL,0 CALL DISPLAYBT ; DISPLAY OLD STRING POP ESI MOV EDI,OFFSET BTEXTFLD MOV ECX,255 BTLOOP: LODSB STOSB CMP ESI,CMDENDADDR ; END? JE BTEND LOOP BTLOOP BTEND: XOR AL,AL STOSB ; NULL ON END ; ; SET UP TO SEND IT AS A UI ; MOV ECX,EDI MOV ESI,OFFSET BTHDDR SUB ECX,ESI MOV MSGLENGTH[ESI],CX ; ; PASS TO SWITCH ; MOV ESI,OFFSET BTEXTFLD SUB ECX,6 ; DONT NEED HEADER MOV AH,12 ; UPDATE FUNCTIONS MOV DX,1 ; UPDATE BT CALL NODE ; PASS TO NODE JMP SENDOK BTDIS: MOV AL,1 CALL DISPLAYBT ; DISPLAY CURRENT SETTING JMP SENDOK DISPLAYBT: ; PUSH EAX MOV ESI,OFFSET BTCMD MOV ECX,8 CALL PUTSTRINGINBUFFER MOV ESI,OFFSET WAS ; DISPLAY "was" MOV ECX,5 POP EAX OR AL,AL JZ DISPBT00 ; NO MOV ECX,1 ; LEAVE OUT "WAS" DISPBT00: CALL PUTSTRINGINBUFFER MOV ESI,OFFSET BTEXTFLD DISPBT10: LODSB OR AL,AL JZ DISPBT20 CALL PUTCHARINBUFFER JMP DISPBT10 DISPBT20: MOV AL,0DH CALL PUTCHARINBUFFER RET */ VOID DOCONMODECHANGE(struct TNCDATA * TNC, int Stream) { struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[Stream]; TNCStream->VMSR |= 0x88; // SET CONNECTED // IF NOMODE IS ON LEAVE IN TNC COMMAND MODE, ELSE PUT INTO CONV MODE // (MAY NEED TO IMPLEMENT CONMODE SOMETIME) if (TNC->NOMODE) return; TNCStream->MODEFLAG |= CONV; // INTO CONVERSE MODE TNCStream->MODEFLAG &= ~(COMMAND+TRANS); } VOID SENDREPLY(struct TNCDATA * TNC, char * Msg, int Len) { int n = 0; CheckForStreamChange(TNC, TNC->TXStream); // Send Stream Switched Message if changed for (n= 0; n < Len; n++) { PUTCHARINBUFFER(TNC, Msg[n]); } } VOID SEND_CONNECTED(struct TNCDATA * TNC, int ToStream) { // SEND TAPR-STYLE *** CONNECTED TO CURRENT PORT struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[ToStream]; int len; char Response[128]; char Call[11] = ""; int paclen, dummy; BPQVECSTRUC * SESS; TRANSPORTENTRY * L4 = NULL; int stream; GetConnectionInfo(TNCStream->BPQPort, Call, &dummy, &dummy, &paclen, &dummy, &dummy); if (paclen) TNCStream->TPACLEN = paclen; if (TNCStream->MODEFLAG & TRANS) return; //NOT IF TRANSPARENT strlop(Call, ' '); strcpy(TNCStream->RemoteCall, Call); CheckForStreamChange(TNC, ToStream); // Send Stream Switched Message if changed if (TNC->CBELL) len = sprintf(Response, "%s%s%c\r", CONMSG, Call, 7); // Add BELL char else len = sprintf(Response, "%s%s\r", CONMSG, Call); SENDREPLY(TNC, Response, len); // If incoming session Send CTEXT if set stream = TNCStream->BPQPort; stream--; // API uses 1 - 64 if (stream < 0 || stream > 63) return; SESS = &BPQHOSTVECTOR[stream]; if (SESS && SESS->HOSTSESSION) L4 = SESS->HOSTSESSION; if (L4 && (L4->L4CIRCUITTYPE & DOWNLINK)) { if (TNC->CMSG && TNC->CTEXT[0]) { // Add CTEXT int n; char Msg[256]; n = sprintf(Msg, "%s\r", TNC->CTEXT); SendMsg(TNCStream->BPQPort, Msg, n); } // if CHECK_FOR_ESC set in applflags send "^d to disconnect msg if ((TNC->APPLFLAGS & CHECK_FOR_ESC)) // If incoming session { char Msg[] = "Send ^D to disconnect\r"; SendMsg(TNCStream->BPQPort, Msg, (int)strlen(Msg)); } } } VOID PUTCHARINBUFFER(struct TNCDATA * TNC, int Char) { // CALLED BY L4 CODE TO PASS DATA TO VIRTUAL TNC ; if (TNC->RXCOUNT >= TNCBUFFLEN) { // OVERRUN - LOSE IT TNC->VLSR |= 2; // SET OVERRUN ERROR return; } TNC->VLSR &= ~2; // CLEAR OVERRRUN *(TNC->PUTPTR++) = Char; TNC->RXCOUNT++; if (TNC->PUTPTR == &TNC->TOUSERBUFFER[TNCBUFFLEN]) TNC->PUTPTR = &TNC->TOUSERBUFFER[0]; if(TNC->RXCOUNT > TNCBUFFLEN-300) // ALLOW FOR FULL PACKET { // BUFFER GETTING FULL - DROP RTS/DTR TNC->RTSFLAG |= 1; // SET BUSY } if (Char == 13 && TNC->AUTOLF) PUTCHARINBUFFER(TNC, 10); // Add LF } VOID CHECKCTS(struct TNCDATA * TNC) { // SEE IF CROSS-SESSION STILL BUSY if (RXCount(TNC->BPQPort) > 4) { // Busy if ((TNC->VMSR & 0x10) == 0) // ALREADY OFF? return; // No Change TNC->VMSR &= 0xef; // Drop CTS TNC->VMSR |= 1; // Delta bit return; } // Not busy if (TNC->VMSR & 0x10) // ALREADY ON? return; // No Change TNC->VMSR |= 0x11; // CTS AND DELTA CTS } VOID CONNECTTONODE(struct TNCDATA * TNC) { char AXCALL[7]; struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; int SaveAuthProg = AuthorisedProgram; AuthorisedProgram = 1; SessionControl(TNCStream->BPQPort, 1, TNC->APPLICATION); AuthorisedProgram = SaveAuthProg; ConvToAX25(TNC->MYCALL, AXCALL); ChangeSessionCallsign(TNCStream->BPQPort, AXCALL); // Set default Paclen TNCStream->TPACLEN = TNC->TPACLEN; } VOID GETDATA(struct TNCDATA * TNC) { // I'm sure this should only be called for TNC2 devices struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream]; int state, change, InputLen, count, n, i; char InputBuffer[512]; // LOOK FOR STATUS CHANGE for (i = 0; i < TNC->HOSTSTREAMS; i++) { TNCStream = TNC->TNC2Stream[i]; LocalSessionState(TNCStream->BPQPort, &state, &change, TRUE); if (change == 1) { if (state == 1) // Connected { SEND_CONNECTED(TNC, i); DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED } else { TNCStream->MODEFLAG |= COMMAND; TNCStream->MODEFLAG &= ~(CONV+TRANS); TNCStream->VMSR &= 0x7F; // DROP DCD TNCStream->VMSR |= 8; // DELTA DCD CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed SENDREPLY(TNC, DISCONNMSG, 18); } } else { // No Change // VERIFY CURRENT STATE if (state == 1) // Connected { // SWITCH THINKS WE ARE CONNECTED if ((TNCStream->VMSR & 0x80) == 0) { // TNC Doesn't SEND_CONNECTED(TNC, i); DOCONMODECHANGE(TNC, i); // SET CONNECTED AND CHANGE MODE IF NEEDED } } else { // SWITCH THINKS WE ARE DISCONNECTED if (TNCStream->VMSR & 0x80) { // We Disagree, so force off TNCStream->MODEFLAG |= COMMAND; TNCStream->MODEFLAG &= ~(CONV+TRANS); TNCStream->VMSR &= 0x7F; // DROP DCD TNCStream->VMSR |= 8; // DELTA DCD CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed SENDREPLY(TNC, DISCONNMSG, 18); } } } // SEE IF ANYTHING QUEUED if (TNC->RTSFLAG & 1) continue; GetMsg(TNCStream->BPQPort, InputBuffer, &InputLen, &count); if (InputLen == 0) continue; CheckForStreamChange(TNC, i); // Send Stream Switched Message if changed // if CHECK_FOR_ESC set in APPLFLAGS looks for Disconnect Escape if (TNC->APPLFLAGS & CHECK_FOR_ESC) { // look for ^D (or ^d) if (InputLen == 3) { if (_memicmp(InputBuffer, "^D\r", 3) == 0) { Disconnect(TNCStream->BPQPort); continue; } } } for (n = 0; n < InputLen; n++) { char c = InputBuffer[n]; if (TNC->StreamDbl && c == TNC->StreamSW) PUTCHARINBUFFER(TNC, TNC->StreamSW); PUTCHARINBUFFER(TNC, c); } } } // DED Mode Support unsigned char PARAMREPLY[]="* 0 0 64 10 4 4 10 100 18000 30 2 0 2\r\n"; #define PARAMPORT PARAMREPLY[2] #define LPARAMREPLY 39 unsigned char BADCMDREPLY[]="\x2" "INVALID COMMAND\x0"; #define LBADCMDREPLY 17 //sizeof BADCMDREPLY unsigned char DATABUSYMSG[]="\x2" "TNC BUSY - LINE IGNORED\x0"; #define LDATABUSY 25 unsigned char BADCONNECT[]="\x2" "INVALID CALLSIGN\x0"; #define LBADCONNECT 18 unsigned char BUSYMSG[]="BUSY fm SWITCH\x0"; //unsigned char CONSWITCH[]="\x3" "(1) CONNECTED to \x0"; unsigned char DEDSWITCH[]="\x1" "0:SWITCH \x0"; #define LSWITCH 14 unsigned char NOTCONMSG[]="\x1" "CHANNEL NOT CONNECTED\x0"; #define LNOTCON 23 unsigned char ALREADYCONMSG[]="You are already connected on another port\r"; #define ALREADYLEN 45 byte * EncodeCall(byte * Call); VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len); VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len); int DOCOMMAND(struct TNCDATA * conn); int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel); VOID PUTSTRING(struct TNCDATA * conn, UCHAR * Msg) { int len = (int)strlen(Msg); while (len) { *(conn->PUTPTR++) = *(Msg++); if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; conn->RXCOUNT++; len--; } } int PUTCHARx(struct TNCDATA * conn, UCHAR c) { *(conn->PUTPTR++) = c; if (conn->PUTPTR == &conn->TOUSERBUFFER[TNCBUFFLEN]) conn->PUTPTR = (UCHAR *)&conn->TOUSERBUFFER; conn->RXCOUNT++; return 0; } VOID DisableAppl(struct TNCDATA * TNC) { int i, Stream; for (i = 0; i <= TNC->HOSTSTREAMS; i++) { Stream = TNC->Channels[i]->BPQStream; SetAppl(Stream, TNC->APPLFLAGS, 0); Disconnect(Stream); READCHANGE(Stream); // Prevent Initial *** disconnected } } VOID EnableAppl(struct TNCDATA * TNC) { int i; for (i = 1; i <= TNC->HOSTSTREAMS; i++) { SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); } } VOID EnableDEDAppl(struct TNCDATA * TNC) { int i; SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); for (i = 1; i <= TNC->HOSTSTREAMS; i++) { SetAppl(TNC->Channels[i]->BPQStream, TNC->APPLFLAGS, TNC->APPLICATION); } } BOOL TfPut(struct TNCDATA * TNC, UCHAR character) { struct StreamInfo * Channel; TRANSPORTENTRY * L4 = NULL; if (!TNC->MODE) goto CHARMODE; // HOST MODE if (TNC->HOSTSTATE == 0) { TNC->MSGCHANNEL = character; TNC->HOSTSTATE++; return TRUE; } if (TNC->HOSTSTATE == 1) { TNC->MSGTYPE = character; TNC->HOSTSTATE++; return TRUE; } if (TNC->HOSTSTATE == 2) { TNC->MSGCOUNT = character; TNC->MSGLENGTH = character; TNC->MSGCOUNT++; TNC->MSGLENGTH++; TNC->HOSTSTATE++; TNC->DEDCURSOR = &TNC->DEDTXBUFFER[0]; return TRUE; } // RECEIVING COMMAND/DATA *(TNC->DEDCURSOR++) = character; TNC->MSGCOUNT--; if (TNC->MSGCOUNT) return TRUE; // MORE TO COME TNC->HOSTSTATE=0; if (TNC->MSGCHANNEL <= TNC->HOSTSTREAMS) Channel = TNC->Channels[TNC->MSGCHANNEL]; else Channel = TNC->Channels[1]; DEDPROCESSHOSTPACKET(Channel, TNC); TNC->HOSTSTATE = 0; return TRUE; CHARMODE: if (character == 0x11) return TRUE; if (character == 0x18) { // CANCEL INPUT TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; return(TRUE); } *(TNC->CURSOR++) = character; if (character == 1 && (TNC->CURSOR > &TNC->TONODEBUFFER[4]) && *(TNC->CURSOR - 2) == 1 && *(TNC->CURSOR - 3) == 1) { // Looks like a resync request - Appl thinks we are in host mode TNC->MODE = 1; TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; EnableDEDAppl(TNC); return(TRUE); } if (TNC->CURSOR == &TNC->TONODEBUFFER[300]) TNC->CURSOR--; if (character == 0x0d) { // PROCESS COMMAND (UNLESS HOST MODE) *(TNC->CURSOR++) = 0; DOCOMMAND(TNC); } return TRUE; } int DEDPROCESSHOSTPACKET(struct StreamInfo * Channel, struct TNCDATA * TNC) { UCHAR * TXBUFFERPTR; int i; int Work; char WorkString[256]; int State, Change, Count; TRANSPORTENTRY * L4 = NULL; unsigned char * MONCURSOR=0; int SaveAuthProg = 0; unsigned char * mcmdptr = &TNC->DEDTXBUFFER[1]; TXBUFFERPTR = &TNC->DEDTXBUFFER[0]; if (Channel->Chan_TXQ == (UCHAR *)(ptrdiff_t) -1) { Channel->Chan_TXQ = 0; } if (TNC->MSGTYPE != 0) goto NOTDATA; goto HOSTDATAPACKET; //HOSTCMDS: // DD 'G','I', 'J', 'C', 'D', 'L', '@', 'Y', 'M' // DD POLL,ICMD,JCMD,CCMD,DCMD,LCMD,ATCOMMAND,YCMD,HOSTMON NOTDATA: if (TNC->DEDTXBUFFER[0] == 1) { // recovering // if (!TNC->Recovering) // { // sprintf(msg, "Port %d DED Recovery started\n", TNC->ComPort); // OutputDebugString(msg); // TNC->Recovering = TRUE; // } } else { // Not recovery // if (TNC->Recovering) // { // sprintf(msg, "Port %d DED Recovery completed\n", TNC->ComPort); // OutputDebugString(msg); // TNC->Recovering = FALSE; // } } // sprintf(msg,"DED CMD: Port %d CMD %c MSGCHANNEL %d\n", TNC->ComPort, TNC->TONODEBUFFER[0], MSGCHANNEL); // OutputDebugString(msg); if (_memicmp(TNC->DEDTXBUFFER, "QRES", 4 == 0)) return SendHostOK(TNC); switch (toupper(TNC->DEDTXBUFFER[0])) { case 1: // Recovery PUTCHARx(TNC, TNC->MSGCHANNEL); for (i=0; i < LBADCMDREPLY; i++) { PUTCHARx(TNC, BADCMDREPLY[i]); } return TRUE; case 'G': PROCESSPOLL(TNC, Channel); return TRUE; case 'I': goto ICMD; case 'J': TNC->MODE = TNC->DEDTXBUFFER[5] & 1; if (TNC->MODE) EnableDEDAppl(TNC); else DisableAppl(TNC); return SendHostOK(TNC); case 'C': goto CCMD; case 'D': // DISCONNECT REQUEST Disconnect(Channel->BPQStream); return SendHostOK(TNC); case 'L': // Poll PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 1); // GET STATE AND QUEUED BUFFERS if (TNC->MSGCHANNEL) { // Data Channel LocalSessionState(Channel->BPQStream, &State, &Change, FALSE); if (State == 0) Work = '0'; else Work = '4'; // AX.25 STATE PUTCHARx(TNC, Change + '0'); // Status Messages PUTCHARx(TNC, ' '); // GET OTHER QUEUE COUNTS Count = RXCount(Channel->BPQStream); sprintf(WorkString, "%d", Count); // message count PUTSTRING(TNC, WorkString); PUTCHARx(TNC, ' '); // NOT SENT IS NUMBER ON OUR QUEUE, NOT ACKED NUMBER FROM SWITCH // SEE HOW MANY BUFFERS ATTACHED TO Q HEADER IN BX Count = 0;// C_Q_COUNT(Channel->Chan_TXQ); sprintf(WorkString, "%d", Count); // message count PUTSTRING(TNC, WorkString); PUTCHARx(TNC, ' '); if (Count > 8) Work = '8'; // Busy Count = CountFramesQueuedOnSession(L4); sprintf(WorkString, "%d", Count); // message count PUTSTRING(TNC, WorkString); PUTCHARx(TNC, ' '); } else { //SEE IF MONITORED FRAMES AVAILABLE TNC->LastDEDPollTime = time(NULL); if (MONCount(TNC->Channels[0]->BPQStream)) Work = 0x31; else Work = 0x30; } PUTCHARx(TNC, '0'); PUTCHARx(TNC, ' '); PUTCHARx(TNC, Work); PUTCHARx(TNC, 0); return TRUE; case '@': goto ATCOMMAND; case 'Y': goto YCMD; case 'E': goto ECMD; case 'M': // Support BPQ Extensions IUSC followed by optional port list TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; if (*mcmdptr == ' ') mcmdptr++; if (mcmdptr[0] == 'N') TNC->TRACEFLAG = 0; else { char * ptr, * ptr2; int port; uint64_t mask = 0; ptr = strlop(mcmdptr, ' '); _strupr(mcmdptr); if (strchr(mcmdptr, 'U')) TNC->MUIONLY = 1; // Ptocess Mask while (ptr && ptr[0]) { ptr2 = strlop(ptr, ','); port = atoi(ptr); if (port) { mask |= ((uint64_t)1 << (port - 1)); } ptr = ptr2; } if (mask) TNC->MMASK = mask; TNC->TRACEFLAG = 0x80; } SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION); return SendHostOK(TNC); case 'K': case 'O': return SendHostOK(TNC); case 'V': // Vesrion PUTCHARx(TNC, TNC->MSGCHANNEL); PUTCHARx(TNC, 1); PUTSTRING(TNC, "DSPTNC Firmware V.1.3a, (C) 2005-2010 SCS GmbH & Co."); PUTCHARx(TNC, 0); return TRUE; default: return SendHostOK(TNC); ATCOMMAND: if (TNC->DEDTXBUFFER[1] == 'B') goto BUFFSTAT; if (TNC->DEDTXBUFFER[1] == 'M') goto BUFFMIN; // Not recognised PUTCHARx(TNC, TNC->MSGCHANNEL); for (i=0; i < LBADCMDREPLY; i++) { PUTCHARx(TNC, BADCMDREPLY[i]); } return TRUE; BUFFMIN: Work = MINBUFFCOUNT; goto BUFFCOMM; BUFFSTAT: Work = QCOUNT; BUFFCOMM: PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 1); sprintf(WorkString, "%d", Work); // Buffer count PUTSTRING(TNC, WorkString); PUTCHARx(TNC, 0); return TRUE; ICMD: { char * Call = &TNC->DEDTXBUFFER[1]; int len; char Reply[80]; char ReplyCall[10]; if (TNC->MSGLENGTH > 2) { // Save callsign TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0; if (*Call == ' ') *Call++; // May have leading space _strupr(Call); memset(Channel->MYCall, ' ', 10); memcpy(Channel->MYCall, Call, (int)strlen(Call)); Debugprintf("DED Host I chan %d call %s", TNC->MSGCHANNEL, Call); strcpy(ReplyCall, Call); /* if (TNC->MSGCHANNEL == 0) // if setting zero, copy to all others { int i; for (i = 1; i <= TNC->HOSTSTREAMS; i++) { memcpy(TNC->Channels[i]->MYCall, TNC->Channels[0]->MYCall, 10); Debugprintf("DED Capy to chan %d call %s", i, Channel->MYCall); } } */ } else { memcpy(ReplyCall, Channel->MYCall, 10); strlop(ReplyCall, ' '); } len = sprintf(Reply, "\x2%s", ReplyCall); SENDCMDREPLY(TNC, Reply, len + 1); // include the null return TRUE; } ECMD: return SendHostOK(TNC); CCMD: // CONNECT REQUEST if (TNC->MSGCHANNEL == 0) return SendHostOK(TNC); // SETTING UNPROTO ADDR - JUST ACK IT *TNC->DEDCURSOR = 0; if (TNC->MSGLENGTH > 1) goto REALCALL; // STATUS REQUEST - IF CONNECTED, GET CALL DEDSWITCH[3] = 0; GetCallsign(Channel->BPQStream, &DEDSWITCH[3]); Debugprintf("CCMD %d %d %s", TNC->MSGCHANNEL, TNC->Channels[TNC->MSGCHANNEL]->BPQStream, &DEDSWITCH[3]); if (DEDSWITCH[3] == 0) SENDCMDREPLY(TNC, NOTCONMSG, LNOTCON); else SENDCMDREPLY(TNC, DEDSWITCH, LSWITCH); return TRUE; REALCALL: // If to Switch, just connect, else pass c command to Node Debugprintf("CCMD %d %s", TNC->MSGCHANNEL, TXBUFFERPTR); SaveAuthProg = AuthorisedProgram; AuthorisedProgram =1; Connect(Channel->BPQStream); AuthorisedProgram = SaveAuthProg; // CONNECT WILL BE REPORTED VIA NORMAL STATUS CHANGE if (Channel->MYCall[0] > ' ') ChangeSessionCallsign(Channel->BPQStream, EncodeCall(Channel->MYCall)); else ChangeSessionCallsign(Channel->BPQStream, EncodeCall(TNC->Channels[0]->MYCall)); _strupr(TXBUFFERPTR); if (strstr(TXBUFFERPTR, "SWITCH") == 0) // Not switch { char * Call, * Arg1; char * Context; char seps[] = " ,\r"; Call = strtok_s(TXBUFFERPTR + 1, seps, &Context); Arg1 = strtok_s(NULL, seps, &Context); if (Arg1) { // Have a digi string // First digi is used as a port number. Any others are rwal digis or WINMOR/PACTOR if (Context[0]) TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s v %s\r", Arg1, Call, Context); else TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s %s\r", Call, Arg1); } else TNC->MSGLEN = sprintf(TXBUFFERPTR + 100, "C %s\r", Call); strcpy(TXBUFFERPTR, TXBUFFERPTR + 100); SendMsg(Channel->BPQStream, TXBUFFERPTR, TNC->MSGLEN); // READCHANGE(Channel->BPQStream); // Suppress Connected to Switch return SendHostOK(TNC); } } return SendHostOK(TNC); HOSTDATAPACKET: // } // { // UCHAR msg[100]; // sprintf(msg,"Host Data Packet: Port %d\n", TNC->ComPort); // OutputDebugString(msg); // } // // IF WE ALREADY HAVE DATA QUEUED, ADD THIS IT QUEUE // if (Channel->Chan_TXQ) // { // // COPY MESSAGE TO A CHAIN OF BUFFERS // if (QCOUNT < 10) // goto CANTSEND; // NO SPACE - RETURN ERROR (?) //QUEUEFRAME: // C_Q_ADD(Channel->Chan_TXQ, COPYMSGTOBUFFERS()); // RETURNS EDI = FIRST (OR ONLY) FRAGMENT // return SendHostOK(TNC); // MAKE SURE NODE ISNT BUSY if (TNC->MSGCHANNEL == 0) // UNPROTO Channel goto SendUnproto; Count = CountFramesQueuedOnSession(L4); // if (Count > 4 || QCOUNT < 40) // goto QUEUEFRAME; // OK TO PASS TO NODE SENDENFORCINGPACLEN(Channel, TNC->DEDTXBUFFER, TNC->MSGLENGTH); return SendHostOK(TNC); SendUnproto: SendMsg(0, TXBUFFERPTR, TNC->MSGLENGTH); return SendHostOK(TNC); YCMD: *TNC->DEDCURSOR = 0; Work = atoi(&TXBUFFERPTR[1]); if (Work == 0) Work = 1; // Mustn't release last stream if (Work >= 0 && Work <= MAXSTREAMS) { int Stream; if (Work < TNC->HOSTSTREAMS) { // Need to get rid of some streams for (i = Work + 1; i <= TNC->HOSTSTREAMS; i++) { Stream = TNC->Channels[i]->BPQStream; if (Stream) { Disconnect(Stream); READCHANGE(Stream); // Prevent Initial *** disconnected DeallocateStream(Stream); Debugprintf("DED YCMD Release Stream %d", Stream); } free(TNC->Channels[i]); TNC->Channels[i] = 0; } } else { for (i = TNC->HOSTSTREAMS+1; i <= Work; i++) { AllocateDEDChannel(TNC, i); // Also used by Y command handler } } TNC->HOSTSTREAMS = Work; } return SendHostOK(TNC); } int SendHostOK(struct TNCDATA * TNC) { PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 0); // NOTHING DOING return TRUE; } int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) { // DED Mode - ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA if (TNC->MSGLENGTH == 1) // General Poll { if (STATUSPOLL(TNC, Channel)) return TRUE; // Status was reported if (DATAPOLL(TNC, Channel)) return TRUE; // Data Sent PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 0); // NOTHING DOING return TRUE; } // HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL if (TNC->TONODEBUFFER[1] == '0') // Data only { if (DATAPOLL(TNC, Channel)) return TRUE; // Data Sent PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 0); // NOTHING DOING return TRUE; } // Must be Status only if (STATUSPOLL(TNC, Channel)) return TRUE; // Status was reported PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 0); // NOTHING DOING return TRUE; } int ConvertToDEDMonFormat(struct TNCDATA * TNC, char * Decoded, int Len, MESSAGE * Rawdata) { // convert tnc2 format monitor to ded format unsigned char * MONCURSOR=0; unsigned char MONHEADER[256]; char * From, * To, * via, * ctl, *Context, *ptr, *iptr; int pid, NR, NS, MonLen; char rest[20]; /* From DEDHOST Documentation Codes of 4 and 5 both signify a monitor header. This is a null-terminated format message containing the fm {call} to {call} via {digipeaters} ctl {name} pid {hex} string that forms a monitor header. The monitor header is also identical to the monitor header displayed in user mode. If the code was 4, the monitored frame contained no information field, so the monitor header is all you get. If you monitor KB6C responding to a connect request from me and then poll channel 0, you'll get: 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! ! ! ! ! +---- Code = 4 (Monitor, no info) Null termination ----+ +------- Channel = 0 (Monitor info is always on channel 0) If the code was 5, the monitored frame did contain an information field. In this case, another G command to channel 0 will return the monitored information with a code of 6. Since data transmissions must be transparent, the monitored information is passed as a byte-count format transmission. That is, it is preceded by a count byte (one less than the number of bytes in the field). No null terminator is used in this case. Since codes 4, 5, and 6 pertain to monitored information, they will be seen only on channel 0. If you hear KB6C say "Hi" to NK6K, and then poll channel 0, you'll get: 0005666D204B42364320746F204E4B364B2063746C204930302070494420463000 ! ! f m K B 6 C t o N K 6 K c t l I 0 0 p i d F 0 ! ! ! ! ! ! ! ! or whatever ----+-+ ! ! ! ! ! +---- Code = 5 (Monitor, info follows) Null termination ----+ +------ Channel = 0 (Monitor info is always on channel 0) and then the very next poll to channel 0 will get: 00 06 02 48 69 0D ! ! ! H i CR ! ! ! ! ! ! ! +---- (this is a data byte) ! ! +---- Count = 2 (three bytes of data) ! +------- Code = 6 (monitored information) +---------- Channel = 0 (Monitor info is always on channel 0) */ Decoded[Len] = 0; iptr = strchr(&Decoded[10], ':'); // Info if present MONHEADER[0] = 4; // NO DATA FOLLOWS MONCURSOR = &MONHEADER[1]; if (strstr(Decoded, "NET/ROM") || strstr(Decoded, "NODES br") || strstr(Decoded, "INP3 RIF")) return 0; //pid = 0xcf; else pid = 0xf0; From = strtok_s(&Decoded[10], ">", &Context); To = strtok_s(NULL, " ", &Context); via = strlop(To, ','); Context = strchr(Context, '<'); if (Context == 0) return 0; ctl = strtok_s(NULL, ">", &Context); if (via) MONCURSOR += sprintf(MONCURSOR, "fm %s to %s via %s ctl ", From, To, via); else MONCURSOR += sprintf(MONCURSOR, "fm %s to %s ctl ", From, To); rest[0] = 0; switch (ctl[1]) { case 'R': NR = ctl[strlen(ctl)-1] - 48; strlop(ctl, ' '); sprintf(rest, "%s%d", &ctl[1], NR); break; case 'I': ptr = strchr(ctl, 'S'); if (ptr) NS = ptr[1] - 48; ptr = strchr(ctl, 'R'); if (ptr) NR = ptr[1] - 48; sprintf(rest, "I%d%d pid %X", NS, NR, pid); if (pid == 0xcf) { // NETROM - pass the raw data MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); MONHEADER[0] = 5; // Data to follow TNC->MONFLAG = 1; // Data to follow TNC->MONBUFFER[0] = 6; TNC->MONLENGTH = MonLen + 2; TNC->MONBUFFER[1] = (MonLen - 1); } else { if (iptr) { iptr += 2; // Skip colon and cr MonLen = Len - (int)(iptr - Decoded); if (MonLen > 256) MonLen = 256; memcpy(&TNC->MONBUFFER[2], iptr, MonLen); if (MonLen == 0) // No data { MONHEADER[0] = 4; // No Data to follow TNC->MONFLAG = 0; // No Data to follow } else { MONHEADER[0] = 5; // Data to follow TNC->MONFLAG = 1; // Data to follow TNC->MONBUFFER[0] = 6; TNC->MONLENGTH = MonLen + 2; TNC->MONBUFFER[1] = (MonLen - 1); } } } break; case 'C': strcpy(rest, "SABM"); break; case 'D': if (ctl[1] == 'M') strcpy(rest, "DM"); else strcpy(rest, "DISC"); break; case 'U': if (ctl[2] == 'A') strcpy(rest, "UA"); else { // UI size_t MonLen;; MONHEADER[0] = 5; // Data to follow sprintf(rest, "UI pid %X", pid); TNC->MONFLAG = 1; // Data to follow TNC->MONBUFFER[0] = 6; if (pid ==0xcf) { // NETROM - pass th raw data MonLen = Rawdata->LENGTH - (MSGHDDRLEN + 16); // Data portion of frame memcpy(&TNC->MONBUFFER[2], &Rawdata->L2DATA[0], MonLen); } else { ptr = strchr(Context, ':'); if (ptr == 0) { TNC->MONFLAG = 0; return 0; } ptr += 2; // Skip colon and cr MonLen = Len - (ptr - Decoded); memcpy(&TNC->MONBUFFER[2], ptr, MonLen); } if (MonLen == 0) // No data { MONHEADER[0] = 4; // No Data to follow TNC->MONFLAG = 0; // No Data to follow } else { TNC->MONLENGTH = (int)MonLen + 2; TNC->MONBUFFER[1] = (int)(MonLen - 1); } break; } default: rest[0] = 0; } MONCURSOR += sprintf(MONCURSOR, "%s", rest); if (MONCURSOR == &MONHEADER[1]) return 0; // NOTHING DOING *MONCURSOR++ = 0; // NULL TERMINATOR SENDCMDREPLY(TNC, MONHEADER, (int)(MONCURSOR - &MONHEADER[0])); return 1; } // GET THE CONTROL BYTE, TO SEE IF THIS FRAME IS TO BE DISPLAYED /* static char CTL_MSG[]=" ctl "; static char VIA_MSG[]=" via "; static char PID_MSG[]=" pid "; static char SABM_MSG[]="SABM"; static char DISC_MSG[]="DISC"; static char UA_MSG[]="UA"; static char DM_MSG []="DM"; static char RR_MSG []="RR"; static char RNR_MSG[]="RNR"; static char I_MSG[]="I pid "; static char UI_MSG[]="UI pid "; static char FRMR_MSG[]="FRMR"; static char REJ_MSG[]="REJ"; PUSH EDI MOV ECX,8 ; MAX DIGIS CTRLLOOP: TEST BYTE PTR (MSGCONTROL-1)[EDI],1 JNZ CTRLFOUND ADD EDI,7 LOOP CTRLLOOP ; ; INVALID FRAME ; POP EDI RET CTRLFOUND: MOV AL,MSGCONTROL[EDI] and AL,NOT PFBIT ; Remove P/F bit mov FRAME_TYPE,AL POP EDI ; TEST AL,1 ; I FRAME JZ IFRAME CMP AL,3 ; UI JE OKTOTRACE ; ALWAYS DO UI CMP AL,FRMR JE OKTOTRACE ; ALWAYS DO FRMR ; ; USEQ/CONTROL - TRACE IF MCOM ON ; CMP MCOM,0 JNE OKTOTRACE RET ;-----------------------------------------------------------------------------; ; Check for MALL ; ;-----------------------------------------------------------------------------; IFRAME: cmp MALL,0 jne OKTOTRACE ret OKTOTRACE: ; ;-----------------------------------------------------------------------------; ; Get the port number of the received frame ; ;-----------------------------------------------------------------------------; ; ; CHECK FOR PORT SELECTIVE MONITORING ; MOV CL,MSGPORT[EDI] mov PORT_NO,CL DEC CL MOV EAX,1 SHL EAX,CL ; SHIFT BIT UP TEST MMASK,EAX JNZ TRACEOK1 RET TRACEOK1: MOV FRMRFLAG,0 push EDI mov AH,MSGDEST+6[EDI] mov AL,MSGORIGIN+6[EDI] ; ; Display Origin Callsign ; ; ; 0004666D204B42364320746F204B42354D552063746C2055612070494420463000 ; ! ! f m K B 6 C t o K B 5 M U c t l U A p i d F 0 ! ; ! ! ! ; ! +---- Code = 4 (Monitor, no info) Null termination ----+ ; +------- Channel = 0 (Monitor info is always on channel 0) mov ESI,OFFSET FM_MSG call NORMSTR lea ESI,MSGORIGIN[EDI] call CONVFROMAX25 mov ESI,OFFSET NORMCALL call DISPADDR pop EDI push EDI mov ESI,OFFSET TO_MSG call NORMSTR ; ; Display Destination Callsign ; ; lea ESI,MSGDEST[EDI] call CONVFROMAX25 mov ESI,OFFSET NORMCALL call DISPADDR pop EDI push EDI mov AX,MMSGLENGTH[EDI] mov FRAME_LENGTH,AX mov ECX,8 ; Max number of digi-peaters ; ; Display any Digi-Peaters ; ; test MSGORIGIN+6[EDI],1 jnz NO_MORE_DIGIS mov ESI,OFFSET VIA_MSG call NORMSTR jmp short skipspace NEXT_DIGI: test MSGORIGIN+6[EDI],1 jnz NO_MORE_DIGIS mov AL,' ' call MONPUTCHAR skipspace: add EDI,7 sub FRAME_LENGTH,7 ; Reduce length push EDI push ECX lea ESI,MSGORIGIN[EDI] call CONVFROMAX25 ; Convert to call push EAX ; Last byte is in AH mov ESI,OFFSET NORMCALL call DISPADDR pop EAX test AH,80H jz NOT_REPEATED mov AL,'*' call MONPUTCHAR NOT_REPEATED: pop ECX pop EDI loop NEXT_DIGI NO_MORE_DIGIS: ;----------------------------------------------------------------------------; ; Display ctl ; ;----------------------------------------------------------------------------; mov ESI,OFFSET CTL_MSG call NORMSTR ;-----------------------------------------------------------------------------; ; Start displaying the frame information ; ;-----------------------------------------------------------------------------; mov INFO_FLAG,0 mov AL,FRAME_TYPE test AL,1 jne NOT_I_FRAME ;-----------------------------------------------------------------------------; ; Information frame ; ;-----------------------------------------------------------------------------; mov AL,'I' call MONPUTCHAR mov INFO_FLAG,1 mov ESI,OFFSET I_MSG call NORMSTR lea ESI,MSGPID[EDI] lodsb call BYTE_TO_HEX jmp END_OF_TYPE NOT_I_FRAME: ;-----------------------------------------------------------------------------; ; Un-numbered Information Frame ; ;-----------------------------------------------------------------------------; cmp AL,UI jne NOT_UI_FRAME mov ESI,OFFSET UI_MSG call NORMSTR lea ESI,MSGPID[EDI] lodsb call BYTE_TO_HEX mov INFO_FLAG,1 jmp END_OF_TYPE NOT_UI_FRAME: test AL,10B jne NOT_R_FRAME ;-----------------------------------------------------------------------------; ; Process supervisory frames ; ;-----------------------------------------------------------------------------; and AL,0FH ; Mask the interesting bits cmp AL,RR jne NOT_RR_FRAME mov ESI,OFFSET RR_MSG call NORMSTR jmp END_OF_TYPE NOT_RR_FRAME: cmp AL,RNR jne NOT_RNR_FRAME mov ESI,OFFSET RNR_MSG call NORMSTR jmp END_OF_TYPE NOT_RNR_FRAME: cmp AL,REJ jne NOT_REJ_FRAME mov ESI,OFFSET REJ_MSG call NORMSTR jmp SHORT END_OF_TYPE NOT_REJ_FRAME: mov AL,'?' ; Print "?" call MONPUTCHAR jmp SHORT END_OF_TYPE ; ; Process all other frame types ; ; NOT_R_FRAME: cmp AL,UA jne NOT_UA_FRAME mov ESI,OFFSET UA_MSG call NORMSTR jmp SHORT END_OF_TYPE NOT_UA_FRAME: cmp AL,DM jne NOT_DM_FRAME mov ESI,OFFSET DM_MSG call NORMSTR jmp SHORT END_OF_TYPE NOT_DM_FRAME: cmp AL,SABM jne NOT_SABM_FRAME mov ESI,OFFSET SABM_MSG call NORMSTR jmp SHORT END_OF_TYPE NOT_SABM_FRAME: cmp AL,DISC jne NOT_DISC_FRAME mov ESI,OFFSET DISC_MSG call NORMSTR jmp SHORT END_OF_TYPE NOT_DISC_FRAME: cmp AL,FRMR jne NOT_FRMR_FRAME mov ESI,OFFSET FRMR_MSG call NORMSTR MOV FRMRFLAG,1 jmp SHORT END_OF_TYPE NOT_FRMR_FRAME: mov AL,'?' call MONPUTCHAR END_OF_TYPE: CMP FRMRFLAG,0 JE NOTFRMR ; ; DISPLAY FRMR BYTES ; lea ESI,MSGPID[EDI] MOV CX,3 ; TESTING FRMRLOOP: lodsb CALL BYTE_TO_HEX LOOP FRMRLOOP JMP NO_INFO NOTFRMR: MOVZX ECX,FRAME_LENGTH cmp INFO_FLAG,1 ; Is it an information packet ? jne NO_INFO XOR AL,AL ; IN CASE EMPTY sub ECX,23 CMP ecx,0 je NO_INFO ; EMPTY I FRAME ; ; PUT DATA IN MONBUFFER, LENGTH IN MONLENGTH ; pushad } TNC->MONFLAG = 1; _asm{ popad MOV MONHEADER,5 ; DATA FOLLOWS cmp ECX,257 jb LENGTH_OK ; mov ECX,256 ; LENGTH_OK: ; mov MonDataLen, ECX pushad } TNC->MONBUFFER[1] = MonDataLen & 0xff; TNC->MONBUFFER[1]--; TNC->MONLENGTH = MonDataLen+2; ptr1=&TNC->MONBUFFER[2]; _asm{ popad MOV EDI,ptr1 MONCOPY: LODSB CMP AL,7 ; REMOVE BELL JNE MONC00 MOV AL,20H MONC00: STOSB LOOP MONCOPY POP EDI RET NO_INFO: ; ; ADD CR UNLESS DATA ALREADY HAS ONE ; CMP AL,CR JE NOTANOTHER mov AL,CR call MONPUTCHAR NOTANOTHER: ; pop EDI ret ;----------------------------------------------------------------------------; ; Display ASCIIZ strings ; ;----------------------------------------------------------------------------; NORMSTR: lodsb cmp AL,0 ; End of String ? je NORMSTR_RET ; Yes call MONPUTCHAR jmp SHORT NORMSTR NORMSTR_RET: ret ;-----------------------------------------------------------------------------; ; Display Callsign pointed to by SI ; ;-----------------------------------------------------------------------------; DISPADDR: jcxz DISPADDR_RET lodsb call MONPUTCHAR loop DISPADDR DISPADDR_RET: ret ;-----------------------------------------------------------------------------; ; Convert byte in AL to nn format ; ;-----------------------------------------------------------------------------; DISPLAY_BYTE_2: cmp AL,100 jb TENS_2 sub AL,100 jmp SHORT DISPLAY_BYTE_2 TENS_2: mov AH,0 TENS_LOOP_2: cmp AL,10 jb TENS_LOOP_END_2 sub AL,10 inc AH jmp SHORT TENS_LOOP_2 TENS_LOOP_END_2: push EAX mov AL,AH add AL,30H call MONPUTCHAR pop EAX add AL,30H call MONPUTCHAR ret ;-----------------------------------------------------------------------------; ; Convert byte in AL to Hex display ; ;-----------------------------------------------------------------------------; BYTE_TO_HEX: push EAX shr AL,1 shr AL,1 shr AL,1 shr AL,1 call NIBBLE_TO_HEX pop EAX call NIBBLE_TO_HEX ret NIBBLE_TO_HEX: and AL,0FH cmp AL,10 jb LESS_THAN_10 add AL,7 LESS_THAN_10: add AL,30H call MONPUTCHAR ret CONVFROMAX25: ; ; CONVERT AX25 FORMAT CALL IN [SI] TO NORMAL FORMAT IN NORMCALL ; RETURNS LENGTH IN CX AND NZ IF LAST ADDRESS BIT IS SET ; PUSH ESI ; SAVE MOV EDI,OFFSET NORMCALL MOV ECX,10 ; MAX ALPHANUMERICS MOV AL,20H REP STOSB ; CLEAR IN CASE SHORT CALL MOV EDI,OFFSET NORMCALL MOV CL,6 CONVAX50: LODSB CMP AL,40H JE CONVAX60 ; END IF CALL - DO SSID SHR AL,1 STOSB LOOP CONVAX50 CONVAX60: POP ESI ADD ESI,6 ; TO SSID LODSB MOV AH,AL ; SAVE FOR LAST BIT TEST SHR AL,1 AND AL,0FH JZ CONVAX90 ; NO SSID - FINISHED ; MOV BYTE PTR [EDI],'-' INC EDI CMP AL,10 JB CONVAX70 SUB AL,10 MOV BYTE PTR [EDI],'1' INC EDI CONVAX70: ADD AL,30H ; CONVERT TO DIGIT STOSB CONVAX90: MOV ECX,EDI SUB ECX,OFFSET NORMCALL MOV NORMLEN,ECX ; SIGNIFICANT LENGTH TEST AH,1 ; LAST BIT SET? RET PUTCHAR: pushad push eax push TNC call PUTCHARx pop eax pop eax popad ret } } */ VOID SENDENFORCINGPACLEN(struct StreamInfo * Channel, char * Msg, int Len) { int Paclen = 0; if (Len == 0) return; if (BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION) Paclen = BPQHOSTVECTOR[Channel->BPQStream-1].HOSTSESSION->SESSPACLEN; if (Paclen == 0) goto nochange; // paclen not set fragloop: if (Len <= Paclen) goto nochange; // msglen <= paclen // need to fragment SendMsg(Channel->BPQStream, Msg, Paclen); Msg += Paclen; Len -= Paclen; if (Len) goto fragloop; return; nochange: SendMsg(Channel->BPQStream, Msg, Len); return; } int DOCOMMAND(struct TNCDATA * TNC) { char Errbuff[500]; int i; // PROCESS NORMAL MODE COMMAND Debugprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",TNC->ComPort, TNC->TONODEBUFFER); // IF ECHO ENABLED, ECHO IT if (TNC->ECHOFLAG) { UCHAR * ptr = TNC->TONODEBUFFER; UCHAR c; do { c = *(ptr++); if (c == 0x1b) c = ':'; PUTCHARx(TNC, c); } while (c != 13); } if (TNC->TONODEBUFFER[0] != 0x1b) goto NOTCOMMAND; // DATA IN NORMAL MODE - IGNORE switch (toupper(TNC->TONODEBUFFER[1])) { case 'J': if (TNC->TONODEBUFFER[6] == 0x0d) TNC->MODE = 0; else TNC->MODE = TNC->TONODEBUFFER[6] & 1; if (TNC->MODE) { // send host mode ack // PUTCHARx(conn, 0); // PUTCHARx(conn, 0); EnableDEDAppl(TNC); } else { DisableAppl(TNC); return 0; } break; case 'E': TNC->ECHOFLAG = TNC->TONODEBUFFER[2] & 1; break; case 'I': { // Save call char * Call = &TNC->TONODEBUFFER[2]; *(TNC->CURSOR - 2) = 0; for (i = 0; i <= TNC->HOSTSTREAMS; i++) { strcpy(TNC->Channels[i]->MYCall, Call); } break;; } case 'P': // PARAMS COMMAND - RETURN FIXED STRING PARAMPORT = TNC->TONODEBUFFER[2]; for (i=0; i < LPARAMREPLY; i++) { PUTCHARx(TNC, PARAMREPLY[i]); } break; case 'S': case 'D': // Return Channel Not Connected PUTSTRING(TNC, "* CHANNEL NOT CONNECTED *\r"); default: break; } // PUTCHARx(conn, 'c'); // PUTCHARx(conn, 'm'); // PUTCHARx(conn, 'd'); // PUTCHARx(conn, ':'); // PUTCHARx(conn, 13); NOTCOMMAND: TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER; return 0; } VOID SENDCMDREPLY(struct TNCDATA * TNC, char * Msg, int Len) { int n; if (Len == 0) return; PUTCHARx(TNC, TNC->MSGCHANNEL); for (n = 0; n < Len; n++) { PUTCHARx(TNC, Msg[n]); } } int STATUSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) { // Status Poll int State, Change, i; char WorkString[256]; char ConMsg[64]; if (TNC->MSGCHANNEL == 0) // Monitor Chan return 0; LocalSessionState(Channel->BPQStream, &State, &Change, TRUE); if (Change == 0) return 0; // PORT HAS CONNECTED OR DISCONNECTED - SEND STATUS CHANGE TO PC if (State == 0) { // DISCONNECTED i = sprintf(ConMsg, "\x3(%d) DISCONNECTED fm 0:SWITCH\r", TNC->MSGCHANNEL); i++; } else { // GET CALLSIGN GetCallsign(Channel->BPQStream, WorkString); strlop(WorkString, ' '); i = sprintf(ConMsg, "\x3(%d) CONNECTED to %s\r", TNC->MSGCHANNEL, WorkString); i++; } SENDCMDREPLY(TNC, ConMsg, i); return 1; } int DATAPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel) { unsigned char NODEBUFFER[300]; // MESSAGE FROM NODE int Len, Count, i; time_t stamp; char * ptr1; if (TNC->MSGCHANNEL == 0) { // POLL FOR MONITOR DATA if (TNC->MONFLAG == 0) goto NOMONITOR; // HAVE ALREADY GOT DATA PART OF MON FRAME OT SEND TNC->MONFLAG = 0; ptr1 = (UCHAR *)&TNC->MONBUFFER; if (TNC->MONLENGTH) { SENDCMDREPLY(TNC, ptr1, TNC->MONLENGTH); return TRUE; } Debugprintf("BPQHOST Mondata Flag Set with no data"); NOMONITOR: // SEE IF ANYTHING TO MONITOR stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); while (Len) { // Use Normal Decode, then reformat to DED standard uint64_t SaveMMASK = MMASK; BOOL SaveMTX = MTX; BOOL SaveMCOM = MCOM; BOOL SaveMUI = MUIONLY; unsigned char Decoded[1000]; IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, TNC->MUIONLY); Len = IntDecodeFrame(&MONITORDATA, Decoded, stamp, TNC->MMASK, FALSE, FALSE); IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI); if (Len) { if (ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA)) return 1; stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count); } } return 0; } // Look for session data GetMsg(Channel->BPQStream, NODEBUFFER, &Len, &Count); if (Len == 0) return 0; if (Len > 256) { Debugprintf("BPQHOST Corrupt Length = %d", Len); return 0; } // SEND DATA // If a failure, set a close timer (for Airmail, etc) NODEBUFFER[Len] = 0; // For strstr if (strstr(NODEBUFFER, "} Downlink connect needs port number") || strstr(NODEBUFFER, "} Error - TNC Not Ready") || strstr(NODEBUFFER, "} Failure with ") || strstr(NODEBUFFER, "} Sorry, ")) Channel->CloseTimer = CloseDelay * 10; else Channel->CloseTimer = 0; // Cancel Timer PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL PUTCHARx(TNC, 7); PUTCHARx(TNC, Len - 1); for (i = 0; i < Len; i++) { PUTCHARx(TNC, NODEBUFFER[i]); } return 1; // HAVE SEND SOMETHING } // Kantronics Host Mode Stuff // Kantronics Host Mode Stuff #define FEND 0xC0 // KISS CONTROL CODES #define FESC 0xDB #define TFEND 0xDC #define TFESC 0xDD static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len); VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer); VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len); static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len); static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len); static int DoReceivedData(struct TNCDATA * conn, struct StreamInfo * channel); VOID ProcessPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) { UCHAR * FendPtr; size_t NewLen; if (!conn->MODE) { // In Terminal Mode - Pass to Term Mode Handler ProcessKPacket(conn, rxbuffer, Len); return; } // Split into KISS Packets. By far the most likely is a single KISS frame // so treat as special case if (!(rxbuffer[0] == FEND)) { // Getting Non Host Data in Host Mode - Appl will have to sort the mess // Discard any data conn->RXBPtr = 0; return; } conn->RXBPtr = 0; // Assume we will use all data in buffer - will reset if part packet received FendPtr = memchr(&rxbuffer[1], FEND, Len-1); if (FendPtr == &rxbuffer[Len-1]) { ProcessKHOSTPacket(conn, &rxbuffer[1], Len - 2); return; } if (FendPtr == NULL) { // We have a partial Packet - Save it conn->RXBPtr = Len; memcpy(&conn->TOUSERBUFFER[0], rxbuffer, Len); return; } // Process the first Packet in the buffer NewLen = FendPtr - rxbuffer -1; ProcessKHOSTPacket(conn, &rxbuffer[1], (int)NewLen ); // Loop Back ProcessPacket(conn, FendPtr+1, Len - (int)NewLen -2); return; } VOID ProcessKPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) { UCHAR Char; UCHAR * cmdStart; int cmdlen = 0; // we will often get a whole connamd at once, but may not, so be prepared to receive char by char // Could also get more than one command per packet cmdStart = rxbuffer; if (rxbuffer[0] == FEND && rxbuffer[Len-1] == FEND) { // Term thinks it is hosr mode // Unless it is FEND FF FEND (exit KISS) or FEND Q FEND (exit host) if (rxbuffer[2] == FEND) { if (rxbuffer[1] == 255 || rxbuffer[1] == 'q') { // If any more , process it. if (Len == 3) return; Len -= 3; rxbuffer+= 3; ProcessKPacket(conn, rxbuffer, Len); return; } } conn->MODE = 1; return; } while (Len > 0) { Char = *(rxbuffer++); Len--; cmdlen++; // if (conn->TermPtr > 120) conn->TermPtr = 120; // Prevent overflow if (conn->ECHOFLAG) BPQSerialSendData(conn, &Char, 1); if (Char == 0x0d) { // We have a command line *(rxbuffer-1) = 0; ProcessKNormCommand(conn, cmdStart); conn->RXBPtr -= cmdlen; cmdlen = 0; cmdStart = rxbuffer; } } } VOID ProcessKNormCommand(struct TNCDATA * conn, UCHAR * rxbuffer) { // UCHAR CmdReply[]="C00"; UCHAR ResetReply[] = "\xC0\xC0S00\xC0"; int Len; char seps[] = " \t\r"; char * Command, * Arg1, * Arg2; char * Context; if (conn->Channels[1]->Connected) { Len = (int)strlen(rxbuffer); rxbuffer[Len] = 0x0d; SendMsg(conn->Channels[1]->BPQStream, rxbuffer, Len+1); return; } Command = strtok_s(rxbuffer, seps, &Context); Arg1 = strtok_s(NULL, seps, &Context); Arg2 = strtok_s(NULL, seps, &Context); if (Command == NULL) { BPQSerialSendData(conn, "cmd:", 4); return; } if (_stricmp(Command, "RESET") == 0) { if (conn->nextMode) BPQSerialSendData(conn, ResetReply, 6); else BPQSerialSendData(conn, "cmd:", 4); conn->MODE = conn->nextMode; if (conn->MODE) EnableAppl(conn); else DisableAppl(conn); return; } if (_stricmp(Command, "K") == 0) { int SaveAuthProg = AuthorisedProgram; AuthorisedProgram = 1; SessionControl(conn->Channels[1]->BPQStream, 1, 0); AuthorisedProgram = SaveAuthProg; return; } if (_memicmp(Command, "IN", 2) == 0) { if (Arg1) { if (_stricmp(Arg1, "HOST") == 0) conn->nextMode = TRUE; else conn->nextMode = FALSE; } BPQSerialSendData(conn, "INTFACE was TERMINAL\r", 21); BPQSerialSendData(conn, "cmd:", 4); return; } //cmd: //INTFACE HOST //INTFACE was TERMINAL //cmd:RESET //ÀÀS00À //ÀC20XFLOW OFFÀ //SendKISSData(conn, CmdReply, 3); BPQSerialSendData(conn, "cmd:", 4); // Process Non-Hostmode Packet return; } int FreeBytes = 999; static VOID ProcessKHOSTPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Len) { struct StreamInfo * channel; UCHAR Command[80]; UCHAR Reply[400]; UCHAR TXBuff[400]; UCHAR CmdReply[]="C00"; UCHAR Chan, Stream; int i, j, TXLen, StreamNo; char * Cmd, * Arg1, * Arg2, * Arg3; char * Context; char seps[] = " \t\r\xC0"; int CmdLen; if ((Len == 1) && ((rxbuffer[0] == 'q') || (rxbuffer[0] == 'Q'))) { // Force Back to Command Mode Sleep(3000); conn->MODE = FALSE; BPQSerialSendData(conn, "\r\r\rcmd:", 7); return; } if (rxbuffer[0] == '?') { // what is this ??? memcpy(Reply,CmdReply,3); SendKISSData(conn, Reply, 3); return; } Chan = rxbuffer[1]; Stream = rxbuffer[2]; StreamNo = Stream == '0' ? 0 : Stream - '@'; if (StreamNo > conn->HOSTSTREAMS) { SendKISSData(conn, "C00Invalid Stream", 17); return; } switch (rxbuffer[0]) { case 'C': // Command Packet. Extract Command if (Len > 80) Len = 80; memcpy(Command, &rxbuffer[3], Len-3); Command[Len-3] = 0; Cmd = strtok_s(Command, seps, &Context); Arg1 = strtok_s(NULL, seps, &Context); Arg2 = strtok_s(NULL, seps, &Context); Arg3 = strtok_s(NULL, seps, &Context); CmdLen = (int)strlen(Cmd); if (_stricmp(Cmd, "S") == 0) { // Status FreeBytes = 2000; // Ideally I should do flow control by channel, but Paclink (at least) doesn't have a mechanism for (i = 1; i < conn->HOSTSTREAMS; i++) { if (conn->Channels[i]->Connected) if (TXCount(conn->Channels[1]->BPQStream) > 10) FreeBytes = 0; } // This format works with Paclink and RMS Packet, but it doesn't seem to conform to the spec // I think maybe the Channel status should be in the same Frame. TXLen = sprintf(Reply, "C00FREE BYTES %d\r", FreeBytes); SendKISSData(conn, Reply, TXLen); for (j=1; j <= conn->HOSTSTREAMS; j++) { channel = conn->Channels[j]; if (channel->Connected) { TXLen = sprintf(Reply, "C00%c/V stream - CONNECTED to %s", j + '@', "SWITCH"); SendKISSData(conn, Reply, TXLen); } // else // TXLen = sprintf(Reply, "C00%c/V stream - DISCONNECTED", j + '@'); } return; } if (_memicmp(Cmd, "C", CmdLen) == 0) { int Port; struct StreamInfo * channel; int BPQStream; UCHAR * MYCall; // Connect. If command has a via string and first call is numeric use it as a port number if (StreamNo == 0) { Stream = 'A'; StreamNo = 1; } if (Arg2 && Arg3) { if (_memicmp(Arg2, "via", (int)strlen(Arg2)) == 0) { // Have a via string as 2nd param Port = atoi(Arg3); { if (Port > 0) // First Call is numeric { if (strlen(Context) > 0) // More Digis TXLen = sprintf(TXBuff, "c %s %s v %s\r", Arg3, Arg1, Context); else TXLen = sprintf(TXBuff, "c %s %s\r", Arg3, Arg1); } else { // First Call Isn't Numeric. This won't work, as Digis without a port is invalid SendKISSData(conn, "C00Invalid via String (First must be Port)", 42); return; } } } else TXLen = sprintf(TXBuff, "%s %s %s %s %s\r", Cmd, Arg1, Arg2, Arg3, Context); } else { TXLen = sprintf(TXBuff, "C %s\r", Arg1); } Reply[0] = 'C'; Reply[1] = Chan; Reply[2] = Stream; SendKISSData(conn, Reply, 3); channel = conn->Channels[StreamNo]; BPQStream = channel->BPQStream; MYCall = (UCHAR *)&channel->MYCall; Connect(BPQStream); if (MYCall[0] > 0) { ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); } SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); return; } if (_stricmp(Cmd, "D") == 0) { // Disconnect if (StreamNo == 0) { Stream = 'A'; StreamNo = 1; } SessionControl(conn->Channels[StreamNo]->BPQStream, 2, 0); return; } if (_memicmp(Cmd, "INT", 3) == 0) { SendKISSData(conn, "C00INTFACE HOST", 15); return; } if (_stricmp(Cmd, "PACLEN") == 0) { SendKISSData(conn, "C00PACLEN 128/128", 17); return; } if (_memicmp(Cmd, "MYCALL", CmdLen > 1 ? CmdLen : 2) == 0) { if (strlen(Arg1) < 30) strcpy(conn->Channels[StreamNo]->MYCall, Arg1); } memcpy(Reply,CmdReply,3); SendKISSData(conn, Reply, 3); return; case 'D': // Data to send if (StreamNo > conn->HOSTSTREAMS) return; // Protect TXLen = KissDecode(&rxbuffer[3], TXBuff, Len-3); SendMsg(conn->Channels[StreamNo]->BPQStream, TXBuff, TXLen); conn->Channels[StreamNo]->CloseTimer = 0; // Cancel Timer return; default: memcpy(Reply,CmdReply,3); SendKISSData(conn, Reply, 3); return; } } static int KissDecode(UCHAR * inbuff, UCHAR * outbuff, int len) { int i,txptr=0; UCHAR c; for (i=0;iBPQStream, ConnectingCall); strlop(ConnectingCall, ' '); if (conn->MODE) { Len = sprintf (Msg, "S1%c*** CONNECTED to %s ", Stream + '@', ConnectingCall); SendKISSData(conn, Msg, Len); } else { Len = sprintf (Msg, "*** CONNECTED to %s\r", ConnectingCall); BPQSerialSendData(conn, Msg, Len); BPQSerialSetDCD(conn->hDevice); } channel->Connected = TRUE; return 0; } int KANTDisconnected (struct TNCDATA * conn, struct StreamInfo * channel, int Stream) { UCHAR Msg[50]; int Len; if (conn->MODE) { Len = sprintf (Msg, "S1%c*** DISCONNECTED", Stream + '@'); SendKISSData(conn, Msg, Len); } else { BPQSerialSendData(conn, "*** DISCONNECTED\r", 17); BPQSerialClrDCD(conn->hDevice); } channel->Connected = FALSE; channel->CloseTimer = 0; return 0; } // SCS Mode Stuff unsigned short int compute_crc(unsigned char *buf,int len); VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len); int APIENTRY ChangeSessionPaclen(int Stream, int Paclen); int EmUnstuff(UCHAR * MsgIn, UCHAR * MsgOut, int len) { int i, j=0; for (i=0; iOutgoingCall[0]) strcpy(ConnectedCall, Channel->OutgoingCall); else GetCallsign(BPQStream, ConnectedCall); SCSReply[2] = HostStream; SCSReply[3] = 3; ReplyLen = sprintf(&SCSReply[4], "(%d) CONNECTED to %s", Channel->BPQStream, ConnectedCall); ReplyLen += 5; EmCRCStuffAndSend(conn, SCSReply, ReplyLen); Channel->Connected = TRUE; return TRUE; } // Disconnected SCSReply[2] = HostStream; SCSReply[3] = 3; ReplyLen = sprintf(&SCSReply[4], "(%d) DISCONNECTED fm G8BPQ", Channel->BPQStream); ReplyLen += 5; // Include Null EmCRCStuffAndSend(conn, SCSReply, ReplyLen); Channel->Connected = FALSE; return TRUE; } return FALSE; } BOOL SCSCheckForData(struct TNCDATA * conn, struct StreamInfo * Channel, int HostStream, int BPQStream) { int Length, Count; GetMsg(BPQStream, &SCSReply[5], &Length, &Count); if (Length == 0) return FALSE; if (strstr(&SCSReply[5], "} Downlink connect needs port number") || strstr(&SCSReply[5], "} Failure with ") || strstr(&SCSReply[5], "} Sorry, ")) Channel->CloseTimer = CloseDelay * 10; else Channel->CloseTimer = 0; // Cancel Timer SCSReply[2] = HostStream; SCSReply[3] = 7; SCSReply[4] = Length - 1; ReplyLen = Length + 5; EmCRCStuffAndSend(conn, SCSReply, ReplyLen); return TRUE; } VOID ProcessSCSHostFrame(struct TNCDATA * conn, UCHAR * Buffer, int Length) { int Channel = Buffer[0]; int Command = Buffer[1] & 0x3f; int Len = Buffer[2]; struct StreamInfo * channel; UCHAR TXBuff[400]; int BPQStream; char * MYCall; UCHAR Stream; int TXLen, i; BPQVECSTRUC * HOST; // SCS Channel 31 is the Pactor channel, mapped to the first stream if (Channel == 0) Stream = -1; else if (Channel == 31) Stream = 0; else Stream = Channel; channel = conn->Channels[Stream]; if (conn->Toggle == (Buffer[1] & 0x80) && (Buffer[1] & 0x40) == 0) { // Repeat Condition //EmCRCStuffAndSend(conn, SCSReply, ReplyLen); //return; } conn->Toggle = (Buffer[1] & 0x80); conn->Toggle ^= 0x80; // if (Channel == 255 && Len == 0) if (Channel == 255) { UCHAR * NextChan = &SCSReply[4]; // General Poll // See if any channels have anything avaiilable for (i = 1; i <= conn->HOSTSTREAMS; i++) { channel = conn->Channels[i]; HOST = &BPQHOSTVECTOR[channel->BPQStream - 1]; // API counts from 1 if ((HOST->HOSTFLAGS & 3)) { *(NextChan++) = i + 1; // Something for this channel continue; } if (RXCount(channel->BPQStream)) *(NextChan++) = i + 1; // Something for this channel } *(NextChan++) = 0; SCSReply[2] = 255; SCSReply[3] = 1; ReplyLen = (int)(NextChan - &SCSReply[0]); EmCRCStuffAndSend(conn, SCSReply, ReplyLen); return; } if (Channel == 254) // Status { // Extended Status Poll //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]; SCSReply[2] = 254; SCSReply[3] = 7; // Status SCSReply[4] = 3; // Len -1 if (conn->Channels[0]->Connected) { SCSReply[5] = 0x0AA; SCSReply[6] = 3; SCSReply[7] = 5; SCSReply[8] = 128; } else { SCSReply[5] = 0; SCSReply[6] = 0; SCSReply[7] = 0; SCSReply[8] = 0; } ReplyLen = 9; EmCRCStuffAndSend(conn, SCSReply, 9); return; } if (Command == 0) { // Data Frame SendMsg(channel->BPQStream, &Buffer[3], Buffer[2]+ 1); goto AckIt; } switch (Buffer[3]) { case 'J': // JHOST conn->MODE = FALSE; DisableAppl(conn); return; case 'G': // Specific Poll if (CheckStatusChange(conn, channel, Channel, channel->BPQStream)) return; // It has sent reply if (SCSCheckForData(conn, channel, Channel, channel->BPQStream)) return; // It has sent reply SCSReply[2] = Channel; SCSReply[3] = 0; ReplyLen = 4; EmCRCStuffAndSend(conn, SCSReply, 4); return; case 'C': // Connect // Could be real, or just C to request status if (Channel == 0) goto AckIt; if (Length == 0) { // STATUS REQUEST - IF CONNECTED, GET CALL return; } Buffer[Length - 2] = 0; // Save call for connected report if (Buffer[5] = '%' ) // Pactor long path? { TXLen = sprintf(TXBuff, "C %s\r", &Buffer[6]); strcpy(channel->OutgoingCall, &Buffer[6]); } else { TXLen = sprintf(TXBuff, "C %s\r", &Buffer[5]); strcpy(channel->OutgoingCall, &Buffer[5]); } BPQStream = channel->BPQStream; MYCall = &channel->MYCall[0]; if (MYCall[0] == 0) MYCall = (char *)&conn->MYCALL; Connect(BPQStream); if (MYCall[0] > 0) { ChangeSessionCallsign(BPQStream, EncodeCall(MYCall)); } ChangeSessionPaclen(BPQStream, 100); SendMsg(BPQStream, TXBuff, TXLen); AckIt: SCSReply[2] = Channel; SCSReply[3] = 0; ReplyLen = 4; EmCRCStuffAndSend(conn, SCSReply, 4); return; case 'D': // Disconnect Disconnect(channel->BPQStream); goto AckIt; case '%': // %X commands switch (Buffer[4]) { case 'V': // Version SCSReply[2] = Channel; SCSReply[3] = 1; strcpy(&SCSReply[4], "4.8 1.32"); ReplyLen = 13; EmCRCStuffAndSend(conn, SCSReply, 13); return; case 'M': default: SCSReply[2] = Channel; SCSReply[3] = 1; SCSReply[4] = 0; ReplyLen = 5; EmCRCStuffAndSend(conn, SCSReply, 5); return; } case '@': default: SCSReply[2] = Channel; SCSReply[3] = 1; SCSReply[4] = 0; ReplyLen = 5; EmCRCStuffAndSend(conn, SCSReply, 5); } } VOID ProcessSCSTextCommand(struct TNCDATA * conn, char * Command, int Len) { // Command to SCS in non-Host mode. // We can probably just dump anything but JHOST 4 and MYCALL if (Len == 1) goto SendPrompt; // Just a CR Debugprintf("%s", Command); if (_memicmp(Command, "JHOST4", 6) == 0) { conn->MODE = TRUE; conn->Toggle = 0; EnableAppl(conn); return; } if (_memicmp(Command, "TERM 4", 6) == 0) conn->Term4Mode = TRUE; else if (_memicmp(Command, "T 0", 3) == 0) conn->Term4Mode = FALSE; else if (_memicmp(Command, "PAC 4", 5) == 0) conn->PACMode = TRUE; if (_memicmp(Command, "MYC", 3) == 0) { char * ptr = strchr(Command, ' '); char MYResp[80]; Command[Len-1] = 0; // Remove CR if (ptr && (strlen(ptr) > 2)) { strcpy(conn->MYCALL, ++ptr); } sprintf(MYResp, "\rMycall: >%s<", conn->MYCALL); PUTSTRING(conn, MYResp); } else if (_memicmp(Command, "SYS SERN", 8) == 0) { char SerialNo[] = "\r\nSerial number: 0100000000000000"; PUTSTRING(conn, SerialNo); } else { char SerialNo[] = "\rXXXX"; PUTSTRING(conn, SerialNo); } SendPrompt: if (conn->PACMode) { PUTCHARx(conn, 13); PUTCHARx(conn, 'p'); PUTCHARx(conn, 'a'); PUTCHARx(conn, 'c'); PUTCHARx(conn, ':'); PUTCHARx(conn, ' '); return; } if (conn->Term4Mode) { PUTCHARx(conn, 13); PUTCHARx(conn, 4); PUTCHARx(conn, 13); PUTCHARx(conn, 'c'); PUTCHARx(conn, 'm'); PUTCHARx(conn, 'd'); PUTCHARx(conn, ':'); PUTCHARx(conn, ' '); PUTCHARx(conn, 1); } else { PUTCHARx(conn, 13); PUTCHARx(conn, 'c'); PUTCHARx(conn, 'm'); PUTCHARx(conn, 'd'); PUTCHARx(conn, ':'); PUTCHARx(conn, ' '); } /* if (conn->Term4Mode) PUTCHARx(conn, 4); PUTCHARx(conn, 13); PUTCHARx(conn, 'c'); PUTCHARx(conn, 'm'); PUTCHARx(conn, 'd'); PUTCHARx(conn, ':'); PUTCHARx(conn, ' '); */ return; } VOID ProcessSCSPacket(struct TNCDATA * conn, UCHAR * rxbuffer, int Length) { unsigned short crc; char UnstuffBuffer[500]; // 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 Loop: if (rxbuffer[0] != 170) { UCHAR *ptr; int cmdlen; // Char Mode Frame I think we need to see CR on end (and we could have more than one in buffer // If we think we are in host mode, then to could be noise - just discard. if (conn->MODE) { conn->FROMUSERLEN = 0; return; } rxbuffer[Length] = 0; if (rxbuffer[0] == 0) { // Just ignore conn->FROMUSERLEN--; if (conn->FROMUSERLEN) { memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); Length--; goto Loop; } return; } if (rxbuffer[0] == 0x1b) { // Just ignore (I think!) conn->FROMUSERLEN--; if (conn->FROMUSERLEN) { memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); Length--; goto Loop; } return; } if (rxbuffer[0] == 0x1e) { // Status POLL conn->FROMUSERLEN--; if (conn->FROMUSERLEN) { memmove(rxbuffer, rxbuffer+1, conn->FROMUSERLEN + 1); Length--; goto Loop; } PUTCHARx(conn, 30); PUTCHARx(conn, 0x87); if (conn->Term4Mode) { PUTCHARx(conn, 13); PUTCHARx(conn, 4); PUTCHARx(conn, 13); PUTCHARx(conn, 'c'); PUTCHARx(conn, 'm'); PUTCHARx(conn, 'd'); PUTCHARx(conn, ':'); PUTCHARx(conn, ' '); PUTCHARx(conn, 1); } else { PUTCHARx(conn, 13); PUTCHARx(conn, 'c'); PUTCHARx(conn, 'm'); PUTCHARx(conn, 'd'); PUTCHARx(conn, ':'); PUTCHARx(conn, ' '); } return; } ptr = strchr(rxbuffer, 13); if (ptr == 0) return; // Wait for rest of frame ptr++; cmdlen = (int)(ptr - rxbuffer); // Complete Char Mode Frame conn->FROMUSERLEN -= cmdlen; // Ready for next frame ProcessSCSTextCommand(conn, rxbuffer, cmdlen); if (conn->FROMUSERLEN) { memmove(rxbuffer, ptr, conn->FROMUSERLEN + 1); if (conn->Mode) { // now in host mode, so pass rest up a level ProcessSCSPacket(conn, conn->FROMUSERBUFFER, conn->FROMUSERLEN); return; } goto Loop; } return; } // Receiving a Host Mode frame if (Length < 6) // Minimum Frame Sise return; if (rxbuffer[2] == 170) { // Retransmit Request conn->RXBPtr = 0; return; // Ignore for now } // Can't unstuff into same buffer - fails if partial msg received, and we unstuff twice Length = EmUnstuff(&rxbuffer[2], &UnstuffBuffer[2], Length - 2); if (Length == -1) { // Unstuff returned an errors (170 not followed by 0) conn->FROMUSERLEN = 0; return; // Ignore for now } crc = compute_crc(&UnstuffBuffer[2], Length); if (crc == 0xf0b8) // Good CRC { conn->FROMUSERLEN = 0; // Ready for next frame ProcessSCSHostFrame(conn, &UnstuffBuffer[2], Length); return; } // Bad CRC - assume incomplete frame, and wait for rest. If it was a full bad frame, timeout and retry will recover link. return; } VOID EmCRCStuffAndSend(struct TNCDATA * conn, UCHAR * Msg, int Len) { unsigned short int crc; UCHAR StuffedMsg[500]; int i, j; 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); } Msg[0] = 170; Msg[1] = 170; BPQSerialSendData(conn, Msg, Len); }