linbpq/6pack.c

2130 lines
53 KiB
C
Raw Permalink Normal View History

2024-08-30 10:14:56 +01:00
/*
Copyright 2001-2022 John Wiseman G8BPQ
This file is part of LinBPQ/BPQ32.
LinBPQ/BPQ32 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
LinBPQ/BPQ32 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with LinBPQ/BPQ32. If not, see http://www.gnu.org/licenses
*/
/*
Using code from 6pack Linux Kernel driver with the following licence and credits
* 6pack driver version 0.4.2, 1999/08/22
*
* This module:
* This module 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
* 2 of the License, or (at your option) any later version.
*
* This module implements the AX.25 protocol for kernel-based
* devices like TTYs. It interfaces between a raw TTY, and the
* kernel's AX.25 protocol layers, just like slip.c.
* AX.25 needs to be separated from slip.c while slip.c is no
* longer a static kernel device since it is a module.
*
* Author: Andreas K<EFBFBD>nsgen <ajk@ccac.rwth-aachen.de>
*
* Lots of stuff has been taken from mkiss.c, written by
* Hans Alblas <hans@esrac.ele.tue.nl>
*
* with the fixes from
*
* Jonathan (G4KLX) Fixed to match Linux networking changes - 2.1.15.
* Matthias (DG2FEF) Fixed bug in ax25_close(): dev_lock_wait() was
* called twice, causing a deadlock.
*/
// 6pack needs fast response to received characters, and I want to be able to operate over TCP links as well as serial.
// So I think the character level stuff may need to run in a separate thread, probably using select.
//
// I also need to support multiple 6pack ports.
// ?? Do we add this as a backend to KISS driver or a separate Driver. KISS Driver is already quite messy. Not decided yet.
// ?? If using serial/real TNC we need to be able to interleave control and data bytes, but I think with TCP/QtSM it won't be necessary
// ?? Also a don't see any point in running multiple copies of QtSM on one port, but maybe should treat the QtSM channels as
// multidropped ports for scheduling (?? only if on same radio ??)
// ?? I think it needs to look like a KISS (L2) driver but will need a transmit scheduler level to do DCD/CSMA/PTT processing,
// ideally with an interlock to other drivers on same port. This needs some thought with QtSM KISS with multiple modems on one channel
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#define _CRT_SECURE_NO_DEPRECATE
#include "compatbits.h"
#include <string.h>
#include "CHeaders.h"
#include "bpq32.h"
#include "tncinfo.h"
#ifndef WIN32
#define APIENTRY
#define DllExport
#define VOID void
#else
#include <windows.h>
#endif
/****************************************************************************
* Defines for the 6pack driver.
****************************************************************************/
/*
provisoric define
*/
#define SIXP_MAJOR MKISS_MAJOR
/* end of provisoric defines */
#define TRUE 1
#define FALSE 0
#define AX25_MAXDEV 16 /* MAX number of AX25 channels;
This can be overridden with
insmod -oax25_maxdev=nnn */
#define AX_MTU 236
/* 6pack protocol bytes/masks. */
#define SIXP_INIT_CMD 0xE8
#define SIXP_TNC_FOUND 0xE9
#define SIXP_CMD_MASK 0xC0
#define SIXP_PRIO_CMD_MASK 0x80
#define SIXP_PRIO_DATA_MASK 0x38
#define SIXP_STD_CMD_MASK 0x40
#define SIXP_DCD_MASK 0x08
#define SIXP_RX_DCD_MASK 0x18
#define SIXP_CHN_MASK 0x07
#define SIXP_TX_MASK 0x20
#define SIXP_CON_LED_ON 0x68
#define SIXP_STA_LED_ON 0x70
#define SIXP_LED_OFF 0x60
/* checksum for a valid 6pack encapsulated packet */
#define SIXP_CHKSUM 0xFF
/* priority commands */
#define SIXP_SEOF 0x40 /* TX underrun */
#define SIXP_TX_URUN 0x48 /* TX underrun */
#define SIXP_RX_ORUN 0x50 /* RX overrun */
#define SIXP_RX_BUF_OVL 0x58 /* RX overrun */
struct sixPackTNCInfo
{
// info for each TNC in chain
int magic;
char * name;
/* These are pointers to the malloc()ed frame buffers. */
unsigned char *rbuff; /* receiver buffer */
int rcount; /* received chars counter */
unsigned char *xbuff; /* transmitter buffer */
unsigned char *xhead; /* pointer to next byte to XMIT */
int xleft; /* bytes left in XMIT queue */
/* SLIP interface statistics. */
unsigned long rx_packets; /* inbound frames counter */
unsigned long tx_packets; /* outbound frames counter */
unsigned long rx_errors; /* Parity, etc. errors */
unsigned long tx_errors; /* Planned stuff */
unsigned long rx_dropped; /* No memory for skb */
unsigned long tx_dropped; /* When MTU change */
unsigned long rx_over_errors; /* Frame bigger then SLIP buf. */
/* Detailed SLIP statistics. */
int mtu; /* Our mtu (to spot changes!) */
int buffsize; /* Max buffers sizes */
unsigned char flags; /* Flag values/ mode etc */
#define AXF_INUSE 0 /* Channel in use */
#define AXF_ESCAPE 1 /* ESC received */
#define AXF_ERROR 2 /* Parity, etc. error */
#define AXF_KEEPTEST 3 /* Keepalive test flag */
#define AXF_OUTWAIT 4 /* is outpacket was flag */
int mode;
/* variables for the state machine */
unsigned char tnc_ok;
unsigned char status;
unsigned char status1;
unsigned char status2;
unsigned char duplex;
unsigned char led_state;
unsigned char tx_enable;
unsigned char raw_buf[4]; /* receive buffer */
unsigned char cooked_buf[400]; /* receive buffer after 6pack decoding */
unsigned int rx_count; /* counter for receive buffer */
unsigned int rx_count_cooked; /* counter for receive buffer after 6pack decoding */
unsigned char tx_delay;
unsigned char persistance;
unsigned char slottime;
};
struct sixPackPortInfo
{
// Per port (chain of TNC's)
unsigned int linkOK; // Set if response is received
unsigned int reinitTimer;
struct sixPackTNCInfo * TNCS[8]; // Max TNCs in chain Not sure if real 6pack uses o or 1 for first
};
#define AX25_MAGIC 0x5316
#define SIXP_DRIVER_MAGIC 0x5304
#define SIXP_INIT_RESYNC_TIMEOUT 150 /* in 10 ms */
#define SIXP_RESYNC_TIMEOUT 500 /* in 10 ms */
/* default radio channel access parameters */
#define SIXP_TXDELAY 25 /* in 10 ms */
#define SIXP_PERSIST 50
#define SIXP_SLOTTIME 10 /* in 10 ms */
static int sixpack_encaps(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay);
static void sixpack_decaps(struct sixPackPortInfo *, unsigned char);
static void decode_prio_command(unsigned char, struct sixPackTNCInfo *);
static void decode_std_command(unsigned char, struct sixPackTNCInfo *);
static void decode_data(unsigned char, struct sixPackTNCInfo *);
static void resync_tnc(unsigned long);
static void xmit_on_air(struct sixPackTNCInfo *ax);
static void start_tx_timer(struct sixPackTNCInfo *ax);
int Connectto6Pack(int port);
VOID __cdecl Debugprintf(const char * format, ...);
/* Set the "sending" flag. This must be atomic, hence the ASM. */
static void ax_lock(struct sixPackTNCInfo *ax)
{
// if (test_and_set_bit(0, (void *)&ax->dev->tbusy))
// printk(KERN_ERR "6pack: %s: trying to lock already locked device!\n", ax->dev->name);
}
/* Clear the "sending" flag. This must be atomic, hence the ASM. */
static void ax_unlock(struct sixPackTNCInfo *ax)
{
// if (!test_and_clear_bit(0, (void *)&ax->dev->tbusy))
// printk(KERN_ERR "6pack: %s: trying to unlock already unlocked device!\n", ax->dev->name);
}
/* Send one completely decapsulated AX.25 packet to the AX.25 layer. */
static void ax_bump(struct sixPackTNCInfo *ax)
{
}
VOID SixPackProcessReceivedPacket(struct TNCINFO * TNC)
{
int InputLen, MsgLen;
unsigned char * ptr;
char Buffer[4096];
if (TNC->InputLen > 8000) // Shouldnt have packets longer than this
TNC->InputLen=0;
InputLen = recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0);
if (InputLen == 0 || InputLen == SOCKET_ERROR)
{
// Does this mean closed?
int err = GetLastError();
closesocket(TNC->TCPSock);
TNC->TCPSock = 0;
TNC->CONNECTED = FALSE;
TNC->Streams[0].ReportDISC = TRUE;
sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost");
MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
return;
}
TNC->InputLen += InputLen;
// Process Bytes
}
static int sixpack_encaps(unsigned char *tx_buf, unsigned char *tx_buf_raw, int length, unsigned char tx_delay)
{
int count = 0;
unsigned char checksum = 0, buf[400];
int raw_count = 0;
tx_buf_raw[raw_count++] = SIXP_PRIO_CMD_MASK | SIXP_TX_MASK;
tx_buf_raw[raw_count++] = SIXP_SEOF;
buf[0] = tx_delay;
for(count = 1; count < length; count++)
buf[count] = tx_buf[count];
for(count = 0; count < length; count++)
checksum += buf[count];
buf[length] = (unsigned char)0xff - checksum;
for(count = 0; count <= length; count++) {
if((count % 3) == 0) {
tx_buf_raw[raw_count++] = (buf[count] & 0x3f);
tx_buf_raw[raw_count] = ((buf[count] >> 2) & 0x30);
}
else if((count % 3) == 1) {
tx_buf_raw[raw_count++] |= (buf[count] & 0x0f);
tx_buf_raw[raw_count] =
((buf[count] >> 2) & 0x3c);
} else {
tx_buf_raw[raw_count++] |= (buf[count] & 0x03);
tx_buf_raw[raw_count++] =
(buf[count] >> 2);
} /* else */
} /* for */
if ((length % 3) != 2)
raw_count++;
tx_buf_raw[raw_count++] = SIXP_SEOF;
return(raw_count);
}
static void sixpack_decaps(struct sixPackPortInfo *ax, unsigned char inbyte)
{
// if (inbyte == SIXP_TNC_FOUND) {
// This is wrong. It will only work with one TNC in the chain. Should look for E8 in top bits. Bottome 3 bits are number in chain
if ((inbyte & SIXP_INIT_CMD) == 0)
{
int Count = inbyte & SIXP_CHN_MASK;
struct sixPackTNCInfo * sixTNC;
int i;
Debugprintf("6pack: %d TNCs found. ", Count);
// clear existing TNC strucs and allocate new ones if needed
for (i = 0; i < Count; i++)
{
sixTNC = ax->TNCS[i];
if (sixTNC)
memset(sixTNC, 0, sizeof(struct sixPackTNCInfo));
else
sixTNC = ax->TNCS[i] = zalloc(sizeof(struct sixPackTNCInfo));
sixTNC->tnc_ok = 1;
}
ax->linkOK = TRUE;
}
/*
if((inbyte & SIXP_PRIO_CMD_MASK) != 0)
decode_prio_command(inbyte, ax);
else if((inbyte & SIXP_STD_CMD_MASK) != 0)
decode_std_command(inbyte, ax);
else {
if ((ax->status & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)
decode_data(inbyte, ax);
}
*/
}
/* identify and execute a 6pack priority command byte */
void decode_prio_command(unsigned char cmd, struct sixPackTNCInfo *ax)
{
unsigned char channel;
channel = cmd & SIXP_CHN_MASK;
if ((cmd & SIXP_PRIO_DATA_MASK) != 0) { /* idle ? */
/* RX and DCD flags can only be set in the same prio command,
if the DCD flag has been set without the RX flag in the previous
prio command. If DCD has not been set before, something in the
transmission has gone wrong. In this case, RX and DCD are
cleared in order to prevent the decode_data routine from
reading further data that might be corrupt. */
if (((ax->status & SIXP_DCD_MASK) == 0) &&
((cmd & SIXP_RX_DCD_MASK) == SIXP_RX_DCD_MASK)) {
if (ax->status != 1)
Debugprintf("6pack: protocol violation\n");
else
ax->status = 0;
cmd &= !SIXP_RX_DCD_MASK;
}
ax->status = cmd & SIXP_PRIO_DATA_MASK;
} /* if */
/* if the state byte has been received, the TNC is present,
so the resync timer can be reset. */
if (ax->tnc_ok == 1) {
// del_timer(&(ax->resync_t));
// ax->resync_t.data = (unsigned long) ax;
// ax->resync_t.function = resync_tnc;
// ax->resync_t.expires = jiffies + SIXP_INIT_RESYNC_TIMEOUT;
// add_timer(&(ax->resync_t));
}
ax->status1 = cmd & SIXP_PRIO_DATA_MASK;
}
/* try to resync the TNC. Called by the resync timer defined in
decode_prio_command */
static void resync_tnc(unsigned long channel)
{
static char resync_cmd = SIXP_INIT_CMD;
struct sixPackTNCInfo *ax = (struct sixPackTNCInfo *) channel;
Debugprintf("6pack: resyncing TNC\n");
/* clear any data that might have been received */
ax->rx_count = 0;
ax->rx_count_cooked = 0;
/* reset state machine */
ax->status = 1;
ax->status1 = 1;
ax->status2 = 0;
ax->tnc_ok = 0;
/* resync the TNC */
ax->led_state = SIXP_LED_OFF;
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
// ax->tty->driver.write(ax->tty, 0, &resync_cmd, 1);
/* Start resync timer again -- the TNC might be still absent */
// del_timer(&(ax->resync_t));
// ax->resync_t.data = (unsigned long) ax;
// ax->resync_t.function = resync_tnc;
// ax->resync_t.expires = jiffies + SIXP_RESYNC_TIMEOUT;
// add_timer(&(ax->resync_t));
}
/* identify and execute a standard 6pack command byte */
void decode_std_command(unsigned char cmd, struct sixPackTNCInfo *ax)
{
unsigned char checksum = 0, channel;
unsigned int i;
channel = cmd & SIXP_CHN_MASK;
switch(cmd & SIXP_CMD_MASK) { /* normal command */
case SIXP_SEOF:
if ((ax->rx_count == 0) && (ax->rx_count_cooked == 0)) {
if ((ax->status & SIXP_RX_DCD_MASK) ==
SIXP_RX_DCD_MASK) {
ax->led_state = SIXP_CON_LED_ON;
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
} /* if */
} else {
ax->led_state = SIXP_LED_OFF;
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
/* fill trailing bytes with zeroes */
if (ax->rx_count == 2) {
decode_data(0, ax);
decode_data(0, ax);
ax->rx_count_cooked -= 2;
}
else if (ax->rx_count == 3) {
decode_data(0, ax);
ax->rx_count_cooked -= 1;
}
for (i=0; i< ax->rx_count_cooked; i++)
checksum += ax->cooked_buf[i];
if (checksum != SIXP_CHKSUM) {
Debugprintf("6pack: bad checksum %2.2x\n", checksum);
} else {
ax->rcount = ax->rx_count_cooked-1;
ax_bump(ax);
} /* else */
ax->rx_count_cooked = 0;
} /* else */
break;
case SIXP_TX_URUN:
Debugprintf("6pack: TX underrun\n");
break;
case SIXP_RX_ORUN:
Debugprintf("6pack: RX overrun\n");
break;
case SIXP_RX_BUF_OVL:
Debugprintf("6pack: RX buffer overflow\n");
} /* switch */
} /* function */
/* decode 4 sixpack-encoded bytes into 3 data bytes */
void decode_data(unsigned char inbyte, struct sixPackTNCInfo *ax)
{
unsigned char *buf;
if (ax->rx_count != 3)
ax->raw_buf[ax->rx_count++] = inbyte;
else {
buf = ax->raw_buf;
ax->cooked_buf[ax->rx_count_cooked++] =
buf[0] | ((buf[1] << 2) & 0xc0);
ax->cooked_buf[ax->rx_count_cooked++] =
(buf[1] & 0x0f) | ((buf[2] << 2) & 0xf0);
ax->cooked_buf[ax->rx_count_cooked++] =
(buf[2] & 0x03) | (inbyte << 2);
ax->rx_count = 0;
}
}
// This takes a packet from the KISS driver and converts to 6pack format
static void ax_encaps(struct sixPackTNCInfo *ax, unsigned char *icp, int len)
{
unsigned char *p;
int count;
struct sixPackTNCInfo *tmp_ax;
if (len > ax->mtu) { /* Sigh, shouldn't occur BUT ... */
len = ax->mtu;
Debugprintf("6pack: %s: truncating oversized transmit packet!\n", ax->name);
ax->tx_dropped++;
ax_unlock(ax);
return;
}
p = icp;
if (p[0] > 5)
{
Debugprintf("%s: invalid KISS command -- dropped\n", ax->name);
ax_unlock(ax);
return;
}
if ((p[0] != 0) && (len > 2))
{
Debugprintf("%s: KISS command packet too long -- dropped\n", ax->name);
ax_unlock(ax);
return;
}
if ((p[0] == 0) && (len < 15))
{
Debugprintf("%s: bad AX.25 packet to transmit -- dropped\n", ax->name);
ax->tx_dropped++;
ax_unlock(ax);
return;
}
switch(p[0])
{
case 1: tmp_ax->tx_delay = p[1]; break;
case 2: tmp_ax->persistance = p[1]; break;
case 3: tmp_ax->slottime = p[1]; break;
case 4: Debugprintf("6pack: got KISS command 4 -- ignored\n");
break;
case 5: tmp_ax->duplex = p[1]; break;
}
// How do we handle ACKMODE ??
if (p[0] != 0) {
ax_unlock(ax);
return;
}
count = sixpack_encaps(p, (unsigned char *) tmp_ax->xbuff, len, tmp_ax->tx_delay);
tmp_ax->xleft = count;
tmp_ax->status2 = count;
tmp_ax->xhead = tmp_ax->xbuff;
/* in case of DAMA or fullduplex operation, we don't take care
about the state of the DCD or of any timers, as the determination
of the correct time to send is the job of the AX.25 layer. We send
immediately after data has arrived. */
if (tmp_ax->duplex == 1) { /* DAMA / fullduplex */
xmit_on_air(tmp_ax);
} else { /* CSMA */
start_tx_timer(tmp_ax);
} /* if ax->duplex */
} /* ax_encaps */
// This starts sending a packet to a TNC. TNC will immediately send (after TXD)
/* the next two functions perform the persistence/slottime algorithm for CSMA access.
If the persistence check was successful, write the data to the serial driver.
Note that in case of DAMA operation, the data is not sent here. */
/* set timer to time out slottime */
static void start_tx_timer(struct sixPackTNCInfo *ax)
{
// del_timer(&(ax->tx_t));
// ax->tx_t.data = (unsigned long) ax;
// ax->tx_t.function = tx_schedule;
// ax->tx_t.expires = jiffies + (((ax->slottime)+1)*HZ)/100;
// add_timer(&(ax->tx_t));
}
/* compute random number and check if it is ok to send */
static void tx_schedule(unsigned long channel)
{
struct sixPackTNCInfo *ax = (struct sixPackTNCInfo *) channel;
static unsigned char random;
random = random * 17 + 41;
if (((ax->status1 & SIXP_DCD_MASK) == 0) && (random < ax->persistance))
xmit_on_air(ax);
else
start_tx_timer(ax);
}
static void xmit_on_air(struct sixPackTNCInfo *ax)
{
int actual;
// ax->tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
ax->led_state = SIXP_STA_LED_ON;
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
ax->tx_enable = 1;
// actual = ax->tty->driver.write(ax->tty, 0, ax->xbuff, ax->status2);
ax->tx_packets++;
// ax->dev->trans_start = jiffies;
ax->xleft -= actual;
ax->xhead = ax->xbuff + actual;
ax->led_state = SIXP_LED_OFF;
// ax->tty->driver.write(ax->tty, 0, &(ax->led_state), 1);
} /* xmit_on_air */
// BPQ Driver Module Interface
extern int (WINAPI FAR *GetModuleFileNameExPtr)();
extern int (WINAPI FAR *EnumProcessesPtr)();
static int Socket_Data(int sock, int error, int eventcode);
VOID MoveWindows(struct TNCINFO * TNC);
static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen);
int ReadCOMBlockEx(HANDLE fd, char * Block, int MaxLength, BOOL * Error);
BOOL SerialWriteCommBlock(struct TNCINFO * TNC);
void SerialCheckRX(struct TNCINFO * TNC);
int SerialSendData(struct TNCINFO * TNC, UCHAR * data, int txlen);
int SerialSendCommand(struct TNCINFO * TNC, UCHAR * data);
int DoScanLine(struct TNCINFO * TNC, char * Buff, int Len);
VOID SendInitScript(struct TNCINFO * TNC);
int SIXPACKGetLine(char * buf);
int ProcessEscape(UCHAR * TXMsg);
VOID SIXPACKProcessReceivedPacket(struct TNCINFO * TNC);
static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len);
int ConnecttoKISS(int port);
TRANSPORTENTRY * SetupNewSession(TRANSPORTENTRY * Session, char * Bufferptr);
BOOL DecodeCallString(char * Calls, BOOL * Stay, BOOL * Spy, UCHAR * AXCalls);
BOOL FindLink(UCHAR * LinkCall, UCHAR * OurCall, int Port, struct _LINKTABLE ** REQLINK);
VOID RESET2(struct _LINKTABLE * LINK);
VOID L2SENDXID(struct _LINKTABLE * LINK);
VOID SENDSABM(struct _LINKTABLE * LINK);
static char ClassName[]="KISSSTATUS";
static char WindowTitle[] = "SIXPACK";
static int RigControlRow = 165;
#ifndef LINBPQ
#include <commctrl.h>
#endif
extern int SemHeldByAPI;
static RECT Rect;
static int ProcessLine(char * buf, int Port);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
#define FEND 0xC0 // KISS CONTROL CODES
#define FESC 0xDB
#define TFEND 0xDC
#define TFESC 0xDD
static int ProcessLine(char * buf, int Port)
{
UCHAR * ptr,* p_cmd;
char * p_ipad = 0;
char * p_port = 0;
unsigned short WINMORport = 0;
int BPQport;
int len=510;
struct TNCINFO * TNC;
char errbuf[256];
strcpy(errbuf, buf);
ptr = strtok(buf, " \t\n\r");
if(ptr == NULL) return (TRUE);
if(*ptr =='#') return (TRUE); // comment
if(*ptr ==';') return (TRUE); // comment
if (_stricmp(buf, "ADDR"))
return FALSE; // Must start with ADDR
ptr = strtok(NULL, " \t\n\r");
BPQport = Port;
p_ipad = ptr;
TNC = TNCInfo[BPQport] = malloc(sizeof(struct TNCINFO));
memset(TNC, 0, sizeof(struct TNCINFO));
TNC->DefaultMode = TNC->WL2KMode = 0; // Packet 1200
TNC->InitScript = malloc(1000);
TNC->InitScript[0] = 0;
if (p_ipad == NULL)
p_ipad = strtok(NULL, " \t\n\r");
if (p_ipad == NULL) return (FALSE);
p_port = strtok(NULL, " \t\n\r");
if (p_port == NULL) return (FALSE);
WINMORport = atoi(p_port);
TNC->destaddr.sin_family = AF_INET;
TNC->destaddr.sin_port = htons(WINMORport);
TNC->HostName = malloc(strlen(p_ipad)+1);
if (TNC->HostName == NULL) return TRUE;
strcpy(TNC->HostName,p_ipad);
ptr = strtok(NULL, " \t\n\r");
if (ptr)
{
if (_stricmp(ptr, "PTT") == 0)
{
ptr = strtok(NULL, " \t\n\r");
if (ptr)
{
DecodePTTString(TNC, ptr);
ptr = strtok(NULL, " \t\n\r");
}
}
}
if (ptr)
{
if (_memicmp(ptr, "PATH", 4) == 0)
{
p_cmd = strtok(NULL, "\n\r");
if (p_cmd) TNC->ProgramPath = _strdup(p_cmd);
}
}
// Read Initialisation lines
while (TRUE)
{
if (GetLine(buf) == 0)
return TRUE;
strcpy(errbuf, buf);
if (memcmp(buf, "****", 4) == 0)
return TRUE;
ptr = strchr(buf, ';');
if (ptr)
{
*ptr++ = 13;
*ptr = 0;
}
if (_memicmp(buf, "UPDATEMAP", 9) == 0)
TNC->PktUpdateMap = TRUE;
else if (standardParams(TNC, buf) == FALSE)
strcat(TNC->InitScript, buf);
}
return (TRUE);
}
char * Config;
static char * ptr1, * ptr2;
int SIXPACKGetLine(char * buf)
{
loop:
if (ptr2 == NULL)
return 0;
memcpy(buf, ptr1, ptr2 - ptr1 + 2);
buf[ptr2 - ptr1 + 2] = 0;
ptr1 = ptr2 + 2;
ptr2 = strchr(ptr1, 13);
if (buf[0] < 0x20) goto loop;
if (buf[0] == '#') goto loop;
if (buf[0] == ';') goto loop;
if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0;
if (buf[strlen(buf)-1] < 0x20) buf[strlen(buf)-1] = 0;
buf[strlen(buf)] = 13;
return 1;
}
VOID SuspendOtherPorts(struct TNCINFO * ThisTNC);
VOID ReleaseOtherPorts(struct TNCINFO * ThisTNC);
VOID WritetoTrace(struct TNCINFO * TNC, char * Msg, int Len);
static time_t ltime;
static VOID SendToTNC(struct TNCINFO * TNC, int Stream, UCHAR * Encoded, int EncLen)
{
if (TNC->hDevice)
{
// Serial mode. Queue to Hostmode driver
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr == 0) return; // No buffers, so ignore
buffptr->Len = EncLen;
memcpy(&buffptr->Data[0], Encoded, EncLen);
C_Q_ADD(&TNC->Streams[Stream].BPQtoPACTOR_Q, buffptr);
TNC->Streams[Stream].FramesQueued++;
return;
}
}
static SOCKADDR_IN sinx;
static SOCKADDR_IN rxaddr;
static int addrlen=sizeof(sinx);
static size_t ExtProc(int fn, int port, PDATAMESSAGE buff)
{
int datalen;
PMSGWITHLEN buffptr;
char txbuff[500];
unsigned int bytes,txlen = 0;
UCHAR * TXMsg;
size_t Param;
int Stream = 0;
HKEY hKey=0;
struct TNCINFO * TNC = TNCInfo[port];
struct STREAMINFO * STREAM = &TNC->Streams[0];
struct ScanEntry * Scan;
if (TNC == NULL)
return 0; // Port not defined
if (TNC->CONNECTED == 0 && TNC->CONNECTING == 0)
{
// Try to reopen every 30 secs
if (fn > 3 && fn < 7)
goto ok;
TNC->ReopenTimer++;
if (TNC->ReopenTimer < 150)
return 0;
TNC->ReopenTimer = 0;
Connectto6Pack(TNC->Port);
return 0;
SendInitScript(TNC);
}
ok:
switch (fn)
{
case 7:
// 100 mS Timer. May now be needed, as Poll can be called more frequently in some circumstances
SerialCheckRX(TNC);
return 0;
case 1: // poll
STREAM = &TNC->Streams[0];
if (STREAM->NeedDisc)
{
STREAM->NeedDisc--;
if (STREAM->NeedDisc == 0)
{
// Send the DISCONNECT
SerialSendCommand(TNC, "DISCONNECT\r");
}
}
if (TNC->PortRecord->ATTACHEDSESSIONS[Stream] && STREAM->Attached == 0)
{
// New Attach
int calllen;
char Msg[80];
STREAM->Attached = TRUE;
calllen = ConvFromAX25(TNC->PortRecord->ATTACHEDSESSIONS[Stream]->L4USER, TNC->Streams[Stream].MyCall);
TNC->Streams[Stream].MyCall[calllen] = 0;
// Stop other ports in same group
SuspendOtherPorts(TNC);
sprintf(TNC->WEB_TNCSTATE, "In Use by %s", TNC->Streams[0].MyCall);
MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
// Stop Scanning
sprintf(Msg, "%d SCANSTOP", TNC->Port);
Rig_Command( (TRANSPORTENTRY *) -1, Msg);
}
if (STREAM->Attached)
CheckForDetach(TNC, Stream, STREAM, TidyClose, ForcedClose, CloseComplete);
// See if any frames for this port
STREAM = &TNC->Streams[0];
if (STREAM->BPQtoPACTOR_Q)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)Q_REM(&STREAM->BPQtoPACTOR_Q);
UCHAR * data = &buffptr->Data[0];
STREAM->FramesQueued--;
txlen = (int)buffptr->Len;
2024-11-05 21:03:15 +00:00
STREAM->bytesTXed += txlen;
2024-08-30 10:14:56 +01:00
bytes=SerialSendData(TNC, data, txlen);
WritetoTrace(TNC, data, txlen);
}
if (STREAM->PACTORtoBPQ_Q != 0)
{
buffptr = (PMSGWITHLEN)Q_REM(&STREAM->PACTORtoBPQ_Q);
datalen = (int)buffptr->Len;
buff->PORT = Stream; // Compatibility with Kam Driver
buff->PID = 0xf0;
memcpy(&buff->L2DATA, &buffptr->Data[0], datalen); // Data goes to + 7, but we have an extra byte
datalen += sizeof(void *) + 4;
PutLengthinBuffer(buff, datalen);
ReleaseBuffer(buffptr);
return (1);
}
if (STREAM->ReportDISC) // May need a delay so treat as a counter
{
STREAM->ReportDISC--;
if (STREAM->ReportDISC == 0)
{
buff->PORT = Stream;
return -1;
}
}
return (0);
case 2: // send
Stream = 0;
if (!TNC->CONNECTED)
return 0; // Don't try if not connected
STREAM = &TNC->Streams[0];
if (TNC->SwallowSignon)
{
TNC->SwallowSignon = FALSE; // Discard *** connected
return 0;
}
// We may get KISS packets (UI or session related) or text commands such as RADIO, CONNECT
txlen = GetLengthfromBuffer(buff) - (MSGHDDRLEN);
TXMsg = &buff->L2DATA[0];
if (buff->PID != 240) // ax.25 address
{
txlen = KissEncode(&buff->PID, txbuff, txlen);
txlen = send(TNC->TCPSock, txbuff, txlen, 0);
return 1;
}
TXMsg[txlen - 1] = 0;
strcpy(txbuff, TXMsg);
if (STREAM->Attached == 0)
return 0;
if (STREAM->Connected)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr == 0) return 1; // No buffers, so ignore
buffptr->Len = txlen;
memcpy((UCHAR *)&buffptr->Data[0], &buff->L2DATA[0], txlen);
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
// connected data
return 1;
}
// Process as Text Command
if (_memicmp(txbuff, "D\r", 2) == 0 || _memicmp(txbuff, "BYE\r", 4) == 0)
{
STREAM->ReportDISC = TRUE; // Tell Node
return 0;
}
if (_memicmp(txbuff, "RADIO ", 6) == 0)
{
sprintf(&buff->L2DATA[0], "%d %s", TNC->Port, &txbuff[6]);
if (Rig_Command(TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK, &buff->L2DATA[0]))
{
}
else
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr == 0) return 1; // No buffers, so ignore
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "%s", &buff->L2DATA[0]);
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
}
return 1;
}
if (_memicmp(txbuff, "OVERRIDEBUSY", 12) == 0)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
TNC->OverrideBusy = TRUE;
if (buffptr)
{
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SIXPACK} OK\r");
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
}
return 0;
}
if (_memicmp(&buff->L2DATA[0], "SessionTimeLimit", 16) == 0)
{
if (buff->L2DATA[16] != 13)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
TNC->SessionTimeLimit = atoi(&buff->L2DATA[16]) * 60;
if (buffptr)
{
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SIXPACK} OK\r");
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
}
return 0;
}
}
if (toupper(buff->L2DATA[0]) == 'C' && buff->L2DATA[1] == ' ' && txlen > 2) // Connect
{
// Connect Command. Pass to L2 code to start session
char * ptr = strchr(&buff->L2DATA[2], 13);
TRANSPORTENTRY * NewSess = L4TABLE;
struct _LINKTABLE * LINK;
TRANSPORTENTRY * Session = TNC->PortRecord->ATTACHEDSESSIONS[0]->L4CROSSLINK;
struct PORTCONTROL * PORT = &TNC->PortRecord->PORTCONTROL;
UCHAR axcalls[64];
UCHAR ourcall[7]; // Call we are using (may have SSID bits inverted
int Stay = 0, Spy = 0, CQFLAG = 0, n;
if (ptr)
*ptr = 0;
_strupr(&buff->L2DATA[2]);
if (DecodeCallString(&buff->L2DATA[2], &Stay, &Spy, &axcalls[0]) == 0)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr)
{
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SIXPACK} Invalid Call\r");
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
}
return 0;
}
// Code copied from cmdc00
// Get Session Entry for Downlink
NewSess = SetupNewSession(Session, NULL);
if (NewSess == NULL)
return 0;
NewSess->L4CIRCUITTYPE = L2LINK + DOWNLINK;
// FORMAT LINK TABLE ENTRY FOR THIS CONNECTION
memcpy(Session->L4USER, NewSess->L4USER, 7);
memcpy(ourcall, NewSess->L4USER, 7);
// SSID SWAP TEST - LEAVE ALONE FOR HOST or Pactor like (unless UZ7HO)
if ((Session->L4CIRCUITTYPE & BPQHOST))// host
goto noFlip3;
if ((Session->L4CIRCUITTYPE & PACTOR))
{
// incoming is Pactorlike - see if UZ7HO
if (memcmp(Session->L4TARGET.EXTPORT->PORT_DLL_NAME, "UZ7HO", 5) != 0)
goto noFlip3;
if (Session->L4TARGET.EXTPORT->MAXHOSTMODESESSIONS < 2) // Not multisession
goto noFlip3;
ourcall[6] ^= 0x1e; // UZ7HO Uplink - flip
}
else
// Must be L2 uplink - flip
ourcall[6] ^= 0x1e; // Flip SSID
noFlip3:
// SET UP NEW SESSION (OR RESET EXISTING ONE)
FindLink(axcalls, ourcall, TNC->Port, &LINK);
if (LINK == NULL)
{
PMSGWITHLEN buffptr = (PMSGWITHLEN)GetBuff();
if (buffptr)
{
buffptr->Len = sprintf((UCHAR *)&buffptr->Data[0], "SIXPACK} Sorry - System Tables Full\r");
C_Q_ADD(&TNC->Streams[Stream].PACTORtoBPQ_Q, buffptr);
}
return 0;
}
memcpy(LINK->LINKCALL, axcalls, 7);
memcpy(LINK->OURCALL, ourcall, 7);
LINK->LINKPORT = PORT;
LINK->L2TIME = PORT->PORTT1;
// Copy Digis
n = 7;
ptr = &LINK->DIGIS[0];
while (axcalls[n])
{
memcpy(ptr, &axcalls[n], 7);
n += 7;
ptr += 7;
LINK->L2TIME += 2 * PORT->PORTT1; // ADJUST TIMER VALUE FOR 1 DIGI
}
LINK->LINKTYPE = 2; // DOWNLINK
LINK->LINKWINDOW = PORT->PORTWINDOW;
RESET2(LINK); // RESET ALL FLAGS
// if (CMD->String[0] == 'N' && SUPPORT2point2)
// LINK->L2STATE = 1; // New (2.2) send XID
// else
LINK->L2STATE = 2; // Send SABM
LINK->CIRCUITPOINTER = NewSess;
NewSess->L4TARGET.LINK = LINK;
if (PORT->PORTPACLEN)
NewSess->SESSPACLEN = Session->SESSPACLEN = PORT->PORTPACLEN;
STREAM->Connecting = TRUE;
if (CQFLAG == 0) // if a CQ CALL DONT SEND SABM
{
if (LINK->L2STATE == 1)
L2SENDXID(LINK);
else
SENDSABM(LINK);
}
return 0;
}
return 0;
case 3:
// CHECK IF OK TO SEND (And check TNC Status)
return ((TNC->CONNECTED) << 8 | TNC->Streams[0].Disconnecting << 15); // OK
case 4: // reinit7
return 0;
case 5: // Close
return 0;
case 6: // Scan Stop Interface
Param = (size_t)buff;
if (Param == 2) // Check Permission (Shouldn't happen)
{
Debugprintf("Scan Check Permission called on SIXPACK");
return 1; // OK to change
}
if (Param == 1) // Request Permission
{
if (!TNC->CONNECTED)
return 0; // No connection so no interlock
if (TNC->ConnectPending == 0 && TNC->PTTState == 0)
{
SerialSendCommand(TNC, "CONOK OFF");
TNC->GavePermission = TRUE;
return 0; // OK to Change
}
if (TNC->ConnectPending)
TNC->ConnectPending--; // Time out if set too long
return TRUE;
}
if (Param == 3) // Release Permission
{
if (TNC->GavePermission)
{
TNC->GavePermission = FALSE;
if (TNC->ARDOPCurrentMode[0] != 'S') // Skip
SerialSendCommand(TNC, "CONOK ON");
}
return 0;
}
// Param is Address of a struct ScanEntry
Scan = (struct ScanEntry *)buff;
return 0;
}
return 0;
}
VOID SIXPACKReleaseTNC(struct TNCINFO * TNC)
{
// Set mycall back to Node or Port Call, and Start Scanner
UCHAR TXMsg[64];
// Start Scanner
sprintf(TXMsg, "%d SCANSTART 15", TNC->Port);
Rig_Command( (TRANSPORTENTRY *) -1, TXMsg);
strcpy(TNC->WEB_TNCSTATE, "Free");
MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
ReleaseOtherPorts(TNC);
}
VOID SIXPACKSuspendPort(struct TNCINFO * TNC, struct TNCINFO * ThisTNC)
{
}
VOID SIXPACKReleasePort(struct TNCINFO * TNC)
{
}
static int WebProc(struct TNCINFO * TNC, char * Buff, BOOL LOCAL)
{
int Len = sprintf(Buff, "<html><meta http-equiv=expires content=0><meta http-equiv=refresh content=15>"
"<script type=\"text/javascript\">\r\n"
"function ScrollOutput()\r\n"
"{var textarea = document.getElementById('textarea');"
"textarea.scrollTop = textarea.scrollHeight;}</script>"
"</head><title>VARA Status</title></head><body id=Text onload=\"ScrollOutput()\">"
"<h2><form method=post target=\"POPUPW\" onsubmit=\"POPUPW = window.open('about:blank','POPUPW',"
"'width=440,height=150');\" action=ARDOPAbort?%d>SIXPACK Status"
"<input name=Save value=\"Abort Session\" type=submit style=\"position: absolute; right: 20;\"></form></h2>",
TNC->Port);
Len += sprintf(&Buff[Len], "<table style=\"text-align: left; width: 500px; font-family: monospace; align=center \" border=1 cellpadding=2 cellspacing=2>");
Len += sprintf(&Buff[Len], "<tr><td width=110px>Comms State</td><td>%s</td></tr>", TNC->WEB_COMMSSTATE);
Len += sprintf(&Buff[Len], "<tr><td>TNC State</td><td>%s</td></tr>", TNC->WEB_TNCSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Mode</td><td>%s</td></tr>", TNC->WEB_MODE);
Len += sprintf(&Buff[Len], "<tr><td>Channel State</td><td>%s</td></tr>", TNC->WEB_CHANSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Proto State</td><td>%s</td></tr>", TNC->WEB_PROTOSTATE);
Len += sprintf(&Buff[Len], "<tr><td>Traffic</td><td>%s</td></tr>", TNC->WEB_TRAFFIC);
// Len += sprintf(&Buff[Len], "<tr><td>TNC Restarts</td><td></td></tr>", TNC->WEB_RESTARTS);
Len += sprintf(&Buff[Len], "</table>");
Len += sprintf(&Buff[Len], "<textarea rows=10 style=\"width:500px; height:250px;\" id=textarea >%s</textarea>", TNC->WebBuffer);
Len = DoScanLine(TNC, Buff, Len);
return Len;
}
VOID * SIXPACKExtInit(EXTPORTDATA * PortEntry)
{
int port;
char Msg[255];
char * ptr;
struct TNCINFO * TNC;
char * TempScript;
struct PORTCONTROL * PORT = &PortEntry->PORTCONTROL;
port = PORT->PORTNUMBER;
if (TNCInfo[port]) // If restarting, free old config
free(TNCInfo[port]);
TNC = TNCInfo[port] = malloc(sizeof(struct TNCINFO));
memset(TNC, 0, sizeof(struct TNCINFO));
TNC->InitScript = malloc(1000);
TNC->InitScript[0] = 0;
if (PortConfig[port]) // May not have config
ReadConfigFile(port, ProcessLine);
TNC = TNCInfo[port];
if (TNC == NULL)
{
// Not defined in Config file
sprintf(Msg," ** Error - no info in BPQ32.cfg for this port\n");
WritetoConsole(Msg);
return ExtProc;
}
TNC->sixPack = zalloc(sizeof(struct sixPackTNCInfo));
TNC->Port = port;
TNC->Hardware = H_SIXPACK;
TNC->ARDOPBuffer = malloc(8192);
TNC->PortRecord = PortEntry;
if (PortEntry->PORTCONTROL.PORTCALL[0] == 0)
memcpy(TNC->NodeCall, MYNODECALL, 10);
else
ConvFromAX25(&PortEntry->PORTCONTROL.PORTCALL[0], TNC->NodeCall);
if (PortEntry->PORTCONTROL.PORTINTERLOCK && TNC->RXRadio == 0 && TNC->TXRadio == 0)
TNC->RXRadio = TNC->TXRadio = PortEntry->PORTCONTROL.PORTINTERLOCK;
PortEntry->PORTCONTROL.PROTOCOL = 10;
PortEntry->PORTCONTROL.PORTQUALITY = 0;
PortEntry->PORTCONTROL.USERS = 1; // Max 1 Session
TNC->PacketChannels = 0;
PortEntry->MAXHOSTMODESESSIONS = 1;
PortEntry->SCANCAPABILITIES = SIMPLE; // Scan Control - pending connect only
PortEntry->PERMITGATEWAY = TRUE; // Can change ax.25 call on each stream
PortEntry->PORTCONTROL.UICAPABLE = TRUE;
if (PortEntry->PORTCONTROL.PORTPACLEN == 0)
PortEntry->PORTCONTROL.PORTPACLEN = 236;
TNC->SuspendPortProc = SIXPACKSuspendPort;
TNC->ReleasePortProc = SIXPACKReleasePort;
// PortEntry->PORTCONTROL.PORTSTARTCODE = KISSStartPort;
// PortEntry->PORTCONTROL.PORTSTOPCODE = KISSStopPort;
ptr=strchr(TNC->NodeCall, ' ');
if (ptr) *(ptr) = 0; // Null Terminate
// Set Essential Params and MYCALL
// Put overridable ones on front, essential ones on end
TempScript = zalloc(1000);
// cant think of any yet
if (TNC->InitScript)
{
strcat(TempScript, TNC->InitScript);
free(TNC->InitScript);
}
TNC->InitScript = TempScript;
if (TNC->WL2K == NULL)
if (PortEntry->PORTCONTROL.WL2KInfo.RMSCall[0]) // Alrerady decoded
TNC->WL2K = &PortEntry->PORTCONTROL.WL2KInfo;
PortEntry->PORTCONTROL.TNC = TNC;
TNC->WebWindowProc = WebProc;
TNC->WebWinX = 520;
TNC->WebWinY = 500;
TNC->WebBuffer = zalloc(5000);
TNC->WEB_COMMSSTATE = zalloc(100);
TNC->WEB_TNCSTATE = zalloc(100);
TNC->WEB_CHANSTATE = zalloc(100);
TNC->WEB_BUFFERS = zalloc(100);
TNC->WEB_PROTOSTATE = zalloc(100);
TNC->WEB_RESTARTTIME = zalloc(100);
TNC->WEB_RESTARTS = zalloc(100);
TNC->WEB_MODE = zalloc(20);
TNC->WEB_TRAFFIC = zalloc(100);
#ifndef LINBPQ
CreatePactorWindow(TNC, ClassName, WindowTitle, RigControlRow, PacWndProc, 700, 450, ForcedClose);
CreateWindowEx(0, "STATIC", "Comms State", WS_CHILD | WS_VISIBLE, 10,6,120,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_COMMSSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,6,386,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "TNC State", WS_CHILD | WS_VISIBLE, 10,28,106,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_TNCSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,28,520,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Mode", WS_CHILD | WS_VISIBLE, 10,50,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_MODE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,50,200,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Channel State", WS_CHILD | WS_VISIBLE, 10,72,110,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_CHANSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE, 116,72,144,20, TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Proto State", WS_CHILD | WS_VISIBLE,10,94,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_PROTOSTATE = CreateWindowEx(0, "STATIC", "", WS_CHILD | WS_VISIBLE,116,94,374,20 , TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Traffic", WS_CHILD | WS_VISIBLE,10,116,80,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_TRAFFIC = CreateWindowEx(0, "STATIC", "0 0 0 0", WS_CHILD | WS_VISIBLE,116,116,374,20 , TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "TNC Restarts", WS_CHILD | WS_VISIBLE,10,138,100,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_RESTARTS = CreateWindowEx(0, "STATIC", "0", WS_CHILD | WS_VISIBLE,116,138,40,20 , TNC->hDlg, NULL, hInstance, NULL);
CreateWindowEx(0, "STATIC", "Last Restart", WS_CHILD | WS_VISIBLE,140,138,100,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->xIDC_RESTARTTIME = CreateWindowEx(0, "STATIC", "Never", WS_CHILD | WS_VISIBLE,250,138,200,20, TNC->hDlg, NULL, hInstance, NULL);
TNC->hMonitor= CreateWindowEx(0, "LISTBOX", "", WS_CHILD | WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
LBS_DISABLENOSCROLL | WS_HSCROLL | WS_VSCROLL,
0,170,250,300, TNC->hDlg, NULL, hInstance, NULL);
TNC->ClientHeight = 450;
TNC->ClientWidth = 500;
TNC->hMenu = CreatePopupMenu();
AppendMenu(TNC->hMenu, MF_STRING, WINMOR_KILL, "Kill TNC");
AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTART, "Kill and Restart TNC");
AppendMenu(TNC->hMenu, MF_STRING, WINMOR_RESTARTAFTERFAILURE, "Restart TNC after failed Connection");
CheckMenuItem(TNC->hMenu, WINMOR_RESTARTAFTERFAILURE, (TNC->RestartAfterFailure) ? MF_CHECKED : MF_UNCHECKED);
AppendMenu(TNC->hMenu, MF_STRING, ARDOP_ABORT, "Abort Current Session");
MoveWindows(TNC);
#endif
Consoleprintf("SIXPACK Host %s %d", TNC->HostName, htons(TNC->destaddr.sin_port));
Connectto6Pack(port);
time(&TNC->lasttime); // Get initial time value
return ExtProc;
}
VOID TidyClose(struct TNCINFO * TNC, int Stream)
{
// If all acked, send disc
if (TNC->Streams[Stream].BytesOutstanding == 0)
SerialSendCommand(TNC, "DISCONNECT\r");
}
VOID ForcedClose(struct TNCINFO * TNC, int Stream)
{
SerialSendCommand(TNC, "DISCONNECT\r");
}
VOID CloseComplete(struct TNCINFO * TNC, int Stream)
{
if (Stream == 0)
{
SIXPACKReleaseTNC(TNC);
}
}
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;
}
static int KissEncode(UCHAR * inbuff, UCHAR * outbuff, int len)
{
int i,txptr=0;
UCHAR c;
outbuff[0]=FEND;
outbuff[1]=0;
txptr=2;
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;
}
VOID SIXPACKProcessReceivedPacket(struct TNCINFO * TNC)
{
int InputLen, MsgLen;
unsigned char * ptr;
char Buffer[4096];
if (TNC->InputLen > 8000) // Shouldnt have packets longer than this
TNC->InputLen=0;
InputLen = recv(TNC->TCPSock, &TNC->ARDOPBuffer[TNC->InputLen], 8192 - TNC->InputLen, 0);
if (InputLen == 0 || InputLen == SOCKET_ERROR)
{
// Does this mean closed?
int err = GetLastError();
closesocket(TNC->TCPSock);
TNC->TCPSock = 0;
TNC->CONNECTED = FALSE;
TNC->Streams[0].ReportDISC = TRUE;
sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost");
MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
return;
}
TNC->InputLen += InputLen;
// Extract and decode KISS frames
ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1); // Ignore leading FEND
while (ptr) // FEND in buffer
{
ptr++;
MsgLen = ptr - TNC->ARDOPBuffer;
if (MsgLen > 360)
{
TNC->InputLen = 0;
return;
}
TNC->InputLen -= MsgLen;
if (MsgLen > 1)
{
PMESSAGE Buff = GetBuff();
MsgLen = KissDecode(TNC->ARDOPBuffer, Buffer, MsgLen);
// we dont need the FENDS or control byte
MsgLen -= 3;
if (Buff)
{
memcpy(&Buff->DEST, &Buffer[2], MsgLen);
MsgLen += (3 + sizeof(void *));
PutLengthinBuffer((PDATAMESSAGE)Buff, MsgLen); // Needed for arm5 portability
C_Q_ADD(&TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buff);
}
}
if (TNC->InputLen == 0)
return;
memmove(TNC->ARDOPBuffer, ptr, TNC->InputLen);
ptr = memchr(TNC->ARDOPBuffer + 1, FEND, TNC->InputLen - 1); // Ignore leading FEND
}
}
/*
// we dont need the control byte
len --;
if (Buffer)
{
memcpy(&Buffer->DEST, &Port->RXMSG[1], len);
len += (3 + sizeof(void *));
PutLengthinBuffer((PDATAMESSAGE)Buffer, len); // Needed for arm5 portability
C_Q_ADD(TNC->PortRecord->PORTCONTROL.PORTRX_Q, (UINT *)Buffer);
}
*/
// TNC->InputLen -= MsgLen;
// goto loop;
void AttachSIXPACK(struct PORTCONTROL * PORT, MESSAGE * Buffer)
{
// SABM on HFKISS port. L2 code will accepr call and connect to appl if necessary, but
// need to attach the port
char Call[16] = "";
char OrigCall[16] = "";
struct TNCINFO * TNC = PORT->TNC;
struct WL2KInfo * WL2K = TNC->WL2K;
if (TNC->PortRecord->ATTACHEDSESSIONS[0] == 0)
{
TRANSPORTENTRY * SESS;
struct TNCINFO * TNC = PORT->TNC;
// Incoming Connect
Call[ConvFromAX25(Buffer->DEST, Call)] = 0;
OrigCall[ConvFromAX25(Buffer->ORIGIN, OrigCall)] = 0;
// Stop other ports in same group
SuspendOtherPorts(TNC);
TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit
ProcessIncommingConnectEx(TNC, Call, 0, FALSE, FALSE);
SESS = TNC->PortRecord->ATTACHEDSESSIONS[0];
SESS->Mode = TNC->WL2KMode;
TNC->ConnectPending = FALSE;
if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT"))
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound Freq %s", OrigCall, Call, TNC->RIG->Valchar);
SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq
if (SESS->Frequency == 1500)
{
// try to get from WL2K record
if (WL2K)
{
SESS->Frequency = WL2K->Freq;
}
}
SESS->Mode = TNC->WL2KMode;
}
else
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Inbound", OrigCall, Call);
if (WL2K)
{
SESS->Frequency = WL2K->Freq;
SESS->Mode = WL2K->mode;
}
}
if (WL2K)
strcpy(SESS->RMSCall, WL2K->RMSCall);
MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
}
}
void DetachSIXPACK(struct PORTCONTROL * PORT)
{
// L2 Link Closed. Detach.
struct TNCINFO * TNC = PORT->TNC;
struct STREAMINFO * STREAM = &TNC->Streams[0];
if (STREAM->Attached)
STREAM->ReportDISC = TRUE; // Tell Node
STREAM->Connecting = FALSE;
STREAM->Connected = FALSE;
}
void SIXPACKConnected(struct PORTCONTROL * PORT, struct _LINKTABLE * LINK)
{
// UA received when connecting
struct TNCINFO * TNC = PORT->TNC;
struct STREAMINFO * STREAM = &TNC->Streams[0];
struct WL2KInfo * WL2K = TNC->WL2K;
TRANSPORTENTRY * SESS;
char Call[16] = "";
char OrigCall[16] = "";
if (STREAM->Connecting)
{
STREAM->Connecting = FALSE;
STREAM->Connected = TRUE;
Call[ConvFromAX25(LINK->LINKCALL, Call)] = 0;
OrigCall[ConvFromAX25(LINK->OURCALL, OrigCall)] = 0;
TNC->SessionTimeLimit = TNC->DefaultSessionTimeLimit; // Reset Limit
SESS = TNC->PortRecord->ATTACHEDSESSIONS[0];
SESS->Mode = TNC->WL2KMode;
if (TNC->RIG && TNC->RIG != &TNC->DummyRig && strcmp(TNC->RIG->RigName, "PTT"))
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound Freq %s", OrigCall, Call, TNC->RIG->Valchar);
SESS->Frequency = (int)(atof(TNC->RIG->Valchar) * 1000000.0) + 1500; // Convert to Centre Freq
if (SESS->Frequency == 1500)
{
// try to get from WL2K record
if (WL2K)
{
SESS->Frequency = WL2K->Freq;
}
}
SESS->Mode = TNC->WL2KMode;
}
else
{
sprintf(TNC->WEB_TNCSTATE, "%s Connected to %s Outbound", OrigCall, Call);
if (WL2K)
{
SESS->Frequency = WL2K->Freq;
SESS->Mode = WL2K->mode;
}
}
if (WL2K)
strcpy(SESS->RMSCall, WL2K->RMSCall);
MySetWindowText(TNC->xIDC_TNCSTATE, TNC->WEB_TNCSTATE);
}
}
VOID x6PackThead(void * portptr);
int Connectto6Pack(int port)
{
_beginthread(x6PackThead, 0, (void *)(size_t)port);
return 0;
}
VOID x6PackThead(void * portptr)
{
// 6pack over TNC thread
// Opens socket and looks for data
int port = (int)(size_t)portptr;
char Msg[255];
int err, i, ret;
u_long param=1;
BOOL bcopt=TRUE;
struct hostent * HostEnt;
struct TNCINFO * TNC = TNCInfo[port];
fd_set readfs;
fd_set errorfs;
struct timeval timeout;
char * ptr1;
char * ptr2;
UINT * buffptr;
int reinitTimer = 0;
int linkactive = 0;
struct sixPackPortInfo * sixPack = TNC->sixPack;
if (sixPack == NULL || TNC->HostName == NULL)
return;
TNC->BusyFlags = 0;
TNC->CONNECTING = TRUE;
Sleep(3000); // Allow init to complete
#ifdef WIN32
if (strcmp(TNC->HostName, "127.0.0.1") == 0)
{
// can only check if running on local host
TNC->PID = GetListeningPortsPID(TNC->destaddr.sin_port);
if (TNC->PID == 0)
{
TNC->CONNECTING = FALSE;
return; // Not listening so no point trying to connect
}
// Get the File Name in case we want to restart it.
if (TNC->ProgramPath == NULL)
{
if (GetModuleFileNameExPtr)
{
HANDLE hProc;
char ExeName[256] = "";
hProc = OpenProcess(PROCESS_QUERY_INFORMATION |PROCESS_VM_READ, FALSE, TNC->PID);
if (hProc)
{
GetModuleFileNameExPtr(hProc, 0, ExeName, 255);
CloseHandle(hProc);
TNC->ProgramPath = _strdup(ExeName);
}
}
}
}
#endif
// // If we started the TNC make sure it is still running.
// if (!IsProcess(TNC->PID))
// {
// RestartTNC(TNC);
// Sleep(3000);
// }
TNC->destaddr.sin_addr.s_addr = inet_addr(TNC->HostName);
if (TNC->destaddr.sin_addr.s_addr == INADDR_NONE)
{
// Resolve name to address
HostEnt = gethostbyname (TNC->HostName);
if (!HostEnt)
{
TNC->CONNECTING = FALSE;
return; // Resolve failed
}
memcpy(&TNC->destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
memcpy(&TNC->Datadestaddr.sin_addr.s_addr,HostEnt->h_addr,4);
}
// closesocket(TNC->TCPSock);
TNC->TCPSock=socket(AF_INET,SOCK_STREAM,0);
if (TNC->TCPSock == INVALID_SOCKET)
{
i=sprintf(Msg, "Socket Failed for 6Pack socket - error code = %d\r\n", WSAGetLastError());
WritetoConsole(Msg);
TNC->CONNECTING = FALSE;
return;
}
setsockopt(TNC->TCPSock, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt, 4);
sinx.sin_family = AF_INET;
sinx.sin_addr.s_addr = INADDR_ANY;
sinx.sin_port = 0;
if (connect(TNC->TCPSock,(LPSOCKADDR) &TNC->destaddr,sizeof(TNC->destaddr)) == 0)
{
//
// Connected successful
//
}
else
{
if (TNC->Alerted == FALSE)
{
err=WSAGetLastError();
i=sprintf(Msg, "Connect Failed for 6Pack socket - error code = %d\r\n", err);
WritetoConsole(Msg);
sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC failed");
MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
TNC->Alerted = TRUE;
}
closesocket(TNC->TCPSock);
TNC->TCPSock = 0;
TNC->CONNECTING = FALSE;
return;
}
Sleep(1000);
TNC->LastFreq = 0; // so V4 display will be updated
TNC->CONNECTING = FALSE;
TNC->CONNECTED = TRUE;
TNC->BusyFlags = 0;
TNC->InputLen = 0;
TNC->Alerted = TRUE;
sprintf(TNC->WEB_COMMSSTATE, "Connected to TNC");
MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
FreeSemaphore(&Semaphore);
sprintf(Msg, "Connected to TNC Port %d\r\n", TNC->Port);
WritetoConsole(Msg);
while (TNC->CONNECTED)
{
FD_ZERO(&readfs);
FD_ZERO(&errorfs);
FD_SET(TNC->TCPSock,&readfs);
FD_SET(TNC->TCPSock,&errorfs);
timeout.tv_sec = 1;
timeout.tv_usec = 0; // We use this to run timeouts
ret = select((int)TNC->TCPSock + 1, &readfs, NULL, &errorfs, &timeout);
if (ret == SOCKET_ERROR)
{
Debugprintf("6Pack Select failed %d ", WSAGetLastError());
goto Lost;
}
if (ret > 0)
{
// See what happened
if (FD_ISSET(TNC->TCPSock, &readfs))
{
GetSemaphore(&Semaphore, 52);
SixPackProcessReceivedPacket(TNC);
FreeSemaphore(&Semaphore);
}
if (FD_ISSET(TNC->TCPSock, &errorfs))
{
Lost:
sprintf(Msg, "6Pack Connection lost for Port %d\r\n", TNC->Port);
WritetoConsole(Msg);
sprintf(TNC->WEB_COMMSSTATE, "Connection to TNC lost");
MySetWindowText(TNC->xIDC_COMMSSTATE, TNC->WEB_COMMSSTATE);
TNC->CONNECTED = FALSE;
TNC->Alerted = FALSE;
if (TNC->PTTMode)
Rig_PTT(TNC, FALSE); // Make sure PTT is down
if (TNC->Streams[0].Attached)
TNC->Streams[0].ReportDISC = TRUE;
TNC->TCPSock = 0;
return;
}
}
else
{
// Timeout. See if we need to reinit tnc chain
if (sixPack->linkOK == 0)
{
sixPack->reinitTimer++;
if (sixPack->reinitTimer > 30)
{
// send SIXP_INIT_CMD which should rescan the tnc chain
unsigned char Msg = SIXP_INIT_CMD;
int txlen = send(TNC->TCPSock, &Msg, 1, 0);
sixPack->reinitTimer = 0;
}
}
}
}
sprintf(Msg, "6Pack Thread Terminated Port %d\r\n", TNC->Port);
WritetoConsole(Msg);
}