1 //
2 //    This file is part of Dire Wolf, an amateur radio packet TNC.
3 //
4 //    Copyright (C) 2014, 2015, 2016, 2018  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:      dlq.c
24  *
25  * Purpose:   	Received frame queue.
26  *
27  * Description: In earlier versions, the main thread read from the
28  *		audio device and performed the receive demodulation/decoding.
29  *
30  *		Since version 1.2 we have a separate receive thread
31  *		for each audio device.  This queue is used to collect
32  *		received frames from all channels and process them
33  *		serially.
34  *
35  *		In version 1.4, other types of events also go into this
36  *		queue and we use it to drive the data link state machine.
37  *
38  *---------------------------------------------------------------*/
39 
40 #include "direwolf.h"
41 
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <stdlib.h>
45 #include <assert.h>
46 #include <string.h>
47 #if __WIN32__
48 #else
49 #include <errno.h>
50 #endif
51 
52 #include "ax25_pad.h"
53 #include "textcolor.h"
54 #include "audio.h"
55 #include "dlq.h"
56 #include "dedupe.h"
57 #include "dtime_now.h"
58 
59 
60 /* The queue is a linked list of these. */
61 
62 static struct dlq_item_s *queue_head = NULL;	/* Head of linked list for queue. */
63 
64 #if __WIN32__
65 
66 // TODO1.2: use dw_mutex_t
67 
68 static CRITICAL_SECTION dlq_cs;			/* Critical section for updating queues. */
69 
70 static HANDLE wake_up_event;			/* Notify received packet processing thread when queue not empty. */
71 
72 #else
73 
74 static pthread_mutex_t dlq_mutex;		/* Critical section for updating queues. */
75 
76 static pthread_cond_t wake_up_cond;		/* Notify received packet processing thread when queue not empty. */
77 
78 static pthread_mutex_t wake_up_mutex;		/* Required by cond_wait. */
79 
80 static volatile int recv_thread_is_waiting = 0;
81 
82 #endif
83 
84 static int was_init = 0;			/* was initialization performed? */
85 
86 static void append_to_queue (struct dlq_item_s *pnew);
87 
88 static volatile int s_new_count = 0;		/* To detect memory leak for queue items. */
89 static volatile int s_delete_count = 0;		// TODO:  need to test.
90 
91 
92 static volatile int s_cdata_new_count = 0;		/* To detect memory leak for connected mode data. */
93 static volatile int s_cdata_delete_count = 0;		// TODO:  need to test.
94 
95 
96 
97 /*-------------------------------------------------------------------
98  *
99  * Name:        dlq_init
100  *
101  * Purpose:     Initialize the queue.
102  *
103  * Inputs:	None.
104  *
105  * Outputs:
106  *
107  * Description:	Initialize the queue to be empty and set up other
108  *		mechanisms for sharing it between different threads.
109  *
110  *--------------------------------------------------------------------*/
111 
112 
dlq_init(void)113 void dlq_init (void)
114 {
115 #if DEBUG
116 	text_color_set(DW_COLOR_DEBUG);
117 	dw_printf ("dlq_init ( )\n");
118 #endif
119 
120 	queue_head = NULL;
121 
122 
123 #if DEBUG
124 	text_color_set(DW_COLOR_DEBUG);
125 	dw_printf ("dlq_init: pthread_mutex_init...\n");
126 #endif
127 
128 #if __WIN32__
129 	InitializeCriticalSection (&dlq_cs);
130 #else
131 	int err;
132 	err = pthread_mutex_init (&wake_up_mutex, NULL);
133 	err = pthread_mutex_init (&dlq_mutex, NULL);
134 	if (err != 0) {
135 	  text_color_set(DW_COLOR_ERROR);
136 	  dw_printf ("dlq_init: pthread_mutex_init err=%d", err);
137 	  perror ("");
138 	  exit (1);
139 	}
140 #endif
141 
142 
143 
144 #if DEBUG
145 	text_color_set(DW_COLOR_DEBUG);
146 	dw_printf ("dlq_init: pthread_cond_init...\n");
147 #endif
148 
149 #if __WIN32__
150 
151 	wake_up_event = CreateEvent (NULL, 0, 0, NULL);
152 
153 	if (wake_up_event == NULL) {
154 	  text_color_set(DW_COLOR_ERROR);
155 	  dw_printf ("dlq_init: pthread_cond_init: can't create receive wake up event");
156 	  exit (1);
157 	}
158 
159 #else
160 	err = pthread_cond_init (&wake_up_cond, NULL);
161 
162 
163 #if DEBUG
164 	text_color_set(DW_COLOR_DEBUG);
165 	dw_printf ("dlq_init: pthread_cond_init returns %d\n", err);
166 #endif
167 
168 
169 	if (err != 0) {
170 	  text_color_set(DW_COLOR_ERROR);
171 	  dw_printf ("dlq_init: pthread_cond_init err=%d", err);
172 	  perror ("");
173 	  exit (1);
174 	}
175 
176 	recv_thread_is_waiting = 0;
177 #endif
178 
179 	was_init = 1;
180 
181 } /* end dlq_init */
182 
183 
184 
185 /*-------------------------------------------------------------------
186  *
187  * Name:        dlq_rec_frame
188  *
189  * Purpose:     Add a received packet to the end of the queue.
190  *		Normally this was received over the radio but we can create
191  *		our own from APRStt or beaconing.
192  *
193  *		This would correspond to PH-DATA Indication in the AX.25 protocol spec.
194  *
195  * Inputs:	chan	- Channel, 0 is first.
196  *
197  *		subchan	- Which modem caught it.
198  *			  Special case -1 for APRStt gateway.
199  *
200  *		slice	- Which slice we picked.
201  *
202  *		pp	- Address of packet object.
203  *				Caller should NOT make any references to
204  *				it after this point because it could
205  *				be deleted at any time.
206  *
207  *		alevel	- Audio level, range of 0 - 100.
208  *				(Special case, use negative to skip
209  *				 display of audio level line.
210  *				 Use -2 to indicate DTMF message.)
211  *
212  *		is_fx25 - Was it from FX.25?  Need to know because
213  *			  meaning of retries is different.
214  *
215  *		retries	- Level of bit correction used.
216  *
217  *		spectrum - Display of how well multiple decoders did.
218  *
219  *
220  * IMPORTANT!	Don't make an further references to the packet object after
221  *		giving it to dlq_append.
222  *
223  *--------------------------------------------------------------------*/
224 
dlq_rec_frame(int chan,int subchan,int slice,packet_t pp,alevel_t alevel,int is_fx25,retry_t retries,char * spectrum)225 void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alevel, int is_fx25, retry_t retries, char *spectrum)
226 {
227 
228 	struct dlq_item_s *pnew;
229 
230 
231 #if DEBUG
232 	text_color_set(DW_COLOR_DEBUG);
233 	dw_printf ("dlq_rec_frame (chan=%d, pp=%p, ...)\n", chan, pp);
234 #endif
235 
236 	assert (chan >= 0 && chan < MAX_CHANS);
237 
238 	if (pp == NULL) {
239 	  text_color_set(DW_COLOR_ERROR);
240 	  dw_printf ("INTERNAL ERROR:  dlq_rec_frame NULL packet pointer. Please report this!\n");
241 	  return;
242 	}
243 
244 #if AX25MEMDEBUG
245 
246 	if (ax25memdebug_get()) {
247 	  text_color_set(DW_COLOR_DEBUG);
248 	  dw_printf ("dlq_rec_frame (chan=%d.%d, seq=%d, ...)\n", chan, subchan, ax25memdebug_seq(pp));
249 	}
250 #endif
251 
252 
253 /* Allocate a new queue item. */
254 
255 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
256 	s_new_count++;
257 
258 	if (s_new_count > s_delete_count + 50) {
259 	  text_color_set(DW_COLOR_ERROR);
260 	  dw_printf ("INTERNAL ERROR:  DLQ memory leak, new=%d, delete=%d\n", s_new_count, s_delete_count);
261 	}
262 
263 	pnew->nextp = NULL;
264 	pnew->type = DLQ_REC_FRAME;
265 	pnew->chan = chan;
266 	pnew->slice = slice;
267 	pnew->subchan = subchan;
268 	pnew->pp = pp;
269 	pnew->alevel = alevel;
270 	pnew->is_fx25 = is_fx25;
271 	pnew->retries = retries;
272 	if (spectrum == NULL)
273 	  strlcpy(pnew->spectrum, "", sizeof(pnew->spectrum));
274 	else
275 	  strlcpy(pnew->spectrum, spectrum, sizeof(pnew->spectrum));
276 
277 /* Put it into queue. */
278 
279 	append_to_queue (pnew);
280 
281 } /* end dlq_rec_frame */
282 
283 
284 
285 /*-------------------------------------------------------------------
286  *
287  * Name:        append_to_queue
288  *
289  * Purpose:     Append some type of event to queue.
290  *		This includes frames received over the radio,
291  *		requests from client applications, and notifications
292  *		from the frame transmission process.
293  *
294  *
295  * Inputs:	pnew		- Pointer to queue element structure.
296  *
297  * Outputs:	Information is appended to queue.
298  *
299  * Description:	Add item to end of linked list.
300  *		Signal the receive processing thread if the queue was formerly empty.
301  *
302  *--------------------------------------------------------------------*/
303 
append_to_queue(struct dlq_item_s * pnew)304 static void append_to_queue (struct dlq_item_s *pnew)
305 {
306 	struct dlq_item_s *plast;
307 	int queue_length = 0;
308 
309 	if ( ! was_init) {
310 	  dlq_init ();
311 	}
312 
313 	pnew->nextp = NULL;
314 
315 #if DEBUG1
316 	text_color_set(DW_COLOR_DEBUG);
317 	dw_printf ("dlq append_to_queue: enter critical section\n");
318 #endif
319 #if __WIN32__
320 	EnterCriticalSection (&dlq_cs);
321 #else
322 	int err;
323 	err = pthread_mutex_lock (&dlq_mutex);
324 	if (err != 0) {
325 	  text_color_set(DW_COLOR_ERROR);
326 	  dw_printf ("dlq append_to_queue: pthread_mutex_lock err=%d", err);
327 	  perror ("");
328 	  exit (1);
329 	}
330 #endif
331 
332 	if (queue_head == NULL) {
333 	  queue_head = pnew;
334 	  queue_length = 1;
335 	}
336 	else {
337 	  queue_length = 2;	/* head + new one */
338 	  plast = queue_head;
339 	  while (plast->nextp != NULL) {
340 	    plast = plast->nextp;
341 	    queue_length++;
342 	  }
343 	  plast->nextp = pnew;
344 	}
345 
346 
347 #if __WIN32__
348 	LeaveCriticalSection (&dlq_cs);
349 #else
350 	err = pthread_mutex_unlock (&dlq_mutex);
351 	if (err != 0) {
352 	  text_color_set(DW_COLOR_ERROR);
353 	  dw_printf ("dlq append_to_queue: pthread_mutex_unlock err=%d", err);
354 	  perror ("");
355 	  exit (1);
356 	}
357 #endif
358 #if DEBUG1
359 	text_color_set(DW_COLOR_DEBUG);
360 	dw_printf ("dlq append_to_queue: left critical section\n");
361 	dw_printf ("dlq append_to_queue (): about to wake up recv processing thread.\n");
362 #endif
363 
364 
365 /*
366  * Bug:  June 2015, version 1.2
367  *
368  * It has long been known that we will eventually block trying to write to a
369  * pseudo terminal if nothing is reading from the other end.  There is even
370  * a warning at start up time:
371  *
372  *	Virtual KISS TNC is available on /dev/pts/2
373  *	WARNING - Dire Wolf will hang eventually if nothing is reading from it.
374  *	Created symlink /tmp/kisstnc -> /dev/pts/2
375  *
376  * In earlier versions, where the audio input and demodulation was in the main
377  * thread, that would stop and it was pretty obvious something was wrong.
378  * In version 1.2, the audio in / demodulating was moved to a device specific
379  * thread.  Packet objects are appended to this queue.
380  *
381  * The main thread should wake up and process them which includes printing and
382  * forwarding to clients over multiple protocols and transport methods.
383  * Just before the 1.2 release someone reported a memory leak which only showed
384  * up after about 20 hours.  It happened to be on a Cubie Board 2, which shouldn't
385  * make a difference unless there was some operating system difference.
386  * (cubieez 2.0 is based on Debian wheezy, just like Raspian.)
387  *
388  * The debug output revealed:
389  *
390  *	It was using AX.25 for Linux (not APRS).
391  *	The pseudo terminal KISS interface was being used.
392  *	Transmitting was continuing fine.  (So something must be writing to the other end.)
393  *	Frames were being received and appended to this queue.
394  *	They were not coming out of the queue.
395  *
396  * My theory is that writing to the the pseudo terminal is blocking so the
397  * main thread is stopped.   It's not taking anything from this queue and we detect
398  * it as a memory leak.
399  *
400  * Add a new check here and complain if the queue is growing too large.
401  * That will get us a step closer to the root cause.
402  * This has been documented in the User Guide and the CHANGES.txt file which is
403  * a minimal version of Release Notes.
404  * The proper fix will be somehow avoiding or detecting the pseudo terminal filling up
405  * and blocking on a write.
406  */
407 
408 	if (queue_length > 10) {
409 	  text_color_set(DW_COLOR_ERROR);
410 	  dw_printf ("Received frame queue is out of control. Length=%d.\n", queue_length);
411 	  dw_printf ("Reader thread is probably frozen.\n");
412 	  dw_printf ("This can be caused by using a pseudo terminal (direwolf -p) where another\n");
413 	  dw_printf ("application is not reading the frames from the other side.\n");
414 	}
415 
416 
417 
418 #if __WIN32__
419 	SetEvent (wake_up_event);
420 #else
421 	if (recv_thread_is_waiting) {
422 
423 	  err = pthread_mutex_lock (&wake_up_mutex);
424 	  if (err != 0) {
425 	    text_color_set(DW_COLOR_ERROR);
426 	    dw_printf ("dlq append_to_queue: pthread_mutex_lock wu err=%d", err);
427 	    perror ("");
428 	    exit (1);
429 	  }
430 
431 	  err = pthread_cond_signal (&wake_up_cond);
432 	  if (err != 0) {
433 	    text_color_set(DW_COLOR_ERROR);
434 	    dw_printf ("dlq append_to_queue: pthread_cond_signal err=%d", err);
435 	    perror ("");
436 	    exit (1);
437 	  }
438 
439 	  err = pthread_mutex_unlock (&wake_up_mutex);
440 	  if (err != 0) {
441 	    text_color_set(DW_COLOR_ERROR);
442 	    dw_printf ("dlq append_to_queue: pthread_mutex_unlock wu err=%d", err);
443 	    perror ("");
444 	    exit (1);
445 	  }
446 	}
447 #endif
448 
449 } /* end append_to_queue */
450 
451 
452 /*-------------------------------------------------------------------
453  *
454  * Name:        dlq_connect_request
455  *
456  * Purpose:     Client application has requested connection to another station.
457  *
458  * Inputs:	addrs		- Source (owncall), destination (peercall),
459  *				  and possibly digipeaters.
460  *
461  *		num_addr	- Number of addresses.  2 to 10.
462  *
463  *		chan		- Channel, 0 is first.
464  *
465  *		client		- Client application instance.  We could have multiple
466  *				  applications, all on the same channel, connecting
467  *				  to different stations.   We need to know which one
468  *				  should get the results.
469  *
470  *		pid		- Protocol ID for data.  Normally 0xf0 but the API
471  *				  allows the client app to use something non-standard
472  *				  for special situations.
473  *						TODO: remove this.   PID is only for I and UI frames.
474  *
475  * Outputs:	Request is appended to queue for processing by
476  *		the data link state machine.
477  *
478  *--------------------------------------------------------------------*/
479 
dlq_connect_request(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN],int num_addr,int chan,int client,int pid)480 void dlq_connect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid)
481 {
482 	struct dlq_item_s *pnew;
483 
484 
485 #if DEBUG
486 	text_color_set(DW_COLOR_DEBUG);
487 	dw_printf ("dlq_connect_request (...)\n");
488 #endif
489 
490 	assert (chan >= 0 && chan < MAX_CHANS);
491 
492 /* Allocate a new queue item. */
493 
494 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
495 	s_new_count++;
496 
497 	pnew->type = DLQ_CONNECT_REQUEST;
498 	pnew->chan = chan;
499 	memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
500 	pnew->num_addr = num_addr;
501 	pnew->client = client;
502 
503 /* Put it into queue. */
504 
505 	append_to_queue (pnew);
506 
507 } /* end dlq_connect_request */
508 
509 
510 
511 /*-------------------------------------------------------------------
512  *
513  * Name:        dlq_disconnect_request
514  *
515  * Purpose:     Client application has requested to disconnect.
516  *
517  * Inputs:	addrs		- Source (owncall), destination (peercall),
518  *				  and possibly digipeaters.
519  *
520  *		num_addr	- Number of addresses.  2 to 10.
521  *				  Only first two matter in this case.
522  *
523  *		chan		- Channel, 0 is first.
524  *
525  *		client		- Client application instance.  We could have multiple
526  *				  applications, all on the same channel, connecting
527  *				  to different stations.   We need to know which one
528  *				  should get the results.
529  *
530  * Outputs:	Request is appended to queue for processing by
531  *		the data link state machine.
532  *
533  *--------------------------------------------------------------------*/
534 
dlq_disconnect_request(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN],int num_addr,int chan,int client)535 void dlq_disconnect_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client)
536 {
537 	struct dlq_item_s *pnew;
538 #if DEBUG
539 	text_color_set(DW_COLOR_DEBUG);
540 	dw_printf ("dlq_disconnect_request (...)\n");
541 #endif
542 
543 	assert (chan >= 0 && chan < MAX_CHANS);
544 
545 /* Allocate a new queue item. */
546 
547 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
548 	s_new_count++;
549 
550 	pnew->type = DLQ_DISCONNECT_REQUEST;
551 	pnew->chan = chan;
552 	memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
553 	pnew->num_addr = num_addr;
554 	pnew->client = client;
555 
556 /* Put it into queue. */
557 
558 	append_to_queue (pnew);
559 
560 } /* end dlq_disconnect_request */
561 
562 
563 
564 /*-------------------------------------------------------------------
565  *
566  * Name:        dlq_outstanding_frames_request
567  *
568  * Purpose:     Client application wants to know number of outstanding information
569  *		frames supplied, supplied by the client, that have not yet been
570  *		delivered to the remote station.
571  *
572  * Inputs:	addrs		- Source (owncall), destination (peercall)
573  *
574  *		num_addr	- Number of addresses.  Should be 2.
575  *				  If more they will be ignored.
576  *
577  *		chan		- Channel, 0 is first.
578  *
579  *		client		- Client application instance.  We could have multiple
580  *				  applications, all on the same channel, connecting
581  *				  to different stations.   We need to know which one
582  *				  should get the results.
583  *
584  * Outputs:	Request is appended to queue for processing by
585  *		the data link state machine.
586  *
587  * Description:	The data link state machine will count up all information frames
588  *		for the given source(mycall) / destination(remote) / channel link.
589  *		A 'Y' response will be sent back to the client application.
590  *
591  *--------------------------------------------------------------------*/
592 
dlq_outstanding_frames_request(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN],int num_addr,int chan,int client)593 void dlq_outstanding_frames_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client)
594 {
595 	struct dlq_item_s *pnew;
596 #if DEBUG
597 	text_color_set(DW_COLOR_DEBUG);
598 	dw_printf ("dlq_outstanding_frames_request (...)\n");
599 #endif
600 
601 	assert (chan >= 0 && chan < MAX_CHANS);
602 
603 /* Allocate a new queue item. */
604 
605 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
606 	s_new_count++;
607 
608 	pnew->type = DLQ_OUTSTANDING_FRAMES_REQUEST;
609 	pnew->chan = chan;
610 	memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
611 	pnew->num_addr = num_addr;
612 	pnew->client = client;
613 
614 /* Put it into queue. */
615 
616 	append_to_queue (pnew);
617 
618 } /* end dlq_outstanding_frames_request */
619 
620 
621 
622 
623 
624 
625 /*-------------------------------------------------------------------
626  *
627  * Name:        dlq_xmit_data_request
628  *
629  * Purpose:     Client application has requested transmission of connected
630  *		data over an established link.
631  *
632  * Inputs:	addrs		- Source (owncall), destination (peercall),
633  *				  and possibly digipeaters.
634  *
635  *		num_addr	- Number of addresses.  2 to 10.
636  *				  First two are used to uniquely identify link.
637  *				  Any digipeaters involved are remembered
638  *				  from when the link was established.
639  *
640  *		chan		- Channel, 0 is first.
641  *
642  *		client		- Client application instance.
643  *
644  *		pid		- Protocol ID for data.  Normally 0xf0 but the API
645  *				  allows the client app to use something non-standard
646  *				  for special situations.
647  *
648  *		xdata_ptr	- Pointer to block of data.
649  *
650  *		xdata_len	- Length of data in bytes.
651  *
652  * Outputs:	Request is appended to queue for processing by
653  *		the data link state machine.
654  *
655  *--------------------------------------------------------------------*/
656 
657 
dlq_xmit_data_request(char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN],int num_addr,int chan,int client,int pid,char * xdata_ptr,int xdata_len)658 void dlq_xmit_data_request (char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN], int num_addr, int chan, int client, int pid, char *xdata_ptr, int xdata_len)
659 {
660 	struct dlq_item_s *pnew;
661 
662 
663 #if DEBUG
664 	text_color_set(DW_COLOR_DEBUG);
665 	dw_printf ("dlq_xmit_data_request (...)\n");
666 #endif
667 
668 	assert (chan >= 0 && chan < MAX_CHANS);
669 
670 /* Allocate a new queue item. */
671 
672 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
673 	s_new_count++;
674 
675 	pnew->type = DLQ_XMIT_DATA_REQUEST;
676 	pnew->chan = chan;
677 	memcpy (pnew->addrs, addrs, sizeof(pnew->addrs));
678 	pnew->num_addr = num_addr;
679 	pnew->client = client;
680 
681 /* Attach the transmit data. */
682 
683 	pnew->txdata = cdata_new(pid,xdata_ptr,xdata_len);
684 
685 /* Put it into queue. */
686 
687 	append_to_queue (pnew);
688 
689 } /* end dlq_xmit_data_request */
690 
691 
692 
693 /*-------------------------------------------------------------------
694  *
695  * Name:        dlq_register_callsign
696  *		dlq_unregister_callsign
697  *
698  * Purpose:     Register callsigns that we will recognize for incoming connection requests.
699  *
700  * Inputs:	addrs		- Source (owncall), destination (peercall),
701  *				  and possibly digipeaters.
702  *
703  *		chan		- Channel, 0 is first.
704  *
705  *		client		- Client application instance.
706  *
707  * Outputs:	Request is appended to queue for processing by
708  *		the data link state machine.
709  *
710  * Description:	The data link state machine does not use MYCALL from the APRS configuration.
711  *		For outgoing frames, the client supplies the source callsign.
712  *		For incoming connection requests, we need to know what address(es) to respond to.
713  *
714  *		Note that one client application can register multiple callsigns for
715  *		multiple channels.
716  *		Different clients can register different different addresses on the same channel.
717  *
718  *--------------------------------------------------------------------*/
719 
720 
dlq_register_callsign(char addr[AX25_MAX_ADDR_LEN],int chan,int client)721 void dlq_register_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
722 {
723 	struct dlq_item_s *pnew;
724 
725 
726 #if DEBUG
727 	text_color_set(DW_COLOR_DEBUG);
728 	dw_printf ("dlq_register_callsign (%s, chan=%d, client=%d)\n", addr, chan, client);
729 #endif
730 
731 	assert (chan >= 0 && chan < MAX_CHANS);
732 
733 /* Allocate a new queue item. */
734 
735 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
736 	s_new_count++;
737 
738 	pnew->type = DLQ_REGISTER_CALLSIGN;
739 	pnew->chan = chan;
740 	strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN);
741 	pnew->num_addr = 1;
742 	pnew->client = client;
743 
744 /* Put it into queue. */
745 
746 	append_to_queue (pnew);
747 
748 } /* end dlq_register_callsign */
749 
750 
dlq_unregister_callsign(char addr[AX25_MAX_ADDR_LEN],int chan,int client)751 void dlq_unregister_callsign (char addr[AX25_MAX_ADDR_LEN], int chan, int client)
752 {
753 	struct dlq_item_s *pnew;
754 
755 
756 #if DEBUG
757 	text_color_set(DW_COLOR_DEBUG);
758 	dw_printf ("dlq_unregister_callsign (%s, chan=%d, client=%d)\n", addr, chan, client);
759 #endif
760 
761 	assert (chan >= 0 && chan < MAX_CHANS);
762 
763 /* Allocate a new queue item. */
764 
765 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
766 	s_new_count++;
767 
768 	pnew->type = DLQ_UNREGISTER_CALLSIGN;
769 	pnew->chan = chan;
770 	strlcpy (pnew->addrs[0], addr, AX25_MAX_ADDR_LEN);
771 	pnew->num_addr = 1;
772 	pnew->client = client;
773 
774 /* Put it into queue. */
775 
776 	append_to_queue (pnew);
777 
778 } /* end dlq_unregister_callsign */
779 
780 
781 
782 /*-------------------------------------------------------------------
783  *
784  * Name:        dlq_channel_busy
785  *
786  * Purpose:     Inform data link state machine about activity on the radio channel.
787  *
788  * Inputs:	chan		- Radio channel number.
789  *
790  *		activity	- OCTYPE_PTT or OCTYPE_DCD, as defined in audio.h.
791  *				  Other values will be discarded.
792  *
793  *		status		- 1 for active or 0 for quiet.
794  *
795  * Outputs:	Request is appended to queue for processing by
796  *		the data link state machine.
797  *
798  * Description:	Notify the link state machine about changes in carrier detect
799  *		and our transmitter.
800  *		This is needed for pausing some of our timers.   For example,
801  *		if we transmit a frame and expect a response in 3 seconds, that
802  *		might be delayed because someone else is using the channel.
803  *
804  *--------------------------------------------------------------------*/
805 
dlq_channel_busy(int chan,int activity,int status)806 void dlq_channel_busy (int chan, int activity, int status)
807 {
808 	struct dlq_item_s *pnew;
809 
810 	if (activity == OCTYPE_PTT || activity == OCTYPE_DCD) {
811 #if DEBUG
812 	  text_color_set(DW_COLOR_DEBUG);
813 	  dw_printf ("dlq_channel_busy (...)\n");
814 #endif
815 
816 
817 /* Allocate a new queue item. */
818 
819 	  pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
820 	  s_new_count++;
821 
822 	  pnew->type = DLQ_CHANNEL_BUSY;
823 	  pnew->chan = chan;
824 	  pnew->activity = activity;
825 	  pnew->status = status;
826 
827 /* Put it into queue. */
828 
829 	  append_to_queue (pnew);
830 	}
831 
832 } /* end dlq_channel_busy */
833 
834 
835 
836 
837 
838 /*-------------------------------------------------------------------
839  *
840  * Name:        dlq_seize_confirm
841  *
842  * Purpose:     Inform data link state machine that the transmitter is on.
843  *		This is in reponse to lm_seize_request.
844  *
845  * Inputs:	chan		- Radio channel number.
846  *
847  * Outputs:	Request is appended to queue for processing by
848  *		the data link state machine.
849  *
850  * Description:	When removed from the data link state machine queue, this
851  *		becomes lm_seize_confirm.
852  *
853  *--------------------------------------------------------------------*/
854 
dlq_seize_confirm(int chan)855 void dlq_seize_confirm (int chan)
856 {
857 	struct dlq_item_s *pnew;
858 
859 #if DEBUG
860 	text_color_set(DW_COLOR_DEBUG);
861 	dw_printf ("dlq_seize_confirm (chan=%d)\n", chan);
862 #endif
863 
864 
865 /* Allocate a new queue item. */
866 
867 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
868 	s_new_count++;
869 
870 	pnew->type = DLQ_SEIZE_CONFIRM;
871 	pnew->chan = chan;
872 
873 /* Put it into queue. */
874 
875 	append_to_queue (pnew);
876 
877 
878 } /* end dlq_seize_confirm */
879 
880 
881 
882 
883 /*-------------------------------------------------------------------
884  *
885  * Name:        dlq_client_cleanup
886  *
887  * Purpose:     Client application has disappeared.
888  *		i.e. The TCP connection has been broken.
889  *
890  * Inputs:	client		- Client application instance.
891  *
892  * Outputs:	Request is appended to queue for processing by
893  *		the data link state machine.
894  *
895  * Description:	Notify the link state machine that given client has gone away.
896  *		Clean up all information related to that client application.
897  *
898  *--------------------------------------------------------------------*/
899 
dlq_client_cleanup(int client)900 void dlq_client_cleanup (int client)
901 {
902 	struct dlq_item_s *pnew;
903 #if DEBUG
904 	text_color_set(DW_COLOR_DEBUG);
905 	dw_printf ("dlq_client_cleanup (...)\n");
906 #endif
907 
908 	// assert (client >= 0 && client < MAX_NET_CLIENTS);
909 
910 /* Allocate a new queue item. */
911 
912 	pnew = (struct dlq_item_s *) calloc (sizeof(struct dlq_item_s), 1);
913 	s_new_count++;
914 
915 	// All we care about is the client number.
916 
917 	pnew->type = DLQ_CLIENT_CLEANUP;
918 	pnew->client = client;
919 
920 /* Put it into queue. */
921 
922 	append_to_queue (pnew);
923 
924 } /* end dlq_client_cleanup */
925 
926 
927 
928 /*-------------------------------------------------------------------
929  *
930  * Name:        dlq_wait_while_empty
931  *
932  * Purpose:     Sleep while the received data queue is empty rather than
933  *		polling periodically.
934  *
935  * Inputs:	timeout		- Return at this time even if queue is empty.
936  *				  Zero for no timeout.
937  *
938  * Returns:	True if timed out before any event arrived.
939  *
940  * Description:	In version 1.4, we add timeout option so we can continue after
941  *		some amount of time even if no events are in the queue.
942  *
943  *--------------------------------------------------------------------*/
944 
945 
dlq_wait_while_empty(double timeout)946 int dlq_wait_while_empty (double timeout)
947 {
948 	int timed_out_result = 0;
949 
950 #if DEBUG1
951 	text_color_set(DW_COLOR_DEBUG);
952 	dw_printf ("dlq_wait_while_empty (%.3f)\n", timeout);
953 #endif
954 
955 	if ( ! was_init) {
956 	  dlq_init ();
957 	}
958 
959 
960 	if (queue_head == NULL) {
961 
962 #if DEBUG
963 	  text_color_set(DW_COLOR_DEBUG);
964 	  dw_printf ("dlq_wait_while_empty (): prepare to SLEEP...\n");
965 #endif
966 
967 
968 #if __WIN32__
969 
970 	  if (timeout != 0.0) {
971 
972 	    DWORD ms = (timeout - dtime_now()) * 1000;
973 	    if (ms <= 0) ms = 1;
974 #if DEBUG
975 	    text_color_set(DW_COLOR_DEBUG);
976 	    dw_printf ("WaitForSingleObject: timeout after %d ms\n", ms);
977 #endif
978 	    if (WaitForSingleObject (wake_up_event, ms) == WAIT_TIMEOUT) {
979 	      timed_out_result = 1;
980 	    }
981 	  }
982 	  else {
983 	    WaitForSingleObject (wake_up_event, INFINITE);
984 	  }
985 
986 #else
987 	  int err;
988 
989 	  err = pthread_mutex_lock (&wake_up_mutex);
990 	  if (err != 0) {
991 	    text_color_set(DW_COLOR_ERROR);
992 	    dw_printf ("dlq_wait_while_empty: pthread_mutex_lock wu err=%d", err);
993 	    perror ("");
994 	    exit (1);
995 	  }
996 
997 	  recv_thread_is_waiting = 1;
998 	  if (timeout != 0.0) {
999 	    struct timespec abstime;
1000 
1001 	    abstime.tv_sec = (time_t)(long)timeout;
1002 	    abstime.tv_nsec = (long)((timeout - (long)abstime.tv_sec) * 1000000000.0);
1003 
1004 	    err = pthread_cond_timedwait (&wake_up_cond, &wake_up_mutex, &abstime);
1005 	    if (err == ETIMEDOUT) {
1006 	      timed_out_result = 1;
1007 	    }
1008 	  }
1009 	  else {
1010 	    err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex);
1011 	  }
1012 	  recv_thread_is_waiting = 0;
1013 
1014 	  err = pthread_mutex_unlock (&wake_up_mutex);
1015 	  if (err != 0) {
1016 	    text_color_set(DW_COLOR_ERROR);
1017 	    dw_printf ("dlq_wait_while_empty: pthread_mutex_unlock wu err=%d", err);
1018 	    perror ("");
1019 	    exit (1);
1020 	  }
1021 #endif
1022 	}
1023 
1024 
1025 #if DEBUG
1026 	text_color_set(DW_COLOR_DEBUG);
1027 	dw_printf ("dlq_wait_while_empty () returns timedout=%d\n", timed_out_result);
1028 #endif
1029 	return (timed_out_result);
1030 
1031 } /* end dlq_wait_while_empty */
1032 
1033 
1034 
1035 /*-------------------------------------------------------------------
1036  *
1037  * Name:        dlq_remove
1038  *
1039  * Purpose:     Remove an item from the head of the queue.
1040  *
1041  * Inputs:	None.
1042  *
1043  * Returns:	Pointer to a queue item.  Caller is responsible for deleting it.
1044  *		NULL if queue is empty.
1045  *
1046  *--------------------------------------------------------------------*/
1047 
1048 
dlq_remove(void)1049 struct dlq_item_s *dlq_remove (void)
1050 {
1051 
1052 	struct dlq_item_s *result = NULL;
1053 	//int err;
1054 
1055 #if DEBUG1
1056 	text_color_set(DW_COLOR_DEBUG);
1057 	dw_printf ("dlq_remove() enter critical section\n");
1058 #endif
1059 
1060 	if ( ! was_init) {
1061 	  dlq_init ();
1062 	}
1063 
1064 #if __WIN32__
1065 	EnterCriticalSection (&dlq_cs);
1066 #else
1067 	int err;
1068 
1069 	err = pthread_mutex_lock (&dlq_mutex);
1070 	if (err != 0) {
1071 	  text_color_set(DW_COLOR_ERROR);
1072 	  dw_printf ("dlq_remove: pthread_mutex_lock err=%d", err);
1073 	  perror ("");
1074 	  exit (1);
1075 	}
1076 #endif
1077 
1078 	if (queue_head != NULL) {
1079 	  result = queue_head;
1080 	  queue_head = queue_head->nextp;
1081 	}
1082 
1083 #if __WIN32__
1084 	LeaveCriticalSection (&dlq_cs);
1085 #else
1086 	err = pthread_mutex_unlock (&dlq_mutex);
1087 	if (err != 0) {
1088 	  text_color_set(DW_COLOR_ERROR);
1089 	  dw_printf ("dlq_remove: pthread_mutex_unlock err=%d", err);
1090 	  perror ("");
1091 	  exit (1);
1092 	}
1093 #endif
1094 
1095 #if DEBUG
1096 	text_color_set(DW_COLOR_DEBUG);
1097 	dw_printf ("dlq_remove()  returns \n");
1098 #endif
1099 
1100 #if AX25MEMDEBUG
1101 
1102 	if (ax25memdebug_get() && result != NULL) {
1103 	  text_color_set(DW_COLOR_DEBUG);
1104 	  if (result->pp != NULL) {
1105 // TODO: mnemonics for type.
1106 	    dw_printf ("dlq_remove (type=%d, chan=%d.%d, seq=%d, ...)\n", result->type, result->chan, result->subchan, ax25memdebug_seq(result->pp));
1107 	  }
1108 	  else {
1109 	    dw_printf ("dlq_remove (type=%d, chan=%d, ...)\n", result->type, result->chan);
1110 	  }
1111 	}
1112 #endif
1113 
1114 	return (result);
1115 }
1116 
1117 
1118 /*-------------------------------------------------------------------
1119  *
1120  * Name:        dlq_delete
1121  *
1122  * Purpose:     Release storage used by a queue item.
1123  *
1124  * Inputs:	pitem		- Pointer to a queue item.
1125  *
1126  *--------------------------------------------------------------------*/
1127 
1128 
dlq_delete(struct dlq_item_s * pitem)1129 void dlq_delete (struct dlq_item_s *pitem)
1130 {
1131 	if (pitem == NULL) {
1132 	  text_color_set(DW_COLOR_ERROR);
1133 	  dw_printf ("INTERNAL ERROR: dlq_delete()  given NULL pointer.\n");
1134 	  return;
1135 	}
1136 
1137 	s_delete_count++;
1138 
1139 	if (pitem->pp != NULL) {
1140 	  ax25_delete (pitem->pp);
1141 	  pitem->pp = NULL;
1142 	}
1143 
1144 	if (pitem->txdata != NULL) {
1145 	  cdata_delete (pitem->txdata);
1146 	  pitem->txdata = NULL;
1147 	}
1148 
1149 	free (pitem);
1150 
1151 } /* end dlq_delete */
1152 
1153 
1154 
1155 
1156 /*-------------------------------------------------------------------
1157  *
1158  * Name:        cdata_new
1159  *
1160  * Purpose:     Allocate blocks of data for sending and receiving connected data.
1161  *
1162  * Inputs:	pid	- protocol id.
1163  *		data	- pointer to data.  Can be NULL for segment reassembler.
1164  *		len	- length of data.
1165  *
1166  * Returns:	Structure with a copy of the data.
1167  *
1168  * Description:	The flow goes like this:
1169  *
1170  *		Client application extablishes a connection with another station.
1171  *		Client application calls "dlq_xmit_data_request."
1172  *		A copy of the data is made with this function and attached to the queue item.
1173  *		The txdata block is attached to the appropriate link state machine.
1174  *		At the proper time, it is transmitted in an I frame.
1175  *		It needs to be kept around in case it needs to be retransmitted.
1176  *		When no longer needed, it is freed with cdata_delete.
1177  *
1178  *--------------------------------------------------------------------*/
1179 
1180 
cdata_new(int pid,char * data,int len)1181 cdata_t *cdata_new (int pid, char *data, int len)
1182 {
1183 	int size;
1184 	cdata_t *cdata;
1185 
1186 	s_cdata_new_count++;
1187 
1188 	/* Round up the size to the next 128 bytes. */
1189 	/* The theory is that a smaller number of unique sizes might be */
1190 	/* beneficial for memory fragmentation and garbage collection. */
1191 
1192 	size = ( len + 127 ) & ~0x7f;
1193 
1194 	cdata = malloc ( sizeof(cdata_t) + size );
1195 
1196 	cdata->magic = TXDATA_MAGIC;
1197 	cdata->next = NULL;
1198 	cdata->pid = pid;
1199 	cdata->size = size;
1200 	cdata->len = len;
1201 
1202 	assert (len >= 0 && len <= size);
1203 	if (data == NULL) {
1204 	  memset (cdata->data, '?', size);
1205 	}
1206 	else {
1207 	  memcpy (cdata->data, data, len);
1208 	}
1209 	return (cdata);
1210 
1211 }  /* end cdata_new */
1212 
1213 
1214 
1215 /*-------------------------------------------------------------------
1216  *
1217  * Name:        cdata_delete
1218  *
1219  * Purpose:     Release storage used by a connected data block.
1220  *
1221  * Inputs:	cdata		- Pointer to a data block.
1222  *
1223  *--------------------------------------------------------------------*/
1224 
1225 
cdata_delete(cdata_t * cdata)1226 void cdata_delete (cdata_t *cdata)
1227 {
1228 	if (cdata == NULL) {
1229 	  text_color_set(DW_COLOR_ERROR);
1230 	  dw_printf ("INTERNAL ERROR: cdata_delete()  given NULL pointer.\n");
1231 	  return;
1232 	}
1233 
1234 	if (cdata->magic != TXDATA_MAGIC) {
1235 	  text_color_set(DW_COLOR_ERROR);
1236 	  dw_printf ("INTERNAL ERROR: cdata_delete()  given corrupted data.\n");
1237 	  return;
1238 	}
1239 
1240 	s_cdata_delete_count++;
1241 
1242 	cdata->magic = 0;
1243 
1244 	free (cdata);
1245 
1246 } /* end cdata_delete */
1247 
1248 
1249 /*-------------------------------------------------------------------
1250  *
1251  * Name:        cdata_check_leak
1252  *
1253  * Purpose:     Check for memory leak of cdata items.
1254  *
1255  * Description:	This is called when we expect no outstanding allocations.
1256  *
1257  *--------------------------------------------------------------------*/
1258 
1259 
cdata_check_leak(void)1260 void cdata_check_leak (void)
1261 {
1262 	if (s_cdata_delete_count != s_cdata_new_count) {
1263 
1264 	  text_color_set(DW_COLOR_ERROR);
1265 	  dw_printf ("Internal Error, %s, new=%d, delete=%d\n", __func__, s_cdata_new_count, s_cdata_delete_count);
1266 	}
1267 
1268 } /* end cdata_check_leak */
1269 
1270 
1271 
1272 /* end dlq.c */
1273