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