2017-05-03 22:33:31 +00:00
|
|
|
//
|
|
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2011, 2013, 2014, 2016, 2017 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 <http://www.gnu.org/licenses/>.
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Module: kissserial.c
|
|
|
|
*
|
|
|
|
* Purpose: Act as a virtual KISS TNC for use by other packet radio applications.
|
|
|
|
* This file provides the service by good old fashioned serial port.
|
|
|
|
* Other files implement a pseudo terminal or TCP KISS interface.
|
|
|
|
*
|
|
|
|
* Description: This implements the KISS TNC protocol as described in:
|
|
|
|
* http://www.ka9q.net/papers/kiss.html
|
|
|
|
*
|
|
|
|
* Briefly, a frame is composed of
|
|
|
|
*
|
|
|
|
* * FEND (0xC0)
|
|
|
|
* * Contents - with special escape sequences so a 0xc0
|
|
|
|
* byte in the data is not taken as end of frame.
|
|
|
|
* as part of the data.
|
|
|
|
* * FEND
|
|
|
|
*
|
|
|
|
* The first byte of the frame contains:
|
|
|
|
*
|
|
|
|
* * port number in upper nybble.
|
|
|
|
* * command in lower nybble.
|
|
|
|
*
|
|
|
|
* Commands from application recognized:
|
|
|
|
*
|
|
|
|
* _0 Data Frame AX.25 frame in raw format.
|
|
|
|
*
|
|
|
|
* _1 TXDELAY See explanation in xmit.c.
|
|
|
|
*
|
|
|
|
* _2 Persistence " "
|
|
|
|
*
|
|
|
|
* _3 SlotTime " "
|
|
|
|
*
|
|
|
|
* _4 TXtail " "
|
|
|
|
* Spec says it is obsolete but Xastir
|
|
|
|
* sends it and we respect it.
|
|
|
|
*
|
|
|
|
* _5 FullDuplex Ignored.
|
|
|
|
*
|
|
|
|
* _6 SetHardware TNC specific.
|
|
|
|
*
|
|
|
|
* FF Return Exit KISS mode. Ignored.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Messages sent to client application:
|
|
|
|
*
|
|
|
|
* _0 Data Frame Received AX.25 frame in raw format.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Platform differences:
|
|
|
|
*
|
|
|
|
* This file implements KISS over a serial port.
|
|
|
|
* It should behave pretty much the same for both Windows and Linux.
|
|
|
|
*
|
|
|
|
* When running a client application on Windows, two applications
|
|
|
|
* can be connected together using a a "Null-modem emulator"
|
|
|
|
* such as com0com from http://sourceforge.net/projects/com0com/
|
|
|
|
*
|
|
|
|
* (When running a client application, on the same host, with Linux,
|
|
|
|
* a pseudo terminal can be used for old applications. More modern
|
|
|
|
* applications will generally have AGW and/or KISS over TCP.)
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* version 1.5: Split out from kiss.c, simplified, consistent for Windows and Linux.
|
|
|
|
* Add polling option for use with Bluetooth.
|
|
|
|
*
|
|
|
|
*---------------------------------------------------------------*/
|
|
|
|
|
|
|
|
#include "direwolf.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
|
|
|
|
#include "ax25_pad.h"
|
|
|
|
#include "textcolor.h"
|
|
|
|
#include "serial_port.h"
|
|
|
|
#include "kissserial.h"
|
|
|
|
#include "kiss_frame.h"
|
|
|
|
#include "xmit.h"
|
|
|
|
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
typedef HANDLE MYFDTYPE;
|
|
|
|
#define MYFDERROR INVALID_HANDLE_VALUE
|
|
|
|
#else
|
|
|
|
typedef int MYFDTYPE;
|
|
|
|
#define MYFDERROR (-1)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save Configuration for later use.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static struct misc_config_s *g_misc_config_p;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Accumulated KISS frame and state of decoder.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static kiss_frame_t kf;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The serial port device handle.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static MYFDTYPE serialport_fd = MYFDERROR;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: define in one place, use everywhere.
|
|
|
|
#if __WIN32__
|
|
|
|
#define THREAD_F unsigned __stdcall
|
|
|
|
#else
|
|
|
|
#define THREAD_F void *
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static THREAD_F kissserial_listen_thread (void *arg);
|
|
|
|
|
|
|
|
|
|
|
|
static int kissserial_debug = 0; /* Print information flowing from and to client. */
|
|
|
|
|
|
|
|
void kissserial_set_debug (int n)
|
|
|
|
{
|
|
|
|
kissserial_debug = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* In server.c. Should probably move to some misc. function file. */
|
|
|
|
|
|
|
|
void hex_dump (unsigned char *p, int len);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: kissserial_init
|
|
|
|
*
|
|
|
|
* Purpose: Set up a serial port acting as a virtual KISS TNC.
|
|
|
|
*
|
|
|
|
* Inputs: mc->
|
|
|
|
* kiss_serial_port - Name of device for real or virtual serial port.
|
|
|
|
* kiss_serial_speed - Speed, bps, or 0 meaning leave it alone.
|
|
|
|
* kiss_serial_poll - When non-zero, poll each n seconds to see if
|
|
|
|
* device has appeared.
|
|
|
|
*
|
|
|
|
* Outputs:
|
|
|
|
*
|
|
|
|
* Description: (1) Open file descriptor for the device.
|
|
|
|
* (2) Start a new thread to listen for commands from client app
|
|
|
|
* so the main application doesn't block while we wait.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
void kissserial_init (struct misc_config_s *mc)
|
|
|
|
{
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
HANDLE kissserial_listen_th;
|
|
|
|
#else
|
|
|
|
pthread_t kissserial_listen_tid;
|
|
|
|
int e;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
g_misc_config_p = mc;
|
|
|
|
|
|
|
|
memset (&kf, 0, sizeof(kf));
|
|
|
|
|
|
|
|
|
|
|
|
if (strlen(g_misc_config_p->kiss_serial_port) > 0) {
|
|
|
|
|
|
|
|
if (g_misc_config_p->kiss_serial_poll == 0) {
|
|
|
|
|
|
|
|
// Normal case, try to open the serial port at start up time.
|
|
|
|
|
|
|
|
serialport_fd = serial_port_open (g_misc_config_p->kiss_serial_port, g_misc_config_p->kiss_serial_speed);
|
|
|
|
|
|
|
|
if (serialport_fd != MYFDERROR) {
|
|
|
|
text_color_set(DW_COLOR_INFO);
|
|
|
|
dw_printf ("Opened %s for serial port KISS.\n", g_misc_config_p->kiss_serial_port);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// An error message was already displayed.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Polling case. Defer until read and device not opened.
|
|
|
|
text_color_set(DW_COLOR_INFO);
|
|
|
|
dw_printf ("Will be checking periodically for %s\n", g_misc_config_p->kiss_serial_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_misc_config_p->kiss_serial_poll != 0 || serialport_fd != MYFDERROR) {
|
|
|
|
#if __WIN32__
|
|
|
|
kissserial_listen_th = (HANDLE)_beginthreadex (NULL, 0, kissserial_listen_thread, NULL, 0, NULL);
|
|
|
|
if (kissserial_listen_th == NULL) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("Could not create kiss serial thread\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
e = pthread_create (&kissserial_listen_tid, NULL, kissserial_listen_thread, NULL);
|
|
|
|
if (e != 0) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
perror("Could not create kiss serial thread.");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
text_color_set (DW_COLOR_DEBUG);
|
|
|
|
|
|
|
|
dw_printf ("end of kiss_init: serialport_fd = %d, polling = %d\n", serialport_fd, g_misc_config_p->kiss_serial_poll);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: kissserial_send_rec_packet
|
|
|
|
*
|
|
|
|
* Purpose: Send a received packet or text string to the client app.
|
|
|
|
*
|
|
|
|
* Inputs: chan - Channel number where packet was received.
|
|
|
|
* 0 = first, 1 = second if any.
|
|
|
|
*
|
2017-09-12 02:28:54 +00:00
|
|
|
* kiss_cmd - Usually KISS_CMD_DATA_FRAME but we can also have
|
|
|
|
* KISS_CMD_SET_HARDWARE when responding to a query.
|
|
|
|
*
|
2017-05-03 22:33:31 +00:00
|
|
|
* pp - Identifier for packet object.
|
|
|
|
*
|
|
|
|
* fbuf - Address of raw received frame buffer
|
|
|
|
* or a text string.
|
|
|
|
*
|
|
|
|
* flen - Length of raw received frame not including the FCS
|
|
|
|
* or -1 for a text string.
|
|
|
|
*
|
|
|
|
* client - Not used for serial port version.
|
|
|
|
* Here so that 3 related functions all have
|
|
|
|
* the same parameter list.
|
|
|
|
*
|
|
|
|
* Description: Send message to client.
|
|
|
|
* We really don't care if anyone is listening or not.
|
|
|
|
* I don't even know if we can find out.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
2017-09-12 02:28:54 +00:00
|
|
|
void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf, int flen, int client)
|
2017-05-03 22:33:31 +00:00
|
|
|
{
|
|
|
|
unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
|
|
|
|
int kiss_len;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Quietly discard if we don't have open connection.
|
|
|
|
*/
|
|
|
|
if (serialport_fd == MYFDERROR) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flen < 0) {
|
|
|
|
flen = strlen((char*)fbuf);
|
|
|
|
if (kissserial_debug) {
|
|
|
|
kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
|
|
|
|
}
|
|
|
|
strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
|
|
|
|
kiss_len = strlen((char *)kiss_buff);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
|
|
|
|
|
|
|
|
if (flen > (int)(sizeof(stemp)) - 1) {
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("\nSerial Port KISS buffer too small. Truncated.\n\n");
|
|
|
|
flen = (int)(sizeof(stemp)) - 1;
|
|
|
|
}
|
|
|
|
|
2017-09-12 02:28:54 +00:00
|
|
|
stemp[0] = (chan << 4) | kiss_cmd;
|
2017-05-03 22:33:31 +00:00
|
|
|
memcpy (stemp+1, fbuf, flen);
|
|
|
|
|
|
|
|
if (kissserial_debug >= 2) {
|
|
|
|
/* AX.25 frame with the CRC removed. */
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("\n");
|
|
|
|
dw_printf ("Packet content before adding KISS framing and any escapes:\n");
|
|
|
|
hex_dump (fbuf, flen);
|
|
|
|
}
|
|
|
|
|
|
|
|
kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
|
|
|
|
|
|
|
|
/* This has KISS framing and escapes for sending to client app. */
|
|
|
|
|
|
|
|
if (kissserial_debug) {
|
|
|
|
kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This write can block on Windows if using the virtual null modem
|
|
|
|
* and nothing is connected to the other end.
|
|
|
|
* The solution is found in the com0com ReadMe file:
|
|
|
|
*
|
|
|
|
* Q. My application hangs during its startup when it sends anything to one paired
|
|
|
|
* COM port. The only way to unhang it is to start HyperTerminal, which is connected
|
|
|
|
* to the other paired COM port. I didn't have this problem with physical serial
|
|
|
|
* ports.
|
|
|
|
* A. Your application can hang because receive buffer overrun is disabled by
|
|
|
|
* default. You can fix the problem by enabling receive buffer overrun for the
|
|
|
|
* receiving port. Also, to prevent some flow control issues you need to enable
|
|
|
|
* baud rate emulation for the sending port. So, if your application use port CNCA0
|
|
|
|
* and other paired port is CNCB0, then:
|
|
|
|
*
|
|
|
|
* 1. Launch the Setup Command Prompt shortcut.
|
|
|
|
* 2. Enter the change commands, for example:
|
|
|
|
*
|
|
|
|
* command> change CNCB0 EmuOverrun=yes
|
|
|
|
* command> change CNCA0 EmuBR=yes
|
|
|
|
*/
|
|
|
|
|
|
|
|
err = serial_port_write (serialport_fd, (char*)kiss_buff, kiss_len);
|
|
|
|
|
|
|
|
if (err != kiss_len)
|
|
|
|
{
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("\nError sending KISS message to client application thru serial port.\n\n");
|
|
|
|
serial_port_close (serialport_fd);
|
|
|
|
serialport_fd = MYFDERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* kissserial_send_rec_packet */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: kissserial_get
|
|
|
|
*
|
|
|
|
* Purpose: Read one byte from the KISS client app.
|
|
|
|
*
|
|
|
|
* Global In: serialport_fd
|
|
|
|
*
|
|
|
|
* Returns: one byte (value 0 - 255) or terminate thread on error.
|
|
|
|
*
|
|
|
|
* Description: There is room for improvment here. Reading one byte
|
|
|
|
* at a time is inefficient. We could read a large block
|
|
|
|
* into a local buffer and return a byte from that most of the time.
|
|
|
|
* Is it worth the effort? I don't know. With GHz processors and
|
|
|
|
* the low data rate here it might not make a noticable difference.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
static int kissserial_get (void)
|
|
|
|
{
|
|
|
|
int ch; // normally 0-255 but -1 for error.
|
|
|
|
|
|
|
|
|
|
|
|
if (g_misc_config_p->kiss_serial_poll == 0) {
|
|
|
|
/*
|
|
|
|
* Normal case, was opened at start up time.
|
|
|
|
*/
|
|
|
|
ch = serial_port_get1 (serialport_fd);
|
|
|
|
|
|
|
|
if (ch < 0) {
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("\nSerial Port KISS read error. Closing connection.\n\n");
|
|
|
|
serial_port_close (serialport_fd);
|
|
|
|
serialport_fd = MYFDERROR;
|
|
|
|
#if __WIN32__
|
|
|
|
ExitThread (0);
|
|
|
|
#else
|
|
|
|
pthread_exit (NULL);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DEBUGx
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("kissserial_get(%d) returns 0x%02x\n", fd, ch);
|
|
|
|
#endif
|
|
|
|
return (ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Polling case. Wait until device is present and open.
|
|
|
|
*/
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
if (serialport_fd != MYFDERROR) {
|
|
|
|
|
|
|
|
// Open, try to read.
|
|
|
|
|
|
|
|
ch = serial_port_get1 (serialport_fd);
|
|
|
|
|
|
|
|
if (ch >= 0) {
|
|
|
|
return (ch);
|
|
|
|
}
|
|
|
|
|
|
|
|
text_color_set(DW_COLOR_ERROR);
|
|
|
|
dw_printf ("\nSerial Port KISS read error. Closing connection.\n\n");
|
|
|
|
serial_port_close (serialport_fd);
|
|
|
|
serialport_fd = MYFDERROR;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
|
|
|
|
// Not open. Wait for it to appear and try opening.
|
|
|
|
|
|
|
|
struct stat buf;
|
|
|
|
|
|
|
|
SLEEP_SEC (g_misc_config_p->kiss_serial_poll);
|
|
|
|
|
|
|
|
if (stat(g_misc_config_p->kiss_serial_port, &buf) == 0) {
|
|
|
|
|
|
|
|
// It's there now. Try to open.
|
|
|
|
|
|
|
|
serialport_fd = serial_port_open (g_misc_config_p->kiss_serial_port, g_misc_config_p->kiss_serial_speed);
|
|
|
|
|
|
|
|
if (serialport_fd != MYFDERROR) {
|
|
|
|
text_color_set(DW_COLOR_INFO);
|
|
|
|
dw_printf ("\nOpened %s for serial port KISS.\n\n", g_misc_config_p->kiss_serial_port);
|
|
|
|
|
|
|
|
memset (&kf, 0, sizeof(kf)); // Start with clean state.
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// An error message was already displayed.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} /* end kissserial_get */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* Name: kissserial_listen_thread
|
|
|
|
*
|
|
|
|
* Purpose: Read messages from serial port KISS client application.
|
|
|
|
*
|
|
|
|
* Global In: serialport_fd
|
|
|
|
*
|
|
|
|
* Description: Reads bytes from the serial port KISS client app and
|
|
|
|
* sends them to kiss_rec_byte for processing.
|
|
|
|
* kiss_rec_byte is a common function used by all 3 KISS
|
|
|
|
* interfaces: serial port, pseudo terminal, and TCP.
|
|
|
|
*
|
|
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
static THREAD_F kissserial_listen_thread (void *arg)
|
|
|
|
{
|
|
|
|
unsigned char ch;
|
|
|
|
|
|
|
|
#if DEBUG
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
|
|
dw_printf ("kissserial_listen_thread ( %d )\n", fd);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ch = kissserial_get();
|
|
|
|
kiss_rec_byte (&kf, ch, kissserial_debug, -1, kissserial_send_rec_packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
return(0);
|
|
|
|
#else
|
|
|
|
return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* end kissserial.c */
|