From 7125b5b22fc47873b8432d6bf947604879878959 Mon Sep 17 00:00:00 2001 From: WB2OSZ Date: Sat, 27 Feb 2016 15:42:26 -0500 Subject: [PATCH] Rewrite, test, and document hamlib interface for PTT. modified: audio.h modified: config.c modified: direwolf.c modified: man1/direwolf.1 modified: ptt.c --- audio.h | 4 +- config.c | 121 ++++++++++------------------------ direwolf.c | 22 ++++++- man1/direwolf.1 | 2 + ptt.c | 169 +++++++++++++++++++++++++++++++----------------- 5 files changed, 169 insertions(+), 149 deletions(-) diff --git a/audio.h b/audio.h index 5957ae6..674c27e 100644 --- a/audio.h +++ b/audio.h @@ -182,6 +182,7 @@ struct audio_s { ptt_method_t ptt_method; /* none, serial port, GPIO, LPT, HAMLIB. */ char ptt_device[20]; /* Serial device name for PTT. e.g. COM1 or /dev/ttyS0 */ + /* Also used for HAMLIB. Could be host:port when model is 1. */ ptt_line_t ptt_line; /* Control line when using serial port. PTT_LINE_RTS, PTT_LINE_DTR. */ ptt_line_t ptt_line2; /* Optional second one: PTT_LINE_NONE when not used. */ @@ -195,7 +196,8 @@ struct audio_s { int ptt_invert2; /* Invert the secondary output. */ #ifdef USE_HAMLIB - int ptt_rig; /* HAMLib rig. */ + + int ptt_model; /* HAMLIB model. -1 for AUTO. 2 for rigctld. Others are radio model. */ #endif } octrl[NUM_OCTYPES]; diff --git a/config.c b/config.c index c2646fe..8090c85 100644 --- a/config.c +++ b/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 John Langner, WB2OSZ +// Copyright (C) 2011, 2012, 2013, 2014, 2015, 2016 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 @@ -46,9 +46,6 @@ #include /* for DEFAULT_GPSD_PORT (2947) */ #endif -#ifdef USE_HAMLIB -#include -#endif #include "ax25_pad.h" #include "textcolor.h" @@ -612,12 +609,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, int adevice; int m; -#if USE_HAMLIB - RIG *rig; - int rigs = 0; -#endif - - #if DEBUG text_color_set(DW_COLOR_DEBUG); dw_printf ("config_init ( %s )\n", fname); @@ -1460,6 +1451,12 @@ void config_init (char *fname, struct audio_s *p_audio_config, * xxx serial-port [-]rts-or-dtr [ [-]rts-or-dtr ] * xxx GPIO [-]gpio-num * xxx LPT [-]bit-num + * PTT RIG model port + * PTT RIG AUTO port + * + * When model is 2, port would host:port like 127.0.0.1:4532 + * Otherwise, port would be a serial port like /dev/ttyS0 + * * * Applies to most recent CHANNEL command. */ @@ -1544,16 +1541,41 @@ void config_init (char *fname, struct audio_s *p_audio_config, } else if (strcasecmp(t, "RIG") == 0) { #ifdef USE_HAMLIB - p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB; t = split(NULL,0); if (t == NULL) { text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Missing RIG number.\n", line); + dw_printf ("Config file line %d: Missing model number for hamlib.\n", line); continue; } + if (strcasecmp(t, "AUTO") == 0) { + p_audio_config->achan[channel].octrl[ot].ptt_model = -1; + } + else { + int n = atoi(t); + if (n < 1 || n > 9999) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: Unreasonable model number %d for hamlib.\n", line, n); + continue; + } + p_audio_config->achan[channel].octrl[ot].ptt_model = n; + } - p_audio_config->achan[channel].octrl[ot].ptt_rig = atoi(t); + t = split(NULL,0); + if (t == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: Missing port for hamlib.\n", line); + continue; + } + strlcpy (p_audio_config->achan[channel].octrl[ot].ptt_device, t, sizeof(p_audio_config->achan[channel].octrl[ot].ptt_device)); + + t = split(NULL,0); + if (t != NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Config file line %d: %s was not expected after model & port for hamlib.\n", line, t); + } + + p_audio_config->achan[channel].octrl[ot].ptt_method = PTT_METHOD_HAMLIB; #else text_color_set(DW_COLOR_ERROR); @@ -1691,79 +1713,6 @@ void config_init (char *fname, struct audio_s *p_audio_config, } } -/* - * RIG - HAMLib rig configuration. - * - * xxx port model - * - * For example a Yeasu FT-817 on /dev/ttyUSB0: - * RIG /dev/ttyUSB0 120 - * - * For example rigctld on localhost: - * RIG 127.0.0.1:4532 2 - */ - - else if (strcasecmp(t, "RIG") == 0) { -#ifdef USE_HAMLIB - int n; - hamlib_port_t port; - rig_model_t rig_model; - - if (rigs == MAX_RIGS) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Maximum number of rigs reached.\n", line); - continue; - } - - t = split(NULL,0); - if (t == NULL) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Missing port, model[, key=value].\n", - line); - continue; - } - - strncpy (port.pathname, t, FILPATHLEN - 1); - - t = split(NULL,0); - if (t == NULL) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Missing model[, key=value]\n", line); - continue; - } - - if (strcasecmp(t, "AUTO") == 0) { - rig_load_all_backends(); - rig_model = rig_probe(&port); - } - else { - rig_model = atoi(t); - } - - rig = rig_init(rig_model); - if (!rig) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Unknown rig %d, please check riglist.h.\n", line, rig_model); - continue; - } - - strncpy (rig->state.rigport.pathname, port.pathname, FILPATHLEN - 1); - n = rig_open(rig); - if (n != RIG_OK) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: Rig open error %d: %s\n", line, n, rigerror(n)); - continue; - } - - p_audio_config->rig[rigs++] = rig; - p_audio_config->rigs = rigs; - -#else - text_color_set(DW_COLOR_ERROR); - dw_printf ("Config file line %d: RIG is only available when hamlib support is enabled.\n", line); - continue; -#endif - } /* * DWAIT - Extra delay for receiver squelch. diff --git a/direwolf.c b/direwolf.c index 59a3cc0..f1126b6 100644 --- a/direwolf.c +++ b/direwolf.c @@ -67,6 +67,10 @@ #include #endif +#if USE_HAMLIB +#include +#endif + #define DIREWOLF_C 1 @@ -185,7 +189,9 @@ int main (int argc, char *argv[]) int d_g_opt = 0; /* "-d g" option for GPS. Can be repeated for more detail. */ int d_o_opt = 0; /* "-d o" option for output control such as PTT and DCD. */ int d_i_opt = 0; /* "-d i" option for IGate. Repeat for more detail */ - +#if USE_HAMLIB + int d_h_opt = 0; /* "-d h" option for hamlib debugging. Repeat for more detail */ +#endif strlcpy(l_opt, "", sizeof(l_opt)); strlcpy(P_opt, "", sizeof(P_opt)); @@ -230,8 +236,8 @@ 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\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); - dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "K", __DATE__); + dw_printf ("Dire Wolf version %d.%d (%s) Beta Test\n", MAJOR_VERSION, MINOR_VERSION, __DATE__); + //dw_printf ("Dire Wolf DEVELOPMENT version %d.%d %s (%s)\n", MAJOR_VERSION, MINOR_VERSION, "K", __DATE__); //dw_printf ("Dire Wolf version %d.%d\n", MAJOR_VERSION, MINOR_VERSION); #if defined(ENABLE_GPSD) || defined(USE_HAMLIB) @@ -453,6 +459,9 @@ int main (int argc, char *argv[]) case 'i': d_i_opt++; break; #if AX25MEMDEBUG case 'm': ax25memdebug_set(); break; // Track down memory leak. Not documented. +#endif +#if USE_HAMLIB + case 'h': d_h_opt++; break; // Hamlib verbose level. #endif default: break; } @@ -527,6 +536,10 @@ int main (int argc, char *argv[]) * Possibly override some by command line options. */ +#if USE_HAMLIB + rig_set_debug(d_h_opt); +#endif + symbols_init (); config_init (config_file, &audio_config, &digi_config, &tt_config, &igate_config, &misc_config); @@ -1067,6 +1080,9 @@ static void usage (char **argv) dw_printf (" t t = Tracker beacon.\n"); dw_printf (" o o = output controls such as PTT and DCD.\n"); dw_printf (" i i = IGate.\n"); +#if USE_HAMLIB + dw_printf (" h h = hamlib increase verbose level.\n"); +#endif 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"); diff --git a/man1/direwolf.1 b/man1/direwolf.1 index 8f9acc7..b81b8a2 100644 --- a/man1/direwolf.1 +++ b/man1/direwolf.1 @@ -84,6 +84,8 @@ t = Tracker beacon. o = Output controls such as PTT and DCD. .P i = IGate +.P +h = Hamlib verbose level. Repeat for more. .RE .RE .PD diff --git a/ptt.c b/ptt.c index d2cb324..749ada6 100644 --- a/ptt.c +++ b/ptt.c @@ -1,7 +1,7 @@ // // This file is part of Dire Wolf, an amateur radio packet TNC. // -// Copyright (C) 2011, 2013, 2014, 2015 John Langner, WB2OSZ +// Copyright (C) 2011, 2013, 2014, 2015, 2016 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 @@ -44,6 +44,8 @@ * Version 1.2: More than two radio channels. * Generalize for additional signals besides PTT. * + * Version 1.3: HAMLIB support. + * * References: http://www.robbayer.com/files/serial-win.pdf * * https://www.kernel.org/doc/Documentation/gpio.txt @@ -53,7 +55,7 @@ /* Idea for future enhancement: - A couple people have asked about support for the DMK URI. + A growing number of people have been asking about support for the DMK URI. This uses a C-Media CM108/CM119 with one interesting addition, a GPIO pin is used to drive PTT. Here is some related information. @@ -84,6 +86,12 @@ https://github.com/signal11/hidapi/blob/master/libusb/hid.c http://stackoverflow.com/questions/899008/howto-write-to-the-gpio-pin-of-the-cm108-chip-in-linux https://www.kernel.org/doc/Documentation/hid/hidraw.txt + + In version 1.3, we add HAMLIB support which should be able to do this. + (Linux only & haven't verified that it actually works yet!) + + Might want to have CM108 GPIO support built in, someday, for simpler building & configuration. + Maybe even for Windows. ;-) */ #include @@ -104,6 +112,10 @@ #include #include +#ifdef USE_HAMLIB +#include +#endif + /* So we can have more common code for fd. */ typedef int HANDLE; #define INVALID_HANDLE_VALUE (-1) @@ -221,6 +233,7 @@ void export_gpio(int gpio, int invert, int direction) err = system (stemp); snprintf (stemp, sizeof(stemp), "sudo chmod go+rw /sys/class/gpio/gpio%d/value", gpio); err = system (stemp); + (void)err; snprintf (stemp, sizeof(stemp), "/sys/class/gpio/gpio%d/value", gpio); @@ -297,10 +310,11 @@ void export_gpio(int gpio, int invert, int direction) * PTT_METHOD_SERIAL - serial (com) port. * PTT_METHOD_GPIO - general purpose I/O. * PTT_METHOD_LPT - Parallel printer port. - * PTT_METHOD_HAMLIB - HAMLib rig control. + * PTT_METHOD_HAMLIB - HAMLib rig control. * * ptt_device Name of serial port device. * e.g. COM1 or /dev/ttyS0. + * HAMLIB can also use hostaddr:port. * * ptt_line RTS or DTR when using serial port. * @@ -314,6 +328,11 @@ void export_gpio(int gpio, int invert, int direction) * ptt_invert Invert the signal. * Normally higher voltage means transmit or LED on. * + * ptt_model Only for HAMLIB. + * 2 to communicate with rigctld. + * >= 3 for specific radio model. + * -1 guess at what is out there. (AUTO option in config file.) + * * Outputs: Remember required information for future use. * * Description: @@ -328,7 +347,9 @@ static HANDLE ptt_fd[MAX_CHANS][NUM_OCTYPES]; /* Serial port handle or fd. */ /* Could be the same for two channels */ /* if using both RTS and DTR. */ - +#if USE_HAMLIB +static RIG *rig[MAX_CHANS][NUM_OCTYPES]; +#endif static char otnames[NUM_OCTYPES][8]; @@ -359,7 +380,9 @@ void ptt_init (struct audio_s *audio_config_p) for (ot = 0; ot < NUM_OCTYPES; ot++) { ptt_fd[ch][ot] = INVALID_HANDLE_VALUE; - +#if USE_HAMLIB + rig[ch][ot] = NULL; +#endif if (ptt_debug_level >= 2) { text_color_set(DW_COLOR_DEBUG); @@ -464,7 +487,7 @@ void ptt_init (struct audio_s *audio_config_p) audio_config_p->achan[ch].octrl[ot].ptt_device, ch); #if __WIN32__ #else - dw_printf ("%s\n", strerror(errno)); + dw_printf ("%s\n", strerror(e)); #endif /* Don't try using it later if device open failed. */ @@ -491,7 +514,7 @@ void ptt_init (struct audio_s *audio_config_p) #else /* - * Does any of them use GPIO or HAMLIB? + * Does any of them use GPIO? */ using_gpio = 0; @@ -637,7 +660,7 @@ void ptt_init (struct audio_s *audio_config_p) text_color_set(DW_COLOR_ERROR); dw_printf ("ERROR - Can't open /dev/port for parallel printer port PTT control.\n"); - dw_printf ("%s\n", strerror(errno)); + dw_printf ("%s\n", strerror(e)); dw_printf ("You probably don't have adequate permissions to access I/O ports.\n"); dw_printf ("Either run direwolf as root or change these permissions:\n"); dw_printf (" sudo chmod go+rw /dev/port\n"); @@ -667,31 +690,61 @@ void ptt_init (struct audio_s *audio_config_p) #ifdef USE_HAMLIB for (ch = 0; ch < MAX_CHANS; ch++) { if (save_audio_config_p->achan[ch].valid) { - int ot, retcode; - RIG *rig; - freq_t freq; - + int ot; for (ot = 0; ot < NUM_OCTYPES; ot++) { if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) { - if (audio_config_p->achan[ch].octrl[ot].ptt_rig - 1 >= audio_config_p->rigs) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error: RIG %d not available.\n", audio_config_p->achan[ch].octrl[ot].ptt_rig); - audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE; - } + if (ot == OCTYPE_PTT) { + + /* For "AUTO" model, try to guess what is out there. */ + + if (audio_config_p->achan[ch].octrl[ot].ptt_model == -1) { + hamlib_port_t hport; + + memset (&hport, 0, sizeof(hport)); + strlcpy (hport.pathname, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(hport.pathname)); + rig_load_all_backends(); + audio_config_p->achan[ch].octrl[ot].ptt_model = rig_probe(&hport); + + if (audio_config_p->achan[ch].octrl[ot].ptt_model == RIG_MODEL_NONE) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Couldn't guess rig model number for AUTO option. Run \"rigctl --list\" for a list of model numbers.\n"); + continue; + } + + text_color_set(DW_COLOR_INFO); + dw_printf ("Hamlib AUTO option detected rig model %d. Run \"rigctl --list\" for a list of model numbers.\n", + audio_config_p->achan[ch].octrl[ot].ptt_model); + } + + rig[ch][ot] = rig_init(audio_config_p->achan[ch].octrl[ot].ptt_model); + if (rig[ch][ot] == NULL) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Unknown rig model %d for hamlib. Run \"rigctl --list\" for a list of model numbers.\n", + audio_config_p->achan[ch].octrl[ot].ptt_model); + continue; + } + + strlcpy (rig[ch][ot]->state.rigport.pathname, audio_config_p->achan[ch].octrl[ot].ptt_device, sizeof(rig[ch][ot]->state.rigport.pathname)); + int err = rig_open(rig[ch][ot]); + if (err != RIG_OK) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Hamlib Rig open error %d: %s\n", err, rigerror(err)); + rig_cleanup (rig[ch][ot]); + rig[ch][ot] = NULL; + continue; + } + + /* Successful. Later code should check for rig[ch][ot] not NULL. */ + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("HAMLIB can only be used for PTT. Not DCD or other output.\n"); + } + } + } + } + } - rig = audio_config_p->rig[audio_config_p->achan[ch].octrl[ot].ptt_rig]; - retcode = rig_get_freq(rig, RIG_VFO_CURR, &freq); - if (retcode == RIG_OK) { - text_color_set(DW_COLOR_INFO); - dw_printf ("RIG tuned on %"PRIfreq"\n", freq); - } else { - text_color_set(DW_COLOR_ERROR); - dw_printf ("RIG rig_get_freq error %s, PTT probably will not work\n", rigerror(retcode)); - } - } - } - } - } #endif @@ -863,7 +916,7 @@ void ptt_set (int ot, int chan, int ptt_signal) ptt_fd[chan][ot] != INVALID_HANDLE_VALUE) { char lpt_data; - ssize_t n; + //ssize_t n; lseek (ptt_fd[chan][ot], (off_t)LPT_IO_ADDR, SEEK_SET); if (read (ptt_fd[chan][ot], &lpt_data, (size_t)1) != 1) { @@ -897,16 +950,22 @@ void ptt_set (int ot, int chan, int ptt_signal) */ if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_HAMLIB) { - int retcode; - RIG *rig = save_audio_config_p->rig[save_audio_config_p->achan[chan].octrl[ot].ptt_rig]; - if ((retcode = rig_set_ptt(rig, RIG_VFO_CURR, ptt ? RIG_PTT_ON : RIG_PTT_OFF)) != RIG_OK) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error sending rig_set_ptt command for channel %d %s\n", chan, otnames[ot]); - dw_printf ("%s\n", rigerror(retcode)); - } - } + if (rig[chan][ot] != NULL) { + int retcode = rig_set_ptt(rig[chan][ot], RIG_VFO_CURR, ptt ? RIG_PTT_ON : RIG_PTT_OFF); + + if (retcode != RIG_OK) { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Error sending rig_set_ptt command for channel %d %s\n", chan, otnames[ot]); + dw_printf ("%s\n", rigerror(retcode)); + } + } + else { + text_color_set(DW_COLOR_ERROR); + dw_printf ("Can't use rig_set_ptt for channel %d %s because rig_open failed.\n", chan, otnames[ot]); + } + } #endif @@ -1016,28 +1075,20 @@ void ptt_term (void) } #ifdef USE_HAMLIB - for (n = 0; n < save_audio_config_p->rigs; n++) { - RIG *rig = save_audio_config_p->rig[n]; - int retcode; - if ((retcode = rig_set_ptt(rig, RIG_VFO_CURR, RIG_PTT_OFF)) != RIG_OK) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error sending rig_set_ptt command for rig %d\n", n); - dw_printf ("%s\n", rigerror(retcode)); - } + for (n = 0; n < MAX_CHANS; n++) { + if (save_audio_config_p->achan[n].valid) { + int ot; + for (ot = 0; ot < NUM_OCTYPES; ot++) { + if (rig[n][ot] != NULL) { - if ((retcode = rig_close(rig)) != RIG_OK) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error sending rig_close command for rig %d\n", n); - dw_printf ("%s\n", rigerror(retcode)); - } - - if ((retcode = rig_cleanup(rig)) != RIG_OK) { - text_color_set(DW_COLOR_ERROR); - dw_printf ("Error sending rig_cleanup command for rig %d\n", n); - dw_printf ("%s\n", rigerror(retcode)); - } - } + rig_close(rig[n][ot]); + rig_cleanup(rig[n][ot]); + rig[n][ot] = NULL; + } + } + } + } #endif }