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