linbpq/TNCEmulators.c

6307 lines
129 KiB
C
Raw Blame History

/*
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 <util.h>
#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, &param);
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
//<2F><>S00<30>
//<2F>C20XFLOW OFF<46>
//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;i<len;i++)
{
c=inbuff[i];
if (c == FESC)
{
c=inbuff[++i];
{
if (c == TFESC)
c=FESC;
else
if (c == TFEND)
c=FEND;
}
}
outbuff[txptr++]=c;
}
return txptr;
}
VOID SendKISSData(struct TNCDATA * conn, UCHAR * txbuffer, int Len)
{
// Send A Packet With KISS Encoding
UCHAR EncodedReply[800];
int TXLen;
TXLen = KissEncode(txbuffer, EncodedReply, Len);
BPQSerialSendData(conn, EncodedReply, TXLen);
}
static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
int i, txptr = 0;
UCHAR c;
outbuff[txptr++] = FEND;
for (i=0;i<len;i++)
{
c=inbuff[i];
switch (c)
{
case FEND:
outbuff[txptr++] = FESC;
outbuff[txptr++] = TFEND;
break;
case FESC:
outbuff[txptr++] = FESC;
outbuff[txptr++] = TFESC;
break;
default:
outbuff[txptr++] = c;
}
}
outbuff[txptr++] = FEND;
return txptr;
}
int KANTConnected(struct TNCDATA * conn, struct StreamInfo * channel, int Stream)
{
byte ConnectingCall[10];
UCHAR Msg[50];
int Len;
GetCallsign(channel->BPQStream, 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; i<len; i++, j++)
{
MsgOut[j] = MsgIn[i];
if (MsgIn[i] == 170) // Remove next byte
{
i++;
if (MsgIn[i] != 0)
if (i != len) return -1;
}
}
return j;
}
UCHAR SCSReply[400];
int ReplyLen;
BOOL CheckStatusChange(struct TNCDATA * conn, struct StreamInfo * Channel, int HostStream, int BPQStream)
{
int state, change;
SessionState(BPQStream, &state, &change);
if (change == 1)
{
if (state == 1)
{
// Connected
char ConnectedCall[16];
if (Channel->OutgoingCall[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);
}