mirror of https://github.com/wb2osz/direwolf.git
1049 lines
28 KiB
C
1049 lines
28 KiB
C
//
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
//
|
|
// Copyright (C) 2011, 2013, 2014, 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Module: ptt.c
|
|
*
|
|
* Purpose: Activate the output control lines for push to talk (PTT) and other purposes.
|
|
*
|
|
* Description: Traditionally this is done with the RTS signal of the serial port.
|
|
*
|
|
* If we have two radio channels and only one serial port, DTR
|
|
* can be used for the second channel.
|
|
*
|
|
* If __WIN32__ is defined, we use the Windows interface.
|
|
* Otherwise we use the Linux interface.
|
|
*
|
|
* Version 0.9: Add ability to use GPIO pins on Linux.
|
|
*
|
|
* Version 1.1: Add parallel printer port for x86 Linux only.
|
|
*
|
|
* Version 1.2: More than two radio channels.
|
|
* Generalize for additional signals besides PTT.
|
|
*
|
|
* References: http://www.robbayer.com/files/serial-win.pdf
|
|
*
|
|
* https://www.kernel.org/doc/Documentation/gpio.txt
|
|
*
|
|
*---------------------------------------------------------------*/
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
|
|
#if __WIN32__
|
|
#include <windows.h>
|
|
#else
|
|
#include <sys/termios.h>
|
|
#include <sys/ioctl.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
/* So we can have more common code for fd. */
|
|
typedef int HANDLE;
|
|
#define INVALID_HANDLE_VALUE (-1)
|
|
|
|
#endif
|
|
|
|
#include "direwolf.h"
|
|
#include "textcolor.h"
|
|
#include "audio.h"
|
|
#include "ptt.h"
|
|
|
|
|
|
#if __WIN32__
|
|
|
|
#define RTS_ON(fd) EscapeCommFunction(fd,SETRTS);
|
|
#define RTS_OFF(fd) EscapeCommFunction(fd,CLRRTS);
|
|
#define DTR_ON(fd) EscapeCommFunction(fd,SETDTR);
|
|
#define DTR_OFF(fd) EscapeCommFunction(fd,CLRDTR);
|
|
|
|
#else
|
|
|
|
#define RTS_ON(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff |= TIOCM_RTS; ioctl (fd, TIOCMSET, &stuff); }
|
|
#define RTS_OFF(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff &= ~TIOCM_RTS; ioctl (fd, TIOCMSET, &stuff); }
|
|
#define DTR_ON(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff |= TIOCM_DTR; ioctl (fd, TIOCMSET, &stuff); }
|
|
#define DTR_OFF(fd) { int stuff; ioctl (fd, TIOCMGET, &stuff); stuff &= ~TIOCM_DTR; ioctl (fd, TIOCMSET, &stuff); }
|
|
|
|
#define LPT_IO_ADDR 0x378
|
|
|
|
#endif
|
|
|
|
|
|
#if TEST
|
|
#define dw_printf printf
|
|
#endif
|
|
|
|
|
|
static int ptt_debug_level = 0;
|
|
|
|
void ptt_set_debug(int debug)
|
|
{
|
|
ptt_debug_level = debug;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
*
|
|
* Name: ptt_init
|
|
*
|
|
* Purpose: Open serial port(s) used for PTT signals and set to proper state.
|
|
*
|
|
* Inputs: audio_config_p - Structure with communication parameters.
|
|
*
|
|
* for each channel we have:
|
|
*
|
|
* ptt_method Method for PTT signal.
|
|
* PTT_METHOD_NONE - not configured. Could be using VOX.
|
|
* 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_device Name of serial port device.
|
|
* e.g. COM1 or /dev/ttyS0.
|
|
*
|
|
* ptt_line RTS or DTR when using serial port.
|
|
*
|
|
* ptt_gpio GPIO number. Only used for Linux.
|
|
* Valid only when ptt_method is PTT_METHOD_GPIO.
|
|
*
|
|
* ptt_lpt_bit Bit number for parallel printer port.
|
|
* Bit 0 = pin 2, ..., bit 7 = pin 9.
|
|
* Valid only when ptt_method is PTT_METHOD_LPT.
|
|
*
|
|
* ptt_invert Invert the signal.
|
|
* Normally higher voltage means transmit or LED on.
|
|
*
|
|
* Outputs: Remember required information for future use.
|
|
*
|
|
* Description:
|
|
*
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
static struct audio_s *save_audio_config_p; /* Save config information for later use. */
|
|
|
|
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. */
|
|
|
|
|
|
static char otnames[NUM_OCTYPES][8];
|
|
|
|
void ptt_init (struct audio_s *audio_config_p)
|
|
{
|
|
int ch;
|
|
HANDLE fd;
|
|
#if __WIN32__
|
|
#else
|
|
int using_gpio;
|
|
#endif
|
|
|
|
#if DEBUG
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("ptt_init ( ... )\n");
|
|
#endif
|
|
|
|
save_audio_config_p = audio_config_p;
|
|
|
|
strcpy (otnames[OCTYPE_PTT], "PTT");
|
|
strcpy (otnames[OCTYPE_DCD], "DCD");
|
|
strcpy (otnames[OCTYPE_RIG], "RIG");
|
|
strcpy (otnames[OCTYPE_FUTURE], "FUTURE");
|
|
|
|
|
|
for (ch = 0; ch < MAX_CHANS; ch++) {
|
|
int ot;
|
|
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
|
|
ptt_fd[ch][ot] = INVALID_HANDLE_VALUE;
|
|
|
|
if (ptt_debug_level >= 2) {
|
|
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("ch=%d, %s method=%d, device=%s, line=%d, gpio=%d, lpt_bit=%d, invert=%d\n",
|
|
ch,
|
|
otnames[ot],
|
|
audio_config_p->achan[ch].octrl[ot].ptt_method,
|
|
audio_config_p->achan[ch].octrl[ot].ptt_device,
|
|
audio_config_p->achan[ch].octrl[ot].ptt_line,
|
|
audio_config_p->achan[ch].octrl[ot].ptt_gpio,
|
|
audio_config_p->achan[ch].octrl[ot].ptt_lpt_bit,
|
|
audio_config_p->achan[ch].octrl[ot].ptt_invert);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set up serial ports.
|
|
*/
|
|
|
|
for (ch = 0; ch < MAX_CHANS; ch++) {
|
|
|
|
if (audio_config_p->achan[ch].valid) {
|
|
int ot;
|
|
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
|
|
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_SERIAL) {
|
|
|
|
#if __WIN32__
|
|
#else
|
|
/* Translate Windows device name into Linux name. */
|
|
/* COM1 -> /dev/ttyS0, etc. */
|
|
|
|
if (strncasecmp(audio_config_p->achan[ch].octrl[ot].ptt_device, "COM", 3) == 0) {
|
|
int n = atoi (audio_config_p->achan[ch].octrl[ot].ptt_device + 3);
|
|
text_color_set(DW_COLOR_INFO);
|
|
dw_printf ("Converted %s device '%s'", audio_config_p->achan[ch].octrl[ot].ptt_device, otnames[ot]);
|
|
if (n < 1) n = 1;
|
|
sprintf (audio_config_p->achan[ch].octrl[ot].ptt_device, "/dev/ttyS%d", n-1);
|
|
dw_printf (" to Linux equivalent '%s'\n", audio_config_p->achan[ch].octrl[ot].ptt_device);
|
|
}
|
|
#endif
|
|
/* Can't open the same device more than once so we */
|
|
/* need more logic to look for the case of multiple radio */
|
|
/* channels using different pins of the same COM port. */
|
|
|
|
/* Did some earlier channel use the same device name? */
|
|
|
|
int same_device_used = 0;
|
|
int j, k;
|
|
|
|
for (j = ch; j >= 0; j--) {
|
|
if (audio_config_p->achan[j].valid) {
|
|
for (k = ((j==ch) ? (ot - 1) : (NUM_OCTYPES-1)); k >= 0; k--) {
|
|
if (strcmp(audio_config_p->achan[ch].octrl[ot].ptt_device,audio_config_p->achan[j].octrl[k].ptt_device) == 0) {
|
|
fd = ptt_fd[j][k];
|
|
same_device_used = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! same_device_used) {
|
|
|
|
#if __WIN32__
|
|
char bettername[50];
|
|
// Bug fix in release 1.1 - Need to munge name for COM10 and up.
|
|
// http://support.microsoft.com/kb/115831
|
|
|
|
strcpy (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device);
|
|
if (strncasecmp(bettername, "COM", 3) == 0) {
|
|
int n;
|
|
n = atoi(bettername+3);
|
|
if (n >= 10) {
|
|
strcpy (bettername, "\\\\.\\");
|
|
strcat (bettername, audio_config_p->achan[ch].octrl[ot].ptt_device);
|
|
}
|
|
}
|
|
fd = CreateFile(bettername,
|
|
GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
|
|
#else
|
|
|
|
/* O_NONBLOCK added in version 0.9. */
|
|
/* Was hanging with some USB-serial adapters. */
|
|
/* https://bugs.launchpad.net/ubuntu/+source/linux/+bug/661321/comments/12 */
|
|
|
|
fd = open (audio_config_p->achan[ch].octrl[ot].ptt_device, O_RDONLY | O_NONBLOCK);
|
|
#endif
|
|
}
|
|
|
|
if (fd != INVALID_HANDLE_VALUE) {
|
|
ptt_fd[ch][ot] = fd;
|
|
}
|
|
else {
|
|
#if __WIN32__
|
|
#else
|
|
int e = errno;
|
|
#endif
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("ERROR can't open device %s for channel %d PTT control.\n",
|
|
audio_config_p->achan[ch].octrl[ot].ptt_device, ch);
|
|
#if __WIN32__
|
|
#else
|
|
dw_printf ("%s\n", strerror(errno));
|
|
#endif
|
|
/* Don't try using it later if device open failed. */
|
|
|
|
audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
|
|
}
|
|
|
|
/*
|
|
* Set initial state off.
|
|
* ptt_set will invert output signal if appropriate.
|
|
*/
|
|
ptt_set (ot, ch, 0);
|
|
|
|
} /* if serial method. */
|
|
} /* for each output type. */
|
|
} /* if channel valid. */
|
|
} /* For each channel. */
|
|
|
|
|
|
/*
|
|
* Set up GPIO - for Linux only.
|
|
*/
|
|
|
|
#if __WIN32__
|
|
#else
|
|
|
|
/*
|
|
* Does any of them use GPIO or HAMLIB?
|
|
*/
|
|
|
|
using_gpio = 0;
|
|
for (ch=0; ch<MAX_CHANS; ch++) {
|
|
if (save_audio_config_p->achan[ch].valid) {
|
|
int ot;
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
|
using_gpio = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (using_gpio) {
|
|
|
|
struct stat finfo;
|
|
/*
|
|
* Normally the device nodes are set up for access
|
|
* only by root. Try to change it here so we don't
|
|
* burden user with another configuration step.
|
|
*
|
|
* Does /sys/class/gpio/export even exist?
|
|
*/
|
|
|
|
if (stat("/sys/class/gpio/export", &finfo) < 0) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("This system is not configured with the GPIO user interface.\n");
|
|
dw_printf ("Use a different method for PTT control.\n");
|
|
exit (1);
|
|
}
|
|
|
|
/*
|
|
* Do we have permission to access it?
|
|
*
|
|
* pi@raspberrypi /sys/class/gpio $ ls -l
|
|
* total 0
|
|
* --w------- 1 root root 4096 Aug 20 07:59 export
|
|
* lrwxrwxrwx 1 root root 0 Aug 20 07:59 gpiochip0 -> ../../devices/virtual/gpio/gpiochip0
|
|
* --w------- 1 root root 4096 Aug 20 07:59 unexport
|
|
*/
|
|
if (geteuid() != 0) {
|
|
if ( ! (finfo.st_mode & S_IWOTH)) {
|
|
int err;
|
|
|
|
/* Try to change protection. */
|
|
err = system ("sudo chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport");
|
|
|
|
if (stat("/sys/class/gpio/export", &finfo) < 0) {
|
|
/* Unexpected because we could do it before. */
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("This system is not configured with the GPIO user interface.\n");
|
|
dw_printf ("Use a different method for PTT control.\n");
|
|
exit (1);
|
|
}
|
|
|
|
/* Did we succeed in changing the protection? */
|
|
if ( ! (finfo.st_mode & S_IWOTH)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
|
dw_printf ("Log in as root and type this command:\n");
|
|
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
|
|
exit (1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
* We should now be able to create the device nodes for
|
|
* the pins we want to use.
|
|
*/
|
|
|
|
for (ch = 0; ch < MAX_CHANS; ch++) {
|
|
if (save_audio_config_p->achan[ch].valid) {
|
|
int ot;
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
|
char stemp[80];
|
|
struct stat finfo;
|
|
int err;
|
|
|
|
fd = open("/sys/class/gpio/export", O_WRONLY);
|
|
if (fd < 0) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
|
dw_printf ("Log in as root and type this command:\n");
|
|
dw_printf (" chmod go+w /sys/class/gpio/export /sys/class/gpio/unexport\n");
|
|
exit (1);
|
|
}
|
|
sprintf (stemp, "%d", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
if (write (fd, stemp, strlen(stemp)) != strlen(stemp)) {
|
|
int e = errno;
|
|
/* Ignore EBUSY error which seems to mean */
|
|
/* the device node already exists. */
|
|
if (e != EBUSY) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error writing \"%s\" to /sys/class/gpio/export, errno=%d\n", stemp, e);
|
|
dw_printf ("%s\n", strerror(e));
|
|
exit (1);
|
|
}
|
|
}
|
|
close (fd);
|
|
|
|
/*
|
|
* We will have the same permission problem if not root.
|
|
* We only care about "direction" and "value".
|
|
*/
|
|
sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
err = system (stemp);
|
|
sprintf (stemp, "sudo chmod go+rw /sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
err = system (stemp);
|
|
|
|
sprintf (stemp, "/sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
|
|
if (stat(stemp, &finfo) < 0) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Failed to get status for %s \n", stemp);
|
|
dw_printf ("%s\n", strerror(e));
|
|
exit (1);
|
|
}
|
|
|
|
if (geteuid() != 0) {
|
|
if ( ! (finfo.st_mode & S_IWOTH)) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Permissions do not allow ordinary users to access GPIO.\n");
|
|
dw_printf ("Log in as root and type these commands:\n");
|
|
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
dw_printf (" chmod go+rw /sys/class/gpio/gpio%d/value", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Set output direction with initial state off.
|
|
*/
|
|
|
|
sprintf (stemp, "/sys/class/gpio/gpio%d/direction", audio_config_p->achan[ch].octrl[ot].ptt_gpio);
|
|
fd = open(stemp, O_WRONLY);
|
|
if (fd < 0) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error opening %s\n", stemp);
|
|
dw_printf ("%s\n", strerror(e));
|
|
exit (1);
|
|
}
|
|
|
|
char hilo[8];
|
|
if (audio_config_p->achan[ch].octrl[ot].ptt_invert) {
|
|
strcpy (hilo, "high");
|
|
}
|
|
else {
|
|
strcpy (hilo, "low");
|
|
}
|
|
if (write (fd, hilo, strlen(hilo)) != strlen(hilo)) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error writing initial state to %s\n", stemp);
|
|
dw_printf ("%s\n", strerror(e));
|
|
exit (1);
|
|
}
|
|
close (fd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
* Set up parallel printer port.
|
|
*
|
|
* Restrictions:
|
|
* Only the primary printer port.
|
|
* For x86 Linux only.
|
|
*/
|
|
|
|
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
|
|
|
|
for (ch = 0; ch < MAX_CHANS; ch++) {
|
|
if (save_audio_config_p->achan[ch].valid) {
|
|
int ot;
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
if (audio_config_p->achan[ch].octrl[ot].ptt_method == PTT_METHOD_LPT) {
|
|
|
|
/* Can't open the same device more than once so we */
|
|
/* need more logic to look for the case of mutiple radio */
|
|
/* channels using different pins of the LPT port. */
|
|
|
|
/* Did some earlier channel use the same ptt device name? */
|
|
|
|
int same_device_used = 0;
|
|
int j, k;
|
|
|
|
for (j = ch; j >= 0; j--) {
|
|
if (audio_config_p->achan[j].valid) {
|
|
for (k = ((j==ch) ? (ot - 1) : (NUM_OCTYPES-1)); k >= 0; k--) {
|
|
if (strcmp(audio_config_p->achan[ch].octrl[ot].ptt_device,audio_config_p->achan[j].octrl[k].ptt_device) == 0) {
|
|
fd = ptt_fd[j][k];
|
|
same_device_used = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! same_device_used) {
|
|
fd = open ("/dev/port", O_RDWR | O_NDELAY);
|
|
}
|
|
|
|
if (fd != INVALID_HANDLE_VALUE) {
|
|
ptt_fd[ch][ot] = fd;
|
|
}
|
|
else {
|
|
|
|
int e = errno;
|
|
|
|
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 ("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");
|
|
dw_printf (" sudo setcap cap_sys_rawio=ep `which direwolf`\n");
|
|
|
|
/* Don't try using it later if device open failed. */
|
|
|
|
audio_config_p->achan[ch].octrl[ot].ptt_method = PTT_METHOD_NONE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Set initial state off.
|
|
* ptt_set will invert output signal if appropriate.
|
|
*/
|
|
ptt_set (ot, ch, 0);
|
|
|
|
} /* if parallel printer port method. */
|
|
} /* for each output type */
|
|
} /* if valid channel. */
|
|
} /* For each channel. */
|
|
|
|
|
|
|
|
#endif /* x86 Linux */
|
|
|
|
#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;
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
|
|
|
|
/* Why doesn't it transmit? Probably forgot to specify PTT option. */
|
|
|
|
for (ch=0; ch<MAX_CHANS; ch++) {
|
|
if (audio_config_p->achan[ch].valid) {
|
|
if(audio_config_p->achan[ch].octrl[OCTYPE_PTT].ptt_method == PTT_METHOD_NONE) {
|
|
text_color_set(DW_COLOR_INFO);
|
|
dw_printf ("Note: PTT not configured for channel %d. (Ignore this if using VOX.)\n", ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
} /* end ptt_init */
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
*
|
|
* Name: ptt_set
|
|
*
|
|
* Purpose: Turn output control line on or off.
|
|
* Originally this was just for PTT, hence the name.
|
|
* Now that it is more general purpose, it should
|
|
* probably be renamed something like octrl_set.
|
|
*
|
|
* Inputs: ot - Output control type:
|
|
* OCTYPE_PTT, OCTYPE_DCD, OCTYPE_HAMLIB, OCTYPE_FUTURE
|
|
*
|
|
* chan - channel, 0 .. (number of channels)-1
|
|
*
|
|
* ptt_signal - 1 for transmit, 0 for receive.
|
|
*
|
|
*
|
|
* Assumption: ptt_init was called first.
|
|
*
|
|
* Description: Set the RTS or DTR line or GPIO pin.
|
|
* More positive output corresponds to 1 unless invert is set.
|
|
*
|
|
*--------------------------------------------------------------------*/
|
|
|
|
|
|
void ptt_set (int ot, int chan, int ptt_signal)
|
|
{
|
|
|
|
int ptt = ptt_signal;
|
|
int ptt2 = ptt_signal;
|
|
|
|
assert (ot >= 0 && ot < NUM_OCTYPES);
|
|
assert (chan >= 0 && chan < MAX_CHANS);
|
|
|
|
if (ptt_debug_level >= 1) {
|
|
text_color_set(DW_COLOR_DEBUG);
|
|
dw_printf ("%s %d = %d\n", otnames[ot], chan, ptt_signal);
|
|
}
|
|
|
|
assert (chan >= 0 && chan < MAX_CHANS);
|
|
|
|
if ( ! save_audio_config_p->achan[chan].valid) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Internal error, ptt_set ( %s, %d, %d ), did not expect invalid channel.\n", otnames[ot], chan, ptt);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Inverted output?
|
|
*/
|
|
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_invert) {
|
|
ptt = ! ptt;
|
|
}
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_invert2) {
|
|
ptt2 = ! ptt2;
|
|
}
|
|
|
|
/*
|
|
* Using serial port?
|
|
*/
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_SERIAL &&
|
|
ptt_fd[chan][ot] != INVALID_HANDLE_VALUE) {
|
|
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_line == PTT_LINE_RTS) {
|
|
|
|
if (ptt) {
|
|
RTS_ON(ptt_fd[chan][ot]);
|
|
}
|
|
else {
|
|
RTS_OFF(ptt_fd[chan][ot]);
|
|
}
|
|
}
|
|
else if (save_audio_config_p->achan[chan].octrl[ot].ptt_line == PTT_LINE_DTR) {
|
|
|
|
if (ptt) {
|
|
DTR_ON(ptt_fd[chan][ot]);
|
|
}
|
|
else {
|
|
DTR_OFF(ptt_fd[chan][ot]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Second serial port control line? Typically driven with opposite phase but could be in phase.
|
|
*/
|
|
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_line2 == PTT_LINE_RTS) {
|
|
|
|
if (ptt2) {
|
|
RTS_ON(ptt_fd[chan][ot]);
|
|
}
|
|
else {
|
|
RTS_OFF(ptt_fd[chan][ot]);
|
|
}
|
|
}
|
|
else if (save_audio_config_p->achan[chan].octrl[ot].ptt_line2 == PTT_LINE_DTR) {
|
|
|
|
if (ptt2) {
|
|
DTR_ON(ptt_fd[chan][ot]);
|
|
}
|
|
else {
|
|
DTR_OFF(ptt_fd[chan][ot]);
|
|
}
|
|
}
|
|
/* else neither one */
|
|
|
|
}
|
|
|
|
/*
|
|
* Using GPIO?
|
|
*/
|
|
|
|
#if __WIN32__
|
|
#else
|
|
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_GPIO) {
|
|
int fd;
|
|
char stemp[80];
|
|
|
|
sprintf (stemp, "/sys/class/gpio/gpio%d/value", save_audio_config_p->achan[chan].octrl[ot].ptt_gpio);
|
|
|
|
fd = open(stemp, O_WRONLY);
|
|
if (fd < 0) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error opening %s to set %s signal.\n", stemp, otnames[ot]);
|
|
dw_printf ("%s\n", strerror(e));
|
|
return;
|
|
}
|
|
|
|
sprintf (stemp, "%d", ptt);
|
|
|
|
if (write (fd, stemp, 1) != 1) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error setting GPIO %d for %s\n", save_audio_config_p->achan[chan].octrl[ot].ptt_gpio, otnames[ot]);
|
|
dw_printf ("%s\n", strerror(e));
|
|
}
|
|
close (fd);
|
|
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Using parallel printer port?
|
|
*/
|
|
|
|
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
|
|
|
|
if (save_audio_config_p->achan[chan].octrl[ot].ptt_method == PTT_METHOD_LPT &&
|
|
ptt_fd[chan][ot] != INVALID_HANDLE_VALUE) {
|
|
|
|
char lpt_data;
|
|
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) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error reading current state of LPT for channel %d %s\n", chan, otnames[ot]);
|
|
dw_printf ("%s\n", strerror(e));
|
|
}
|
|
|
|
if (ptt) {
|
|
lpt_data |= ( 1 << save_audio_config_p->achan[chan].octrl[ot].ptt_lpt_bit );
|
|
}
|
|
else {
|
|
lpt_data &= ~ ( 1 << save_audio_config_p->achan[chan].octrl[ot].ptt_lpt_bit );
|
|
}
|
|
|
|
lseek (ptt_fd[chan][ot], (off_t)LPT_IO_ADDR, SEEK_SET);
|
|
if (write (ptt_fd[chan][ot], &lpt_data, (size_t)1) != 1) {
|
|
int e = errno;
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("Error writing to LPT for channel %d %s\n", chan, otnames[ot]);
|
|
dw_printf ("%s\n", strerror(e));
|
|
}
|
|
}
|
|
|
|
#endif /* x86 Linux */
|
|
|
|
#ifdef USE_HAMLIB
|
|
/*
|
|
* Using hamlib?
|
|
*/
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
} /* end ptt_set */
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------
|
|
*
|
|
* Name: ptt_term
|
|
*
|
|
* Purpose: Make sure PTT and others are turned off when we exit.
|
|
*
|
|
* Inputs: none
|
|
*
|
|
* Description:
|
|
*
|
|
*--------------------------------------------------------------------*/
|
|
|
|
void ptt_term (void)
|
|
{
|
|
int n;
|
|
|
|
for (n = 0; n < MAX_CHANS; n++) {
|
|
if (save_audio_config_p->achan[n].valid) {
|
|
int ot;
|
|
for (ot = 0; ot < NUM_OCTYPES; ot++) {
|
|
ptt_set (ot, n, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (ptt_fd[n][ot] != INVALID_HANDLE_VALUE) {
|
|
#if __WIN32__
|
|
CloseHandle (ptt_fd[n][ot]);
|
|
#else
|
|
close(ptt_fd[n][ot]);
|
|
#endif
|
|
ptt_fd[n][ot] = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#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));
|
|
}
|
|
|
|
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));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Quick stand-alone test for above.
|
|
*
|
|
* gcc -DTEST -o ptest ptt.c ; ./ptest
|
|
*
|
|
*/
|
|
|
|
|
|
#if TEST
|
|
|
|
void text_color_set (dw_color_t c) { }
|
|
|
|
#define dw_printf printf
|
|
|
|
main ()
|
|
{
|
|
struct audio_s my_audio_config;
|
|
int n;
|
|
int chan;
|
|
|
|
memset (&my_audio_config, 0, sizeof(my_audio_config));
|
|
|
|
my_audio_config.adev[0].num_channels = 2;
|
|
|
|
my_audio_config.valid[0] = 1;
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
|
|
//strcpy (my_audio_config.ptt_device, "COM1");
|
|
strcpy (my_audio_config.ptt_device, "/dev/ttyUSB0");
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_RTS;
|
|
|
|
my_audio_config.valid[1] = 1;
|
|
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_SERIAL;
|
|
//strcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "COM1");
|
|
strcpy (my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_device, "/dev/ttyUSB0");
|
|
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_line = PTT_LINE_DTR;
|
|
|
|
|
|
/* initialize - both off */
|
|
|
|
ptt_init (&my_audio_config);
|
|
|
|
SLEEP_SEC(2);
|
|
|
|
/* flash each a few times. */
|
|
|
|
dw_printf ("turn on RTS a few times...\n");
|
|
|
|
chan = 0;
|
|
for (n=0; n<3; n++) {
|
|
ptt_set (OCTYPE_PTT, chan, 1);
|
|
SLEEP_SEC(1);
|
|
ptt_set (OCTYPE_PTT, chan, 0);
|
|
SLEEP_SEC(1);
|
|
}
|
|
|
|
dw_printf ("turn on DTR a few times...\n");
|
|
|
|
chan = 1;
|
|
for (n=0; n<3; n++) {
|
|
ptt_set (OCTYPE_PTT, chan, 1);
|
|
SLEEP_SEC(1);
|
|
ptt_set (OCTYPE_PTT, chan, 0);
|
|
SLEEP_SEC(1);
|
|
}
|
|
|
|
ptt_term();
|
|
|
|
/* Same thing again but invert RTS. */
|
|
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_invert = 1;
|
|
|
|
ptt_init (&my_audio_config);
|
|
|
|
SLEEP_SEC(2);
|
|
|
|
dw_printf ("INVERTED - RTS a few times...\n");
|
|
|
|
chan = 0;
|
|
for (n=0; n<3; n++) {
|
|
ptt_set (OCTYPE_PTT, chan, 1);
|
|
SLEEP_SEC(1);
|
|
ptt_set (OCTYPE_PTT, chan, 0);
|
|
SLEEP_SEC(1);
|
|
}
|
|
|
|
dw_printf ("turn on DTR a few times...\n");
|
|
|
|
chan = 1;
|
|
for (n=0; n<3; n++) {
|
|
ptt_set (OCTYPE_PTT, chan, 1);
|
|
SLEEP_SEC(1);
|
|
ptt_set (OCTYPE_PTT, chan, 0);
|
|
SLEEP_SEC(1);
|
|
}
|
|
|
|
ptt_term ();
|
|
|
|
|
|
/* Test GPIO */
|
|
|
|
#if __arm__
|
|
|
|
memset (&my_audio_config, 0, sizeof(my_audio_config));
|
|
my_audio_config.adev[0].num_channels = 1;
|
|
my_audio_config.valid[0] = 1;
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_GPIO;
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_gpio = 25;
|
|
|
|
dw_printf ("Try GPIO %d a few times...\n", my_audio_config.ptt_gpio[0]);
|
|
|
|
ptt_init (&my_audio_config);
|
|
|
|
SLEEP_SEC(2);
|
|
chan = 0;
|
|
for (n=0; n<3; n++) {
|
|
ptt_set (OCTYPE_PTT, chan, 1);
|
|
SLEEP_SEC(1);
|
|
ptt_set (OCTYPE_PTT, chan, 0);
|
|
SLEEP_SEC(1);
|
|
}
|
|
|
|
ptt_term ();
|
|
#endif
|
|
|
|
|
|
memset (&my_audio_config, 0, sizeof(my_audio_config));
|
|
my_audio_config.num_channels = 2;
|
|
my_audio_config.valid[0] = 1;
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_LPT;
|
|
my_audio_config.adev[0].octrl[OCTYPE_PTT].ptt_lpt_bit = 0;
|
|
my_audio_config.valid[1] = 1;
|
|
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_method = PTT_METHOD_LPT;
|
|
my_audio_config.adev[1].octrl[OCTYPE_PTT].ptt_lpt_bit = 1;
|
|
|
|
dw_printf ("Try LPT bits 0 & 1 a few times...\n");
|
|
|
|
ptt_init (&my_audio_config);
|
|
|
|
for (n=0; n<8; n++) {
|
|
ptt_set (OCTYPE_PTT, 0, n & 1);
|
|
ptt_set (OCTYPE_PTT, 1, (n>>1) & 1);
|
|
SLEEP_SEC(1);
|
|
}
|
|
|
|
ptt_term ();
|
|
|
|
/* Parallel printer port. */
|
|
|
|
#if ( defined(__i386__) || defined(__x86_64__) ) && ( defined(__linux__) || defined(__unix__) )
|
|
|
|
// TODO
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
#endif /* TEST */
|
|
|
|
/* end ptt.c */
|
|
|
|
|
|
|