Hamlib/src/event.c

718 wiersze
16 KiB
C

/*
* Hamlib Interface - event handling
* Copyright (c) 2000-2010 by Stephane Fillod
* Copyright (c) 2000-2003 by Frank Singleton
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
/* Doc todo: Verify assignment to rig group. Consider doc of internal rtns. */
/**
* \addtogroup rig
* @{
*/
/**
* \file event.c
* \brief Event handling
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#include <sys/stat.h>
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <hamlib/rig.h>
#include "event.h"
#if defined(WIN32) && !defined(HAVE_TERMIOS_H)
#include "win32termios.h"
#define select win32_serial_select
#endif
#ifndef DOC_HIDDEN
#define CHECK_RIG_ARG(r) (!(r) || !(r)->caps || !(r)->state.comm_state)
#ifdef HAVE_SIGACTION
static struct sigaction hamlib_trn_oldact, hamlib_trn_poll_oldact;
#ifdef HAVE_SIGINFO_T
static RETSIGTYPE sa_sigioaction(int signum, siginfo_t *si, void *data);
static RETSIGTYPE sa_sigalrmaction(int signum, siginfo_t *si, void *data);
#else
static RETSIGTYPE sa_sigiohandler(int signum);
static RETSIGTYPE sa_sigalrmhandler(int signum);
#endif
#endif
/* This one should be in an include file */
extern int foreach_opened_rig(int (*cfunc)(RIG *, rig_ptr_t),rig_ptr_t data);
/*
* add_trn_rig
* not exported in Hamlib API.
* Assumes rig->caps->transceive == RIG_TRN_RIG
*/
int add_trn_rig(RIG *rig)
{
#ifdef HAVE_SIGACTION
struct sigaction act;
int status;
/*
* FIXME: multiple open will register several time SIGIO hndlr
*/
memset(&act, 0, sizeof(act));
#ifdef HAVE_SIGINFO_T
act.sa_sigaction = sa_sigioaction;
#else
act.sa_handler = sa_sigiohandler;
#endif
sigemptyset(&act.sa_mask);
#if defined(HAVE_SIGINFO_T) && defined(SA_SIGINFO)
act.sa_flags = SA_SIGINFO|SA_RESTART;
#else
act.sa_flags = SA_RESTART;
#endif
status = sigaction(SIGIO, &act, &hamlib_trn_oldact);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s: sigaction failed: %s\n",
__func__, strerror(errno));
status = fcntl(rig->state.rigport.fd, F_SETOWN, getpid());
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s: fcntl SETOWN failed: %s\n",
__func__, strerror(errno));
#if defined(HAVE_SIGINFO_T) && defined(O_ASYNC)
#ifdef F_SETSIG
status = fcntl(rig->state.rigport.fd, F_SETSIG, SIGIO);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s: fcntl SETSIG failed: %s\n",
__func__, strerror(errno));
#endif
status = fcntl(rig->state.rigport.fd, F_SETFL, O_ASYNC);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s: fcntl SETASYNC failed: %s\n",
__func__, strerror(errno));
#else
return -RIG_ENIMPL;
#endif
return RIG_OK;
#else
return -RIG_ENIMPL;
#endif /* !HAVE_SIGACTION */
}
/*
* remove_trn_rig
* not exported in Hamlib API.
* Assumes rig->caps->transceive == RIG_TRN_RIG
*/
int remove_trn_rig(RIG *rig)
{
#ifdef HAVE_SIGACTION
int status;
/* assert(rig->caps->transceive == RIG_TRN_RIG); */
#if defined(HAVE_SIGINFO_T) && defined(O_ASYNC)
status = fcntl(rig->state.rigport.fd, F_SETFL, 0);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s: fcntl SETASYNC failed: %s\n",
__func__, strerror(errno));
#endif
status = sigaction(SIGIO, &hamlib_trn_oldact, NULL);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s: sigaction failed: %s\n",
__func__, strerror(errno));
return RIG_OK;
#else
return -RIG_ENIMPL;
#endif /* !HAVE_SIGACTION */
}
#ifdef HAVE_SIGACTION
/*
* add_trn_poll_rig
* not exported in Hamlib API.
*/
static int add_trn_poll_rig(RIG *rig)
{
#ifdef HAVE_SIGACTION
struct sigaction act;
int status;
/*
* FIXME: multiple open will register several time SIGALRM hndlr
*/
memset(&act, 0, sizeof(act));
#ifdef HAVE_SIGINFO_T
act.sa_sigaction = sa_sigalrmaction;
#else
act.sa_handler = sa_sigalrmhandler;
#endif
act.sa_flags = SA_RESTART;
sigemptyset(&act.sa_mask);
status = sigaction(SIGALRM, &act, &hamlib_trn_poll_oldact);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s sigaction failed: %s\n",
__func__,
strerror(errno));
return RIG_OK;
#else
return -RIG_ENIMPL;
#endif /* !HAVE_SIGINFO */
}
/*
* remove_trn_poll_rig
* not exported in Hamlib API.
*/
static int remove_trn_poll_rig(RIG *rig)
{
#ifdef HAVE_SIGINFO
int status;
status = sigaction(SIGALRM, &hamlib_trn_poll_oldact, NULL);
if (status < 0)
rig_debug(RIG_DEBUG_ERR,"%s sigaction failed: %s\n",
__func__,
strerror(errno));
return RIG_OK;
#else
return -RIG_ENIMPL;
#endif /* !HAVE_SIGINFO */
}
/*
* This is used by sa_sigio, the SIGIO handler
* to find out which rig generated this event,
* and decode/process it.
*
* assumes rig!=NULL
*/
static int search_rig_and_decode(RIG *rig, rig_ptr_t data)
{
fd_set rfds;
struct timeval tv;
int retval;
/*
* so far, only file oriented ports have event reporting support
*/
if (rig->state.rigport.type.rig != RIG_PORT_SERIAL ||
rig->state.rigport.fd == -1)
return -1;
/* FIXME: siginfo is not portable, however use it where available */
#if 0&&defined(HAVE_SIGINFO_T)
siginfo_t *si = (siginfo_t*)data;
if (rig->state.rigport.fd != si->si_fd)
return -1;
#else
FD_ZERO(&rfds);
FD_SET(rig->state.rigport.fd, &rfds);
/* Read status immediately. */
tv.tv_sec = 0;
tv.tv_usec = 0;
/* don't use FIONREAD to detect activity
* since it is less portable than select
* REM: EINTR possible with 0sec timeout? retval==0?
*/
retval = select(rig->state.rigport.fd+1, &rfds, NULL, NULL, &tv);
if (retval < 0) {
rig_debug(RIG_DEBUG_ERR, "search_rig_and_decode: select: %s\n",
strerror(errno));
return -1;
}
#endif
/*
* Do not disturb, the backend is currently receiving data
*/
if (rig->state.hold_decode)
return -1;
if (rig->caps->decode_event)
rig->caps->decode_event(rig);
return 1; /* process each opened rig */
}
/*
* This is used by sa_sigio, the SIGALRM handler
* to poll each RIG in RIG_TRN_POLL mode.
*
* assumes rig!=NULL
*/
static int search_rig_and_poll(RIG *rig, rig_ptr_t data)
{
struct rig_state *rs = &rig->state;
int retval;
if (rig->state.transceive != RIG_TRN_POLL)
return -1;
/*
* Do not disturb, the backend is currently receiving data
*/
if (rig->state.hold_decode)
return -1;
rig->state.hold_decode = 2;
if (rig->caps->get_vfo && rig->callbacks.vfo_event) {
vfo_t vfo = RIG_VFO_CURR;
retval = rig->caps->get_vfo(rig, &vfo);
if (retval == RIG_OK) {
if (vfo != rs->current_vfo) {
rig->callbacks.vfo_event(rig, vfo, rig->callbacks.vfo_arg);
}
rs->current_vfo = vfo;
}
}
if (rig->caps->get_freq && rig->callbacks.freq_event) {
freq_t freq;
retval = rig->caps->get_freq(rig, RIG_VFO_CURR, &freq);
if (retval == RIG_OK) {
if (freq != rs->current_freq) {
rig->callbacks.freq_event(rig, RIG_VFO_CURR,
freq, rig->callbacks.freq_arg);
}
rs->current_freq = freq;
}
}
if (rig->caps->get_mode && rig->callbacks.mode_event) {
rmode_t rmode;
pbwidth_t width;
retval = rig->caps->get_mode(rig, RIG_VFO_CURR, &rmode, &width);
if (retval == RIG_OK) {
if (rmode != rs->current_mode || width != rs->current_width) {
rig->callbacks.mode_event(rig, RIG_VFO_CURR,
rmode, width, rig->callbacks.mode_arg);
}
rs->current_mode = rmode;
rs->current_width = width;
}
}
rig->state.hold_decode = 0;
return 1; /* process each opened rig */
}
/*
* This is the SIGIO handler
*
* lookup in the list of open rigs,
* check the rig is not holding SIGIO,
* then call rig->caps->decode_event() (this is done by search_rig)
*/
#ifdef HAVE_SIGINFO_T
static RETSIGTYPE sa_sigioaction(int signum, siginfo_t *si, rig_ptr_t data)
{
rig_debug(RIG_DEBUG_VERBOSE, "sa_sigioaction: activity detected\n");
foreach_opened_rig(search_rig_and_decode, si);
}
#else
static RETSIGTYPE sa_sigiohandler(int signum)
{
rig_debug(RIG_DEBUG_VERBOSE, "sa_sigiohandler: activity detected\n");
foreach_opened_rig(search_rig_and_decode, NULL);
}
#endif
/*
* This is the SIGALRM handler
*
* lookup in the list of open rigs,
* check the rig is not holding SIGALRM,
* then call get_freq and check for changes (this is done by search_rig)
*/
#ifdef HAVE_SIGINFO_T
static RETSIGTYPE sa_sigalrmaction(int signum, siginfo_t *si, rig_ptr_t data)
{
rig_debug(RIG_DEBUG_TRACE, "sa_sigalrmaction entered\n");
foreach_opened_rig(search_rig_and_poll, si);
}
#else
static RETSIGTYPE sa_sigalrmhandler(int signum)
{
rig_debug(RIG_DEBUG_TRACE, "sa_sigalrmhandler entered\n");
foreach_opened_rig(search_rig_and_poll, NULL);
}
#endif /* !HAVE_SIGINFO_T */
#endif /* HAVE_SIGINFO */
#endif /* !DOC_HIDDEN */
/**
* \brief set the callback for freq events
* \param rig The rig handle
* \param cb The callback to install
* \param arg A Pointer to some private data to pass later on to the callback
*
* Install a callback for freq events, to be called when in transceive mode.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_set_freq_callback(RIG *rig, freq_cb_t cb, rig_ptr_t arg)
{
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
rig->callbacks.freq_event = cb;
rig->callbacks.freq_arg = arg;
return RIG_OK;
}
/**
* \brief set the callback for mode events
* \param rig The rig handle
* \param cb The callback to install
* \param arg A Pointer to some private data to pass later on to the callback
*
* Install a callback for mode events, to be called when in transceive mode.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_set_mode_callback(RIG *rig, mode_cb_t cb, rig_ptr_t arg)
{
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
rig->callbacks.mode_event = cb;
rig->callbacks.mode_arg = arg;
return RIG_OK;
}
/**
* \brief set the callback for vfo events
* \param rig The rig handle
* \param cb The callback to install
* \param arg A Pointer to some private data to pass later on to the callback
*
* Install a callback for vfo events, to be called when in transceive mode.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_set_vfo_callback(RIG *rig, vfo_cb_t cb, rig_ptr_t arg)
{
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
rig->callbacks.vfo_event = cb;
rig->callbacks.vfo_arg = arg;
return RIG_OK;
}
/**
* \brief set the callback for ptt events
* \param rig The rig handle
* \param cb The callback to install
* \param arg A Pointer to some private data to pass later on to the callback
*
* Install a callback for ptt events, to be called when in transceive mode.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_set_ptt_callback(RIG *rig, ptt_cb_t cb, rig_ptr_t arg)
{
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
rig->callbacks.ptt_event = cb;
rig->callbacks.ptt_arg = arg;
return RIG_OK;
}
/**
* \brief set the callback for dcd events
* \param rig The rig handle
* \param cb The callback to install
* \param arg A Pointer to some private data to pass later on to the callback
*
* Install a callback for dcd events, to be called when in transceive mode.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_set_dcd_callback(RIG *rig, dcd_cb_t cb, rig_ptr_t arg)
{
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
rig->callbacks.dcd_event = cb;
rig->callbacks.dcd_arg = arg;
return RIG_OK;
}
/**
* \brief set the callback for pipelined tuning module
* \param rig The rig handle
* \param cb The callback to install
* \param arg A Pointer to some private data to pass later on to the callback
* used to maintain state during pipelined tuning.
*
* Install a callback for pipelined tuning module, to be called when the
* rig_scan( SCAN_PLT ) loop needs a new frequency, mode and width.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_set_pltune_callback(RIG *rig, pltune_cb_t cb, rig_ptr_t arg)
{
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
rig->callbacks.pltune = cb;
rig->callbacks.pltune_arg = arg;
return RIG_OK;
}
/**
* \brief control the transceive mode
* \param rig The rig handle
* \param trn The transceive status to set to
*
* Enable/disable the transceive handling of a rig and kick off async mode.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_get_trn()
*/
int HAMLIB_API rig_set_trn(RIG *rig, int trn)
{
const struct rig_caps *caps;
int retcode = RIG_OK;
#ifdef HAVE_SETITIMER
struct itimerval value;
#endif
if (CHECK_RIG_ARG(rig))
return -RIG_EINVAL;
caps = rig->caps;
/* detect whether tranceive is active already */
if (trn != RIG_TRN_OFF && rig->state.transceive != RIG_TRN_OFF) {
if (trn == rig->state.transceive) {
return RIG_OK;
} else {
/* when going POLL<->RIG, transtition to OFF */
retcode = rig_set_trn(rig, RIG_TRN_OFF);
if (retcode != RIG_OK)
return retcode;
}
}
switch (trn) {
case RIG_TRN_RIG:
if (caps->transceive != RIG_TRN_RIG)
return -RIG_ENAVAIL;
retcode = add_trn_rig(rig);
/* some protocols (e.g. CI-V's) offer no way
* to turn on/off the transceive mode */
if (retcode == RIG_OK && caps->set_trn) {
retcode = caps->set_trn(rig, RIG_TRN_RIG);
}
break;
case RIG_TRN_POLL:
#ifdef HAVE_SETITIMER
add_trn_poll_rig(rig);
/* install handler here */
value.it_value.tv_sec = 0;
value.it_value.tv_usec = rig->state.poll_interval*1000;
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = rig->state.poll_interval*1000;
retcode = setitimer(ITIMER_REAL, &value, NULL);
if (retcode == -1) {
rig_debug(RIG_DEBUG_ERR, "%s: setitimer: %s\n",
__func__,
strerror(errno));
remove_trn_poll_rig(rig);
return -RIG_EINTERNAL;
}
#else
return -RIG_ENAVAIL;
#endif
break;
case RIG_TRN_OFF:
if (rig->state.transceive == RIG_TRN_POLL) {
#ifdef HAVE_SETITIMER
retcode = remove_trn_poll_rig(rig);
value.it_value.tv_sec = 0;
value.it_value.tv_usec = 0;
value.it_interval.tv_sec = 0;
value.it_interval.tv_usec = 0;
retcode = setitimer(ITIMER_REAL, &value, NULL);
if (retcode == -1) {
rig_debug(RIG_DEBUG_ERR, "%s: setitimer: %s\n",
__func__,
strerror(errno));
return -RIG_EINTERNAL;
}
#else
return -RIG_ENAVAIL;
#endif
} else if (rig->state.transceive == RIG_TRN_RIG) {
retcode = remove_trn_rig(rig);
if (caps->set_trn && caps->transceive == RIG_TRN_RIG) {
retcode = caps->set_trn(rig, RIG_TRN_OFF);
}
}
break;
default:
return -RIG_EINVAL;
}
if (retcode == RIG_OK)
rig->state.transceive = trn;
return retcode;
}
/**
* \brief get the current transceive mode
* \param rig The rig handle
* \param trn The location where to store the current transceive mode
*
* Retrieves the current status of the transceive mode, i.e. if radio
* sends new status automatically when some changes happened on the radio.
*
* \return RIG_OK if the operation has been sucessful, otherwise
* a negative value if an error occured (in which case, cause is
* set appropriately).
*
* \sa rig_set_trn()
*/
int HAMLIB_API rig_get_trn(RIG *rig, int *trn)
{
if (CHECK_RIG_ARG(rig) || !trn)
return -RIG_EINVAL;
if (rig->caps->get_trn != NULL)
return rig->caps->get_trn(rig, trn);
*trn = rig->state.transceive;
return RIG_OK;
}
/** @} */