2022-08-28 09:35:46 +01:00
|
|
|
|
/*
|
2022-11-14 14:02:28 +00:00
|
|
|
|
Copyright 2001-2022 John Wiseman G8BPQ
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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"
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
#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);
|
2023-06-21 08:21:04 +01:00
|
|
|
|
VOID DOMONITORING(int NeedTrace, struct TNCDATA * TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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);
|
2023-06-21 08:21:04 +01:00
|
|
|
|
int SendHostOK(struct TNCDATA * TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
extern struct TNCDATA * TNCCONFIGTABLE;
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
struct TNCDATA * TNC2TABLE = NULL; // malloc'ed
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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
|
|
|
|
|
|
2023-04-06 13:41:09 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
#ifndef WIN32
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2023-04-06 13:41:09 +01:00
|
|
|
|
#ifdef MACBPQ
|
|
|
|
|
#include <util.h>
|
|
|
|
|
#endif
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
extern int posix_openpt (int __oflag);
|
|
|
|
|
extern int grantpt (int __fd);
|
|
|
|
|
extern int unlockpt (int __fd);
|
|
|
|
|
extern char *ptsname (int __fd);
|
|
|
|
|
extern int ptsname_r (int __fd, char *__buf, size_t __buflen);
|
|
|
|
|
extern int getpt (void);
|
|
|
|
|
|
|
|
|
|
HANDLE LinuxOpenPTY(char * Name)
|
|
|
|
|
{
|
|
|
|
|
// Open a Virtual COM Port
|
|
|
|
|
|
|
|
|
|
HANDLE hDevice, slave;;
|
|
|
|
|
char slavedevice[80];
|
|
|
|
|
int ret;
|
|
|
|
|
u_long param=1;
|
|
|
|
|
struct termios term;
|
|
|
|
|
|
|
|
|
|
#ifdef MACBPQ
|
|
|
|
|
|
|
|
|
|
// Create a pty pair
|
|
|
|
|
|
|
|
|
|
openpty(&hDevice, &slave, &slavedevice[0], NULL, NULL);
|
|
|
|
|
close(slave);
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
hDevice = posix_openpt(O_RDWR|O_NOCTTY);
|
|
|
|
|
|
|
|
|
|
if (hDevice == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("posix_openpt Create PTY pair failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (grantpt (hDevice) == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("grantpt Create PTY pair failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (unlockpt (hDevice) == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("unlockpt Create PTY pair failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
if (ptsname_r(hDevice, slavedevice, 80) != 0)
|
|
|
|
|
{
|
|
|
|
|
perror("ptsname_r Create PTY pair failed");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
printf("slave device: %s. ", slavedevice);
|
|
|
|
|
|
|
|
|
|
if (tcgetattr(hDevice, &term) == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("tty_speed: tcgetattr");
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cfmakeraw(&term);
|
|
|
|
|
|
|
|
|
|
if (tcsetattr(hDevice, TCSANOW, &term) == -1)
|
|
|
|
|
{
|
|
|
|
|
perror("tcsetattr");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ioctl(hDevice, FIONBIO, ¶m);
|
|
|
|
|
|
|
|
|
|
chmod(slavedevice, S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP|S_IROTH|S_IWOTH);
|
|
|
|
|
|
|
|
|
|
unlink (Name);
|
|
|
|
|
|
|
|
|
|
ret = symlink (slavedevice, Name);
|
|
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
|
printf ("symlink to %s created\n", Name);
|
|
|
|
|
else
|
|
|
|
|
printf ("symlink to %s failed\n", Name);
|
|
|
|
|
|
|
|
|
|
return hDevice;
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
HANDLE BPQOpenSerialPort(struct TNCDATA * TNC, DWORD * lasterror)
|
|
|
|
|
{
|
|
|
|
|
// Open a Virtual COM Port
|
|
|
|
|
|
|
|
|
|
int port = TNC->ComPort;
|
|
|
|
|
char szPort[80];
|
|
|
|
|
HANDLE hDevice;
|
|
|
|
|
int Err;
|
|
|
|
|
|
|
|
|
|
*lasterror=0;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
{
|
|
|
|
|
sprintf( szPort, "\\\\.\\COM%d",port) ;
|
|
|
|
|
|
|
|
|
|
hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
|
|
|
|
|
0, // exclusive access
|
|
|
|
|
NULL, // no security attrs
|
|
|
|
|
OPEN_EXISTING,
|
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
NULL );
|
|
|
|
|
|
|
|
|
|
if (hDevice == (HANDLE) -1 )
|
|
|
|
|
{
|
|
|
|
|
// If In Use(5) ok, else fail
|
|
|
|
|
|
|
|
|
|
if (GetLastError() == 5)
|
|
|
|
|
return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device
|
|
|
|
|
|
|
|
|
|
return (HANDLE)(ptrdiff_t) - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
|
|
|
|
|
|
return (HANDLE)(ptrdiff_t)(port<<16); // Port Number is a pseudohandle to the device
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Try New Style VCOM firsrt
|
|
|
|
|
|
|
|
|
|
sprintf( szPort, "\\\\.\\pipe\\BPQCOM%d", port ) ;
|
|
|
|
|
|
|
|
|
|
hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
|
|
|
|
|
0, // exclusive access
|
|
|
|
|
NULL, // no security attrs
|
|
|
|
|
OPEN_EXISTING,
|
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
NULL );
|
|
|
|
|
|
|
|
|
|
Err = GetLastError();
|
|
|
|
|
|
|
|
|
|
if (hDevice != (HANDLE) -1)
|
|
|
|
|
{
|
|
|
|
|
TNC->NewVCOM = TRUE;
|
|
|
|
|
TNC->PortEnabled = TRUE;
|
|
|
|
|
Err = GetFileType(hDevice);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
// Try old style
|
|
|
|
|
|
|
|
|
|
sprintf(szPort, "\\\\.\\BPQ%d", port ) ;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hDevice = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
|
|
|
|
|
0, // exclusive access
|
|
|
|
|
NULL, // no security attrs
|
|
|
|
|
OPEN_EXISTING,
|
|
|
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
|
|
|
NULL );
|
|
|
|
|
|
|
|
|
|
if (TNC->PollDelay)
|
|
|
|
|
BPQSerialSetPollDelay(hDevice, TNC->PollDelay);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (hDevice == (HANDLE) -1 )
|
|
|
|
|
{
|
|
|
|
|
*lasterror=GetLastError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hDevice;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
int BPQSerialSetCTS(HANDLE hDevice)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl,(DWORD)hDevice | W98_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_CTS,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialSetDSR(HANDLE hDevice)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DSR,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DSR, NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialSetDCD(HANDLE hDevice)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_SET_DCD,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialClrCTS(HANDLE hDevice)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_CTS,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
int BPQSerialClrDSR(HANDLE hDevice)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DSR,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialClrDCD(HANDLE hDevice)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_CLR_DCD,NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_CLR_DCD, NULL,0,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int SendDataToTNC(struct TNCDATA * TNC, UCHAR * TXMsg, int n)
|
|
|
|
|
{
|
|
|
|
|
// Used for all port types
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
|
|
// WIN32 VCOM Used one of my Drivers
|
|
|
|
|
|
|
|
|
|
if (TNC->VCOM)
|
|
|
|
|
BPQSerialSendData(TNC, TXMsg, n);
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// Linux VCOM uses SOCAT Pairs and normal write
|
|
|
|
|
|
|
|
|
|
return WriteCOMBlock(TNC->hDevice, TXMsg, n);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialSendData(struct TNCDATA * TNC, UCHAR * Message,int MsgLen)
|
|
|
|
|
{
|
|
|
|
|
HANDLE hDevice = TNC->hDevice;
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
// Host Mode code calls BPQSerialSendData for all ports, so it a real port, pass on to real send routine
|
|
|
|
|
|
|
|
|
|
if (!TNC->VCOM)
|
|
|
|
|
return WriteCOMBlock(TNC->hDevice, Message, MsgLen);
|
|
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
|
|
|
|
|
// Linux usies normal IO for all ports
|
|
|
|
|
return WriteCOMBlock(TNC->hDevice, Message, MsgLen);
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
if (MsgLen > 4096 ) return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (TNC->NewVCOM)
|
|
|
|
|
{
|
|
|
|
|
// Have to escape all oxff chars, as these are used to get status info
|
|
|
|
|
|
|
|
|
|
UCHAR NewMessage[1000];
|
|
|
|
|
UCHAR * ptr1 = Message;
|
|
|
|
|
UCHAR * ptr2 = NewMessage;
|
|
|
|
|
UCHAR c;
|
|
|
|
|
|
|
|
|
|
int Length = MsgLen;
|
|
|
|
|
|
|
|
|
|
while (Length != 0)
|
|
|
|
|
{
|
|
|
|
|
c = *(ptr1++);
|
|
|
|
|
*(ptr2++) = c;
|
|
|
|
|
|
|
|
|
|
if (c == 0xff)
|
|
|
|
|
{
|
|
|
|
|
*(ptr2++) = c;
|
|
|
|
|
MsgLen++;
|
|
|
|
|
}
|
|
|
|
|
Length--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return WriteFile(hDevice, NewMessage, MsgLen, &bytesReturned, NULL);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_SETDATA,Message,MsgLen,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error);
|
|
|
|
|
|
|
|
|
|
int GetDataFromTNC(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen)
|
|
|
|
|
{
|
|
|
|
|
// Used for all port types
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
|
|
|
|
// WIN32 VCOM Used one of my Drivers
|
|
|
|
|
|
|
|
|
|
if (TNC->VCOM)
|
|
|
|
|
return BPQSerialGetData(TNC, Message, BufLen, MsgLen);
|
|
|
|
|
|
|
|
|
|
*MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
int Error = 0;
|
|
|
|
|
|
|
|
|
|
if (TNC->VCOM == 0)
|
|
|
|
|
{
|
|
|
|
|
*MsgLen = ReadCOMBlock(TNC->hDevice, Message, BufLen);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Linux VCOM uses SOCAT Pairs and normal Read
|
|
|
|
|
|
|
|
|
|
// If the slave closes connection read returns 5. Need to trap and
|
|
|
|
|
// close/reopen. So use ReadCOMBlockEx
|
|
|
|
|
|
|
|
|
|
*MsgLen = ReadCOMBlockEx(TNC->hDevice, Message, BufLen, &Error);
|
|
|
|
|
|
|
|
|
|
if (Error == 5)
|
|
|
|
|
{
|
|
|
|
|
printf("Read error on TNCPORT %s - Restarting\n", TNC->PORTNAME);
|
|
|
|
|
close(TNC->hDevice);
|
|
|
|
|
TNC->hDevice = LinuxOpenPTY(TNC->PORTNAME);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int BPQSerialGetData(struct TNCDATA * TNC, UCHAR * Message, unsigned int BufLen, ULONG * MsgLen)
|
|
|
|
|
{
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
DWORD dwLength = 0;
|
|
|
|
|
DWORD Available = 0;
|
|
|
|
|
HANDLE hDevice = TNC->hDevice;
|
|
|
|
|
int Length, RealLen = 0;
|
|
|
|
|
|
|
|
|
|
if (BufLen > 4096 ) return ERROR_INVALID_PARAMETER;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL);
|
|
|
|
|
|
|
|
|
|
if (TNC->NewVCOM)
|
|
|
|
|
{
|
|
|
|
|
int ret = PeekNamedPipe(hDevice, NULL, 0, NULL, &Available, NULL);
|
|
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
|
{
|
|
|
|
|
ret = GetLastError();
|
|
|
|
|
|
|
|
|
|
if (ret == ERROR_BROKEN_PIPE)
|
|
|
|
|
{
|
|
|
|
|
CloseHandle(hDevice);
|
|
|
|
|
hDevice = INVALID_HANDLE_VALUE;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (Available > BufLen)
|
|
|
|
|
Available = BufLen;
|
|
|
|
|
|
|
|
|
|
if (Available)
|
|
|
|
|
{
|
|
|
|
|
UCHAR * ptr1 = Message;
|
|
|
|
|
UCHAR * ptr2 = Message;
|
|
|
|
|
UCHAR c;
|
|
|
|
|
|
|
|
|
|
ReadFile(hDevice, Message, Available, &Length, NULL);
|
|
|
|
|
|
|
|
|
|
// Have to look for FF escape chars
|
|
|
|
|
|
|
|
|
|
RealLen = Length;
|
|
|
|
|
|
|
|
|
|
while (Length != 0)
|
|
|
|
|
{
|
|
|
|
|
c = *(ptr1++);
|
|
|
|
|
Length--;
|
|
|
|
|
|
|
|
|
|
if (c == 0xff)
|
|
|
|
|
{
|
|
|
|
|
c = c = *(ptr1++);
|
|
|
|
|
Length--;
|
|
|
|
|
|
|
|
|
|
if (c == 0xff) // ff ff means ff
|
|
|
|
|
{
|
|
|
|
|
RealLen--;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// This is connection statua from other end
|
|
|
|
|
|
|
|
|
|
RealLen -= 2;
|
|
|
|
|
// TNC->PortEnabled = c;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*(ptr2++) = c;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*MsgLen = RealLen;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_GETDATA,NULL,0,Message,BufLen,MsgLen,NULL);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
int BPQSerialGetQCounts(HANDLE hDevice,ULONG * RXCount, ULONG * TXCount)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
SERIAL_STATUS Resp;
|
|
|
|
|
int MsgLen;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
ret = DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL);
|
|
|
|
|
else
|
|
|
|
|
ret = DeviceIoControl(hDevice,IOCTL_SERIAL_GET_COMMSTATUS,NULL,0,&Resp,sizeof(SERIAL_STATUS),&MsgLen,NULL);
|
|
|
|
|
|
|
|
|
|
*RXCount=Resp.AmountInInQueue;
|
|
|
|
|
*TXCount=Resp.AmountInOutQueue;
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialGetDeviceList(HANDLE hDevice,ULONG * Slot,ULONG * Port)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
return DeviceIoControl (hDevice,IOCTL_BPQ_LIST_DEVICES,Slot,4,Port,4,&bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialIsCOMOpen(HANDLE hDevice,ULONG * Count)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_IS_COM_OPEN,NULL,0,Count,4,&bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialGetDTRRTS(HANDLE hDevice, ULONG * Flags)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_SERIAL_GET_DTRRTS,NULL,0,Flags,4,&bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialSetPollDelay(HANDLE hDevice, int PollDelay)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
if (Win98)
|
|
|
|
|
return DeviceIoControl(hControl, (UINT)hDevice | W98_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
else
|
|
|
|
|
return DeviceIoControl(hDevice,IOCTL_BPQ_SET_POLLDELAY,&PollDelay,4,NULL,0, &bytesReturned,NULL);
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int BPQSerialSetDebugMask(HANDLE hDevice, int DebugMask)
|
|
|
|
|
{
|
|
|
|
|
#ifndef WIN32
|
|
|
|
|
return 0;
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
ULONG bytesReturned;
|
|
|
|
|
|
|
|
|
|
return DeviceIoControl(hDevice, IOCTL_BPQ_SET_DEBUGMASK, &DebugMask, 4, NULL, 0, &bytesReturned,NULL);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CheckForStreamChange(struct TNCDATA * TNC, int ToStream)
|
|
|
|
|
{
|
|
|
|
|
// Send Stream Switched Message if changed
|
|
|
|
|
|
|
|
|
|
char Msg[80];
|
|
|
|
|
int Len;
|
|
|
|
|
|
|
|
|
|
if (ToStream == TNC->RXStream)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TNC->RXStream = ToStream;
|
|
|
|
|
|
|
|
|
|
// Send Message
|
|
|
|
|
|
|
|
|
|
// |B:WA7GXD:
|
|
|
|
|
|
|
|
|
|
if (TNC->StreamCall)
|
|
|
|
|
Len = sprintf(Msg, "%c%c:%s:", TNC->StreamSW, ToStream + 'A', TNC->TNC2Stream[ToStream]->RemoteCall);
|
|
|
|
|
else
|
|
|
|
|
Len = sprintf(Msg, "%c%c", TNC->StreamSW, ToStream + 'A');
|
|
|
|
|
|
|
|
|
|
SENDREPLY(TNC, Msg, Len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int LocalSessionState(int stream, int * state, int * change, BOOL ACK)
|
|
|
|
|
{
|
|
|
|
|
// Get current Session State. Any state changed is ACK'ed
|
|
|
|
|
// automatically. See BPQHOST functions 4 and 5.
|
|
|
|
|
|
|
|
|
|
// Local version without semaphore or checktimer
|
|
|
|
|
|
|
|
|
|
BPQVECSTRUC * HOST = &BPQHOSTVECTOR[stream -1]; // API counts from 1
|
|
|
|
|
|
|
|
|
|
// CX = 0 if stream disconnected or CX = 1 if stream connected
|
|
|
|
|
// DX = 0 if no change of state since last read, or DX = 1 if
|
|
|
|
|
// the connected/disconnected state has changed since
|
|
|
|
|
// last read (ie. delta-stream status).
|
|
|
|
|
|
|
|
|
|
// HOSTFLAGS = Bit 80 = Allocated
|
|
|
|
|
// Bit 40 = Disc Request
|
|
|
|
|
// Bit 20 = Stay Flag
|
|
|
|
|
// Bit 02 and 01 State Change Bits
|
|
|
|
|
|
|
|
|
|
if ((HOST->HOSTFLAGS & 3) == 0)
|
|
|
|
|
// No Chaange
|
|
|
|
|
*change = 0;
|
|
|
|
|
else
|
|
|
|
|
*change = 1;
|
|
|
|
|
|
|
|
|
|
if (HOST->HOSTSESSION) // LOCAL SESSION
|
|
|
|
|
// Connected
|
|
|
|
|
*state = 1;
|
|
|
|
|
else
|
|
|
|
|
*state = 0;
|
|
|
|
|
|
|
|
|
|
if (ACK)
|
|
|
|
|
HOST->HOSTFLAGS &= 0xFC; // Clear Change Bits
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID ONOFF(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID ONOFF_CONOK(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID SETMYCALL(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID CTEXTCMD(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID BTEXT(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID VALUE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID VALHEX(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID APPL_VALHEX(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID CSWITCH(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
char Response[80];
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
len = sprintf(Response, "%s", CMDMSG);
|
|
|
|
|
SENDREPLY(TNC, Response, len);
|
|
|
|
|
|
|
|
|
|
CONNECTTONODE(TNC);
|
|
|
|
|
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID CONMODE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
SENDREPLY(TNC, CMDMSG, 4);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID TNCCONV(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
struct TNC2StreamInfo * TNCStream = TNC->TNC2Stream[TNC->TXStream];
|
|
|
|
|
|
|
|
|
|
TNCStream->MODEFLAG |= CONV;
|
|
|
|
|
TNCStream->MODEFLAG &= ~(COMMAND+TRANS);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID TNCNODE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID CStatus(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID TNCCONNECT(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID TNCDISC(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID TNCRELEASE(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
ReturntoNode(TNC->BPQPort);
|
|
|
|
|
|
|
|
|
|
TNC->VMSR &= 0x7F; // DROP DCD
|
|
|
|
|
TNC->VMSR |= 8; //DELTA DCD
|
|
|
|
|
|
|
|
|
|
SENDREPLY(TNC, CMDMSG, 4);
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
VOID TNCTRANS(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
2024-12-16 17:54:16 +00:00
|
|
|
|
static VOID TNCRESTART(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
static VOID TNCUNPROTOCMD(struct TNCDATA * TNC, char * Tail, TCMDX * CMD)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
TCMDX TNCCOMMANDLIST[] =
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
"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,
|
2023-06-21 08:21:04 +01:00
|
|
|
|
"RESTART ",7,TNCRESTART,0,
|
2022-08-28 09:35:46 +01:00
|
|
|
|
"TRANS ",1,TNCTRANS,0,
|
2023-06-21 08:21:04 +01:00
|
|
|
|
"UNPROTO ",1,TNCUNPROTOCMD,0,
|
2022-08-28 09:35:46 +01:00
|
|
|
|
"USERS ",2,VALUE, offsetof(struct TNCDATA, Users),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
int NUMBEROFTNCCOMMANDS = sizeof(TNCCOMMANDLIST)/sizeof(TCMDX);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
/*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)
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
char * Baud = strlop(TNC->PORTNAME, ':');
|
2022-08-28 09:35:46 +01:00
|
|
|
|
char * PNptr;
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (Baud == 0)
|
|
|
|
|
Baud = strlop(TNC->PORTNAME, ',');
|
|
|
|
|
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2023-06-21 08:21:04 +01:00
|
|
|
|
struct TNCDATA * TTNC = 0; // First TNC2
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
while (TNC)
|
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
if (TNC->Mode != TNC2)
|
|
|
|
|
goto NotTNC2;
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
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
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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];
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
if (channel->CloseTimer)
|
|
|
|
|
{
|
|
|
|
|
channel->CloseTimer--;
|
|
|
|
|
if (channel->CloseTimer == 0)
|
|
|
|
|
Disconnect(channel->BPQStream);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NextTNC:
|
|
|
|
|
TNC = TNC->Next;
|
|
|
|
|
}
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TTNC) // Only if we have TNC2 Streams
|
|
|
|
|
DOMONITORING(NeedTrace, TTNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
#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)
|
2023-10-10 22:07:04 +01:00
|
|
|
|
{
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
VOID DOMONITORING(int NeedTrace, struct TNCDATA * TTNC)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// IF ANY PORTS HAVE MONITOR ENABLED, SET MONITOR BIT ON FIRST PORT
|
|
|
|
|
|
|
|
|
|
int Tracebit = 0, len, count, n;
|
|
|
|
|
time_t Stamp;
|
2023-05-25 14:17:53 +01:00
|
|
|
|
uint64_t SaveMMASK = MMASK;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
BOOL SaveMTX = MTX;
|
|
|
|
|
BOOL SaveMCOM = MCOM;
|
|
|
|
|
BOOL SaveMUI = MUIONLY;
|
2023-06-21 08:21:04 +01:00
|
|
|
|
struct TNCDATA * TNC = TNC2TABLE;
|
|
|
|
|
int BPQStream = TTNC->TNC2Stream[0]->BPQPort;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
if (NeedTrace)
|
|
|
|
|
Tracebit = 0x80;
|
|
|
|
|
|
2023-03-16 06:52:27 +00:00
|
|
|
|
if (BPQStream)
|
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TTNC->CONOK && TTNC->Mode == TNC2)
|
|
|
|
|
SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, TTNC->APPLICATION);
|
2023-03-16 06:52:27 +00:00
|
|
|
|
else
|
2023-06-21 08:21:04 +01:00
|
|
|
|
SetAppl(BPQStream, TTNC->APPLFLAGS | Tracebit, 0);
|
2023-03-16 06:52:27 +00:00
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-03-16 06:52:27 +00:00
|
|
|
|
Stamp = GetRaw(BPQStream, (char *)&MONITORDATA, &len, &count);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
if (len == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
len = DecodeFrame(&MONITORDATA, MONBUFFER, Stamp);
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
while (TNC)
|
|
|
|
|
{
|
|
|
|
|
if (TNC->Mode == TNC2 && TNC->TRACEFLAG)
|
|
|
|
|
{
|
2023-05-25 14:17:53 +01:00
|
|
|
|
IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, 0);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
len = IntDecodeFrame(&MONITORDATA, MONBUFFER, Stamp, TNC->MMASK, FALSE, FALSE);
|
|
|
|
|
// printf("%d %d %d %d %d\n", len, MMASK, MTX, MCOM, MUIONLY);
|
2023-05-25 14:17:53 +01:00
|
|
|
|
IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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;
|
2024-12-16 17:54:16 +00:00
|
|
|
|
TCMDX * CMD;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
*(--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
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
CMD = &TNCCOMMANDLIST[0];
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
for (i = 0; i <= TNC->HOSTSTREAMS; i++)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
|
|
|
|
EnableDEDAppl(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
2023-10-10 22:07:04 +01:00
|
|
|
|
unsigned char * mcmdptr = &TNC->DEDTXBUFFER[1];
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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))
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
switch (toupper(TNC->DEDTXBUFFER[0]))
|
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
case 1: // Recovery
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PUTCHARx(TNC, TNC->MSGCHANNEL);
|
|
|
|
|
|
|
|
|
|
for (i=0; i < LBADCMDREPLY; i++)
|
|
|
|
|
{
|
|
|
|
|
PUTCHARx(TNC, BADCMDREPLY[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
case 'G':
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PROCESSPOLL(TNC, Channel);
|
|
|
|
|
return TRUE;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
case 'I':
|
|
|
|
|
goto ICMD;
|
|
|
|
|
|
|
|
|
|
case 'J':
|
|
|
|
|
|
|
|
|
|
TNC->MODE = TNC->DEDTXBUFFER[5] & 1;
|
|
|
|
|
|
|
|
|
|
if (TNC->MODE)
|
2023-06-21 08:21:04 +01:00
|
|
|
|
EnableDEDAppl(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
else
|
|
|
|
|
DisableAppl(TNC);
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
case 'C':
|
|
|
|
|
goto CCMD;
|
|
|
|
|
|
|
|
|
|
case 'D':
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
|
|
|
|
// DISCONNECT REQUEST
|
|
|
|
|
|
|
|
|
|
Disconnect(Channel->BPQStream);
|
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
case 'L':
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
|
|
|
|
// 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;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
case '@':
|
|
|
|
|
goto ATCOMMAND;
|
|
|
|
|
|
|
|
|
|
case 'Y':
|
|
|
|
|
goto YCMD;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
goto ECMD;
|
|
|
|
|
|
|
|
|
|
case 'M':
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
// Support BPQ Extensions IUSC followed by optional port list
|
|
|
|
|
|
2023-10-10 22:07:04 +01:00
|
|
|
|
TNC->DEDTXBUFFER[TNC->MSGLENGTH] = 0;
|
|
|
|
|
|
|
|
|
|
if (*mcmdptr == ' ')
|
|
|
|
|
mcmdptr++;
|
|
|
|
|
|
|
|
|
|
if (mcmdptr[0] == 'N')
|
2023-06-21 08:21:04 +01:00
|
|
|
|
TNC->TRACEFLAG = 0;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
char * ptr, * ptr2;
|
|
|
|
|
int port;
|
|
|
|
|
uint64_t mask = 0;
|
|
|
|
|
|
|
|
|
|
|
2023-10-10 22:07:04 +01:00
|
|
|
|
ptr = strlop(mcmdptr, ' ');
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2023-10-10 22:07:04 +01:00
|
|
|
|
_strupr(mcmdptr);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-10-10 22:07:04 +01:00
|
|
|
|
if (strchr(mcmdptr, 'U'))
|
2023-06-21 08:21:04 +01:00
|
|
|
|
TNC->MUIONLY = 1;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
|
|
|
|
// Ptocess Mask
|
|
|
|
|
|
|
|
|
|
while (ptr && ptr[0])
|
|
|
|
|
{
|
|
|
|
|
ptr2 = strlop(ptr, ',');
|
|
|
|
|
port = atoi(ptr);
|
|
|
|
|
|
|
|
|
|
if (port)
|
|
|
|
|
{
|
|
|
|
|
mask |= ((uint64_t)1 << (port - 1));
|
|
|
|
|
}
|
2023-10-10 22:07:04 +01:00
|
|
|
|
ptr = ptr2;
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mask)
|
|
|
|
|
TNC->MMASK = mask;
|
|
|
|
|
|
|
|
|
|
TNC->TRACEFLAG = 0x80;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetAppl(TNC->Channels[0]->BPQStream, 2 | TNC->TRACEFLAG, TNC->APPLICATION);
|
|
|
|
|
return SendHostOK(TNC);
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
case 'K':
|
|
|
|
|
case 'O':
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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:
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CCMD:
|
|
|
|
|
|
|
|
|
|
// CONNECT REQUEST
|
|
|
|
|
|
|
|
|
|
if (TNC->MSGCHANNEL == 0)
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC); // SETTING UNPROTO ADDR - JUST ACK IT
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
*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
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
// return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
// 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);
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
SendUnproto:
|
|
|
|
|
|
|
|
|
|
SendMsg(0, TXBUFFERPTR, TNC->MSGLENGTH);
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return SendHostOK(TNC);
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
int SendHostOK(struct TNCDATA * TNC)
|
|
|
|
|
{
|
2022-08-28 09:35:46 +01:00
|
|
|
|
PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL
|
|
|
|
|
PUTCHARx(TNC, 0); // NOTHING DOING
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
int PROCESSPOLL(struct TNCDATA * TNC, struct StreamInfo * Channel)
|
|
|
|
|
{
|
|
|
|
|
// DED Mode - ASK SWITCH FOR STATUS CHANGE OR ANY RECEIVED DATA
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TNC->MSGLENGTH == 1) // General Poll
|
|
|
|
|
{
|
|
|
|
|
if (STATUSPOLL(TNC, Channel))
|
|
|
|
|
return TRUE; // Status was reported
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (DATAPOLL(TNC, Channel))
|
|
|
|
|
return TRUE; // Data Sent
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL
|
|
|
|
|
PUTCHARx(TNC, 0); // NOTHING DOING
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
// HE'S BEING AWKWARD, AND USING SPECIFIC DATA/STATUS POLL
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TNC->TONODEBUFFER[1] == '0') // Data only
|
|
|
|
|
{
|
|
|
|
|
if (DATAPOLL(TNC, Channel))
|
|
|
|
|
return TRUE; // Data Sent
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PUTCHARx(TNC, TNC->MSGCHANNEL); // REPLY ON SAME CHANNEL
|
|
|
|
|
PUTCHARx(TNC, 0); // NOTHING DOING
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
// Must be Status only
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (STATUSPOLL(TNC, Channel))
|
|
|
|
|
return TRUE; // Status was reported
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
Decoded[Len] = 0;
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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"))
|
2023-06-21 08:21:04 +01:00
|
|
|
|
return 0; //pid = 0xcf;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
int DOCOMMAND(struct TNCDATA * TNC)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
char Errbuff[500];
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
// PROCESS NORMAL MODE COMMAND
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
Debugprintf(Errbuff, "BPQHOST Port %d Normal Mode CMD %s\n",TNC->ComPort, TNC->TONODEBUFFER);
|
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
// IF ECHO ENABLED, ECHO IT
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TNC->ECHOFLAG)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
UCHAR * ptr = TNC->TONODEBUFFER;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
UCHAR c;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
c = *(ptr++);
|
|
|
|
|
|
|
|
|
|
if (c == 0x1b) c = ':';
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PUTCHARx(TNC, c);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
} while (c != 13);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TNC->TONODEBUFFER[0] != 0x1b)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
goto NOTCOMMAND; // DATA IN NORMAL MODE - IGNORE
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
switch (toupper(TNC->TONODEBUFFER[1]))
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
case 'J':
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TNC->TONODEBUFFER[6] == 0x0d)
|
|
|
|
|
TNC->MODE = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
else
|
2023-06-21 08:21:04 +01:00
|
|
|
|
TNC->MODE = TNC->TONODEBUFFER[6] & 1;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (TNC->MODE)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// send host mode ack
|
|
|
|
|
|
|
|
|
|
// PUTCHARx(conn, 0);
|
|
|
|
|
// PUTCHARx(conn, 0);
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
EnableDEDAppl(TNC);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DisableAppl(TNC);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'E':
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
TNC->ECHOFLAG = TNC->TONODEBUFFER[2] & 1;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'I':
|
|
|
|
|
{
|
|
|
|
|
// Save call
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
char * Call = &TNC->TONODEBUFFER[2];
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
*(TNC->CURSOR - 2) = 0;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
for (i = 0; i <= TNC->HOSTSTREAMS; i++)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
strcpy(TNC->Channels[i]->MYCall, Call);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;;
|
|
|
|
|
}
|
|
|
|
|
case 'P':
|
|
|
|
|
|
|
|
|
|
// PARAMS COMMAND - RETURN FIXED STRING
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PARAMPORT = TNC->TONODEBUFFER[2];
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
for (i=0; i < LPARAMREPLY; i++)
|
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PUTCHARx(TNC, PARAMREPLY[i]);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'S':
|
|
|
|
|
case 'D':
|
|
|
|
|
|
|
|
|
|
// Return Channel Not Connected
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
PUTSTRING(TNC, "* CHANNEL NOT CONNECTED *\r");
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PUTCHARx(conn, 'c');
|
|
|
|
|
// PUTCHARx(conn, 'm');
|
|
|
|
|
// PUTCHARx(conn, 'd');
|
|
|
|
|
// PUTCHARx(conn, ':');
|
|
|
|
|
// PUTCHARx(conn, 13);
|
|
|
|
|
|
|
|
|
|
NOTCOMMAND:
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
TNC->CURSOR = (UCHAR *)&TNC->TONODEBUFFER;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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];
|
2024-12-16 17:54:16 +00:00
|
|
|
|
char ConMsg[64];
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
i = sprintf(ConMsg, "\x3(%d) DISCONNECTED fm 0:SWITCH\r", TNC->MSGCHANNEL);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// GET CALLSIGN
|
|
|
|
|
|
|
|
|
|
GetCallsign(Channel->BPQStream, WorkString);
|
|
|
|
|
strlop(WorkString, ' ');
|
2024-12-16 17:54:16 +00:00
|
|
|
|
i = sprintf(ConMsg, "\x3(%d) CONNECTED to %s\r", TNC->MSGCHANNEL, WorkString);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-16 17:54:16 +00:00
|
|
|
|
SENDCMDREPLY(TNC, ConMsg, i);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
Debugprintf("BPQHOST Mondata Flag Set with no data");
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
|
|
|
|
NOMONITOR:
|
|
|
|
|
|
|
|
|
|
// SEE IF ANYTHING TO MONITOR
|
|
|
|
|
|
|
|
|
|
stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count);
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
while (Len)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
|
|
|
|
// Use Normal Decode, then reformat to DED standard
|
|
|
|
|
|
2023-05-25 14:17:53 +01:00
|
|
|
|
uint64_t SaveMMASK = MMASK;
|
2022-08-28 09:35:46 +01:00
|
|
|
|
BOOL SaveMTX = MTX;
|
|
|
|
|
BOOL SaveMCOM = MCOM;
|
|
|
|
|
BOOL SaveMUI = MUIONLY;
|
|
|
|
|
unsigned char Decoded[1000];
|
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
IntSetTraceOptionsEx(TNC->MMASK, TNC->MTX, TNC->MCOM, TNC->MUIONLY);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
Len = IntDecodeFrame(&MONITORDATA, Decoded, stamp, TNC->MMASK, FALSE, FALSE);
|
2023-05-25 14:17:53 +01:00
|
|
|
|
IntSetTraceOptionsEx(SaveMMASK, SaveMTX, SaveMCOM, SaveMUI);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (Len)
|
2022-08-28 09:35:46 +01:00
|
|
|
|
{
|
2023-06-21 08:21:04 +01:00
|
|
|
|
if (ConvertToDEDMonFormat(TNC, Decoded, Len, &MONITORDATA))
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
stamp = GetRaw(TNC->Channels[0]->BPQStream, (char *)&MONITORDATA, &Len, &Count);
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
2023-06-21 08:21:04 +01:00
|
|
|
|
|
2022-08-28 09:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|