// // This file is part of Dire Wolf, an amateur radio packet TNC. // // Copyright (C) 2011, 2012, 2015 John Langner, WB2OSZ // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // /*------------------------------------------------------------------ * * Module: rdq.c * * Purpose: Retry later decode queue for frames with bad FCS. * * Description: * *---------------------------------------------------------------*/ #include "direwolf.h" #include #include #include #include #include #include "ax25_pad.h" #include "textcolor.h" #include "audio.h" #include "rdq.h" #include "dedupe.h" static rrbb_t queue_head = NULL; /* Head of linked list for queue. */ static int rdq_len = 0; #define RDQ_UNDERRUN_THRESHOLD 30 /* A warning will be emitted if there are still this number of packets to decode in the queue and we try to add another one */ static dw_mutex_t rdq_mutex; /* Critical section for updating queues. */ #if __WIN32__ static HANDLE wake_up_event; /* Notify try decode again thread when queue not empty. */ #else static pthread_cond_t wake_up_cond; /* Notify try decode again thread when queue not empty. */ static dw_mutex_t wake_up_mutex; /* Required by cond_wait. */ #endif /*------------------------------------------------------------------- * * Name: rdq_init * * Purpose: Initialize the receive decode again queue. * * Inputs: None. Only single queue for all channels. * * Outputs: * * Description: Initialize the queue to be empty and set up other * mechanisms for sharing it between different threads. * *--------------------------------------------------------------------*/ void rdq_init (void) { //int c, p; #if __WIN32__ #else int err; #endif #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_init ( )\n"); dw_printf ("rdq_init: pthread_mutex_init...\n"); #endif dw_mutex_init (&rdq_mutex); #if __WIN32__ #else dw_mutex_init (&wake_up_mutex); #endif #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_init: pthread_cond_init...\n"); #endif #if __WIN32__ wake_up_event = CreateEvent (NULL, 0, 0, NULL); if (wake_up_event == NULL) { text_color_set(DW_COLOR_ERROR); dw_printf ("rdq_init: pthread_cond_init: can't create decode wake up event"); exit (1); } #else err = pthread_cond_init (&wake_up_cond, NULL); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_init: pthread_cond_init returns %d\n", err); #endif if (err != 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("rdq_init: pthread_cond_init err=%d", err); perror (""); exit (1); } #endif } /* end rdq_init */ /*------------------------------------------------------------------- * * Name: rdq_append * * Purpose: Add a packet to the end of the queue. * * Inputs: pp - Address of raw received bit buffer. * Caller should NOT make any references to * it after this point because it could * be deleted at any time. * * Outputs: * * Description: Add buffer to end of linked list. * Signal the decode thread if the queue was formerly empty. * *--------------------------------------------------------------------*/ void rdq_append (rrbb_t rrbb) { //int was_empty; rrbb_t plast; rrbb_t pnext; #ifndef __WIN32__ int err; #endif #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_append (rrbb=%p)\n", rrbb); dw_printf ("rdq_append: enter critical section\n"); #endif dw_mutex_lock (&rdq_mutex); //was_empty = 1; //if (queue_head != NULL) { //was_empty = 0; //} if (queue_head == NULL) { queue_head = rrbb; } else { plast = queue_head; while ((pnext = rrbb_get_nextp(plast)) != NULL) { plast = pnext; } rrbb_set_nextp (plast, rrbb); } rdq_len++; if (rdq_len > RDQ_UNDERRUN_THRESHOLD) { text_color_set(DW_COLOR_ERROR); dw_printf ("Too many packets to decode (%d) in the queue, decrease the FIX_BITS value\n", rdq_len); } dw_mutex_unlock (&rdq_mutex); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_append: left critical section\n"); dw_printf ("rdq_append (): about to wake up retry decode thread.\n"); #endif #if __WIN32__ SetEvent (wake_up_event); #else dw_mutex_lock (&wake_up_mutex); err = pthread_cond_signal (&wake_up_cond); if (err != 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("rdq_append: pthread_cond_signal err=%d", err); perror (""); exit (1); } dw_mutex_unlock (&wake_up_mutex); #endif } /*------------------------------------------------------------------- * * Name: rdq_wait_while_empty * * Purpose: Sleep while the queue is empty rather than * polling periodically. * * Inputs: None. * *--------------------------------------------------------------------*/ void rdq_wait_while_empty (void) { int is_empty; #ifndef __WIN32__ int err; #endif #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty () : enter critical section\n"); #endif dw_mutex_lock (&rdq_mutex); #if DEBUG //text_color_set(DW_COLOR_DEBUG); //dw_printf ("rdq_wait_while_empty (): after pthread_mutex_lock\n"); #endif is_empty = 1; if (queue_head != NULL) is_empty = 0; dw_mutex_unlock (&rdq_mutex); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty () : left critical section\n"); #endif #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty (): is_empty = %d\n", is_empty); #endif if (is_empty) { #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty (): SLEEP - about to call cond wait\n"); #endif #if __WIN32__ WaitForSingleObject (wake_up_event, INFINITE); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty (): returned from wait\n"); #endif #else dw_mutex_lock (&wake_up_mutex); err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty (): WOKE UP - returned from cond wait, err = %d\n", err); #endif if (err != 0) { text_color_set(DW_COLOR_ERROR); dw_printf ("rdq_wait_while_empty: pthread_cond_wait err=%d", err); perror (""); exit (1); } dw_mutex_unlock (&wake_up_mutex); #endif } #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_wait_while_empty () returns (%d buffers remaining)\n", rdq_len); #endif } /*------------------------------------------------------------------- * * Name: rdq_remove * * Purpose: Remove raw bit buffer from the head of the queue. * * Inputs: none * * Returns: Pointer to rrbb object. * Caller should destroy it with rrbb_delete when finished with it. * *--------------------------------------------------------------------*/ rrbb_t rdq_remove (void) { rrbb_t result_p; #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_remove() enter critical section\n"); #endif dw_mutex_lock (&rdq_mutex); rdq_len--; #if DEBUG dw_printf ("-rdq_len: %d\n", rdq_len); #endif if (queue_head == NULL) { result_p = NULL; } else { result_p = queue_head; queue_head = rrbb_get_nextp(result_p); rrbb_set_nextp (result_p, NULL); } dw_mutex_unlock (&rdq_mutex); #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("rdq_remove() leave critical section, returns %p\n", result_p); #endif return (result_p); } /* end rdq.c */