//
// This file is part of Dire Wolf, an amateur radio packet TNC.
//
// Copyright (C) 2016 John Langner, WB2OSZ
//
// This program 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 program 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 this program. If not, see .
//
/*------------------------------------------------------------------
*
* Module: tnctest.c
*
* Purpose: Test AX.25 connected mode between two TNCs.
*
* Description: The first TNC will connect to the second TNC and send a bunch of data.
* Proper transfer of data will be verified.
*
* Usage: tnctest [options] port0=name0 port1=name1
*
* Example: tnctest localhost:8000=direwolf COM1=KPC-3+
*
* Each port can have the following forms:
*
* * host-name:tcp-port
* * ip-addr:tcp-port
* * tcp-port
* * serial port name (e.g. COM1, /dev/ttyS0)
*
*---------------------------------------------------------------*/
/*
* Native Windows: Use the Winsock interface.
* Linux: Use the BSD socket interface.
*/
#include "direwolf.h" // Sets _WIN32_WINNT for XP API level needed by ws2tcpip.h
#if __WIN32__
#include
#include // _WIN32_WINNT must be set to 0x0501 before including this
#else
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#endif
#include
#include
#include
#include
#include
#include
//#include "ax25_pad.h"
#include "textcolor.h"
#include "dtime_now.h"
#include "serial_port.h"
/* We don't deal with big-endian processors here. */
/* TODO: Use agwlib (which did not exist when this was written) */
/* rather than duplicating the effort here. */
struct agwpe_s {
#if 1
unsigned char portx; /* 0 for first, 1 for second, etc. */
unsigned char reserved1;
unsigned char reserved2;
unsigned char reserved3;
unsigned char datakind; /* message type, usually written as a letter. */
unsigned char reserved4;
unsigned char pid;
unsigned char reserved5;
#else
short portx; /* 0 for first, 1 for second, etc. */
short port_hi_reserved;
short kind_lo; /* message type */
short kind_hi;
#endif
char call_from[10];
char call_to[10];
int data_len; /* Number of data bytes following. */
int user_reserved;
};
#if __WIN32__
static unsigned __stdcall tnc_thread_net (void *arg);
static unsigned __stdcall tnc_thread_serial (void *arg);
#else
static void * tnc_thread_net (void *arg);
static void * tnc_thread_serial (void *arg);
#endif
static void tnc_connect (int from, int to);
static void tnc_disconnect (int from, int to);
static void tnc_send_data (int from, int to, char * data);
static void tnc_reset (int from, int to);
/*
* Convert Internet address to text.
* Can't use InetNtop because it is supported only on Windows Vista and later.
*/
static char * ia_to_text (int Family, void * pAddr, char * pStringBuf, size_t StringBufSize)
{
struct sockaddr_in *sa4;
struct sockaddr_in6 *sa6;
switch (Family) {
case AF_INET:
sa4 = (struct sockaddr_in *)pAddr;
#if __WIN32__
snprintf (pStringBuf, StringBufSize, "%d.%d.%d.%d", sa4->sin_addr.S_un.S_un_b.s_b1,
sa4->sin_addr.S_un.S_un_b.s_b2,
sa4->sin_addr.S_un.S_un_b.s_b3,
sa4->sin_addr.S_un.S_un_b.s_b4);
#else
inet_ntop (AF_INET, &(sa4->sin_addr), pStringBuf, StringBufSize);
#endif
break;
case AF_INET6:
sa6 = (struct sockaddr_in6 *)pAddr;
#if __WIN32__
snprintf (pStringBuf, StringBufSize, "%x:%x:%x:%x:%x:%x:%x:%x",
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[0]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[1]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[2]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[3]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[4]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[5]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[6]),
ntohs(((unsigned short *)(&(sa6->sin6_addr)))[7]));
#else
inet_ntop (AF_INET6, &(sa6->sin6_addr), pStringBuf, StringBufSize);
#endif
break;
default:
snprintf (pStringBuf, StringBufSize, "Invalid address family!");
}
assert (strlen(pStringBuf) < StringBufSize);
return pStringBuf;
}
/*------------------------------------------------------------------
*
* Name: main
*
* Purpose: Basic test for connected AX.25 data mode between TNCs.
*
* Usage: Described above.
*
*---------------------------------------------------------------*/
#define MAX_TNC 2 // Just 2 for now.
// Could be more later for multiple concurrent connections.
/* Obtained from the command line. */
static int num_tnc; /* How many TNCs for this test? */
/* Initially only 2 but long term we might */
/* enhance it to allow multiple concurrent connections. */
static char hostname[MAX_TNC][50]; /* DNS host name or IPv4 address. */
/* Some of the code is there for IPv6 but */
/* needs more work. */
/* Defaults to "localhost" if not specified. */
static char port[MAX_TNC][30]; /* If it begins with a digit, it is considered */
/* a TCP port number at the hostname. */
/* Otherwise, we treat it as a serial port name. */
static char description[MAX_TNC][50]; /* Name used in the output. */
static int using_tcp[MAX_TNC]; /* Are we using TCP or serial port for each TNC? */
/* Use corresponding one of the next two. */
static int server_sock[MAX_TNC]; /* File descriptor for AGW socket interface. */
/* Set to -1 if not used. */
/* (Don't use SOCKET type because it is unsigned.) */
static MYFDTYPE serial_fd[MAX_TNC]; /* Serial port handle. */
static volatile int busy[MAX_TNC]; /* True when TNC busy and can't accept more data. */
/* For serial port, this is set by XON / XOFF characters. */
#define XOFF 0x13
#define XON 0x11
static char tnc_address[MAX_TNC][12]; /* Name of the TNC used in the frames. Originally, this */
/* was simply TNC0 and TNC1 but that can get hard to read */
/* and confusing. Later used DW0, DW1, for direwolf */
/* so the direction of flow is easier to grasp. */
#if __WIN32__
static HANDLE tnc_th[MAX_TNC];
#else
static pthread_t tnc_tid[MAX_TNC];
#endif
#define LINE_WIDTH 80
//#define LINE_WIDTH 120 /* If I was more ambitious I might try to get */
/* this from the terminal properties. */
static int column_width; /* Line width divided by number of TNCs. */
/*
* Current state for each TNC.
*/
static int is_connected[MAX_TNC]; /* -1 = not yet available. */
/* 0 = not connected. */
/* 1 = not connected. */
static int have_cmd_prompt[MAX_TNC]; /* Set if "cmd:" was the last thing seen. */
static int last_rec_seq[MAX_TNC]; /* Each data packet will contain a sequence number. */
/* This is used to verify that all have been */
/* received in the correct order. */
/*
* Start time so we can print relative elapsed time.
*/
static double start_dtime;
static int max_count;
int main (int argc, char *argv[])
{
int j;
int timeout;
int send_count = 0;
int burst_size = 1;
int errors = 0;
//max_count = 20;
max_count = 200;
//max_count = 6;
max_count = 1000;
max_count = 9999;
#if __WIN32__
#else
int e;
setlinebuf (stdout);
#endif
start_dtime = dtime_now();
/*
* Extract command line args.
*/
num_tnc = argc - 1;
if (num_tnc < 2 || num_tnc > MAX_TNC) {
printf ("Specify minimum 2, maximum %d TNCs on the command line.\n", MAX_TNC);
exit (EXIT_FAILURE);
}
column_width = LINE_WIDTH / num_tnc;
for (j=0; j 0) {
SLEEP_MS(100);
timeout--;
ready = 1;
for (j=0; j last0) {
last0 = last_rec_seq[0];
no_activity = 0;
}
if (last_rec_seq[1] > last1) {
last1 = last_rec_seq[1];
no_activity = 0;
}
}
if (last_rec_seq[0] == max_count) {
printf ("Got last expected reply.\n");
}
else {
printf ("ERROR: Timeout - No incoming activity for %d seconds.\n", no_activity);
errors++;
}
/*
* Did we get all expected replies?
*/
if (last_rec_seq[0] != max_count) {
printf ("ERROR: Last received reply was %d when we were expecting %d.\n", last_rec_seq[0], max_count);
errors++;
}
/*
* Ask for disconnect. Wait until complete.
*/
tnc_disconnect (0, 1);
timeout = 200; // 20 sec should be generous.
ready = 0;
while ( ! ready && timeout > 0) {
SLEEP_MS(100);
timeout--;
ready = 1;
for (j=0; j1 for the other end which answers.
*
* data - Should look something like this:
* 9999 send data
* 9999 reply
*
* Global In/Out: last_rec_seq[my_index]
*
* Description: Look for expected format.
* Extract the sequence number.
* Verify that it is the next expected one.
* Update it.
*
*--------------------------------------------------------------------*/
void process_rec_data (int my_index, char *data)
{
int n;
if (isdigit(*data) && strncmp(data+4, " send", 5) == 0) {
if (my_index > 0) {
last_rec_seq[my_index]++;
n = atoi(data);
if (n != last_rec_seq[my_index]) {
printf ("%*s%s: Received %d when %d was expected.\n", my_index*column_width, "", tnc_address[my_index], n, last_rec_seq[my_index]);
SLEEP_MS(10000);
printf ("TEST FAILED!\n");
exit (EXIT_FAILURE);
}
}
}
else if (isdigit(*data) && strncmp(data+4, " reply", 6) == 0) {
if (my_index == 0) {
last_rec_seq[my_index]++;
n = atoi(data);
if (n != last_rec_seq[my_index]) {
printf ("%*s%s: Received %d when %d was expected.\n", my_index*column_width, "", tnc_address[my_index], n, last_rec_seq[my_index]);
SLEEP_MS(10000);
printf ("TEST FAILED!\n");
exit (EXIT_FAILURE);
}
}
}
else if (data[0] == 'A') {
if (strncmp(data, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", strlen(data)-1) != 0) {
printf ("%*s%s: Segmentation is broken.\n", my_index*column_width, "", tnc_address[my_index]);
SLEEP_MS(10000);
printf ("TEST FAILED!\n");
exit (EXIT_FAILURE);
}
}
}
/*-------------------------------------------------------------------
*
* Name: tnc_thread_net
*
* Purpose: Establish connection with a TNC via network.
*
* Inputs: arg - My instance index, 0 thru MAX_TNC-1.
*
* Outputs: packets - Received packets are put in the corresponding column
* and sent to a common function to check that they
* all arrived in order.
*
* Global Out: is_connected - Updated when connected/disconnected notifications are received.
*
* Description: Perform any necessary configuration for the TNC then wait
* for responses and process them.
*
*--------------------------------------------------------------------*/
#define MAX_HOSTS 30
#if __WIN32__
static unsigned __stdcall tnc_thread_net (void *arg)
#else
static void * tnc_thread_net (void *arg)
#endif
{
int my_index;
struct addrinfo hints;
struct addrinfo *ai_head = NULL;
struct addrinfo *ai;
struct addrinfo *hosts[MAX_HOSTS];
int num_hosts, n;
int err;
char ipaddr_str[46]; /* text form of IP address */
#if __WIN32__
WSADATA wsadata;
#endif
struct agwpe_s mon_cmd;
char data[4096];
double dnow;
my_index = (int)(ptrdiff_t)arg;
#if DEBUGx
printf ("DEBUG: tnc_thread_net %d start, port = '%s'\n", my_index, port[my_index]);
#endif
#if __WIN32__
err = WSAStartup (MAKEWORD(2,2), &wsadata);
if (err != 0) {
printf("WSAStartup failed: %d\n", err);
return (0);
}
if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) {
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
//sleep (1);
return (0);
}
#endif
memset (&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC; /* Allow either IPv4 or IPv6. */
// hints.ai_family = AF_INET; /* IPv4 only. */
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
/*
* Connect to TNC server.
*/
ai_head = NULL;
err = getaddrinfo(hostname[my_index], port[my_index], &hints, &ai_head);
if (err != 0) {
#if __WIN32__
printf ("Can't get address for server %s, err=%d\n",
hostname[my_index], WSAGetLastError());
#else
printf ("Can't get address for server %s, %s\n",
hostname[my_index], gai_strerror(err));
#endif
freeaddrinfo(ai_head);
exit (1);
}
#if DEBUG_DNS
printf ("getaddrinfo returns:\n");
#endif
num_hosts = 0;
for (ai = ai_head; ai != NULL; ai = ai->ai_next) {
#if DEBUG_DNS
ia_to_text (ai->ai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
printf (" %s\n", ipaddr_str);
#endif
hosts[num_hosts] = ai;
if (num_hosts < MAX_HOSTS) num_hosts++;
}
#if DEBUG_DNS
printf ("addresses for hostname:\n");
for (n=0; nai_family, hosts[n]->ai_addr, ipaddr_str, sizeof(ipaddr_str));
printf (" %s\n", ipaddr_str);
}
#endif
// Try each address until we find one that is successful.
for (n=0; nai_family, ai->ai_addr, ipaddr_str, sizeof(ipaddr_str));
is = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
#if __WIN32__
if (is == INVALID_SOCKET) {
printf ("Socket creation failed, err=%d", WSAGetLastError());
WSACleanup();
is = -1;
continue;
}
#else
if (err != 0) {
printf ("Socket creation failed, err=%s", gai_strerror(err));
(void) close (is);
is = -1;
continue;
}
#endif
#ifndef DEBUG_DNS
err = connect(is, ai->ai_addr, (int)ai->ai_addrlen);
#if __WIN32__
if (err == SOCKET_ERROR) {
#if DEBUGx
printf("Connect to %s on %s (%s), port %s failed.\n",
description[my_index], hostname[my_index], ipaddr_str, port[my_index]);
#endif
closesocket (is);
is = -1;
continue;
}
#else
if (err != 0) {
#if DEBUGx
printf("Connect to %s on %s (%s), port %s failed.\n",
description[my_index], hostname[my_index], ipaddr_str, port[my_index]);
#endif
(void) close (is);
is = -1;
continue;
}
int flag = 1;
err = setsockopt (is, IPPROTO_TCP, TCP_NODELAY, (void*)(long)(&flag), (socklen_t)sizeof(flag));
if (err < 0) {
printf("setsockopt TCP_NODELAY failed.\n");
}
#endif
/* Success. */
server_sock[my_index] = is;
#endif
break;
}
freeaddrinfo(ai_head);
if (server_sock[my_index] == -1) {
printf("TNC %d unable to connect to %s on %s (%s), port %s\n",
my_index, description[my_index], hostname[my_index], ipaddr_str, port[my_index] );
exit (1);
}
#if 1 // Temp test just to get something.
/*
* Send command to toggle reception of frames in raw format.
*/
memset (&mon_cmd, 0, sizeof(mon_cmd));
mon_cmd.datakind = 'k';
SOCK_SEND(server_sock[my_index], (char*)(&mon_cmd), sizeof(mon_cmd));
#endif
/*
* Send command to register my callsign for incoming connect request.
* Not really needed when we initiate the connection.
*/
memset (&mon_cmd, 0, sizeof(mon_cmd));
mon_cmd.datakind = 'X';
strlcpy (mon_cmd.call_from, tnc_address[my_index], sizeof(mon_cmd.call_from));
SOCK_SEND(server_sock[my_index], (char*)(&mon_cmd), sizeof(mon_cmd));
/*
* Inform main program and observer that we are ready to go.
*/
printf("TNC %d now available. %s on %s (%s), port %s\n",
my_index, description[my_index], hostname[my_index], ipaddr_str, port[my_index] );
is_connected[my_index] = 0;
/*
* Print what we get from TNC.
*/
while (1) {
int n;
n = SOCK_RECV (server_sock[my_index], (char*)(&mon_cmd), sizeof(mon_cmd));
if (n != sizeof(mon_cmd)) {
printf ("Read error, TNC %d received %d command bytes.\n", my_index, n);
exit (1);
}
#if DEBUGx
printf ("TNC %d received '%c' data, data_len = %d\n",
my_index, mon_cmd.datakind, mon_cmd.data_len);
#endif
assert (mon_cmd.data_len >= 0 && mon_cmd.data_len < (int)(sizeof(data)));
if (mon_cmd.data_len > 0) {
n = SOCK_RECV (server_sock[my_index], data, mon_cmd.data_len);
if (n != mon_cmd.data_len) {
printf ("Read error, TNC %d received %d data bytes.\n", my_index, n);
exit (1);
}
data[mon_cmd.data_len] = '\0';
}
/*
* What did we get?
*/
dnow = dtime_now();
switch (mon_cmd.datakind) {
case 'C': // AX.25 Connection Received
printf("%*s[R %.3f] *** Connected to %s ***\n", my_index*column_width, "", dnow-start_dtime, mon_cmd.call_from);
is_connected[my_index] = 1;
break;
case 'D': // Connected AX.25 Data
printf("%*s[R %.3f] %s\n", my_index*column_width, "", dnow-start_dtime, data);
process_rec_data (my_index, data);
if (isdigit(data[0]) && isdigit(data[1]) && isdigit(data[2]) && isdigit(data[3]) &&
strncmp(data+4, " send", 5) == 0) {
// Expected message. Make sure it is expected sequence and send reply.
int n = atoi(data);
char reply[80];
snprintf (reply, sizeof(reply), "%04d reply\r", n);
tnc_send_data (my_index, 1 - my_index, reply);
// HACK!
// It gets very confusing because N(S) and N(R) are very close.
// Send a couple dozen I frames so they will be easier to distinguish visually.
// Currently don't have the same in serial port version.
// We change the length each time to test segmentation.
// Set PACLEN to some very small number like 5.
if (n == 1 && max_count > 1) {
int j;
for (j = 1; j <= 26; j++) {
snprintf (reply, sizeof(reply), "%.*s\r", j, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
tnc_send_data (my_index, 1 - my_index, reply);
}
}
}
break;
case 'd': // Disconnected
printf("%*s[R %.3f] *** Disconnected from %s ***\n", my_index*column_width, "", dnow-start_dtime, mon_cmd.call_from);
is_connected[my_index] = 0;
break;
case 'y': // Outstanding frames waiting on a Port
printf("%*s[R %.3f] *** Outstanding frames waiting %d ***\n", my_index*column_width, "", dnow-start_dtime, 123); // TODO
break;
default:
//printf("%*s[R %.3f] --- Ignoring cmd kind '%c' ---\n", my_index*column_width, "", dnow-start_dtime, mon_cmd.datakind);
break;
}
}
} /* end tnc_thread_net */
/*-------------------------------------------------------------------
*
* Name: tnc_thread_serial
*
* Purpose: Establish connection with a TNC via serial port.
*
* Inputs: arg - My instance index, 0 thru MAX_TNC-1.
*
* Outputs: packets - Received packets are put in the corresponding column
* and sent to a common function to check that they
* all arrived in order.
*
* Global Out: is_connected - Updated when connected/disconnected notifications are received.
*
* Description: Perform any necessary configuration for the TNC then wait
* for responses and process them.
*
*--------------------------------------------------------------------*/
#if __WIN32__
typedef HANDLE MYFDTYPE;
#define MYFDERROR INVALID_HANDLE_VALUE
#else
typedef int MYFDTYPE;
#define MYFDERROR (-1)
#endif
#if __WIN32__
static unsigned __stdcall tnc_thread_serial (void *arg)
#else
static void * tnc_thread_serial (void *arg)
#endif
{
int my_index = (int)(ptrdiff_t)arg;
char cmd[80];
serial_fd[my_index] = serial_port_open (port[my_index], 9600);
if (serial_fd[my_index] == MYFDERROR) {
printf("TNC %d unable to connect to %s on %s.\n",
my_index, description[my_index], port[my_index] );
exit (1);
}
/*
* Make sure we are in command mode.
*/
strcpy (cmd, "\003\rreset\r");
serial_port_write (serial_fd[my_index], cmd, strlen(cmd));
SLEEP_MS (3000);
strcpy (cmd, "echo on\r");
serial_port_write (serial_fd[my_index], cmd, strlen(cmd));
SLEEP_MS (200);
// do any necessary set up here. such as setting mycall
snprintf (cmd, sizeof(cmd), "mycall %s\r", tnc_address[my_index]);
serial_port_write (serial_fd[my_index], cmd, strlen(cmd));
SLEEP_MS (200);
// Don't want to stop tty output when typing begins.
strcpy (cmd, "flow off\r");
serial_port_write (serial_fd[my_index], cmd, strlen(cmd));
strcpy (cmd, "echo off\r");
serial_port_write (serial_fd[my_index], cmd, strlen(cmd));
/* Success. */
printf("TNC %d now available. %s on %s\n",
my_index, description[my_index], port[my_index] );
is_connected[my_index] = 0;
/*
* Read and print.
*/
while (1) {
int ch;
char result[500];
int len;
int done;
len = 0;
result[len] = '\0';
done = 0;
while ( ! done) {
ch = serial_port_get1(serial_fd[my_index]);
if (ch < 0) {
printf("TNC %d fatal read error.\n", my_index);
exit (1);
}
if (ch == '\r' || ch == '\n') {
done = 1;
}
else if (ch == XOFF) {
double dnow = dtime_now();
printf("%*s[R %.3f] \n", my_index*column_width, "", dnow-start_dtime);
busy[my_index] = 1;
}
else if (ch == XON) {
double dnow = dtime_now();
printf("%*s[R %.3f] \n", my_index*column_width, "", dnow-start_dtime);
busy[my_index] = 0;
}
else if (isprint(ch)) {
result[len] = ch;
len++;
result[len] = '\0';
}
else {
char hex[12];
snprintf (hex, sizeof(hex), "", ch);
strlcat (result, hex, sizeof(result));
len = strlen(result);
}
if (strcmp(result, "cmd:") == 0) {
done = 1;
have_cmd_prompt[my_index] = 1;
}
else {
have_cmd_prompt[my_index] = 0;
}
}
if (len > 0) {
double dnow = dtime_now();
printf("%*s[R %.3f] %s\n", my_index*column_width, "", dnow-start_dtime, result);
if (strncmp(result, "*** CONNECTED", 13) == 0) {
is_connected[my_index] = 1;
}
if (strncmp(result, "*** DISCONNECTED", 16) == 0) {
is_connected[my_index] = 0;
}
if (strncmp(result, "Not while connected", 19) == 0) {
// Not expecting this.
// What to do?
}
process_rec_data (my_index, result);
if (isdigit(result[0]) && isdigit(result[1]) && isdigit(result[2]) && isdigit(result[3]) &&
strncmp(result+4, " send", 5) == 0) {
// Expected message. Make sure it is expected sequence and send reply.
int n = atoi(result);
char reply[80];
snprintf (reply, sizeof(reply), "%04d reply\r", n);
tnc_send_data (my_index, 1 - my_index, reply);
}
}
}
} /* end tnc_thread_serial */
static void tnc_connect (int from, int to)
{
double dnow = dtime_now();
printf("%*s[T %.3f] *** Send connect request ***\n", from*column_width, "", dnow-start_dtime);
if (using_tcp[from]) {
//struct agwpe_s {
// short portx; /* 0 for first, 1 for second, etc. */
// short port_hi_reserved;
// short datakind; /* message type */
// short kind_hi;
// char call_from[10];
// char call_to[10];
// int data_len; /* Number of data bytes following. */
// int user_reserved;
//};
struct agwpe_s cmd;
memset (&cmd, 0, sizeof(cmd));
cmd.datakind = 'C';
strlcpy (cmd.call_from, tnc_address[from], sizeof(cmd.call_from));
strlcpy (cmd.call_to, tnc_address[to], sizeof(cmd.call_to));
SOCK_SEND(server_sock[from], (char*)(&cmd), sizeof(cmd));
}
else {
char cmd[80];
if (! have_cmd_prompt[from]) {
SLEEP_MS (1500);
strcpy (cmd, "\003\003\003");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
SLEEP_MS (1500);
strcpy (cmd, "\r");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
SLEEP_MS (200);
}
snprintf (cmd, sizeof(cmd), "connect %s\r", tnc_address[to]);
serial_port_write (serial_fd[from], cmd, strlen(cmd));
}
} /* end tnc_connect */
static void tnc_disconnect (int from, int to)
{
double dnow = dtime_now();
printf("%*s[T %.3f] *** Send disconnect request ***\n", from*column_width, "", dnow-start_dtime);
if (using_tcp[from]) {
struct agwpe_s cmd;
memset (&cmd, 0, sizeof(cmd));
cmd.datakind = 'd';
strlcpy (cmd.call_from, tnc_address[from], sizeof(cmd.call_from));
strlcpy (cmd.call_to, tnc_address[to], sizeof(cmd.call_to));
SOCK_SEND(server_sock[from], (char*)(&cmd), sizeof(cmd));
}
else {
char cmd[80];
if (! have_cmd_prompt[from]) {
SLEEP_MS (1500);
strcpy (cmd, "\003\003\003");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
SLEEP_MS (1500);
strcpy (cmd, "\r");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
SLEEP_MS (200);
}
strcpy (cmd, "disconnect\r");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
}
} /* end tnc_disconnect */
static void tnc_reset (int from, int to)
{
double dnow = dtime_now();
printf("%*s[T %.3f] *** Send reset ***\n", from*column_width, "", dnow-start_dtime);
if (using_tcp[from]) {
}
else {
char cmd[80];
SLEEP_MS (1500);
strcpy (cmd, "\003\003\003");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
SLEEP_MS (1500);
strcpy (cmd, "\r");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
SLEEP_MS (200);
strcpy (cmd, "reset\r");
serial_port_write (serial_fd[from], cmd, strlen(cmd));
}
} /* end tnc_disconnect */
static void tnc_send_data (int from, int to, char * data)
{
double dnow = dtime_now();
printf("%*s[T %.3f] %s\n", from*column_width, "", dnow-start_dtime, data);
if (using_tcp[from]) {
struct {
struct agwpe_s hdr;
char data[256];
} cmd;
memset (&cmd.hdr, 0, sizeof(cmd.hdr));
cmd.hdr.datakind = 'D';
cmd.hdr.pid = 0xf0;
snprintf (cmd.hdr.call_from, sizeof(cmd.hdr.call_from), "%s", tnc_address[from]);
snprintf (cmd.hdr.call_to, sizeof(cmd.hdr.call_to), "%s", tnc_address[to]);
cmd.hdr.data_len = strlen(data);
strlcpy (cmd.data, data, sizeof(cmd.data));
SOCK_SEND(server_sock[from], (char*)(&cmd), sizeof(cmd.hdr) + strlen(data));
}
else {
// The assumption is that we are in CONVERS mode.
// The data should be terminated by carriage return.
int timeout = 600; // 60 sec. I've seen it take more than 20.
while (timeout > 0 && busy[from]) {
SLEEP_MS(100);
timeout--;
}
if (timeout == 0) {
printf ("ERROR: Gave up waiting while TNC busy.\n");
tnc_disconnect (0,1);
SLEEP_MS(5000);
printf ("TEST FAILED!\n");
exit (EXIT_FAILURE);
}
else {
serial_port_write (serial_fd[from], data, strlen(data));
}
}
} /* end tnc_disconnect */
/* end tnctest.c */