dl-fldigi/src/rigcontrol/ptt.cxx

683 wiersze
15 KiB
C++

// ----------------------------------------------------------------------------
//
// ptt.cxx -- PTT control
//
// Copyright (C) 2006-2009
// Dave Freese, W1HKJ
// Copyright (C) 2008-2009
// Stelios Bounanos, M0GLD
// Copyright (C) 2009
// Diane Bruce, VA3DB
//
// Added gpio for PTT (Lior KK6BWA)
//
// This file is part of fldigi. Adapted from code contained in gmfsk source code
// distribution.
// gmfsk Copyright (C) 2001, 2002, 2003
// Tomi Manninen (oh2bns@sral.fi)
// Copyright (C) 2004
// Lawrence Glaister (ve7it@shaw.ca)
//
// Fldigi 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 3 of the License, or
// (at your option) any later version.
//
// Fldigi 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 fldigi. If not, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#include <config.h>
#include <unistd.h>
#include <sys/types.h>
#if HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
#if HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#if HAVE_TERMIOS_H
# include <termios.h>
#endif
#include <errno.h>
#include <cstring>
#include <stdint.h>
#include "trx.h"
#include "ptt.h"
#include "configuration.h"
#include "rigio.h"
#if USE_HAMLIB
#include "hamlib.h"
#endif
#include "serial.h"
#include "re.h"
#include "debug.h"
#include "fl_digi.h"
LOG_FILE_SOURCE(debug::LOG_RIGCONTROL);
using namespace std;
PTT::PTT(ptt_t dev) : pttdev(PTT_INVALID), oldtio(0)
{
reset(dev);
}
PTT::~PTT()
{
close_all();
}
void PTT::reset(ptt_t dev)
{
close_all();
// LOG_VERBOSE("Setting PTT to %d", dev);
switch (pttdev = dev) {
#if HAVE_UHROUTER
case PTT_UHROUTER:
if (progdefaults.PTTdev.find(UHROUTER_FIFO_PREFIX) == 0) {
pttdev = PTT_UHROUTER;
open_uhrouter();
break;
}
else {
pttdev = PTT_NONE;
break;
}
#endif
#if HAVE_PARPORT
case PTT_PARPORT:
open_parport();
if (pttfd < 0)
pttdev = PTT_NONE;
break;
#endif
case PTT_TTY:
open_tty();
break;
case PTT_GPIO:
open_gpio();
break;
default:
break; // nothing to open
}
set(false);
}
void PTT::set(bool ptt)
{
// add milliseconds - no audio to clear virtual audio card used by Flex systems
if (!ptt && progdefaults.PTT_off_delay)
MilliSleep(progdefaults.PTT_off_delay);
if (active_modem == cw_modem &&
((progdefaults.useCWkeylineRTS) || progdefaults.useCWkeylineDTR == true))
return;
switch (pttdev) {
case PTT_NONE: default:
noCAT_setPTT(ptt);
break;
#if USE_HAMLIB
case PTT_HAMLIB:
hamlib_set_ptt(ptt);
break;
#endif
case PTT_RIGCAT:
rigCAT_set_ptt(ptt);
break;
case PTT_TTY:
set_tty(ptt);
break;
case PTT_GPIO:
set_gpio(ptt);
break;
#if HAVE_PARPORT
case PTT_PARPORT:
set_parport(ptt);
break;
#endif
#if HAVE_UHROUTER
case PTT_UHROUTER:
set_uhrouter(ptt);
break;
#endif
}
if (ptt && progdefaults.PTT_on_delay)
MilliSleep(progdefaults.PTT_on_delay);
if (ptt) start_tx_timer();
else stop_tx_timer();
}
void PTT::close_all(void)
{
set(false);
switch (pttdev) {
case PTT_TTY:
close_tty();
break;
case PTT_GPIO:
close_gpio();
break;
#if HAVE_PARPORT
case PTT_PARPORT:
close_parport();
break;
#endif
#if HAVE_UHROUTER
case PTT_UHROUTER:
close_uhrouter();
break;
#endif
default:
break;
}
pttfd = -1;
}
//-------------------- gpio port PTT --------------------//
void PTT::open_gpio(void)
{
}
void PTT::close_gpio(void)
{
}
void PTT::set_gpio(bool ptt)
{
#define VALUE_MAX 30
static const char s_values_str[] = "01";
char path[VALUE_MAX];
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", progdefaults.GPIOPort);
int fd = open(path, O_WRONLY);
if (-1 == fd) {
LOG_ERROR("Failed to open gpio (%s) value for writing!\n", path);
return;
}
if (1 != write(fd, &s_values_str[ ptt ? 1 : 0], 1)) {
LOG_ERROR("Failed to write value!\n");
return;
}
close(fd);
}
//-------------------- serial port PTT --------------------//
void PTT::open_tty(void)
{
#ifdef __MINGW32__
serPort.Device(progdefaults.PTTdev);
serPort.RTS(progdefaults.RTSplus);
serPort.DTR(progdefaults.DTRplus);
serPort.RTSptt(progdefaults.RTSptt);
serPort.DTRptt(progdefaults.DTRptt);
if (serPort.OpenPort() == false) {
LOG_ERROR("Cannot open serial port %s", rigio.Device().c_str());
pttfd = -1;
return;
}
LOG_DEBUG("Serial port %s open", progdefaults.PTTdev.c_str());
pttfd = -1; // just a dummy return for this implementation
#else
# if HAVE_TTYPORT
string pttdevName = progdefaults.PTTdev;
# ifdef __CYGWIN__
// convert to Linux serial port naming
com_to_tty(pttdevName);
# endif
int oflags = O_RDWR | O_NOCTTY | O_NDELAY;
# ifdef HAVE_O_CLOEXEC
oflags = oflags | O_CLOEXEC;
# endif
if ((pttfd = open(pttdevName.c_str(), oflags)) < 0) {
LOG_ERROR("Could not open \"%s\": %s", pttdevName.c_str(), strerror(errno));
return;
}
oldtio = new struct termios;
tcgetattr(pttfd, oldtio);
int status;
ioctl(pttfd, TIOCMGET, &status);
if (progdefaults.RTSplus)
status |= TIOCM_RTS; // set RTS bit
else
status &= ~TIOCM_RTS; // clear RTS bit
if (progdefaults.DTRplus)
status |= TIOCM_DTR; // set DTR bit
else
status &= ~TIOCM_DTR; // clear DTR bit
ioctl(pttfd, TIOCMSET, &status);
LOG_DEBUG("Serial port %s open; status = %02X, %s",
progdefaults.PTTdev.c_str(), status, uint2bin(status, 8));
# endif // HAVE_TTYPORT
#endif // __MINGW32__
}
void PTT::close_tty(void)
{
#ifdef __MINGW32__
serPort.ClosePort();
LOG_DEBUG("Serial port %s closed", progdefaults.PTTdev.c_str());
#else
# if HAVE_TTYPORT
if (pttfd >= 0) {
tcsetattr(pttfd, TCSANOW, oldtio);
close(pttfd);
}
delete oldtio;
# endif // HAVE_TTYPORT
#endif // __MINGW32__
}
void PTT::set_tty(bool ptt)
{
#ifdef __MINGW32__
serPort.SetPTT(ptt);
#else
# if HAVE_TTYPORT
int status;
ioctl(pttfd, TIOCMGET, &status);
if (ptt) {
if (progdefaults.RTSptt == true && progdefaults.RTSplus == false)
status |= TIOCM_RTS;
if (progdefaults.RTSptt == true && progdefaults.RTSplus == true)
status &= ~TIOCM_RTS;
if (progdefaults.DTRptt == true && progdefaults.DTRplus == false)
status |= TIOCM_DTR;
if (progdefaults.DTRptt == true && progdefaults.DTRplus == true)
status &= ~TIOCM_DTR;
} else {
if (progdefaults.RTSptt == true && progdefaults.RTSplus == false)
status &= ~TIOCM_RTS;
if (progdefaults.RTSptt == true && progdefaults.RTSplus == true)
status |= TIOCM_RTS;
if (progdefaults.DTRptt == true && progdefaults.DTRplus == false)
status &= ~TIOCM_DTR;
if (progdefaults.DTRptt == true && progdefaults.DTRplus == true)
status |= TIOCM_DTR;
}
LOG_DEBUG("Status %02X, %s", status & 0xFF, uint2bin(status, 8));
ioctl(pttfd, TIOCMSET, &status);
# endif // HAVE_TTYPORT
#endif // __MINGW32__
}
#if HAVE_PARPORT
//-------------------- parallel port PTT --------------------//
#if HAVE_LINUX_PPDEV_H
# include <linux/ppdev.h>
# include <linux/parport.h>
#elif HAVE_DEV_PPBUS_PPI_H
# include <dev/ppbus/ppi.h>
# include <dev/ppbus/ppbconf.h>
#endif
void PTT::open_parport(void)
{
if (progdefaults.PTTdev.find("tty") != string::npos) return;
int oflags = O_RDWR | O_NDELAY;
# ifdef HAVE_O_CLOEXEC
oflags = oflags | O_CLOEXEC;
# endif
if ((pttfd = open(progdefaults.PTTdev.c_str(), oflags)) == -1) {
LOG_ERROR("Could not open %s: %s", progdefaults.PTTdev.c_str(), strerror(errno));
return;
}
bool isparport = false;
struct stat st;
int status;
#if HAVE_LINUX_PPDEV_H // Linux (ppdev)
isparport = (fstat(pttfd, &st) == 0 && S_ISCHR(st.st_mode) &&
ioctl(pttfd, PPGETMODE, &status) != -1);
#elif HAVE_DEV_PPBUS_PPI_H // FreeBSD (ppbus/ppi) */
isparport = (fstat(pttfd, &st) == 0 && S_ISCHR(st.st_mode) &&
ioctl(pttfd, PPISSTATUS, &status) != -1);
#else // Fallback (nothing)
isparport = false;
#endif
if (!isparport) {
LOG_VERBOSE("%s: not a supported parallel port device", progdefaults.PTTdev.c_str());
close_parport();
pttfd = -1;
}
}
void PTT::close_parport(void)
{
close(pttfd);
}
void PTT::set_parport(bool ptt)
{
#ifdef HAVE_LINUX_PPDEV_H
struct ppdev_frob_struct frob;
frob.mask = PARPORT_CONTROL_INIT;
frob.val = !ptt;
ioctl(pttfd, PPFCONTROL, &frob);
#elif HAVE_DEV_PPBUS_PPI_H
u_int8_t val;
ioctl(pttfd, PPIGCTRL, &val);
if (ptt)
val |= nINIT;
else
val &= ~nINIT;
ioctl(pttfd, PPISCTRL, &val);
#endif
}
#endif // HAVE_PARPORT
#if HAVE_UHROUTER
//-------------------- uhRouter PTT --------------------//
// See interface documentation at:
// http://homepage.mac.com/chen/w7ay/Router/Contents/routerInterface.html
#define FUNCTIONMASK 0x1f
#define ROUTERFUNCTION 0x80
#define OPENMICROKEYER (ROUTERFUNCTION + 0x01) // get a port to the microKEYER router
#define OPENCWKEYER (ROUTERFUNCTION + 0x02) // get a port to the CW KEYER router
#define OPENDIGIKEYER (ROUTERFUNCTION + 0x03) // get a port to the DIGI KEYER router
#define QUITIFNOKEYER (ROUTERFUNCTION + 0x1f) // quit if there are no keyers
#define QUITIFNOTINUSE (ROUTERFUNCTION + 0x1e) // quit if not connected
#define QUITALWAYS (ROUTERFUNCTION + 0x1d) // quit
#define CLOSEKEYER (ROUTERFUNCTION + FUNCTIONMASK)
#define KEYERFUNCTION 0x40
#define OPENPTT (KEYERFUNCTION + 0x04) // get a port to the PTT flag bit
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif
static ssize_t tm_read(int fd, void* buf, size_t len, const struct timeval* to)
{
fd_set s;
FD_ZERO(&s);
FD_SET(fd, &s);
struct timeval t;
memcpy(&t, to, sizeof(t));
ssize_t n;
if ((n = select(fd + 1, &s, 0, 0, &t)) != 1)
return n;
return read(fd, buf, len);
}
static ssize_t tm_write(int fd, const void* buf, size_t len, const struct timeval* to)
{
fd_set s;
FD_ZERO(&s);
FD_SET(fd, &s);
struct timeval t;
memcpy(&t, to, sizeof(t));
ssize_t n;
if ((n = select(fd + 1, 0, &s, 0, &t)) != 1)
return n;
return write(fd, buf, len);
}
static bool open_fifos(const char* base, int fd[2])
{
struct stat st;
string fifo = base;
size_t len = fifo.length();
fifo += "Read";
if (stat(fifo.c_str(), &st) == -1 || !S_ISFIFO(st.st_mode)) {
LOG_ERROR("%s is not a fifo", fifo.c_str());
return false;
}
int oflags = O_RDONLY | O_NONBLOCK;
# ifdef HAVE_O_CLOEXEC
oflags = oflags | O_CLOEXEC;
# endif
if ((fd[0] = open(fifo.c_str(), oflags)) == -1) {
LOG_ERROR("Could not open %s: %s", fifo.c_str(), strerror(errno));
return false;
}
fifo.erase(len);
fifo += "Write";
if (stat(fifo.c_str(), &st) == -1 || !S_ISFIFO(st.st_mode)) {
LOG_ERROR("%s is not a fifo", fifo.c_str());
return false;
}
oflags = O_WRONLY | O_NONBLOCK;
# ifdef HAVE_O_CLOEXEC
oflags = oflags | O_CLOEXEC;
# endif
if ((fd[1] = open(fifo.c_str(), oflags)) == -1) {
LOG_ERROR("Could not open %s: %s", fifo.c_str(), strerror(errno));
return false;
}
return true;
}
static bool get_fifos(const int fd[2], const unsigned char* msg, size_t msglen, char* base, size_t baselen)
{
struct timeval to = { 2, 0 };
if (tm_write(fd[1], msg, msglen, &to) < (ssize_t)msglen) {
LOG_PERROR("Could not write request");
return false;
}
ssize_t r;
if ((r = tm_read(fd[0], base, baselen-1, &to)) <= 0) {
LOG_PERROR("Could not read FIFO name");
return false;
}
base[r] = '\0';
return true;
}
#ifdef __APPLE__
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#endif
static bool start_uhrouter(void)
{
bool found = false;
#ifdef __APPLE__
FILE *fd = (FILE *)0;
std::string appPath;
std::string buffer;
appPath.assign("/Applications/µH Router.app/Contents/MacOS/µH Router");
fd = fopen(appPath.c_str(), "r");
if(fd) {
found = true;
fclose(fd);
}
if(found) {
buffer.clear();
buffer.assign("\"");
buffer.append(appPath);
buffer.append("\" & ");
system(buffer.c_str());
} else {
LOG_ERROR("File: /Applications/\265H Router.app Not Found!");
}
#endif // __APPLE__
return found;
}
void PTT::open_uhrouter(void)
{
struct {
unsigned char keyer;
const char* name;
const char* abbrev;
} keyers[] = {
{ OPENMICROKEYER, "microKeyer", "MK" },
{ OPENCWKEYER, "CWKeyer", "CK" },
{ OPENDIGIKEYER, "DigiKeyer", "DK" }
};
size_t start = 0, end = sizeof(keyers)/sizeof(*keyers);
// If the device string is something like /tmp/microHamRouter/microKeyer,
// or /tmp/microHamRouter/MK, try that keyer only.
re_t keyer_re("^" UHROUTER_FIFO_PREFIX "/(.+)$", REG_EXTENDED);
if (keyer_re.match(progdefaults.PTTdev.c_str()) && keyer_re.nsub() == 2) {
const char* keyer = keyer_re.submatch(1).c_str();
// do we recognise this keyer name?
for (size_t i = 0; i < sizeof(keyers)/sizeof(*keyers); i++) {
if (!strcasecmp(keyers[i].name, keyer) || !strcasecmp(keyers[i].abbrev, keyer)) {
start = i;
end = start + 1;
break;
}
}
}
LOG_VERBOSE("Will try %s", (start == end ? keyers[start].name : "all keyers"));
int uhrfd[2];
uhrfd[0] = uhrfd[1] = uhkfd[0] = uhkfd[1] = uhfd[0] = uhfd[1] = -1;
if (!open_fifos(UHROUTER_FIFO_PREFIX, uhrfd)) {
// if we just started uhrouter we will retry open_fifos a few times
unsigned retries = start_uhrouter() ? 30 : 0;
while (retries-- && !open_fifos(UHROUTER_FIFO_PREFIX, uhrfd))
MilliSleep(100);
if (uhrfd[0] == -1 || uhrfd[1] == -1) {
LOG_ERROR("Could not open router");
return;
}
}
char fifo_name[PATH_MAX];
size_t len = PATH_MAX - 8;
memset(fifo_name, 0, sizeof(fifo_name));
for (size_t i = start; i < end; i++) {
// open keyer
if (!get_fifos(uhrfd, &keyers[i].keyer, 1, fifo_name, len) || *fifo_name == '\0') {
LOG_VERBOSE("Keyer \"%s\" not found", keyers[i].name);
continue;
}
// open ptt port
if (!open_fifos(fifo_name, uhkfd)) {
LOG_ERROR("Could not open keyer %s", keyers[i].name);
continue;
}
LOG_VERBOSE("Opened keyer %s", keyers[i].name);
unsigned char port = OPENPTT;
if (!get_fifos(uhkfd, &port, 1, fifo_name, len)) {
LOG_ERROR("Could not get PTT port");
continue;
}
if (!open_fifos(fifo_name, uhfd)) {
LOG_ERROR("Could not open PTT port %s", fifo_name);
continue;
}
LOG_VERBOSE("Successfully opened PTT port of keyer %s", keyers[i].name);
break;
}
// close router FIFOs
close(uhrfd[0]);
close(uhrfd[1]);
}
void PTT::close_uhrouter(void)
{
close(uhfd[0]);
close(uhfd[1]);
unsigned char c = QUITIFNOTINUSE;
write(uhkfd[1], &c, 1);
close(uhkfd[0]);
close(uhkfd[1]);
}
void PTT::set_uhrouter(bool ptt)
{
if (uhfd[0] == -1 || uhfd[1] == -1)
return;
unsigned char buf[_POSIX_PIPE_BUF];
// empty the fifo
while (read(uhfd[0], buf, sizeof(buf)) > 0);
// send command
*buf = '0' + ptt;
LOG_VERBOSE("Sending PTT=%uc", *buf);
struct timeval t = { 2, 0 };
if (tm_write(uhfd[1], buf, 1, &t) != 1) {
LOG_ERROR("Could not set PTT: %s", strerror(errno));
return;
}
// wait for status
ssize_t n = tm_read(uhfd[0], buf, sizeof(buf), &t);
switch (n) {
case -1:
LOG_PERROR("tm_read");
break;
case 0:
LOG_ERROR("No reply to PTT command within %jd seconds", (intmax_t)t.tv_sec);
break;
default:
LOG_VERBOSE("Received \"%s\"", str2hex(buf, n));
// last received char should be '1'(?)
break;
}
}
#endif // HAVE_UHROUTER