diff --git a/CMakeLists.txt b/CMakeLists.txt index 84aeb738..3c01045b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(direwolf) # configure version set(direwolf_VERSION_MAJOR "1") -set(direwolf_VERSION_MINOR "7") +set(direwolf_VERSION_MINOR "8") set(direwolf_VERSION_PATCH "0") set(direwolf_VERSION_SUFFIX "Development") diff --git a/src/config.c b/src/config.c index 1ad6c081..739eb2f6 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2021 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2021, 2023 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 @@ -755,12 +755,13 @@ void config_init (char *fname, struct audio_s *p_audio_config, strlcpy (p_audio_config->adev[adevice].adevice_out, DEFAULT_ADEVICE, sizeof(p_audio_config->adev[adevice].adevice_out)); p_audio_config->adev[adevice].defined = 0; + p_audio_config->adev[adevice].copy_from = -1; p_audio_config->adev[adevice].num_channels = DEFAULT_NUM_CHANNELS; /* -2 stereo */ p_audio_config->adev[adevice].samples_per_sec = DEFAULT_SAMPLES_PER_SEC; /* -r option */ p_audio_config->adev[adevice].bits_per_sample = DEFAULT_BITS_PER_SAMPLE; /* -8 option for 8 instead of 16 bits */ } - p_audio_config->adev[0].defined = 1; + p_audio_config->adev[0].defined = 2; // 2 means it was done by default and not the user's config file. for (channel=0; channelmaxframe_extended = AX25_K_MAXFRAME_EXTENDED_DEFAULT; /* Max frames to send before ACK. mod 128 "Window" size. */ - p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Max SABME before falling back to SABM. */ - p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed. */ + p_misc_config->maxv22 = AX25_N2_RETRY_DEFAULT / 3; /* Send SABME this many times before falling back to SABM. */ + p_misc_config->v20_addrs = NULL; /* Go directly to v2.0 for stations listed */ + /* without trying v2.2 first. */ p_misc_config->v20_count = 0; p_misc_config->noxid_addrs = NULL; /* Don't send XID to these stations. */ + /* Might work with a partial v2.2 implementation */ + /* on the other end. */ p_misc_config->noxid_count = 0; /* @@ -1012,7 +1016,11 @@ void config_init (char *fname, struct audio_s *p_audio_config, * ADEVICE plughw:1,0 -- same for in and out. * ADEVICE plughw:2,0 plughw:3,0 -- different in/out for a channel or channel pair. * ADEVICE1 udp:7355 default -- from Software defined radio (SDR) via UDP. - * + * + * New in 1.8: Ability to map to another audio device. + * This allows multiple modems (i.e. data speeds) on the same audio interface. + * + * ADEVICEn = n -- Copy from different already defined channel. */ /* Note that ALSA name can contain comma such as hw:1,0 */ @@ -1040,17 +1048,42 @@ void config_init (char *fname, struct audio_s *p_audio_config, exit(EXIT_FAILURE); } + // Do not allow same adevice to be defined more than once. + // Overriding the default for adevice 0 is ok. + // In that case definded was 2. That's why we check for 1, not just non-zero. + + if (p_audio_config->adev[adevice].defined == 1) { // 1 means defined by user. + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file: ADEVICE%d can't be defined more than once. Line %d.\n", adevice, line); + continue; + } + p_audio_config->adev[adevice].defined = 1; - - /* First channel of device is valid. */ - p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO; - strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); - strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); + // New case for release 1.8. - t = split(NULL,0); - if (t != NULL) { + if (strcmp(t, "=") == 0) { + t = split(NULL,0); + if (t != NULL) { + + } + +///////// to be continued.... FIXME + + } + else { + /* First channel of device is valid. */ + // This might be changed to UDP or STDIN when the device name is examined. + p_audio_config->chan_medium[ADEVFIRSTCHAN(adevice)] = MEDIUM_RADIO; + + strlcpy (p_audio_config->adev[adevice].adevice_in, t, sizeof(p_audio_config->adev[adevice].adevice_in)); strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); + + t = split(NULL,0); + if (t != NULL) { + // Different audio devices for receive and transmit. + strlcpy (p_audio_config->adev[adevice].adevice_out, t, sizeof(p_audio_config->adev[adevice].adevice_out)); + } } } @@ -2173,7 +2206,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, else { p_audio_config->achan[channel].slottime = DEFAULT_SLOTTIME; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid delay time for persist algorithm. Using %d.\n", + dw_printf ("Line %d: Invalid delay time for persist algorithm. Using default %d.\n", line, p_audio_config->achan[channel].slottime); } } @@ -2197,7 +2230,7 @@ void config_init (char *fname, struct audio_s *p_audio_config, else { p_audio_config->achan[channel].persist = DEFAULT_PERSIST; text_color_set(DW_COLOR_ERROR); - dw_printf ("Line %d: Invalid probability for persist algorithm. Using %d.\n", + dw_printf ("Line %d: Invalid probability for persist algorithm. Using default %d.\n", line, p_audio_config->achan[channel].persist); } } @@ -2216,6 +2249,19 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); if (n >= 0 && n <= 255) { + text_color_set(DW_COLOR_ERROR); + if (n == 0) { + dw_printf ("Line %d: Setting TXDELAY to 0 is a REALLY BAD idea if you want other stations to hear you.\n", + line); + dw_printf ("Line %d: See User Guide, \"Radio Channel - Transmit Timing\" for an explanation.\n", + line); + } + if (n >= 100) { + dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXDELAY is in 10 millisecond units.\n", + line); + dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n", + line, n, (double)n * 10. / 1000.); + } p_audio_config->achan[channel].txdelay = n; } else { @@ -2240,6 +2286,18 @@ void config_init (char *fname, struct audio_s *p_audio_config, } n = atoi(t); if (n >= 0 && n <= 255) { + if (n == 0) { + dw_printf ("Line %d: Setting TXTAIL to 0 is a REALLY BAD idea if you want other stations to hear you.\n", + line); + dw_printf ("Line %d: See User Guide, \"Radio Channel - Transmit Timing\" for an explanation.\n", + line); + } + if (n >= 50) { + dw_printf ("Line %d: Keeping with tradition, going back to the 1980s, TXTAIL is in 10 millisecond units.\n", + line); + dw_printf ("Line %d: The value %d would be %.3f seconds which seems rather excessive. Are you sure you want that?\n", + line, n, (double)n * 10. / 1000.); + } p_audio_config->achan[channel].txtail = n; } else { diff --git a/src/direwolf.c b/src/direwolf.c index e23aecb4..c6ed9d4d 100644 --- a/src/direwolf.c +++ b/src/direwolf.c @@ -186,7 +186,7 @@ static int d_u_opt = 0; /* "-d u" command line option to print UTF-8 also in h static int d_p_opt = 0; /* "-d p" option for dumping packets over radio. */ static int q_h_opt = 0; /* "-q h" Quiet, suppress the "heard" line with audio level. */ -static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of decoded of APRS packets. */ +static int q_d_opt = 0; /* "-q d" Quiet, suppress the printing of description of APRS packets. */ static int A_opt_ais_to_obj = 0; /* "-A" Convert received AIS to APRS "Object Report." */ @@ -302,24 +302,24 @@ int main (int argc, char *argv[]) text_color_init(t_opt); text_color_set(DW_COLOR_INFO); //dw_printf ("Dire Wolf version %d.%d (%s) BETA TEST 7\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "G", __DATE__); - dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); + dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "A", __DATE__); + //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); #if defined(ENABLE_GPSD) || defined(USE_HAMLIB) || defined(USE_CM108) || USE_AVAHI_CLIENT || USE_MACOS_DNSSD dw_printf ("Includes optional support for: "); -#if defined(ENABLE_GPSD) + #if defined(ENABLE_GPSD) dw_printf (" gpsd"); -#endif -#if defined(USE_HAMLIB) + #endif + #if defined(USE_HAMLIB) dw_printf (" hamlib"); -#endif -#if defined(USE_CM108) + #endif + #if defined(USE_CM108) dw_printf (" cm108-ptt"); -#endif -#if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) + #endif + #if (USE_AVAHI_CLIENT|USE_MACOS_DNSSD) dw_printf (" dns-sd"); -#endif + #endif dw_printf ("\n"); #endif @@ -1708,7 +1708,7 @@ static void usage (char **argv) dw_printf (" d d = APRStt (DTMF to APRS object translation).\n"); dw_printf (" -q Quiet (suppress output) options:\n"); dw_printf (" h h = Heard line with the audio level.\n"); - dw_printf (" d d = Decoding of APRS packets.\n"); + dw_printf (" d d = Description of APRS packets.\n"); dw_printf (" x x = Silence FX.25 information.\n"); dw_printf (" -t n Text colors. 0=disabled. 1=default. 2,3,4,... alternatives.\n"); dw_printf (" Use 9 to test compatibility with your terminal.\n"); diff --git a/src/dlq.c b/src/dlq.c index f56b8649..aa25b84f 100644 --- a/src/dlq.c +++ b/src/dlq.c @@ -60,6 +60,8 @@ /* The queue is a linked list of these. */ static struct dlq_item_s *queue_head = NULL; /* Head of linked list for queue. */ +static struct dlq_item_s *queue_tail = NULL; /* Tail of linked list for queue. */ +int queue_length = 0; /* Count of items in queue */ #if __WIN32__ @@ -75,8 +77,6 @@ static pthread_mutex_t dlq_mutex; /* Critical section for updating queues. */ static pthread_cond_t wake_up_cond; /* Notify received packet processing thread when queue not empty. */ -static pthread_mutex_t wake_up_mutex; /* Required by cond_wait. */ - static volatile int recv_thread_is_waiting = 0; #endif @@ -117,7 +117,8 @@ void dlq_init (void) dw_printf ("dlq_init ( )\n"); #endif - queue_head = NULL; + queue_head = queue_tail = NULL; + queue_length = 0; #if DEBUG @@ -129,13 +130,6 @@ void dlq_init (void) InitializeCriticalSection (&dlq_cs); #else int err; - err = pthread_mutex_init (&wake_up_mutex, NULL); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("dlq_init: pthread_mutex_init err=%d", err); - perror (""); - exit (EXIT_FAILURE); - } err = pthread_mutex_init (&dlq_mutex, NULL); if (err != 0) { text_color_set(DW_COLOR_ERROR); @@ -314,9 +308,6 @@ void dlq_rec_frame (int chan, int subchan, int slice, packet_t pp, alevel_t alev static void append_to_queue (struct dlq_item_s *pnew) { - struct dlq_item_s *plast; - int queue_length = 0; - if ( ! was_init) { dlq_init (); } @@ -341,30 +332,19 @@ static void append_to_queue (struct dlq_item_s *pnew) #endif if (queue_head == NULL) { - queue_head = pnew; + assert (queue_tail == NULL); + queue_head = queue_tail = pnew; queue_length = 1; + } else { + assert (queue_tail != NULL); + queue_tail->nextp = pnew; + queue_tail = pnew; + queue_length++; + assert (queue_length > 1); } - else { - queue_length = 2; /* head + new one */ - plast = queue_head; - while (plast->nextp != NULL) { - plast = plast->nextp; - queue_length++; - } - plast->nextp = pnew; - } - #if __WIN32__ LeaveCriticalSection (&dlq_cs); -#else - err = pthread_mutex_unlock (&dlq_mutex); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("dlq append_to_queue: pthread_mutex_unlock err=%d", err); - perror (""); - exit (1); - } #endif #if DEBUG1 text_color_set(DW_COLOR_DEBUG); @@ -416,7 +396,7 @@ static void append_to_queue (struct dlq_item_s *pnew) * and blocking on a write. */ - if (queue_length > 10) { + if (queue_length > 15) { text_color_set(DW_COLOR_ERROR); dw_printf ("Received frame queue is out of control. Length=%d.\n", queue_length); dw_printf ("Reader thread is probably frozen.\n"); @@ -431,14 +411,6 @@ static void append_to_queue (struct dlq_item_s *pnew) #else if (recv_thread_is_waiting) { - err = pthread_mutex_lock (&wake_up_mutex); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("dlq append_to_queue: pthread_mutex_lock wu err=%d", err); - perror (""); - exit (1); - } - err = pthread_cond_signal (&wake_up_cond); if (err != 0) { text_color_set(DW_COLOR_ERROR); @@ -446,14 +418,14 @@ static void append_to_queue (struct dlq_item_s *pnew) perror (""); exit (1); } + } - err = pthread_mutex_unlock (&wake_up_mutex); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("dlq append_to_queue: pthread_mutex_unlock wu err=%d", err); - perror (""); - exit (1); - } + err = pthread_mutex_unlock (&dlq_mutex); + if (err != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("dlq append_to_queue: pthread_mutex_unlock wu err=%d", err); + perror (""); + exit (1); } #endif @@ -1011,9 +983,29 @@ int dlq_wait_while_empty (double timeout) dlq_init (); } +#if DEBUG1 + text_color_set(DW_COLOR_DEBUG); + dw_printf ("dlq dlq_wait_while_empty: enter critical section\n"); +#endif +#if __WIN32__ + EnterCriticalSection (&dlq_cs); +#else + int err; + err = pthread_mutex_lock (&dlq_mutex); + if (err != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("dlq append_to_queue: pthread_mutex_lock err=%d", err); + perror (""); + exit (1); + } +#endif if (queue_head == NULL) { +#if __WIN32__ + LeaveCriticalSection (&dlq_cs); +#endif + #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("dlq_wait_while_empty (): prepare to SLEEP...\n"); @@ -1037,18 +1029,15 @@ int dlq_wait_while_empty (double timeout) else { WaitForSingleObject (wake_up_event, INFINITE); } + } else { +#if __WIN32__ + LeaveCriticalSection (&dlq_cs); +#endif + } #else int err; - err = pthread_mutex_lock (&wake_up_mutex); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("dlq_wait_while_empty: pthread_mutex_lock wu err=%d", err); - perror (""); - exit (1); - } - recv_thread_is_waiting = 1; if (timeout != 0.0) { struct timespec abstime; @@ -1056,26 +1045,25 @@ int dlq_wait_while_empty (double timeout) abstime.tv_sec = (time_t)(long)timeout; abstime.tv_nsec = (long)((timeout - (long)abstime.tv_sec) * 1000000000.0); - err = pthread_cond_timedwait (&wake_up_cond, &wake_up_mutex, &abstime); + err = pthread_cond_timedwait (&wake_up_cond, &dlq_mutex, &abstime); if (err == ETIMEDOUT) { timed_out_result = 1; } } else { - err = pthread_cond_wait (&wake_up_cond, &wake_up_mutex); + err = pthread_cond_wait (&wake_up_cond, &dlq_mutex); } recv_thread_is_waiting = 0; - - err = pthread_mutex_unlock (&wake_up_mutex); - if (err != 0) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("dlq_wait_while_empty: pthread_mutex_unlock wu err=%d", err); - perror (""); - exit (1); - } -#endif } + err = pthread_mutex_unlock (&dlq_mutex); + if (err != 0) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("dlq_wait_while_empty: pthread_mutex_unlock wu err=%d", err); + perror (""); + exit (1); + } +#endif #if DEBUG text_color_set(DW_COLOR_DEBUG); @@ -1133,6 +1121,14 @@ struct dlq_item_s *dlq_remove (void) if (queue_head != NULL) { result = queue_head; queue_head = queue_head->nextp; + queue_length--; + if (queue_head == NULL) { + assert (queue_length == 0); + queue_tail = NULL; + } + if (queue_length == 1) { + assert (queue_head == queue_tail); + } } #if __WIN32__