From bef8a97323eb13d7723c0727a8e7c2f30b54506f Mon Sep 17 00:00:00 2001 From: WB2OSZ Date: Wed, 3 May 2017 17:41:37 -0400 Subject: [PATCH] KISS TNC enhancements: Multiple TCP clients, serial port for Linux, polling for Bluetooth device. --- CHANGES.md | 18 ++ Makefile.linux | 21 +- Makefile.macosx | 25 +- Makefile.win | 19 +- config.c | 56 ++++- config.h | 15 +- direwolf.c | 21 +- fcs_calc.c | 16 ++ kiss.c | 614 ++++++++---------------------------------------- kiss.h | 8 +- kiss_frame.c | 86 +++++-- kiss_frame.h | 5 +- kissnet.c | 377 ++++++++++++++++++----------- kissnet.h | 2 +- serial_port.c | 45 ++-- server.c | 5 +- tt_user.c | 6 +- version.h | 4 +- 18 files changed, 613 insertions(+), 730 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0102c2e..0bed884 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,24 @@ # Revision History # +## Version 1.5 -- Development snapshot A -- May 2017 ## + +This is a snapshot of ongoing development towards version of 1.5. Some features might be incomplete or broken or not documented properly. + +### New Features: ### + +- TCP KISS can now handle multiple concurrent applications. + +- Linux can use serial port for KISS in addition to a pseudo terminal. + +- New document ***Bluetooth-KISS-TNC.pdf*** explaining how to use KISS over Bluetooth. + +### Bugs Fixed: ### + +- Little spelling errors in messages ???? + +---------- + ## Version 1.4 -- April 2017 ## diff --git a/Makefile.linux b/Makefile.linux index 55777d0..5d45380 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -292,7 +292,7 @@ z := $(notdir ${CURDIR}) direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \ hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \ fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \ - decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ + decode_aprs.o symbols.o server.o kiss.o kissserial.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ gen_tone.o audio.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o morse.o \ ptt.o beacon.o encode_aprs.o latlong.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \ @@ -570,15 +570,22 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon # doc/README.md contains an overview of the PDF file contents and is more useful here. # $(INSTALL) -D --mode=644 doc/README.md $(INSTALLDIR)/share/doc/direwolf/README.md - $(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf + $(INSTALL) -D --mode=644 doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf $(INSTALLDIR)/share/doc/direwolf/2400-4800-PSK-for-APRS-Packet-Radio.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf + $(INSTALL) -D --mode=644 doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf $(INSTALLDIR)/share/doc/direwolf/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf + $(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf + $(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf + $(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf + $(INSTALL) -D --mode=644 doc/APRStt-Listening-Example.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Listening-Example.pdf + $(INSTALL) -D --mode=644 doc/Bluetooth-KISS-TNC.pdf $(INSTALLDIR)/share/doc/direwolf/Bluetooth-KISS-TNC.pdf + $(INSTALL) -D --mode=644 doc/Going-beyond-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/Going-beyond-9600-baud.pdf $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf $(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf - $(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf - $(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf - $(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf - $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf - $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf + $(INSTALL) -D --mode=644 doc/Successful-APRS-IGate-Operation.pdf $(INSTALLDIR)/share/doc/direwolf/Successful-APRS-IGate-Operation.pdf + $(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf + $(INSTALL) -D --mode=644 doc/WA8LMF-TNC-Test-CD-Results.pdf $(INSTALLDIR)/share/doc/direwolf/WA8LMF-TNC-Test-CD-Results.pdf # # Various sample config and other files go into examples under the doc directory. # When building from source, these can be put in home directory with "make install-conf". diff --git a/Makefile.macosx b/Makefile.macosx index 262ed45..ee9e42e 100644 --- a/Makefile.macosx +++ b/Makefile.macosx @@ -237,7 +237,7 @@ direwolf : direwolf.o aprs_tt.o audio_portaudio.o audio_stats.o ax25_link.o ax25 demod.o digipeater.o cdigipeater.o dlq.o dsp.o dtime_now.o dtmf.o dwgps.o \ encode_aprs.o encode_aprs.o fcs_calc.o fcs_calc.o gen_tone.o \ geotranz.a hdlc_rec.o hdlc_rec2.o hdlc_send.o igate.o kiss_frame.o \ - kiss.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \ + kiss.o kissserial.o kissnet.o latlong.o latlong.o log.o morse.o multi_modem.o \ waypoint.o serial_port.o pfilter.o ptt.o rdq.o recv.o rrbb.o server.o \ symbols.o telemetry.o textcolor.o tq.o tt_text.o tt_user.o xid.o xmit.o \ dwgps.o dwgpsnmea.o mheard.o @@ -359,15 +359,26 @@ install : $(APPS) direwolf.conf tocalls.txt symbols-new.txt symbolsX.txt dw-icon $(INSTALL) -D --mode=644 LICENSE-dire-wolf.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-dire-wolf.txt $(INSTALL) -D --mode=644 LICENSE-other.txt $(INSTALLDIR)/share/doc/direwolf/LICENSE-other.txt # - $(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf +# ./README.md is an overview for the project main page. +# doc/README.md contains an overview of the PDF file contents and is more useful here. +# + $(INSTALL) -D --mode=644 doc/README.md $(INSTALLDIR)/share/doc/direwolf/README.md + $(INSTALL) -D --mode=644 doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf $(INSTALLDIR)/share/doc/direwolf/2400-4800-PSK-for-APRS-Packet-Radio.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf + $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf + $(INSTALL) -D --mode=644 doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf $(INSTALLDIR)/share/doc/direwolf/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf + $(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf + $(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf + $(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf + $(INSTALL) -D --mode=644 doc/APRStt-Listening-Example.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Listening-Example.pdf + $(INSTALL) -D --mode=644 doc/Bluetooth-KISS-TNC.pdf $(INSTALLDIR)/share/doc/direwolf/Bluetooth-KISS-TNC.pdf + $(INSTALL) -D --mode=644 doc/Going-beyond-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/Going-beyond-9600-baud.pdf $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS.pdf $(INSTALL) -D --mode=644 doc/Raspberry-Pi-APRS-Tracker.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-APRS-Tracker.pdf $(INSTALL) -D --mode=644 doc/Raspberry-Pi-SDR-IGate.pdf $(INSTALLDIR)/share/doc/direwolf/Raspberry-Pi-SDR-IGate.pdf - $(INSTALL) -D --mode=644 doc/APRStt-Implementation-Notes.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-Implementation-Notes.pdf - $(INSTALL) -D --mode=644 doc/APRStt-interface-for-SARTrack.pdf $(INSTALLDIR)/share/doc/direwolf/APRStt-interface-for-SARTrack.pdf - $(INSTALL) -D --mode=644 doc/APRS-Telemetry-Toolkit.pdf $(INSTALLDIR)/share/doc/direwolf/APRS-Telemetry-Toolkit.pdf - $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf - $(INSTALL) -D --mode=644 doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf $(INSTALLDIR)/share/doc/direwolf/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf + $(INSTALL) -D --mode=644 doc/Successful-APRS-IGate-Operation.pdf $(INSTALLDIR)/share/doc/direwolf/Successful-APRS-IGate-Operation.pdf + $(INSTALL) -D --mode=644 doc/User-Guide.pdf $(INSTALLDIR)/share/doc/direwolf/User-Guide.pdf + $(INSTALL) -D --mode=644 doc/WA8LMF-TNC-Test-CD-Results.pdf $(INSTALLDIR)/share/doc/direwolf/WA8LMF-TNC-Test-CD-Results.pdf # # Sample config files also go into the doc directory. # When building from source, these can be put in home directory with "make install-conf". diff --git a/Makefile.win b/Makefile.win index 240df0f..95e94d6 100644 --- a/Makefile.win +++ b/Makefile.win @@ -98,7 +98,7 @@ demod_psk.o : fsk_demod_state.h direwolf : direwolf.o config.o recv.o demod.o dsp.o demod_afsk.o demod_psk.o demod_9600.o hdlc_rec.o \ hdlc_rec2.o multi_modem.o rdq.o rrbb.o dlq.o \ fcs_calc.o ax25_pad.o ax25_pad2.o xid.o \ - decode_aprs.o symbols.o server.o kiss.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ + decode_aprs.o symbols.o server.o kiss.o kissserial.o kissnet.o kiss_frame.o hdlc_send.o fcs_calc.o \ gen_tone.o morse.o audio_win.o audio_stats.o digipeater.o cdigipeater.o pfilter.o dedupe.o tq.o xmit.o \ ptt.o beacon.o dwgps.o encode_aprs.o latlong.o textcolor.o \ dtmf.o aprs_tt.o tt_user.o tt_text.o igate.o waypoint.o serial_port.o log.o telemetry.o \ @@ -193,6 +193,12 @@ gen_packets : gen_packets.o ax25_pad.o hdlc_send.o fcs_calc.o gen_tone.o morse. $(CC) $(CFLAGS) -o $@ $^ +# Connected mode packet application server. + +appserver : appserver.o textcolor.o ax25_pad.o fcs_calc.o misc.a + $(CC) $(CFLAGS) -o $@ $^ -lwinmm -lws2_32 + + # ------------------------------------------- Libraries -------------------------------------------- @@ -600,20 +606,25 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l rm -f ../$z-win.zip egrep '^C|^W' generic.conf | cut -c2-999 > direwolf.conf unix2dos direwolf.conf + cp doc/README.md README-doc.md zip --junk-paths ../$z-win.zip \ README.md \ CHANGES.md \ - doc/User-Guide.pdf \ - doc/Raspberry-Pi-APRS.pdf \ + README-doc.md \ + doc/2400-4800-PSK-for-APRS-Packet-Radio.pdf \ doc/A-Better-APRS-Packet-Demodulator-Part-1-1200-baud.pdf \ doc/A-Better-APRS-Packet-Demodulator-Part-2-9600-baud.pdf \ + doc/A-Closer-Look-at-the-WA8LMF-TNC-Test-CD.pdf \ doc/APRS-Telemetry-Toolkit.pdf \ doc/APRStt-Implementation-Notes.pdf \ doc/APRStt-interface-for-SARTrack.pdf \ doc/APRStt-Listening-Example.pdf \ + doc/Bluetooth-KISS-TNC.pdf \ + doc/Going-beyond-9600-baud.pdf \ doc/Raspberry-Pi-APRS.pdf \ doc/Raspberry-Pi-APRS-Tracker.pdf \ doc/Raspberry-Pi-SDR-IGate.pdf \ + doc/Successful-APRS-IGate-Operation.pdf \ doc/User-Guide.pdf \ doc/WA8LMF-TNC-Test-CD-Results.pdf \ LICENSE* \ @@ -630,6 +641,7 @@ dist-win : direwolf.exe decode_aprs.exe text2tt.exe tt2text.exe ll2utm.exe utm2l ttcalc.exe \ dwespeak.bat \ telemetry-toolkit/* + rm README-doc.md # Reminders if pdf files are not up to date. @@ -668,3 +680,4 @@ backup : # # DO NOT DELETE + diff --git a/config.c b/config.c index 455a0d3..e9cd239 100644 --- a/config.c +++ b/config.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 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 @@ -870,8 +870,9 @@ void config_init (char *fname, struct audio_s *p_audio_config, /* Ideally we'd like to figure out if com0com is installed */ /* and automatically enable this. */ - //strlcpy (p_misc_config->nullmodem, DEFAULT_NULLMODEM, sizeof(p_misc_config->nullmodem)); - strlcpy (p_misc_config->nullmodem, "", sizeof(p_misc_config->nullmodem)); + strlcpy (p_misc_config->kiss_serial_port, "", sizeof(p_misc_config->kiss_serial_port)); + p_misc_config->kiss_serial_speed = 0; + p_misc_config->kiss_serial_poll = 0; strlcpy (p_misc_config->gpsnmea_port, "", sizeof(p_misc_config->gpsnmea_port)); strlcpy (p_misc_config->waypoint_port, "", sizeof(p_misc_config->waypoint_port)); @@ -4102,17 +4103,58 @@ void config_init (char *fname, struct audio_s *p_audio_config, } /* - * NULLMODEM - Device name for our end of the virtual "null modem" + * NULLMODEM name [ speed ] - Device name for serial port or our end of the virtual "null modem" + * SERIALKISS name [ speed ] + * + * Version 1.5: Added SERIALKISS which is equivalent to NULLMODEM. + * The original name sort of made sense when it was used only for one end of a virtual + * null modem cable on Windows only. Now it is also available for Linux. + * TODO1.5: In retrospect, this doesn't seem like such a good name. */ - else if (strcasecmp(t, "nullmodem") == 0) { + + else if (strcasecmp(t, "NULLMODEM") == 0 || strcasecmp(t, "SERIALKISS") == 0) { t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file: Missing device name for my end of the 'null modem' on line %d.\n", line); + dw_printf ("Config file: Missing serial port name on line %d.\n", line); continue; } else { - strlcpy (p_misc_config->nullmodem, t, sizeof(p_misc_config->nullmodem)); + if (strlen(p_misc_config->kiss_serial_port) > 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file: Warning serial port name on line %d replaces earlier value.\n", line); + } + strlcpy (p_misc_config->kiss_serial_port, t, sizeof(p_misc_config->kiss_serial_port)); + p_misc_config->kiss_serial_speed = 0; + p_misc_config->kiss_serial_poll = 0; + } + + t = split(NULL,0); + if (t != NULL) { + p_misc_config->kiss_serial_speed = atoi(t); + } + } + +/* + * SERIALKISSPOLL name - Poll for serial port name that might come and go. + * e.g. /dev/rfcomm0 for bluetooth. + */ + + else if (strcasecmp(t, "SERIALKISSPOLL") == 0) { + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file: Missing serial port name on line %d.\n", line); + continue; + } + else { + if (strlen(p_misc_config->kiss_serial_port) > 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file: Warning serial port name on line %d replaces earlier value.\n", line); + } + strlcpy (p_misc_config->kiss_serial_port, t, sizeof(p_misc_config->kiss_serial_port)); + p_misc_config->kiss_serial_speed = 0; + p_misc_config->kiss_serial_poll = 1; // set polling. } } diff --git a/config.h b/config.h index c895e29..4e2a767 100644 --- a/config.h +++ b/config.h @@ -34,13 +34,24 @@ enum sendto_type_e { SENDTO_XMIT, SENDTO_IGATE, SENDTO_RECV }; struct misc_config_s { int agwpe_port; /* Port number for the "AGW TCPIP Socket Interface" */ - int kiss_port; /* Port number for the "KISS" protocol. */ + int kiss_port; /* Port number for the "TCP KISS" protocol. */ int enable_kiss_pt; /* Enable pseudo terminal for KISS. */ /* Want this to be off by default because it hangs */ /* after a while if nothing is reading from other end. */ - char nullmodem[20]; /* Serial port name for our end of the */ + char kiss_serial_port[20]; + /* Serial port name for our end of the */ /* virtual null modem for native Windows apps. */ + /* Version 1.5 add same capability for Linux. */ + + int kiss_serial_speed; /* Speed, in bps, for the KISS serial port. */ + /* If 0, just leave what was already there. */ + + int kiss_serial_poll; /* When using Bluetooth KISS, the /dev/rfcomm0 device */ + /* will appear and disappear as the remote application */ + /* opens and closes the virtual COM port. */ + /* When this is non-zero, we will check periodically to */ + /* see if the device has appeared and we will open it. */ char gpsnmea_port[20]; /* Serial port name for reading NMEA sentences from GPS. */ /* e.g. COM22, /dev/ttyACM0 */ diff --git a/direwolf.c b/direwolf.c index 1a562b8..d1d8717 100644 --- a/direwolf.c +++ b/direwolf.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 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 @@ -94,6 +94,7 @@ #include "server.h" #include "kiss.h" #include "kissnet.h" +#include "kissserial.h" #include "kiss_frame.h" #include "waypoint.h" #include "gen_tone.h" @@ -260,8 +261,8 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); //dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "H", __DATE__); - dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "A", __DATE__); + //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); #if defined(ENABLE_GPSD) || defined(USE_HAMLIB) dw_printf ("Includes optional support for: "); @@ -466,7 +467,7 @@ int main (int argc, char *argv[]) case 'a': server_set_debug(1); break; - case 'k': d_k_opt++; kiss_serial_set_debug (d_k_opt); break; + case 'k': d_k_opt++; kissserial_set_debug (d_k_opt); kisspt_set_debug (d_k_opt); break; case 'n': d_n_opt++; kiss_net_set_debug (d_n_opt); break; case 'u': d_u_opt = 1; break; @@ -766,7 +767,8 @@ int main (int argc, char *argv[]) /* * Create a pseudo terminal and KISS TNC emulator. */ - kiss_init (&misc_config); + kisspt_init (&misc_config); + kissserial_init (&misc_config); kiss_frame_init (&audio_config); /* @@ -1084,9 +1086,10 @@ void app_process_rec_packet (int chan, int subchan, int slice, packet_t pp, alev flen = ax25_pack(pp, fbuf); - server_send_rec_packet (chan, pp, fbuf, flen); - kissnet_send_rec_packet (chan, fbuf, flen); - kiss_send_rec_packet (chan, fbuf, flen); + server_send_rec_packet (chan, pp, fbuf, flen); // AGW net protocol + kissnet_send_rec_packet (chan, fbuf, flen, -1); // KISS TCP + kissserial_send_rec_packet (chan, fbuf, flen, -1); // KISS serial port + kisspt_send_rec_packet (chan, fbuf, flen, -1); // KISS pseudo terminal /* * If it came from DTMF decoder, send it to APRStt gateway. @@ -1204,7 +1207,7 @@ static void usage (char **argv) dw_printf (" -D n Divide audio sample rate by n for channel 0.\n"); dw_printf (" -d Debug options:\n"); dw_printf (" a a = AGWPE network protocol client.\n"); - dw_printf (" k k = KISS serial port client.\n"); + dw_printf (" k k = KISS serial port or pseudo terminal client.\n"); dw_printf (" n n = KISS network client.\n"); dw_printf (" u u = Display non-ASCII text in hexadecimal.\n"); dw_printf (" p p = dump Packets in hexadecimal.\n"); diff --git a/fcs_calc.c b/fcs_calc.c index 97e34a5..f49a2f5 100644 --- a/fcs_calc.c +++ b/fcs_calc.c @@ -87,6 +87,22 @@ unsigned short fcs_calc (unsigned char *data, int len) } +/* + * CRC is also used for duplicate checking for the digipeater and IGate. + * A packet is considered a duplicate if the source, destination, and + * information parts match. In other words, we ignore the via path + * which changes along the way. + * Rather than keeping a variable length string we just keep a 16 bit + * CRC which takes less memory and processing to compare. + * + * This can result in occasional false matches. If we had a random + * 16 bit number, there is a 1/65536 ( = 0.0015 % ) chance that it will + * match and we will drop something that should be passed along. + * + * Looking at it another way, there is a 0.9999847412109375 (out of 1) + * probability of doing the right thing. + */ + /* * This can be used when we want to calculate a single CRC over disjoint data. * diff --git a/kiss.c b/kiss.c index 19c258d..4e6b3ea 100644 --- a/kiss.c +++ b/kiss.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2013, 2014, 2016 John Langner, WB2OSZ +// 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 @@ -24,12 +24,8 @@ * Module: kiss.c * * Purpose: Act as a virtual KISS TNC for use by other packet radio applications. - * On Windows, it is a serial port. On Linux, a pseudo terminal. + * This file implements it with a pseudo terminal for Linux only. * - * Input: - * - * Outputs: - * * Description: It implements the KISS TNC protocol as described in: * http://www.ka9q.net/papers/kiss.html * @@ -49,94 +45,82 @@ * * Commands from application recognized: * - * 0 Data Frame AX.25 frame in raw format. + * _0 Data Frame AX.25 frame in raw format. * - * 1 TXDELAY See explanation in xmit.c. + * _1 TXDELAY See explanation in xmit.c. * - * 2 Persistence " " + * _2 Persistence " " * - * 3 SlotTime " " + * _3 SlotTime " " * - * 4 TXtail " " + * _4 TXtail " " * Spec says it is obsolete but Xastir * sends it and we respect it. * - * 5 FullDuplex Ignored. Always full duplex. + * _5 FullDuplex Ignored. * - * 6 SetHardware TNC specific. 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. - * + * _0 Data Frame Received AX.25 frame in raw format. * * * Platform differences: * - * We can use a pseudo terminal for Linux or Cygwin applications. - * However, Microsoft Windows doesn't seem to have similar functionality. - * Native Windows applications expect to see a device named COM1, - * COM2, COM3, or COM4. Some might offer more flexibility but others - * might be limited to these four choices. - * - * The documentation instucts the user to install the com0com - * "Null-modem emulator" from http://sourceforge.net/projects/com0com/ - * and configure it for COM3 & COM4. - * - * By default Dire Wolf will use COM3 (/dev/ttyS2 or /dev/com3 - lower case!) - * and the client application will use COM4 (available as /dev/ttyS or - * /dev/com4 for Cygwin applications). - * - * - * This can get confusing. - * - * If __WIN32__ is defined, - * We use the Windows interface to the specfied serial port. - * This could be a real serial port or the nullmodem driver - * connected to another application. - * - * If __CYGWIN__ is defined, - * We connect to a serial port as in the previous case but - * use the Linux I/O interface. - * We also supply a pseudo terminal for any Cygwin applications - * such as Xastir so the null modem is not needed. - * * For the Linux case, * We supply a pseudo terminal for use by other applications. * - * - * Reference: http://www.robbayer.com/files/serial-win.pdf + * Version 1.5: Split serial port version off into its own file. * *---------------------------------------------------------------*/ + +#if __WIN32__ // Stub for Windows. + +#include "direwolf.h" +#include "kiss.h" + +void kisspt_init (struct misc_config_s *mc) +{ + return; +} + +void kisspt_set_debug (int n) +{ + return; +} + +void kisspt_send_rec_packet (int chan, unsigned char *fbuf, int flen, int client) +{ + return; +} + + +#else // Rest of file is for Linux only. + + #include "direwolf.h" #include #include - -#if __WIN32__ -#include -#else #include +#include #include #include #include #include #include #include + #ifdef __OpenBSD__ #include #else #include #endif -#endif - -#include -#include - #include "tq.h" #include "ax25_pad.h" @@ -146,29 +130,21 @@ #include "xmit.h" -#if __WIN32__ -typedef HANDLE MYFDTYPE; -#define MYFDERROR INVALID_HANDLE_VALUE -#else -typedef int MYFDTYPE; -#define MYFDERROR (-1) -#endif +/* + * Accumulated KISS frame and state of decoder. + */ - -static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */ +static kiss_frame_t kf; /* - * These are for a Linux/Cygwin pseudo terminal. + * These are for a Linux pseudo terminal. */ -#if ! __WIN32__ - -static MYFDTYPE pt_master_fd = MYFDERROR; /* File descriptor for my end. */ - -static char pt_slave_name[32]; /* Pseudo terminal slave name */ - /* like /dev/pts/999 */ +static int pt_master_fd = -1; /* File descriptor for my end. */ +static char pt_slave_name[32]; /* Pseudo terminal slave name */ + /* like /dev/pts/999 */ /* @@ -177,40 +153,15 @@ static char pt_slave_name[32]; /* Pseudo terminal slave name */ #define TMP_KISSTNC_SYMLINK "/tmp/kisstnc" -#endif -/* - * This is for native Windows applications and a virtual null modem. - */ - -#if __CYGWIN__ || __WIN32__ - -static MYFDTYPE nullmodem_fd = MYFDERROR; - -#endif +static void * kisspt_listen_thread (void *arg); -// TODO: define in one place, use everywhere. -#if __WIN32__ -#define THREAD_F unsigned __stdcall -#else -#define THREAD_F void * -#endif +static int kisspt_debug = 0; /* Print information flowing from and to client. */ -static THREAD_F kiss_listen_thread (void *arg); - - - -#if DEBUG9 -static FILE *log_fp; -#endif - - -static int kiss_debug = 0; /* Print information flowing from and to client. */ - -void kiss_serial_set_debug (int n) +void kisspt_set_debug (int n) { - kiss_debug = n; + kisspt_debug = n; } @@ -224,12 +175,12 @@ void hex_dump (unsigned char *p, int len); /*------------------------------------------------------------------- * - * Name: kiss_init + * Name: kisspt_init * * Purpose: Set up a pseudo terminal acting as a virtual KISS TNC. * * - * Inputs: mc->nullmodem - name of device for our end of nullmodem. + * Inputs: * * Outputs: * @@ -240,43 +191,28 @@ void hex_dump (unsigned char *p, int len); * *--------------------------------------------------------------------*/ -#if __WIN32__ -static MYFDTYPE kiss_open_nullmodem (char *device); -#else -static MYFDTYPE kiss_open_pt (void); -#endif +static int kisspt_open_pt (void); -void kiss_init (struct misc_config_s *mc) +void kisspt_init (struct misc_config_s *mc) { -#if __WIN32__ - HANDLE kiss_nullmodem_listen_th; -#else pthread_t kiss_pterm_listen_tid; - //pthread_t kiss_nullmodem_listen_tid; int e; -#endif memset (&kf, 0, sizeof(kf)); /* * This reads messages from client. */ - -#if ! __WIN32__ - -/* - * Pseudo terminal for Cygwin and Linux versions. - */ - pt_master_fd = MYFDERROR; + pt_master_fd = -1; if (mc->enable_kiss_pt) { - pt_master_fd = kiss_open_pt (); + pt_master_fd = kisspt_open_pt (); - if (pt_master_fd != MYFDERROR) { - e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kiss_listen_thread, NULL); + if (pt_master_fd != -1) { + e = pthread_create (&kiss_pterm_listen_tid, (pthread_attr_t*)NULL, kisspt_listen_thread, NULL); if (e != 0) { text_color_set(DW_COLOR_ERROR); perror("Could not create kiss listening thread for Linux pseudo terminal"); @@ -287,72 +223,22 @@ void kiss_init (struct misc_config_s *mc) text_color_set(DW_COLOR_INFO); dw_printf ("Use -p command line option to enable KISS pseudo terminal.\n"); } -#endif - -#if __CYGWIN__ || __WIN32 - -/* - * Cygwin and native Windows versions have serial port connection. - */ - if (strlen(mc->nullmodem) > 0) { - -#if ! __WIN32__ - - /* Translate Windows device name into Linux name. */ - /* COM1 -> /dev/ttyS0, etc. */ - - if (strncasecmp(mc->nullmodem, "COM", 3) == 0) { - int n = atoi (mc->nullmodem + 3); - text_color_set(DW_COLOR_INFO); - dw_printf ("Converted nullmodem device '%s'", mc->nullmodem); - if (n < 1) n = 1; - snprintf (mc->nullmodem, sizeof(mc->nullmodem), "/dev/ttyS%d", n-1); - dw_printf (" to Linux equivalent '%s'\n", mc->nullmodem); - } -#endif - nullmodem_fd = kiss_open_nullmodem (mc->nullmodem); - - if (nullmodem_fd != MYFDERROR) { -#if __WIN32__ - kiss_nullmodem_listen_th = (HANDLE)_beginthreadex (NULL, 0, kiss_listen_thread, NULL, 0, NULL); - if (kiss_nullmodem_listen_th == NULL) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Could not create kiss nullmodem thread\n"); - return; - } -#else - e = pthread_create (&kiss_nullmodem_listen_tid, NULL, kiss_listen_thread, NULL); - if (e != 0) { - text_color_set(DW_COLOR_ERROR); - perror("Could not create kiss listening thread for Windows virtual COM port."); - - } -#endif - } - } -#endif #if DEBUG text_color_set (DW_COLOR_DEBUG); -#if ! __WIN32__ - dw_printf ("end of kiss_init: pt_master_fd = %d\n", pt_master_fd); -#endif -#if __CYGWIN__ || __WIN32__ - dw_printf ("end of kiss_init: nullmodem_fd = %d\n", nullmodem_fd); + + dw_printf ("end of kisspt_init: pt_master_fd = %d\n", pt_master_fd); #endif -#endif } /* - * Returns fd for master side of pseudo terminal or MYFDERROR for error. + * Returns fd for master side of pseudo terminal or -1 for error. */ -#if ! __WIN32__ - -static MYFDTYPE kiss_open_pt (void) +static int kisspt_open_pt (void) { int fd; char *pts; @@ -362,19 +248,18 @@ static MYFDTYPE kiss_open_pt (void) #if DEBUG text_color_set(DW_COLOR_DEBUG); - dw_printf ("kiss_open_pt ( )\n"); + dw_printf ("kisspt_open_pt ( )\n"); #endif - fd = posix_openpt(O_RDWR|O_NOCTTY); - if (fd == MYFDERROR - || grantpt (fd) == MYFDERROR - || unlockpt (fd) == MYFDERROR + if (fd == -1 + || grantpt (fd) == -1 + || unlockpt (fd) == -1 || (pts = ptsname (fd)) == NULL) { text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Could not create pseudo terminal for KISS TNC.\n"); - return (MYFDERROR); + return (-1); } strlcpy (pt_slave_name, pts, sizeof(pt_slave_name)); @@ -430,11 +315,11 @@ static MYFDTYPE kiss_open_pt (void) #if 1 // Sample code shows this. Why would we open it here? // On Ubuntu, the slave side disappears after a few - // seconds if no one opens it. Same on Raspian which + // seconds if no one opens it. Same on Raspbian which // is also based on Debian. // Need to revisit this. - MYFDTYPE pt_slave_fd; + int pt_slave_fd; pt_slave_fd = open(pt_slave_name, O_RDWR|O_NOCTTY); @@ -442,7 +327,7 @@ static MYFDTYPE kiss_open_pt (void) text_color_set(DW_COLOR_ERROR); dw_printf ("Can't open %s\n", pt_slave_name); perror (""); - return MYFDERROR; + return -1; } #endif @@ -471,149 +356,11 @@ static MYFDTYPE kiss_open_pt (void) return (fd); } -#endif - -/* - * Returns fd for our side of null modem or MYFDERROR for error. - */ - - -#if __CYGWIN__ || __WIN32__ - -static MYFDTYPE kiss_open_nullmodem (char *devicename) -{ - -#if __WIN32__ - - MYFDTYPE fd; - DCB dcb; - int ok; - char bettername[50]; - -#if DEBUG - text_color_set(DW_COLOR_DEBUG); - dw_printf ("kiss_open_nullmodem ( '%s' )\n", devicename); -#endif - -#if DEBUG9 - log_fp = fopen ("kiss-debug.txt", "w"); -#endif - -// Need to use FILE_FLAG_OVERLAPPED for full duplex operation. -// Without it, write blocks when waiting on read. - -// Read http://support.microsoft.com/kb/156932 - -// Bug fix in release 1.1 - Need to munge name for COM10 and up. -// http://support.microsoft.com/kb/115831 - - strlcpy (bettername, devicename, sizeof(bettername)); - if (strncasecmp(devicename, "COM", 3) == 0) { - int n; - n = atoi(devicename+3); - if (n >= 10) { - strlcpy (bettername, "\\\\.\\", sizeof(bettername)); - strlcat (bettername, devicename, sizeof(bettername)); - } - } - - fd = CreateFile(bettername, GENERIC_READ | GENERIC_WRITE, - 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); - - if (fd == MYFDERROR) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("ERROR - Could not connect to %s side of null modem for Windows KISS TNC.\n", devicename); - return (MYFDERROR); - } - - /* Reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa363201(v=vs.85).aspx */ - - memset (&dcb, 0, sizeof(dcb)); - dcb.DCBlength = sizeof(DCB); - - ok = GetCommState (fd, &dcb); - if (! ok) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("kiss_open_nullmodem: GetCommState failed.\n"); - } - - /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa363214(v=vs.85).aspx */ - - dcb.DCBlength = sizeof(DCB); - dcb.BaudRate = CBR_9600; // shouldn't matter - dcb.fBinary = 1; - dcb.fParity = 0; - dcb.fOutxCtsFlow = 0; - dcb.fOutxDsrFlow = 0; - dcb.fDtrControl = 0; - dcb.fDsrSensitivity = 0; - dcb.fOutX = 0; - dcb.fInX = 0; - dcb.fErrorChar = 0; - dcb.fNull = 0; /* Don't drop nul characters! */ - dcb.fRtsControl = 0; - dcb.ByteSize = 8; - dcb.Parity = NOPARITY; - dcb.StopBits = ONESTOPBIT; - - ok = SetCommState (fd, &dcb); - if (! ok) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("kiss_open_nullmodem: SetCommState failed.\n"); - } - - text_color_set(DW_COLOR_INFO); - dw_printf("Virtual KISS TNC is connected to %s side of null modem.\n", devicename); - -#else - -/* Cygwin version. */ - - int fd; - struct termios ts; - int e; - - -#if DEBUG - text_color_set(DW_COLOR_DEBUG); - dw_printf ("kiss_open_nullmodem ( '%s' )\n", devicename); -#endif - - fd = open (devicename, O_RDWR); - - if (fd == MYFDERROR) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("ERROR - Could not connect to %s side of null modem for Windows KISS TNC.\n", devicename); - return (MYFDERROR); - } - - e = tcgetattr (fd, &ts); - if (e != 0) { perror ("nm tcgetattr"); } - - cfmakeraw (&ts); - - ts.c_cc[VMIN] = 1; /* wait for at least one character */ - ts.c_cc[VTIME] = 0; /* no fancy timing. */ - - e = tcsetattr (fd, TCSANOW, &ts); - if (e != 0) { perror ("nm tcsetattr"); } - - text_color_set(DW_COLOR_INFO); - dw_printf("Virtual KISS TNC is connected to %s side of null modem.\n", devicename); - -#endif - - return (fd); -} - -#endif - - /*------------------------------------------------------------------- * - * Name: kiss_send_rec_packet + * Name: kisspt_send_rec_packet * * Purpose: Send a received packet or text string to the client app. * @@ -628,6 +375,10 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename) * flen - Length of raw received frame not including the FCS * or -1 for a text string. * + * client - Not used for pseudo terminal. + * 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. @@ -635,28 +386,20 @@ static MYFDTYPE kiss_open_nullmodem (char *devicename) *--------------------------------------------------------------------*/ -void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) +void kisspt_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; -#if ! __WIN32__ - if (pt_master_fd == MYFDERROR) { + + if (pt_master_fd == -1) { return; } -#endif -#if __CYGWIN__ || __WIN32__ - - if (nullmodem_fd == MYFDERROR) { - return; - } -#endif - if (flen < 0) { flen = strlen((char*)fbuf); - if (kiss_debug) { + if (kisspt_debug) { kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen); } strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff)); @@ -664,15 +407,18 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) } else { - unsigned char stemp[AX25_MAX_PACKET_LEN + 1]; - assert (flen < (int)(sizeof(stemp))); + if (flen > (int)(sizeof(stemp)) - 1) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\nPseudo Terminal KISS buffer too small. Truncated.\n\n"); + flen = (int)(sizeof(stemp)) - 1; + } stemp[0] = (chan << 4) + 0; memcpy (stemp+1, fbuf, flen); - if (kiss_debug >= 2) { + if (kisspt_debug >= 2) { /* AX.25 frame with the CRC removed. */ text_color_set(DW_COLOR_DEBUG); dw_printf ("\n"); @@ -684,16 +430,12 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) /* This has KISS framing and escapes for sending to client app. */ - if (kiss_debug) { + if (kisspt_debug) { kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len); } } -#if ! __WIN32__ - -/* Pseudo terminal for Cygwin and Linux. */ - err = write (pt_master_fd, kiss_buff, (size_t)kiss_len); if (err == -1 && errno == EWOULDBLOCK) { @@ -709,84 +451,18 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) perror ("pt write"); } -#endif - -#if __CYGWIN__ || __WIN32__ - - -/* - * This write can block if 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 - */ - -#if __WIN32__ - - DWORD nwritten; - - /* Without this, write blocks while we are waiting on a read. */ - static OVERLAPPED ov_wr; - memset (&ov_wr, 0, sizeof(ov_wr)); - - if ( ! WriteFile (nullmodem_fd, kiss_buff, kiss_len, &nwritten, &ov_wr)) - { - err = GetLastError(); - if (err != ERROR_IO_PENDING) - { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nError sending KISS message to client application thru null modem. Error %d.\n\n", (int)GetLastError()); - //CloseHandle (nullmodem_fd); - //nullmodem_fd = MYFDERROR; - } - } - else if ((int)nwritten != kiss_len) - { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nError sending KISS message to client application thru null modem. Only %d of %d written.\n\n", (int)nwritten, kiss_len); - //CloseHandle (nullmodem_fd); - //nullmodem_fd = MYFDERROR; - } - -#else - err = write (nullmodem_fd, kiss_buf, (size_t)kiss_len); - if (err != len) - { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nError sending KISS message to client application thru null modem. err=%d\n\n", err); - //close (nullmodem_fd); - //nullmodem_fd = MYFDERROR; - } -#endif - -#endif - -} /* kiss_send_rec_packet */ +} /* kisspt_send_rec_packet */ /*------------------------------------------------------------------- * - * Name: kiss_get + * Name: kisspt_get * * Purpose: Read one byte from the KISS client app. * - * Global In: nullmodem_fd (Windows) or pt_master_fd (Linux) + * Global In: pt_master_fd * * Returns: one byte (value 0 - 255) or terminate thread on error. * @@ -799,78 +475,10 @@ void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen) *--------------------------------------------------------------------*/ -static int kiss_get (/* MYFDTYPE fd*/ void ) +static int kisspt_get (void) { unsigned char ch; -#if __WIN32__ /* Native Windows version. */ - - DWORD n; - static OVERLAPPED ov_rd; - - memset (&ov_rd, 0, sizeof(ov_rd)); - ov_rd.hEvent = CreateEvent (NULL, TRUE, FALSE, NULL); - - - /* Overlapped I/O makes reading rather complicated. */ - /* See: http://msdn.microsoft.com/en-us/library/ms810467.aspx */ - - /* It seems that the read completes OK with a count */ - /* of 0 every time we send a message to the serial port. */ - - n = 0; /* Number of characters read. */ - - while (n == 0) { - - if ( ! ReadFile (nullmodem_fd, &ch, 1, &n, &ov_rd)) - { - int err1 = GetLastError(); - - if (err1 == ERROR_IO_PENDING) - { - /* Wait for completion. */ - - if (WaitForSingleObject (ov_rd.hEvent, INFINITE) == WAIT_OBJECT_0) - { - if ( ! GetOverlappedResult (nullmodem_fd, &ov_rd, &n, 1)) - { - int err3 = GetLastError(); - - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nKISS GetOverlappedResult error %d.\n\n", err3); - } - else - { - /* Success! n should be 1 */ - } - } - } - else - { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nKISS ReadFile error %d. Closing connection.\n\n", err1); - CloseHandle (nullmodem_fd); - nullmodem_fd = MYFDERROR; - //pthread_exit (NULL); - } - } - - } /* end while n==0 */ - - CloseHandle(ov_rd.hEvent); - - if (n != 1) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nKISS failed to get one byte. n=%d.\n\n", (int)n); - -#if DEBUG9 - fprintf (log_fp, "n=%d\n", n); -#endif - } - - -#else /* Linux/Cygwin version */ - int n = 0; fd_set fd_in, fd_ex; int rc; @@ -934,7 +542,7 @@ static int kiss_get (/* MYFDTYPE fd*/ void ) continue; // When could we get a 0? } - if (rc == MYFDERROR + if (rc == -1 || (n = read(pt_master_fd, &ch, (size_t)1)) != 1) { @@ -944,70 +552,52 @@ static int kiss_get (/* MYFDTYPE fd*/ void ) close (pt_master_fd); - pt_master_fd = MYFDERROR; + pt_master_fd = -1; unlink (TMP_KISSTNC_SYMLINK); pthread_exit (NULL); } } -#endif - #if DEBUGx text_color_set(DW_COLOR_DEBUG); - dw_printf ("kiss_get(%d) returns 0x%02x\n", fd, ch); + dw_printf ("kisspt_get(%d) returns 0x%02x\n", fd, ch); #endif -#if DEBUG9 - fprintf (log_fp, "%02x %c %c", ch, - isprint(ch) ? ch : '.' , - (isupper(ch>>1) || isdigit(ch>>1) || (ch>>1) == ' ') ? (ch>>1) : '.'); - if (ch == FEND) fprintf (log_fp, " FEND"); - if (ch == FESC) fprintf (log_fp, " FESC"); - if (ch == TFEND) fprintf (log_fp, " TFEND"); - if (ch == TFESC) fprintf (log_fp, " TFESC"); - if (ch == '\r') fprintf (log_fp, " CR"); - if (ch == '\n') fprintf (log_fp, " LF"); - fprintf (log_fp, "\n"); - if (ch == FEND) fflush (log_fp); -#endif return (ch); } /*------------------------------------------------------------------- * - * Name: kiss_listen_thread + * Name: kisspt_listen_thread * * Purpose: Read messages from serial port KISS client application. * - * Global In: nullmodem_fd (Windows) or pt_master_fd (Linux) + * Global In: * * Description: Reads bytes from the KISS client app and * sends them to kiss_rec_byte for processing. * *--------------------------------------------------------------------*/ - -static THREAD_F kiss_listen_thread (void *arg) +static void * kisspt_listen_thread (void *arg) { unsigned char ch; #if DEBUG text_color_set(DW_COLOR_DEBUG); - dw_printf ("kiss_listen_thread ( %d )\n", fd); + dw_printf ("kisspt_listen_thread ( %d )\n", fd); #endif while (1) { - ch = kiss_get(); - kiss_rec_byte (&kf, ch, kiss_debug, kiss_send_rec_packet); + ch = kisspt_get(); + kiss_rec_byte (&kf, ch, kisspt_debug, -1, kisspt_send_rec_packet); } -#if __WIN32__ - return(0); -#else - return (THREAD_F) 0; /* Unreachable but avoids compiler warning. */ -#endif + return (void *) 0; /* Unreachable but avoids compiler warning. */ } +#endif // Linux version + /* end kiss.c */ diff --git a/kiss.h b/kiss.h index 4c037fb..ce8c811 100644 --- a/kiss.h +++ b/kiss.h @@ -1,6 +1,8 @@ /* * Name: kiss.h + * + * This is for the pseudo terminal KISS interface. */ @@ -11,11 +13,11 @@ -void kiss_init (struct misc_config_s *misc_config); +void kisspt_init (struct misc_config_s *misc_config); -void kiss_send_rec_packet (int chan, unsigned char *fbuf, int flen); +void kisspt_send_rec_packet (int chan, unsigned char *fbuf, int flen, int client); -void kiss_serial_set_debug (int n); +void kisspt_set_debug (int n); /* end kiss.h */ diff --git a/kiss_frame.c b/kiss_frame.c index 3e9c45a..26dd22b 100644 --- a/kiss_frame.c +++ b/kiss_frame.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2013, 2014 John Langner, WB2OSZ +// Copyright (C) 2013, 2014, 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 @@ -43,28 +43,28 @@ * * Commands from application recognized: * - * 0 Data Frame AX.25 frame in raw format. + * _0 Data Frame AX.25 frame in raw format. * - * 1 TXDELAY See explanation in xmit.c. + * _1 TXDELAY See explanation in xmit.c. * - * 2 Persistence " " + * _2 Persistence " " * - * 3 SlotTime " " + * _3 SlotTime " " * - * 4 TXtail " " + * _4 TXtail " " * Spec says it is obsolete but Xastir * sends it and we respect it. * - * 5 FullDuplex Ignored. Always full duplex. + * _5 FullDuplex Ignored. * - * 6 SetHardware TNC specific. 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. + * _0 Data Frame Received AX.25 frame in raw format. * *---------------------------------------------------------------*/ @@ -291,6 +291,8 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out) * Inputs: kf - Current state of building a frame. * ch - A byte from the input stream. * debug - Activates debug output. + * client - Client app number for TCP KISS. + * Ignored for pseudo termal and serial port. * sendfun - Function to send something to the client application. * * Outputs: kf - Current state is updated. @@ -326,7 +328,7 @@ static int kiss_unwrap (unsigned char *in, int ilen, unsigned char *out) -void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int)) +void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,unsigned char*,int,int)) { //dw_printf ("kiss_frame ( %c %02x ) \n", ch, ch); @@ -367,10 +369,10 @@ void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfu /* Try to appease client app by sending something back. */ if (strcasecmp("restart\r", (char*)(kf->noise)) == 0 || strcasecmp("reset\r", (char*)(kf->noise)) == 0) { - (*sendfun) (0, (unsigned char *)"\xc0\xc0", -1); + (*sendfun) (0, (unsigned char *)"\xc0\xc0", -1, client); } else { - (*sendfun) (0, (unsigned char *)"\r\ncmd:", -1); + (*sendfun) (0, (unsigned char *)"\r\ncmd:", -1, client); } kf->noise_len = 0; } @@ -550,19 +552,22 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug) case 5: /* FullDuplex */ text_color_set(DW_COLOR_INFO); - dw_printf ("KISS protocol set FullDuplex = %d, port %d\n", kiss_msg[1], port); + dw_printf ("KISS protocol set FullDuplex = %d, port %d - Ignored\n", kiss_msg[1], port); break; case 6: /* TNC specific */ text_color_set(DW_COLOR_INFO); - dw_printf ("KISS protocol set hardware - ignored.\n"); + dw_printf ("KISS protocol set hardware - Ignored.\n"); + +// TODO: kiss_set_hardware (...) + break; case 15: /* End KISS mode, port should be 15. */ /* Ignore it. */ text_color_set(DW_COLOR_INFO); - dw_printf ("KISS protocol end KISS mode\n"); + dw_printf ("KISS protocol end KISS mode - Ignored.\n"); break; default: @@ -581,6 +586,57 @@ static void kiss_process_msg (unsigned char *kiss_msg, int kiss_len, int debug) } /* end kiss_process_msg */ +/*------------------------------------------------------------------- + * + * Name: kiss_set_hardware + * + * Purpose: Process the "set hardware" command. + * + * Inputs: + * + * + * Description: This is new in version 1.5. "Set hardware" was previously ignored. + * + * There are times when the client app might want to send configuration + * commands, such as modem speed, to the KISS TNC or inquire about its + * current state. + * + * The immediate motivation for adding this is that one application wants + * to know how many frames are currently in the transmit queue. This can + * be used for throttling of large transmissions and performing some action + * after the last frame has been sent. + * + * The original KISS protocol spec offers no guidance on what this might look + * like. I'm aware of only two, drastically different, implementations: + * + * fldigi - http://www.w1hkj.com/FldigiHelp-3.22/kiss_command_page.html + * + * Everything is in human readable text in the form of: + * COMMAND : [ parameter [ , parameter ... ] ] + * + * Used by applications, http://www.w1hkj.com/FldigiHelp/kiss_host_prgs_page.html + * - BPQ32 + * - UIChar + * - YAAC + * + * mobilinkd - https://raw.githubusercontent.com/mobilinkd/tnc1/tnc2/bertos/net/kiss.c + * + * Single byte with the command / response code, followed by + * zero or more value bytes. + * + * Used by applications: + * - APRSdroid + * + * It would be beneficial to adopt one of them rather than doing something + * completely different. It might even be possible to recognize both. + * This might allow leveraging of other existing applications. + * + *--------------------------------------------------------------------*/ + +// static void kiss_set_hardware (...) + + + /*------------------------------------------------------------------- * * Name: kiss_debug_print diff --git a/kiss_frame.h b/kiss_frame.h index ac84b82..73167c8 100644 --- a/kiss_frame.h +++ b/kiss_frame.h @@ -15,7 +15,8 @@ enum kiss_state_e { - KS_SEARCHING, /* Looking for FEND to start KISS frame. */ + KS_SEARCHING = 0, /* Looking for FEND to start KISS frame. */ + /* Must be 0 so we can simply zero whole structure to initialize. */ KS_COLLECTING}; /* In process of collecting KISS frame. */ @@ -44,7 +45,7 @@ void kiss_frame_init (struct audio_s *pa); int kiss_encapsulate (unsigned char *in, int ilen, unsigned char *out); -void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, void (*sendfun)(int,unsigned char*,int)); +void kiss_rec_byte (kiss_frame_t *kf, unsigned char ch, int debug, int client, void (*sendfun)(int,unsigned char*,int,int)); typedef enum fromto_e { FROM_CLIENT=0, TO_CLIENT=1 } fromto_t; diff --git a/kissnet.c b/kissnet.c index ccdbd0a..0f2d378 100644 --- a/kissnet.c +++ b/kissnet.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011-2014, 2015 John Langner, WB2OSZ +// Copyright (C) 2011-2014, 2015, 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 @@ -49,28 +49,28 @@ * * Commands from application recognized: * - * 0 Data Frame AX.25 frame in raw format. + * _0 Data Frame AX.25 frame in raw format. * - * 1 TXDELAY See explanation in xmit.c. + * _1 TXDELAY See explanation in xmit.c. * - * 2 Persistence " " + * _2 Persistence " " * - * 3 SlotTime " " + * _3 SlotTime " " * - * 4 TXtail " " + * _4 TXtail " " * Spec says it is obsolete but Xastir * sends it and we respect it. * - * 5 FullDuplex Ignored. Always full duplex. + * _5 FullDuplex Ignored. * - * 6 SetHardware TNC specific. 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. + * _0 Data Frame Received AX.25 frame in raw format. * * * @@ -91,7 +91,6 @@ /* * Native Windows: Use the Winsock interface. * Linux: Use the BSD socket interface. - * Cygwin: Can use either one. */ @@ -131,15 +130,27 @@ void hex_dump (unsigned char *p, int len); // This should be in a .h file. -static kiss_frame_t kf; /* Accumulated KISS frame and state of decoder. */ - // TODO: multiple instances if multiple KISS network clients! +/* + * Early on we allowed one AGW connection and one KISS TCP connection at a time. + * In version 1.1, we allowed multiple concurrent client apps to attach with the AGW network protocol. + * In Version 1.5, we do essentially the same here to allow multiple concurrent KISS TCP clients. + * The default is a limit of 3 client applications at the same time. + * You can increase the limit by changing the line below. + * A larger number consumes more resources so don't go crazy by making it larger than needed. + */ +#define MAX_NET_CLIENTS 3 -static int client_sock; /* File descriptor for socket for */ +static int client_sock[MAX_NET_CLIENTS]; + /* File descriptor for socket for */ /* communication with client application. */ /* Set to -1 if not connected. */ /* (Don't use SOCKET type because it is unsigned.) */ +static kiss_frame_t kf[MAX_NET_CLIENTS]; + /* Accumulated KISS frame and state of decoder. */ + + // TODO: define in one place, use everywhere. #if __WIN32__ @@ -187,12 +198,14 @@ void kiss_net_set_debug (int n) void kissnet_init (struct misc_config_s *mc) { + int client; + #if __WIN32__ HANDLE connect_listen_th; - HANDLE cmd_listen_th; + HANDLE cmd_listen_th[MAX_NET_CLIENTS]; #else pthread_t connect_listen_tid; - pthread_t cmd_listen_tid; + pthread_t cmd_listen_tid[MAX_NET_CLIENTS]; int e; #endif int kiss_port = mc->kiss_port; @@ -203,9 +216,11 @@ void kissnet_init (struct misc_config_s *mc) dw_printf ("kissnet_init ( %d )\n", kiss_port); #endif - memset (&kf, 0, sizeof(kf)); - client_sock = -1; + for (client=0; client 0) { - SLEEP_SEC(1); /* Already connected. Try again later. */ + int client; + int c; + + client = -1; + for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) { + if (client_sock[c] <= 0) { + client = c; + } } -#define QUEUE_SIZE 5 +/* + * Listen for connection if we have not reached maximum. + */ + if (client >= 0) { - if(listen(listen_sock,QUEUE_SIZE) == SOCKET_ERROR) - { - text_color_set(DW_COLOR_ERROR); - dw_printf("Listen failed with error: %d\n", WSAGetLastError()); - return (0); - } + if(listen(listen_sock, MAX_NET_CLIENTS) == SOCKET_ERROR) + { + text_color_set(DW_COLOR_ERROR); + dw_printf("Listen failed with error: %d\n", WSAGetLastError()); + return (0); + } - text_color_set(DW_COLOR_INFO); - dw_printf("Ready to accept KISS client application on port %s ...\n", kiss_port_str); + text_color_set(DW_COLOR_INFO); + dw_printf("Ready to accept KISS TCP client application %d on port %s ...\n", client, kiss_port_str); - client_sock = accept(listen_sock, NULL, NULL); + client_sock[client] = accept(listen_sock, NULL, NULL); - if (client_sock == -1) { - text_color_set(DW_COLOR_ERROR); - dw_printf("Accept failed with error: %d\n", WSAGetLastError()); - closesocket(listen_sock); - WSACleanup(); - return (0); - } + if (client_sock[client] == -1) { + text_color_set(DW_COLOR_ERROR); + dw_printf("Accept failed with error: %d\n", WSAGetLastError()); + closesocket(listen_sock); + WSACleanup(); + return (0); + } - text_color_set(DW_COLOR_INFO); - dw_printf("\nConnected to KISS client application ...\n\n"); + text_color_set(DW_COLOR_INFO); + dw_printf("\nAttached to KISS TCP client application %d ...\n\n", client); + // Reset the state and buffer. + memset (&(kf[client]), 0, sizeof(kf[client])); + } + else { + SLEEP_SEC(1); /* wait then check again if more clients allowed. */ + } } @@ -430,32 +465,44 @@ static THREAD_F connect_listen_thread (void *arg) #if DEBUG text_color_set(DW_COLOR_DEBUG); - dw_printf("opened KISS socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) ); + dw_printf("opened KISS TCP socket as fd (%d) on port (%d) for stream i/o\n", listen_sock, ntohs(sockaddr.sin_port) ); #endif while (1) { - - while (client_sock > 0) { - SLEEP_SEC(1); /* Already connected. Try again later. */ + + int client; + int c; + + client = -1; + for (c = 0; c < MAX_NET_CLIENTS && client < 0; c++) { + if (client_sock[c] <= 0) { + client = c; + } } -#define QUEUE_SIZE 5 + if (client >= 0) { - if(listen(listen_sock,QUEUE_SIZE) == -1) - { - text_color_set(DW_COLOR_ERROR); - perror ("connect_listen_thread: Listen failed"); - return (NULL); - } + if(listen(listen_sock,MAX_NET_CLIENTS) == -1) + { + text_color_set(DW_COLOR_ERROR); + perror ("connect_listen_thread: Listen failed"); + return (NULL); + } - text_color_set(DW_COLOR_INFO); - dw_printf("Ready to accept KISS client application on port %d ...\n", kiss_port); + text_color_set(DW_COLOR_INFO); + dw_printf("Ready to accept KISS TCP client application %d on port %d ...\n", client, kiss_port); - client_sock = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size); + client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size); - text_color_set(DW_COLOR_INFO); - dw_printf("\nConnected to KISS client application ...\n\n"); + text_color_set(DW_COLOR_INFO); + dw_printf("\nAttached to KISS TCP client application %d...\n\n", client); + // Reset the state and buffer. + memset (&(kf[client]), 0, sizeof(kf[client])); + } + else { + SLEEP_SEC(1); /* wait then check again if more clients allowed. */ + } } #endif } @@ -477,80 +524,121 @@ static THREAD_F connect_listen_thread (void *arg) * or a text string. * * flen - Number of bytes for AX.25 frame. - * or -1 for a text string. - * + * When called from kiss_rec_byte, flen will be -1 + * indicating a text string rather than frame content. + * This is used to fake out an application that thinks + * it is using a traditional TNC and tries to put it + * into KISS mode. * - * Description: Send message to client if connected. + * tcpclient - It is possible to have more than client attached + * at the same time with TCP KISS. + * When a frame is received from the radio we want it + * to go to all of the clients. In this case specify -1. + * When responding to a command from the client, we want + * to send only to that one client app. In this case + * use the value 0 .. MAX_NET_CLIENTS-1. + * + * Description: Send message to client(s) if connected. * Disconnect from client, and notify user, if any error. * *--------------------------------------------------------------------*/ -void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen) +void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen, int tcpclient) { unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN]; int kiss_len; int err; + int first, last, client; +// Something received over the radio would be sent to all attached clients. +// However, there are times we want to send a response only to a particular client. +// In the case of a serial port or pseudo terminal, there is only one potential client. +// so the response would be sent to only one place. A new parameter has been added for this. - if (client_sock == -1) { - return; + if (tcpclient >= 0 && tcpclient < MAX_NET_CLIENTS) { + first = tcpclient; + last = tcpclient; } - if (flen < 0) { - flen = strlen((char*)fbuf); - if (kiss_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 if (tcpclient == -1) { + first = 0; + last = MAX_NET_CLIENTS - 1; } else { - - - unsigned char stemp[AX25_MAX_PACKET_LEN + 1]; - - assert (flen < (int)(sizeof(stemp))); - - stemp[0] = (chan << 4) + 0; - memcpy (stemp+1, fbuf, flen); - - if (kiss_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 the escapes and the surrounding FENDs. */ - - if (kiss_debug) { - kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len); - } + text_color_set(DW_COLOR_ERROR); + dw_printf ("KISS TCP: Internal error, kissnet_send_rec_packet, tcpclient = %d.\n", tcpclient); + return; } + + for (client = first; client <= last; client++) { + + if (client_sock[client] != -1) { + + if (flen < 0) { + +// A client app might think it is attached to a traditional TNC. +// It might try sending commands over and over again trying to get the TNC into KISS mode. +// We recognize this attempt and send it something to keep it happy. + + text_color_set(DW_COLOR_ERROR); + dw_printf ("KISS TCP: Something unexpected from client application.\n"); + dw_printf ("Is client app treating this like an old TNC with command mode?\n"); + + flen = strlen((char*)fbuf); + if (kiss_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]; + + assert (flen < (int)(sizeof(stemp))); + + stemp[0] = (chan << 4) + 0; + memcpy (stemp+1, fbuf, flen); + + if (kiss_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 the escapes and the surrounding FENDs. */ + + if (kiss_debug) { + kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len); + } + } + #if __WIN32__ - err = send (client_sock, (char*)kiss_buff, kiss_len, 0); - if (err == SOCKET_ERROR) - { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nError %d sending message to KISS client application. Closing connection.\n\n", WSAGetLastError()); - closesocket (client_sock); - client_sock = -1; - WSACleanup(); - } + err = send (client_sock[client], (char*)kiss_buff, kiss_len, 0); + if (err == SOCKET_ERROR) + { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\nError %d sending message to KISS client application. Closing connection.\n\n", WSAGetLastError()); + closesocket (client_sock[client]); + client_sock[client] = -1; + WSACleanup(); + } #else - err = write (client_sock, kiss_buff, kiss_len); - if (err <= 0) - { - text_color_set(DW_COLOR_ERROR); - dw_printf ("\nError sending message to KISS client application. Closing connection.\n\n"); - close (client_sock); - client_sock = -1; - } + err = write (client_sock[client], kiss_buff, kiss_len); + if (err <= 0) + { + text_color_set(DW_COLOR_ERROR); + dw_printf ("\nError sending message to KISS client application. Closing connection.\n\n"); + close (client_sock[client]); + client_sock[client] = -1; + } #endif + } + } } /* end kissnet_send_rec_packet */ @@ -586,15 +674,13 @@ static int read_from_socket (int fd, char *ptr, int len) int n; #if __WIN32__ - -//TODO: any flags for send/recv? -//TODO: Would be useful to have more detailed explanation from the error code. - n = recv (fd, ptr + got_bytes, len - got_bytes, 0); #else n = read (fd, ptr + got_bytes, len - got_bytes); #endif +//Would be useful to have more detailed explanation from the error code. + #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("read_from_socket: n = %d\n", n); @@ -621,9 +707,9 @@ static int read_from_socket (int fd, char *ptr, int len) * * Purpose: Wait for KISS messages from an application. * - * Inputs: arg - Not used. + * Inputs: arg - client number, 0 .. MAX_NET_CLIENTS-1 * - * Outputs: client_sock - File descriptor for communicating with client app. + * Outputs: client_sock[n] - File descriptor for communicating with client app. * * Description: Process messages from the client application. * Note that the client can go away and come back again and @@ -635,20 +721,20 @@ static int read_from_socket (int fd, char *ptr, int len) /* Return one byte (value 0 - 255) */ -static int kiss_get (void) +static int kiss_get (int client) { unsigned char ch; int n; while (1) { - while (client_sock <= 0) { + while (client_sock[client] <= 0) { SLEEP_SEC(1); /* Not connected. Try again later. */ } /* Just get one byte at a time. */ - n = read_from_socket (client_sock, (char *)(&ch), 1); + n = read_from_socket (client_sock[client], (char *)(&ch), 1); if (n == 1) { #if DEBUG9 @@ -668,13 +754,13 @@ static int kiss_get (void) } text_color_set(DW_COLOR_ERROR); - dw_printf ("\nError reading KISS byte from client application. Closing connection.\n\n"); + dw_printf ("\nError reading KISS byte from client application %d. Closing connection.\n\n", client); #if __WIN32__ - closesocket (client_sock); + closesocket (client_sock[client]); #else - close (client_sock); + close (client_sock[client]); #endif - client_sock = -1; + client_sock[client] = -1; } } @@ -689,9 +775,26 @@ static THREAD_F kissnet_listen_thread (void *arg) dw_printf ("kissnet_listen_thread ( socket = %d )\n", client_sock); #endif + int client = (int)(long)arg; + + assert (client >= 0 && client < MAX_NET_CLIENTS); + + +// So why is kissnet_send_rec_packet mentioned here for incoming from the client app? +// The logic exists for the serial port case where the client might think it is +// attached to a traditional TNC. It might try sending commands over and over again +// trying to get the TNC into KISS mode. To keep it happy, we recognize this attempt +// and send it something to keep it happy. +// In the case of a serial port or pseudo terminal, there is only one potential client +// so the response would be sent to only one place. +// Starting in version 1.5, this now can have multiple attached clients. We wouldn't +// want to send the response to all of them. Actually, we should be providing only +// "Simply KISS" as some call it. + + while (1) { - ch = kiss_get(); - kiss_rec_byte (&kf, ch, kiss_debug, kissnet_send_rec_packet); + ch = kiss_get(client); + kiss_rec_byte (&(kf[client]), ch, kiss_debug, client, kissnet_send_rec_packet); } #if __WIN32__ diff --git a/kissnet.h b/kissnet.h index 361f435..11c64e3 100644 --- a/kissnet.h +++ b/kissnet.h @@ -13,7 +13,7 @@ void kissnet_init (struct misc_config_s *misc_config); -void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen); +void kissnet_send_rec_packet (int chan, unsigned char *fbuf, int flen, int client); void kiss_net_set_debug (int n); diff --git a/serial_port.c b/serial_port.c index 3ccf43c..a32f804 100644 --- a/serial_port.c +++ b/serial_port.c @@ -1,13 +1,7 @@ - -// TODO: Needs more clean up and testing of error conditions. - -// TODO: use this in place of other similar code. - - // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2014, 2015 John Langner, WB2OSZ +// Copyright (C) 2014, 2015, 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 @@ -73,8 +67,10 @@ * Inputs: devicename - For Windows, usually like COM5. * For Linux, usually /dev/tty... * "COMn" also allowed and converted to /dev/ttyS(n-1) + * Could be /dev/rfcomm0 for Bluetooth. * - * baud - Speed. 4800, 9600, etc. + * baud - Speed. 1200, 4800, 9600 bps, etc. + * If 0, leave it alone. * * Returns Handle for serial port or MYFDERROR for error. * @@ -143,6 +139,7 @@ MYFDTYPE serial_port_open (char *devicename, int baud) switch (baud) { + case 0: /* Leave it alone. */ break; case 1200: dcb.BaudRate = CBR_1200; break; case 2400: dcb.BaudRate = CBR_2400; break; case 4800: dcb.BaudRate = CBR_4800; break; @@ -233,6 +230,7 @@ MYFDTYPE serial_port_open (char *devicename, int baud) switch (baud) { + case 0: /* Leave it alone. */ break; case 1200: cfsetispeed (&ts, B1200); cfsetospeed (&ts, B1200); break; case 2400: cfsetispeed (&ts, B2400); cfsetospeed (&ts, B2400); break; case 4800: cfsetispeed (&ts, B4800); cfsetospeed (&ts, B4800); break; @@ -305,8 +303,10 @@ int serial_port_write (MYFDTYPE fd, char *str, int len) } else if ((int)nwritten != len) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error writing to serial port. Only %d of %d written.\n\n", (int)nwritten, len); + // Do we want this message here? + // Or rely on caller to check and provide something more meaningful for the usage? + //text_color_set(DW_COLOR_ERROR); + //dw_printf ("Error writing to serial port. Only %d of %d written.\n\n", (int)nwritten, len); } return (nwritten); @@ -317,8 +317,10 @@ int serial_port_write (MYFDTYPE fd, char *str, int len) written = write (fd, str, (size_t)len); if (written != len) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error writing to serial port. err=%d\n\n", written); + // Do we want this message here? + // Or rely on caller to check and provide something more meaningful for the usage? + //text_color_set(DW_COLOR_ERROR); + //dw_printf ("Error writing to serial port. err=%d\n\n", written); return (-1); } @@ -402,8 +404,9 @@ int serial_port_get1 (MYFDTYPE fd) CloseHandle(ov_rd.hEvent); if (n != 1) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Serial port failed to get one byte. n=%d.\n\n", (int)n); + //text_color_set(DW_COLOR_ERROR); + //dw_printf ("Serial port failed to get one byte. n=%d.\n\n", (int)n); + return (-1); } @@ -414,8 +417,8 @@ int serial_port_get1 (MYFDTYPE fd) n = read(fd, &ch, (size_t)1); if (n != 1) { - text_color_set(DW_COLOR_DEBUG); - dw_printf ("serial_port_get1(%d) returns -1 for error.\n", fd); + //text_color_set(DW_COLOR_DEBUG); + //dw_printf ("serial_port_get1(%d) returns -1 for error.\n", fd); return (-1); } @@ -447,8 +450,14 @@ int serial_port_get1 (MYFDTYPE fd) * *--------------------------------------------------------------------*/ - -// TODO: +void serial_port_close (MYFDTYPE fd) +{ +#if __WIN32__ + CloseHandle (fd); +#else + close (fd); +#endif +} /* end serial_port.c */ diff --git a/server.c b/server.c index 2a1ef78..70e3af3 100644 --- a/server.c +++ b/server.c @@ -637,8 +637,7 @@ static THREAD_F connect_listen_thread (void *arg) } text_color_set(DW_COLOR_INFO); -// TODO: "attached" or some other term would be better because "connected" has a different meaning for AX.25. - dw_printf("\nConnected to AGW client application %d ...\n\n", client); + dw_printf("\nAttached to AGW client application %d ...\n\n", client); /* * The command to change this is actually a toggle, not explicit on or off. @@ -729,7 +728,7 @@ static THREAD_F connect_listen_thread (void *arg) client_sock[client] = accept(listen_sock, (struct sockaddr*)(&sockaddr),&sockaddr_size); text_color_set(DW_COLOR_INFO); - dw_printf("\nConnected to AGW client application %d...\n\n", client); + dw_printf("\nAttached to AGW client application %d...\n\n", client); /* * The command to change this is actually a toggle, not explicit on or off. diff --git a/tt_user.c b/tt_user.c index f6ce167..deb1042 100644 --- a/tt_user.c +++ b/tt_user.c @@ -58,6 +58,7 @@ #include "server.h" #include "kiss.h" +#include "kissserial.h" #include "kissnet.h" @@ -895,8 +896,9 @@ static void xmit_object_report (int i, int first_time) flen = ax25_pack(pp, fbuf); server_send_rec_packet (save_tt_config_p->obj_recv_chan, pp, fbuf, flen); - kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen); - kiss_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen); + kissnet_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen, -1); + kissserial_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen, -1); + kisspt_send_rec_packet (save_tt_config_p->obj_recv_chan, fbuf, flen, -1); } if (first_time && save_tt_config_p->obj_send_to_ig) { diff --git a/version.h b/version.h index b61ab1a..80787b7 100644 --- a/version.h +++ b/version.h @@ -1,8 +1,8 @@ -/* Dire Wolf version 1.4 */ +/* Dire Wolf version 1.5 */ #define APP_TOCALL "APDW" #define MAJOR_VERSION 1 -#define MINOR_VERSION 4 +#define MINOR_VERSION 5 //#define EXTRA_VERSION "Beta Test"