1 //
2 //    This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 //    Copyright (C) 2011, 2013, 2014, 2016, 2017  John Langner, WB2OSZ
5 //
6 //    This program is free software: you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation, either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    This program is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License
17 //    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 
21 /*------------------------------------------------------------------
22  *
23  * Module:      kissserial.c
24  *
25  * Purpose:   	Act as a virtual KISS TNC for use by other packet radio applications.
26  *		This file provides the service by good old fashioned serial port.
27  *		Other files implement a pseudo terminal or TCP KISS interface.
28  *
29  * Description:	This implements the KISS TNC protocol as described in:
30  *		http://www.ka9q.net/papers/kiss.html
31  *
32  * 		Briefly, a frame is composed of
33  *
34  *			* FEND (0xC0)
35  *			* Contents - with special escape sequences so a 0xc0
36  *				byte in the data is not taken as end of frame.
37  *				as part of the data.
38  *			* FEND
39  *
40  *		The first byte of the frame contains:
41  *
42  *			* port number in upper nybble.
43  *			* command in lower nybble.
44  *
45  *		Commands from application recognized:
46  *
47  *			_0	Data Frame	AX.25 frame in raw format.
48  *
49  *			_1	TXDELAY		See explanation in xmit.c.
50  *
51  *			_2	Persistence	"	"
52  *
53  *			_3 	SlotTime	"	"
54  *
55  *			_4	TXtail		"	"
56  *						Spec says it is obsolete but Xastir
57  *						sends it and we respect it.
58  *
59  *			_5	FullDuplex	Ignored.
60  *
61  *			_6	SetHardware	TNC specific.
62  *
63  *			FF	Return		Exit KISS mode.  Ignored.
64  *
65  *
66  *		Messages sent to client application:
67  *
68  *			_0	Data Frame	Received AX.25 frame in raw format.
69  *
70  *
71  * Platform differences:
72  *
73  *		This file implements KISS over a serial port.
74  *		It should behave pretty much the same for both Windows and Linux.
75  *
76  *		When running a client application on Windows, two applications
77  *		can be connected together using a a "Null-modem emulator"
78  *		such as com0com from http://sourceforge.net/projects/com0com/
79  *
80  *		(When running a client application, on the same host, with Linux,
81  *		a pseudo terminal can be used for old applications.  More modern
82  *		applications will generally have AGW and/or KISS over TCP.)
83  *
84  *
85  * version 1.5:	Split out from kiss.c, simplified, consistent for Windows and Linux.
86  *		Add polling option for use with Bluetooth.
87  *
88  *---------------------------------------------------------------*/
89 
90 #include "direwolf.h"
91 
92 #include <stdio.h>
93 #include <unistd.h>
94 #include <stdlib.h>
95 #include <string.h>
96 #include <sys/types.h>
97 #include <sys/stat.h>
98 
99 
100 #include "ax25_pad.h"
101 #include "textcolor.h"
102 #include "serial_port.h"
103 #include "kissserial.h"
104 #include "kiss_frame.h"
105 #include "xmit.h"
106 
107 
108 /*
109  * Save Configuration for later use.
110  */
111 
112 static struct misc_config_s  *g_misc_config_p;
113 
114 /*
115  * Accumulated KISS frame and state of decoder.
116  */
117 
118 static kiss_frame_t kf;
119 
120 
121 /*
122  * The serial port device handle.
123  * MYFD... are defined in kissserial.h
124  */
125 
126 static MYFDTYPE serialport_fd = MYFDERROR;
127 
128 
129 
130 
131 // TODO:  define in one place, use everywhere.
132 #if __WIN32__
133 #define THREAD_F unsigned __stdcall
134 #else
135 #define THREAD_F void *
136 #endif
137 
138 static THREAD_F kissserial_listen_thread (void *arg);
139 
140 
141 static int kissserial_debug = 0;		/* Print information flowing from and to client. */
142 
kissserial_set_debug(int n)143 void kissserial_set_debug (int n)
144 {
145 	kissserial_debug = n;
146 }
147 
148 
149 /* In server.c.  Should probably move to some misc. function file. */
150 
151 void hex_dump (unsigned char *p, int len);
152 
153 
154 
155 
156 /*-------------------------------------------------------------------
157  *
158  * Name:        kissserial_init
159  *
160  * Purpose:     Set up a serial port acting as a virtual KISS TNC.
161  *
162  * Inputs:	mc->
163  *		    kiss_serial_port	- Name of device for real or virtual serial port.
164  *		    kiss_serial_speed	- Speed, bps, or 0 meaning leave it alone.
165  *		    kiss_serial_poll	- When non-zero, poll each n seconds to see if
166  *					  device has appeared.
167  *
168  * Outputs:
169  *
170  * Description:	(1) Open file descriptor for the device.
171  *		(2) Start a new thread to listen for commands from client app
172  *		    so the main application doesn't block while we wait.
173  *
174  *--------------------------------------------------------------------*/
175 
176 
kissserial_init(struct misc_config_s * mc)177 void kissserial_init (struct misc_config_s *mc)
178 {
179 
180 #if __WIN32__
181 	HANDLE kissserial_listen_th;
182 #else
183 	pthread_t kissserial_listen_tid;
184 	int e;
185 #endif
186 
187 	g_misc_config_p = mc;
188 
189 	memset (&kf, 0, sizeof(kf));
190 
191 
192 	if (strlen(g_misc_config_p->kiss_serial_port) > 0) {
193 
194 	  if (g_misc_config_p->kiss_serial_poll == 0) {
195 
196 	    // Normal case, try to open the serial port at start up time.
197 
198 	    serialport_fd = serial_port_open (g_misc_config_p->kiss_serial_port, g_misc_config_p->kiss_serial_speed);
199 
200 	    if (serialport_fd != MYFDERROR) {
201 	      text_color_set(DW_COLOR_INFO);
202 	      dw_printf ("Opened %s for serial port KISS.\n", g_misc_config_p->kiss_serial_port);
203 	    }
204 	    else {
205 	      // An error message was already displayed.
206 	    }
207 	  }
208 	  else {
209 
210 	    // Polling case.   Defer until read and device not opened.
211 	    text_color_set(DW_COLOR_INFO);
212 	    dw_printf ("Will be checking periodically for %s\n", g_misc_config_p->kiss_serial_port);
213 	  }
214 
215 	  if (g_misc_config_p->kiss_serial_poll != 0 || serialport_fd != MYFDERROR) {
216 #if __WIN32__
217 	    kissserial_listen_th = (HANDLE)_beginthreadex (NULL, 0, kissserial_listen_thread, NULL, 0, NULL);
218 	    if (kissserial_listen_th == NULL) {
219 	      text_color_set(DW_COLOR_ERROR);
220 	      dw_printf ("Could not create kiss serial thread\n");
221 	      return;
222 	    }
223 #else
224 	    e = pthread_create (&kissserial_listen_tid, NULL, kissserial_listen_thread, NULL);
225 	    if (e != 0) {
226 	      text_color_set(DW_COLOR_ERROR);
227 	      perror("Could not create kiss serial thread.");
228 	    }
229 #endif
230 	  }
231 	}
232 
233 
234 #if DEBUG
235 	text_color_set (DW_COLOR_DEBUG);
236 
237 	dw_printf ("end of kiss_init: serialport_fd = %d, polling = %d\n", serialport_fd, g_misc_config_p->kiss_serial_poll);
238 #endif
239 }
240 
241 
242 
243 
244 /*-------------------------------------------------------------------
245  *
246  * Name:        kissserial_send_rec_packet
247  *
248  * Purpose:     Send a received packet or text string to the client app.
249  *
250  * Inputs:	chan		- Channel number where packet was received.
251  *				  0 = first, 1 = second if any.
252  *
253  *		kiss_cmd	- Usually KISS_CMD_DATA_FRAME but we can also have
254  *				  KISS_CMD_SET_HARDWARE when responding to a query.
255  *
256  *		pp		- Identifier for packet object.
257  *
258  *		fbuf		- Address of raw received frame buffer
259  *				  or a text string.
260  *
261  *		flen		- Length of raw received frame not including the FCS
262  *				  or -1 for a text string.
263  *
264  *		client		- Not used for serial port version.
265  *				  Here so that 3 related functions all have
266  *				  the same parameter list.
267  *
268  * Description:	Send message to client.
269  *		We really don't care if anyone is listening or not.
270  *		I don't even know if we can find out.
271  *
272  *--------------------------------------------------------------------*/
273 
274 
kissserial_send_rec_packet(int chan,int kiss_cmd,unsigned char * fbuf,int flen,int client)275 void kissserial_send_rec_packet (int chan, int kiss_cmd, unsigned char *fbuf,  int flen, int client)
276 {
277 	unsigned char kiss_buff[2 * AX25_MAX_PACKET_LEN + 2];
278 	int kiss_len;
279 	int err;
280 
281 
282 /*
283  * Quietly discard if we don't have open connection.
284  */
285 	if (serialport_fd == MYFDERROR) {
286 	  return;
287 	}
288 
289 	if (flen < 0) {
290 	  flen = strlen((char*)fbuf);
291 	  if (kissserial_debug) {
292 	    kiss_debug_print (TO_CLIENT, "Fake command prompt", fbuf, flen);
293 	  }
294 	  strlcpy ((char *)kiss_buff, (char *)fbuf, sizeof(kiss_buff));
295 	  kiss_len = strlen((char *)kiss_buff);
296 	}
297 	else {
298 
299 	  unsigned char stemp[AX25_MAX_PACKET_LEN + 1];
300 
301 	  if (flen > (int)(sizeof(stemp)) - 1) {
302 	    text_color_set(DW_COLOR_ERROR);
303 	    dw_printf ("\nSerial Port KISS buffer too small.  Truncated.\n\n");
304 	    flen = (int)(sizeof(stemp)) - 1;
305 	  }
306 
307 	  stemp[0] = (chan << 4) | kiss_cmd;
308 	  memcpy (stemp+1, fbuf, flen);
309 
310 	  if (kissserial_debug >= 2) {
311 	    /* AX.25 frame with the CRC removed. */
312 	    text_color_set(DW_COLOR_DEBUG);
313 	    dw_printf ("\n");
314 	    dw_printf ("Packet content before adding KISS framing and any escapes:\n");
315 	    hex_dump (fbuf, flen);
316 	  }
317 
318 	  kiss_len = kiss_encapsulate (stemp, flen+1, kiss_buff);
319 
320 	  /* This has KISS framing and escapes for sending to client app. */
321 
322 	  if (kissserial_debug) {
323 	    kiss_debug_print (TO_CLIENT, NULL, kiss_buff, kiss_len);
324 	  }
325 	}
326 
327 /*
328  * This write can block on Windows if using the virtual null modem
329  * and nothing is connected to the other end.
330  * The solution is found in the com0com ReadMe file:
331  *
332  *	Q. My application hangs during its startup when it sends anything to one paired
333  *	   COM port. The only way to unhang it is to start HyperTerminal, which is connected
334  *	   to the other paired COM port. I didn't have this problem with physical serial
335  *	   ports.
336  *	A. Your application can hang because receive buffer overrun is disabled by
337  *	   default. You can fix the problem by enabling receive buffer overrun for the
338  *	   receiving port. Also, to prevent some flow control issues you need to enable
339  *	   baud rate emulation for the sending port. So, if your application use port CNCA0
340  *	   and other paired port is CNCB0, then:
341  *
342  *	   1. Launch the Setup Command Prompt shortcut.
343  *	   2. Enter the change commands, for example:
344  *
345  *	      command> change CNCB0 EmuOverrun=yes
346  *	      command> change CNCA0 EmuBR=yes
347  */
348 
349 	err = serial_port_write (serialport_fd, (char*)kiss_buff, kiss_len);
350 
351 	if (err != kiss_len)
352 	{
353 	  text_color_set(DW_COLOR_ERROR);
354 	  dw_printf ("\nError sending KISS message to client application thru serial port.\n\n");
355 	  serial_port_close (serialport_fd);
356 	  serialport_fd = MYFDERROR;
357 	}
358 
359 } /* kissserial_send_rec_packet */
360 
361 
362 
363 /*-------------------------------------------------------------------
364  *
365  * Name:        kissserial_get
366  *
367  * Purpose:     Read one byte from the KISS client app.
368  *
369  * Global In:	serialport_fd
370  *
371  * Returns:	one byte (value 0 - 255) or terminate thread on error.
372  *
373  * Description:	There is room for improvment here.  Reading one byte
374  *		at a time is inefficient.  We could read a large block
375  *		into a local buffer and return a byte from that most of the time.
376  *		Is it worth the effort?  I don't know.  With GHz processors and
377  *		the low data rate here it might not make a noticable difference.
378  *
379  *--------------------------------------------------------------------*/
380 
381 
kissserial_get(void)382 static int kissserial_get (void)
383 {
384 	int ch;		// normally 0-255 but -1 for error.
385 
386 
387 	if (g_misc_config_p->kiss_serial_poll == 0) {
388 /*
389  * Normal case, was opened at start up time.
390  */
391 	  ch = serial_port_get1 (serialport_fd);
392 
393 	  if (ch < 0) {
394 
395 	    text_color_set(DW_COLOR_ERROR);
396 	    dw_printf ("\nSerial Port KISS read error. Closing connection.\n\n");
397 	    serial_port_close (serialport_fd);
398 	    serialport_fd = MYFDERROR;
399 #if __WIN32__
400 	    ExitThread (0);
401 #else
402 	    pthread_exit (NULL);
403 #endif
404 	  }
405 
406 #if DEBUGx
407 	  text_color_set(DW_COLOR_DEBUG);
408 	  dw_printf ("kissserial_get(%d) returns 0x%02x\n", fd, ch);
409 #endif
410 	  return (ch);
411 	}
412 
413 /*
414  * Polling case.  Wait until device is present and open.
415  */
416 	while (1) {
417 
418 	  if (serialport_fd != MYFDERROR) {
419 
420 	    // Open, try to read.
421 
422 	    ch = serial_port_get1 (serialport_fd);
423 
424 	    if (ch >= 0) {
425 	       return (ch);
426 	    }
427 
428 	    text_color_set(DW_COLOR_ERROR);
429 	    dw_printf ("\nSerial Port KISS read error. Closing connection.\n\n");
430 	    serial_port_close (serialport_fd);
431 	    serialport_fd = MYFDERROR;
432 	  }
433 	  else {
434 
435 	    // Not open.  Wait for it to appear and try opening.
436 
437 	    struct stat buf;
438 
439 	    SLEEP_SEC (g_misc_config_p->kiss_serial_poll);
440 
441 	    if (stat(g_misc_config_p->kiss_serial_port, &buf) == 0) {
442 
443 	      // It's there now.  Try to open.
444 
445 	      serialport_fd = serial_port_open (g_misc_config_p->kiss_serial_port, g_misc_config_p->kiss_serial_speed);
446 
447 	      if (serialport_fd != MYFDERROR) {
448 	        text_color_set(DW_COLOR_INFO);
449 	        dw_printf ("\nOpened %s for serial port KISS.\n\n", g_misc_config_p->kiss_serial_port);
450 
451 	        memset (&kf, 0, sizeof(kf));		// Start with clean state.
452 	      }
453 	      else {
454 	        // An error message was already displayed.
455 	      }
456 	    }
457 	  }
458 	}
459 
460 } /* end kissserial_get */
461 
462 
463 
464 /*-------------------------------------------------------------------
465  *
466  * Name:        kissserial_listen_thread
467  *
468  * Purpose:     Read messages from serial port KISS client application.
469  *
470  * Global In:	serialport_fd
471  *
472  * Description:	Reads bytes from the serial port KISS client app and
473  *		sends them to kiss_rec_byte for processing.
474  *		kiss_rec_byte is a common function used by all 3 KISS
475  *		interfaces: serial port, pseudo terminal, and TCP.
476  *
477  *--------------------------------------------------------------------*/
478 
479 
kissserial_listen_thread(void * arg)480 static THREAD_F kissserial_listen_thread (void *arg)
481 {
482 	unsigned char ch;
483 
484 #if DEBUG
485 	text_color_set(DW_COLOR_DEBUG);
486 	dw_printf ("kissserial_listen_thread ( %d )\n", fd);
487 #endif
488 
489 	while (1) {
490 	  ch = kissserial_get();
491 	  kiss_rec_byte (&kf, ch, kissserial_debug, -1, kissserial_send_rec_packet);
492 	}
493 
494 #if __WIN32__
495 	return(0);
496 #else
497 	return (THREAD_F) 0;	/* Unreachable but avoids compiler warning. */
498 #endif
499 }
500 
501 /* end kissserial.c */
502