diff --git a/kissserial.c b/kissserial.c
new file mode 100644
index 0000000..aeab11b
--- /dev/null
+++ b/kissserial.c
@@ -0,0 +1,506 @@
+//
+// 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 .
+//
+
+
+/*------------------------------------------------------------------
+ *
+ * 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
+#include
+#include
+#include
+#include
+#include
+
+
+#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.
+ *
+ * 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.
+ *
+ *--------------------------------------------------------------------*/
+
+
+void kissserial_send_rec_packet (int chan, unsigned char *fbuf, int flen, int client)
+{
+ 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;
+ }
+
+ stemp[0] = (chan << 4) + 0;
+ 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 */