Hamlib/src/serial.c

1083 wiersze
25 KiB
C
Czysty Zwykły widok Historia

/*
* Hamlib Interface - serial communication low-level support
* Copyright (c) 2000-2013 by Stephane Fillod
* Copyright (c) 2000-2003 by Frank Singleton
* Parts of the PTT handling are derived from soundmodem, an excellent
* ham packet softmodem written by Thomas Sailer, HB9JNX.
*
*
* 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
*
*/
/**
* \addtogroup rig_internal
* @{
*/
/**
* \brief Serial port IO
* \file serial.c
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
2020-03-15 20:32:46 +00:00
#include <ctype.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#ifdef HAVE_SYS_PARAM_H
# include <sys/param.h>
#endif
#ifdef HAVE_TERMIOS_H
# include <termios.h> /* POSIX terminal control definitions */
#else
# ifdef HAVE_TERMIO_H
# include <termio.h>
# else /* sgtty */
# ifdef HAVE_SGTTY_H
# include <sgtty.h>
# endif
# endif
#endif
2020-04-09 22:56:19 +00:00
//! @cond Doxygen_Suppress
#if defined(WIN32) && !defined(HAVE_TERMIOS_H)
# include "win32termios.h"
# define HAVE_TERMIOS_H 1 /* we have replacement */
#else
# define OPEN open
# define CLOSE close
# define IOCTL ioctl
#endif
2020-04-09 22:56:19 +00:00
//! @endcond
#include <hamlib/rig.h>
#include "serial.h"
#include "misc.h"
#ifdef HAVE_SYS_IOCCOM_H
# include <sys/ioccom.h>
#endif
#include "microham.h"
2017-08-05 14:09:12 +00:00
static int uh_ptt_fd = -1;
static int uh_radio_fd = -1;
2020-04-09 22:56:19 +00:00
//! @cond Doxygen_Suppress
2019-11-30 16:19:08 +00:00
typedef struct term_options_backup
{
int fd;
#if defined(HAVE_TERMIOS_H)
struct termios options;
#elif defined(HAVE_TERMIO_H)
struct termio options;
#elif defined(HAVE_SGTTY_H)
struct sgttyb sg;
#endif
struct term_options_backup *next;
} term_options_backup_t;
2020-04-09 22:56:19 +00:00
//! @endcond
static term_options_backup_t *term_options_backup_head = NULL;
/*
* This function simply returns TRUE if the argument matches uh_radio_fd and
* is >= 0
*
* This function is only used in the WIN32 case and implements access "from
* outside" to uh_radio_fd.
*/
2020-04-09 22:56:19 +00:00
//! @cond Doxygen_Suppress
int is_uh_radio_fd(int fd)
{
2019-11-30 16:19:08 +00:00
if (uh_radio_fd >= 0 && uh_radio_fd == fd)
{
2021-02-27 05:38:15 +00:00
return 1;
2019-11-30 16:19:08 +00:00
}
else
{
2021-02-27 05:38:15 +00:00
return 0;
2019-11-30 16:19:08 +00:00
}
}
2020-04-09 22:56:19 +00:00
//! @endcond
/**
* \brief Open serial port using rig.state data
* \param rp port data structure (must spec port id eg /dev/ttyS1)
* \return RIG_OK or < 0 if error
*/
2017-08-05 14:09:12 +00:00
int HAMLIB_API serial_open(hamlib_port_t *rp)
{
2017-08-05 14:09:12 +00:00
int fd; /* File descriptor for the port */
int err;
ENTERFUNC;
if (!rp)
{
RETURNFUNC(-RIG_EINVAL);
2017-08-05 14:09:12 +00:00
}
rig_debug(RIG_DEBUG_VERBOSE, "%s: %s\n", __func__, rp->pathname);
2021-02-28 15:46:01 +00:00
2019-11-30 16:19:08 +00:00
if (!strncmp(rp->pathname, "uh-rig", 6))
{
/*
* If the pathname is EXACTLY "uh-rig", try to use a microHam device
* rather than a conventional serial port.
* The microHam devices ALWAYS use "no parity", and can either use no handshake
* or hardware handshake. Return with error if something else is requested.
*/
2019-11-30 16:19:08 +00:00
if (rp->parm.serial.parity != RIG_PARITY_NONE)
{
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EIO);
}
2019-11-30 16:19:08 +00:00
if ((rp->parm.serial.handshake != RIG_HANDSHAKE_HARDWARE) &&
2019-11-30 16:19:08 +00:00
(rp->parm.serial.handshake != RIG_HANDSHAKE_NONE))
{
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EIO);
}
2019-11-30 16:19:08 +00:00
/*
* Note that serial setup is also don in uh_open_radio.
* So we need to dig into serial_setup().
*/
2019-11-30 16:19:08 +00:00
fd = uh_open_radio(
rp->parm.serial.rate, // baud
rp->parm.serial.data_bits, // databits
rp->parm.serial.stop_bits, // stopbits
(rp->parm.serial.handshake == RIG_HANDSHAKE_HARDWARE)); // rtscts
if (fd == -1)
{
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EIO);
}
2019-11-30 16:19:08 +00:00
rp->fd = fd;
/*
* Remember the fd in a global variable. We can do read(), write() and select()
* on fd but whenever it is tried to do an ioctl(), we have to catch it
* (e.g. setting DTR or tcflush on this fd does not work)
* While this may look dirty, it is certainly easier and more efficient than
* to check whether fd corresponds to a serial line or a socket everywhere.
*
* CAVEAT: for WIN32, it might be necessary to use win_serial_read() instead
* of read() for serial lines in iofunc.c. Therefore, we have to
* export uh_radio_fd to iofunc.c because in the case of sockets,
* read() must be used also in the WIN32 case.
* This is why uh_radio_fd is declared globally in microham.h.
2020-11-29 04:34:15 +00:00
* Notes from Joe Subich about microham behavior
* Microham debug tags
* A-RX ; Asynchronous data received (data not responsive to any
* poll from host or Router) - Set AI0;
* H2-RX; Data received in response to poll from Host 2 poll (2nd CAT port).
* H2-TX; Data poll/command from Host 2 (2nd CAT Port)
* R-RX; Data received in response to poll by microHAM USB Device Router
* R-TX; Router poll
* Note: R-TX; and R-RX; data is not passed to Host ports.
* 1) Router only polls when it has not seen a poll for FA; FB; and IF;
* (or equivalent for other manufacturers) within its timeout period.
* 2) The results of router's polling are not passed to the Host/apps.
* 3) Router only polls when there is no activity from the applications.
2020-11-30 20:53:55 +00:00
* 4) Router is designed to be transparent as far as the applications
2020-11-29 04:34:15 +00:00
* are concerned. The only exception is when the user chooses to
* run two applications (CAT and 2nd CAT) at the same time and has
* "auto-information" or CI-V enabled. In that case asynchronous data
* from the transceiver will be returned to both applications.
*/
2019-11-30 16:19:08 +00:00
uh_radio_fd = fd;
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
2019-11-30 16:19:08 +00:00
2017-08-05 14:09:12 +00:00
/*
* Open in Non-blocking mode. Watch for EAGAIN errors!
*/
fd = OPEN(rp->pathname, O_RDWR | O_NOCTTY | O_NDELAY);
2020-03-15 20:32:46 +00:00
if (fd == -1)
{
2017-08-05 14:09:12 +00:00
/* Could not open the port. */
rig_debug(RIG_DEBUG_ERR,
"%s: Unable to open %s - %s\n",
__func__,
rp->pathname,
strerror(errno));
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EIO);
2017-08-05 14:09:12 +00:00
}
2017-08-05 14:09:12 +00:00
rp->fd = fd;
2017-08-05 14:09:12 +00:00
err = serial_setup(rp);
if (err != RIG_OK)
{
2017-08-05 14:09:12 +00:00
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(err);
2017-08-05 14:09:12 +00:00
}
2019-11-30 16:19:08 +00:00
2019-04-02 13:49:56 +00:00
serial_flush(rp); // ensure nothing is there when we open
hl_usleep(50 * 1000); // give a little time for MicroKeyer to finish
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Set up Serial port according to requests in port
* \param rp
* \return RIG_OK or < 0
*/
int HAMLIB_API serial_setup(hamlib_port_t *rp)
{
2017-08-05 14:09:12 +00:00
int fd;
/* There's a lib replacement for termios under Mingw */
#if defined(HAVE_TERMIOS_H)
2017-08-05 14:09:12 +00:00
speed_t speed; /* serial comm speed */
struct termios options, orig_options;
#elif defined(HAVE_TERMIO_H)
struct termio options, orig_options;
#elif defined(HAVE_SGTTY_H)
struct sgttyb sg, orig_sg;
#else
# error "No term control supported!"
#endif
term_options_backup_t *term_backup = NULL;
ENTERFUNC;
if (!rp)
{
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EINVAL);
2017-08-05 14:09:12 +00:00
}
2017-08-05 14:09:12 +00:00
fd = rp->fd;
2017-08-05 14:09:12 +00:00
/*
* Get the current options for the port...
*/
#if defined(HAVE_TERMIOS_H)
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: tcgetattr\n", __func__);
2017-08-05 14:09:12 +00:00
tcgetattr(fd, &options);
memcpy(&orig_options, &options, sizeof(orig_options));
#elif defined(HAVE_TERMIO_H)
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TCGETA\n", __func__);
2017-08-05 14:09:12 +00:00
IOCTL(fd, TCGETA, &options);
memcpy(&orig_options, &options, sizeof(orig_options));
2017-08-05 14:09:12 +00:00
#else /* sgtty */
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TIOCGETP\n", __func__);
2017-08-05 14:09:12 +00:00
IOCTL(fd, TIOCGETP, &sg);
memcpy(&orig_sg, &sg, sizeof(orig_sg));
#endif
#ifdef HAVE_CFMAKERAW
2017-08-05 14:09:12 +00:00
/* Set serial port to RAW mode by default. */
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: cfmakeraw\n", __func__);
2017-08-05 14:09:12 +00:00
cfmakeraw(&options);
#endif
2017-08-05 14:09:12 +00:00
/*
* Set the baud rates to requested values
*/
switch (rp->parm.serial.rate)
{
2017-08-05 14:09:12 +00:00
case 150:
speed = B150; /* yikes... */
break;
case 300:
speed = B300; /* yikes... */
break;
case 600:
speed = B600;
2017-08-05 14:09:12 +00:00
break;
2017-08-05 14:09:12 +00:00
case 1200:
speed = B1200;
break;
2017-08-05 14:09:12 +00:00
case 2400:
speed = B2400;
break;
2017-08-05 14:09:12 +00:00
case 4800:
speed = B4800;
break;
2017-08-05 14:09:12 +00:00
case 9600:
speed = B9600;
break;
2017-08-05 14:09:12 +00:00
case 19200:
speed = B19200;
break;
case 38400:
speed = B38400;
break;
2017-08-05 14:09:12 +00:00
case 57600:
speed = B57600; /* cool.. */
break;
case 115200:
speed = B115200; /* awesome! */
break;
2020-08-11 03:46:08 +00:00
#ifdef B230400
2020-08-11 03:46:08 +00:00
case 230400:
speed = B230400; /* super awesome! */
break;
#endif
2017-08-05 14:09:12 +00:00
default:
rig_debug(RIG_DEBUG_ERR,
"%s: unsupported rate specified: %d\n",
__func__,
rp->parm.serial.rate);
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF);
2017-08-05 14:09:12 +00:00
}
/* TODO */
2020-08-11 03:36:55 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: cfsetispeed=%d,0x%04x\n", __func__,
2020-11-18 22:31:09 +00:00
(int)rp->parm.serial.rate, (int)speed);
2017-08-05 14:09:12 +00:00
cfsetispeed(&options, speed);
2020-08-11 03:36:55 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: cfsetospeed=%d,0x%04x\n", __func__,
2020-11-18 22:31:09 +00:00
(int)rp->parm.serial.rate, (int)speed);
2017-08-05 14:09:12 +00:00
cfsetospeed(&options, speed);
/*
* Enable the receiver and set local mode...
*/
options.c_cflag |= (CLOCAL | CREAD);
/*
* close doesn't change modem signals
*/
options.c_cflag &= ~HUPCL;
/*
* Set data to requested values.
*
*/
2020-08-11 03:26:25 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: data_bits=%d\n", __func__,
rp->parm.serial.data_bits);
switch (rp->parm.serial.data_bits)
{
2017-08-05 14:09:12 +00:00
case 7:
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS7;
break;
case 8:
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
break;
default:
rig_debug(RIG_DEBUG_ERR,
"%s: unsupported serial_data_bits specified: %d\n",
__func__,
rp->parm.serial.data_bits);
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF);
2017-08-05 14:09:12 +00:00
break;
}
/*
* Set stop bits to requested values.
*
*/
switch (rp->parm.serial.stop_bits)
{
2017-08-05 14:09:12 +00:00
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
rig_debug(RIG_DEBUG_ERR,
"%s: unsupported serial_stop_bits specified: %d\n",
__func__,
rp->parm.serial.stop_bits);
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF);
2017-08-05 14:09:12 +00:00
break;
}
/*
* Set parity to requested values.
*
*/
2020-08-11 03:26:25 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: parity=%d\n", __func__, rp->parm.serial.parity);
switch (rp->parm.serial.parity)
{
2017-08-05 14:09:12 +00:00
case RIG_PARITY_NONE:
options.c_cflag &= ~PARENB;
break;
case RIG_PARITY_EVEN:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
break;
case RIG_PARITY_ODD:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
break;
/* CMSPAR is not POSIX */
#ifdef CMSPAR
2017-08-05 14:09:12 +00:00
case RIG_PARITY_MARK:
options.c_cflag |= PARENB | CMSPAR;
options.c_cflag |= PARODD;
break;
case RIG_PARITY_SPACE:
options.c_cflag |= PARENB | CMSPAR;
options.c_cflag &= ~PARODD;
break;
#endif
2017-08-05 14:09:12 +00:00
default:
rig_debug(RIG_DEBUG_ERR,
"%s: unsupported serial_parity specified: %d\n",
__func__,
rp->parm.serial.parity);
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF);
2017-08-05 14:09:12 +00:00
break;
}
2017-08-05 14:09:12 +00:00
/*
* Set flow control to requested mode
*
*/
switch (rp->parm.serial.handshake)
{
2017-08-05 14:09:12 +00:00
case RIG_HANDSHAKE_NONE:
options.c_cflag &= ~CRTSCTS;
options.c_iflag &= ~IXON;
break;
case RIG_HANDSHAKE_XONXOFF:
options.c_cflag &= ~CRTSCTS;
options.c_iflag |= IXON; /* Enable Xon/Xoff software handshaking */
break;
case RIG_HANDSHAKE_HARDWARE:
options.c_cflag |= CRTSCTS; /* Enable Hardware handshaking */
options.c_iflag &= ~IXON;
break;
default:
rig_debug(RIG_DEBUG_ERR,
"%s: unsupported flow_control specified: %d\n",
__func__,
rp->parm.serial.handshake);
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF);
2017-08-05 14:09:12 +00:00
break;
}
2017-08-05 14:09:12 +00:00
/*
* Choose raw input, no preprocessing please ..
*/
#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
2017-08-05 14:09:12 +00:00
/*
* Choose raw output, no preprocessing please ..
*/
options.c_oflag &= ~OPOST;
2017-08-05 14:09:12 +00:00
#else /* sgtty */
sg.sg_flags = RAW;
#endif
2017-08-05 14:09:12 +00:00
/*
* VTIME in deciseconds, rp->timeout in milliseconds
2017-08-05 14:09:12 +00:00
*/
options.c_cc[VTIME] = (rp->timeout + 99) / 100;
options.c_cc[VMIN] = 1;
2017-08-05 14:09:12 +00:00
/*
* Flush serial port
*/
tcflush(fd, TCIFLUSH);
2017-08-05 14:09:12 +00:00
/*
* Finally, set the new options for the port...
*/
#if defined(HAVE_TERMIOS_H)
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: tcsetattr TCSANOW\n", __func__);
if (tcsetattr(fd, TCSANOW, &options) == -1)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: tcsetattr failed: %s\n",
__func__,
strerror(errno));
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF); /* arg, so close! */
2017-08-05 14:09:12 +00:00
}
#elif defined(HAVE_TERMIO_H)
2017-08-05 14:09:12 +00:00
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TCSETA\n", __func__);
if (IOCTL(fd, TCSETA, &options) == -1)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: ioctl(TCSETA) failed: %s\n",
__func__,
strerror(errno));
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF); /* arg, so close! */
2017-08-05 14:09:12 +00:00
}
#else
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TIOCSETP\n", __func__);
2017-08-05 14:09:12 +00:00
/* sgtty */
if (IOCTL(fd, TIOCSETP, &sg) == -1)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: ioctl(TIOCSETP) failed: %s\n",
__func__,
strerror(errno));
CLOSE(fd);
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_ECONF); /* arg, so close! */
2017-08-05 14:09:12 +00:00
}
#endif
// Store a copy of the original options for this FD, to be restored on close.
term_backup = malloc(sizeof(term_options_backup_t));
term_backup-> fd = fd;
#if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H)
memcpy(&term_backup->options, &orig_options, sizeof(orig_options));
#elif defined(HAVE_SGTTY_H)
memcpy(&term_backup->sg, &orig_sg, sizeof(orig_sg));
#endif
// insert at head of list
term_backup->next = term_options_backup_head;
term_options_backup_head = term_backup;
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
/**
* \brief Flush all characters waiting in RX buffer.
* \param p
* \return RIG_OK
*/
2017-08-05 14:09:12 +00:00
int HAMLIB_API serial_flush(hamlib_port_t *p)
{
ENTERFUNC;
2020-03-15 20:32:46 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd || p->flushx)
2020-03-16 03:40:40 +00:00
{
unsigned char buf[32];
/*
* Catch microHam case:
* if fd corresponds to a microHam device drain the line
* (which is a socket) by reading until it is empty.
*/
2020-11-30 20:53:55 +00:00
int n, nbytes = 0;
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: flushing\n", __func__);
2017-08-05 14:09:12 +00:00
2020-03-16 03:40:40 +00:00
while ((n = read(p->fd, buf, 32)) > 0)
{
nbytes += n;
2020-03-16 03:40:40 +00:00
//int i;
2019-11-30 16:19:08 +00:00
2020-03-16 03:40:40 +00:00
//for (i = 0; i < n; ++i) { printf("0x%02x(%c) ", buf[i], isprint(buf[i]) ? buf[i] : '~'); }
2019-11-30 16:19:08 +00:00
2020-03-16 03:40:40 +00:00
/* do nothing */
}
2020-11-30 20:53:55 +00:00
rig_debug(RIG_DEBUG_TRACE, "read flushed %d bytes\n", nbytes);
2019-11-30 16:19:08 +00:00
2020-03-15 20:32:46 +00:00
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
2020-03-16 03:40:40 +00:00
}
2020-03-15 20:32:46 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "tcflush%s\n", "");
2017-08-05 14:09:12 +00:00
tcflush(p->fd, TCIFLUSH);
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Open serial port
* \param p
* \return fd
*/
int ser_open(hamlib_port_t *p)
{
int ret;
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
2019-11-30 16:19:08 +00:00
if (!strncmp(p->pathname, "uh-rig", 6))
{
/*
* This should not happen: ser_open is only used for
* DTR-only serial ports (ptt_pathname != rig_pathname).
*/
2019-11-30 16:19:08 +00:00
ret = -1;
}
else
{
if (!strncmp(p->pathname, "uh-ptt", 6))
{
/*
* Use microHam device for doing PTT. Although a valid file
* descriptor is returned, it is not used for anything
* but must be remembered in a global variable:
* If it is tried later to set/unset DTR on this fd, we know
* that we cannot use ioctl and must rather call our
* PTT set/unset service routine.
*/
2019-11-30 16:19:08 +00:00
ret = uh_open_ptt();
uh_ptt_fd = ret;
}
else
{
/*
* pathname is not uh_rig or uh_ptt: simply open()
*/
2020-09-30 17:57:31 +00:00
ret = OPEN(p->pathname, O_RDWR | O_NOCTTY | O_NDELAY);
}
}
2019-11-30 16:19:08 +00:00
p->fd = ret;
2021-02-28 15:46:01 +00:00
RETURNFUNC(ret);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Close serial port
* \param p fd
* \return RIG_OK or < 0
*/
int ser_close(hamlib_port_t *p)
{
int rc;
term_options_backup_t *term_backup, *term_backup_prev;
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__);
/*
* For microHam devices, do not close the
* socket via close but call a service routine
* (which might decide to keep the socket open).
* However, unset p->fd and uh_ptt_fd/uh_radio_fd.
*/
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd)
{
uh_close_ptt();
2019-11-30 16:19:08 +00:00
uh_ptt_fd = -1;
p->fd = -1;
2021-02-28 15:46:01 +00:00
RETURNFUNC(0);
}
2019-11-30 16:19:08 +00:00
if (p->fd == uh_radio_fd)
{
uh_close_radio();
2019-11-30 16:19:08 +00:00
uh_radio_fd = -1;
p->fd = -1;
2021-02-28 15:46:01 +00:00
RETURNFUNC(0);
}
// Find backup termios options to restore before closing
term_backup = term_options_backup_head;
term_backup_prev = term_options_backup_head;
2019-11-30 16:19:08 +00:00
while (term_backup)
{
if (term_backup->fd == p->fd)
{
// Found matching. Remove from list
2019-11-30 16:19:08 +00:00
if (term_backup == term_options_backup_head)
{
term_options_backup_head = term_backup->next;
2019-11-30 16:19:08 +00:00
}
else
{
term_backup_prev->next = term_backup->next;
}
2019-11-30 16:19:08 +00:00
break;
}
2019-11-30 16:19:08 +00:00
term_backup_prev = term_backup;
term_backup = term_backup->next;
}
// Restore backup termios
2019-11-30 16:19:08 +00:00
if (term_backup)
{
rig_debug(RIG_DEBUG_VERBOSE, "%s: restoring options\n", __func__);
#if defined(HAVE_TERMIOS_H)
if (tcsetattr(p->fd, TCSANOW, &term_backup->options) == -1)
{
rig_debug(RIG_DEBUG_ERR,
2019-11-30 16:19:08 +00:00
"%s: tcsetattr restore failed: %s\n",
__func__,
strerror(errno));
}
#elif defined(HAVE_TERMIO_H)
if (IOCTL(p->fd, TCSETA, &term_backup->options) == -1)
{
rig_debug(RIG_DEBUG_ERR,
2019-11-30 16:19:08 +00:00
"%s: ioctl(TCSETA) restore failed: %s\n",
__func__,
strerror(errno));
}
#else
/* sgtty */
if (IOCTL(p->fd, TIOCSETP, &term_backup->sg) == -1)
{
rig_debug(RIG_DEBUG_ERR,
2019-11-30 16:19:08 +00:00
"%s: ioctl(TIOCSETP) restore failed: %s\n",
__func__,
strerror(errno));
}
2019-11-30 16:19:08 +00:00
#endif
free(term_backup);
2019-11-30 16:19:08 +00:00
}
else
{
2021-01-22 14:26:03 +00:00
rig_debug(RIG_DEBUG_TRACE, "%s: no options for fd to restore\n", __func__);
}
rc = CLOSE(p->fd);
2017-08-05 14:09:12 +00:00
p->fd = -1;
2021-02-28 15:46:01 +00:00
RETURNFUNC(rc);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Set Request to Send (RTS) bit
* \param p
* \param state true/false
* \return RIG_OK or < 0
*/
int HAMLIB_API ser_set_rts(hamlib_port_t *p, int state)
{
2017-08-05 14:09:12 +00:00
unsigned int y = TIOCM_RTS;
int rc;
ENTERFUNC;
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s: RTS=%d\n", __func__, state);
// ignore this for microHam ports
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd)
{
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
#if defined(TIOCMBIS) && defined(TIOCMBIC)
2017-08-05 14:09:12 +00:00
rc = IOCTL(p->fd, state ? TIOCMBIS : TIOCMBIC, &y);
#else
2017-08-05 14:09:12 +00:00
rc = IOCTL(p->fd, TIOCMGET, &y);
if (rc >= 0)
{
if (state)
{
2017-08-05 14:09:12 +00:00
y |= TIOCM_RTS;
}
else
{
2017-08-05 14:09:12 +00:00
y &= ~TIOCM_RTS;
}
rc = IOCTL(p->fd, TIOCMSET, &y);
}
2017-08-05 14:09:12 +00:00
#endif
2017-08-05 14:09:12 +00:00
if (rc < 0)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: Cannot change RTS - %s\n",
__func__,
strerror(errno));
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EIO);
}
2017-08-05 14:09:12 +00:00
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Get RTS bit
* \param p supposed to be &rig->state.rigport
* \param state non-NULL
*/
int HAMLIB_API ser_get_rts(hamlib_port_t *p, int *state)
{
2017-08-05 14:09:12 +00:00
int retcode;
unsigned int y;
// cannot do this for microHam ports
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd)
{
return (-RIG_ENIMPL);
}
2017-08-05 14:09:12 +00:00
retcode = IOCTL(p->fd, TIOCMGET, &y);
*state = (y & TIOCM_RTS) == TIOCM_RTS;
return (retcode < 0 ? -RIG_EIO : RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Set Data Terminal Ready (DTR) bit
* \param p
* \param state true/false
* \return RIG_OK or < 0
*/
int HAMLIB_API ser_set_dtr(hamlib_port_t *p, int state)
{
2017-08-05 14:09:12 +00:00
unsigned int y = TIOCM_DTR;
int rc;
ENTERFUNC;
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_VERBOSE, "%s: DTR=%d\n", __func__, state);
// silently ignore on microHam RADIO channel,
// but (un)set ptt on microHam PTT channel.
2019-11-30 16:19:08 +00:00
if (p->fd == uh_radio_fd)
{
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd)
{
uh_set_ptt(state);
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
#if defined(TIOCMBIS) && defined(TIOCMBIC)
2017-08-05 14:09:12 +00:00
rc = IOCTL(p->fd, state ? TIOCMBIS : TIOCMBIC, &y);
#else
2017-08-05 14:09:12 +00:00
rc = IOCTL(p->fd, TIOCMGET, &y);
if (rc >= 0)
{
if (state)
{
2017-08-05 14:09:12 +00:00
y |= TIOCM_DTR;
}
else
{
2017-08-05 14:09:12 +00:00
y &= ~TIOCM_DTR;
}
rc = IOCTL(p->fd, TIOCMSET, &y);
}
2017-08-05 14:09:12 +00:00
#endif
2017-08-05 14:09:12 +00:00
if (rc < 0)
{
2017-08-05 14:09:12 +00:00
rig_debug(RIG_DEBUG_ERR,
"%s: Cannot change DTR - %s\n",
__func__,
strerror(errno));
2021-02-28 15:46:01 +00:00
RETURNFUNC(-RIG_EIO);
}
2017-08-05 14:09:12 +00:00
2021-02-28 15:46:01 +00:00
RETURNFUNC(RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Get DTR bit
* \param p supposed to be &rig->state.rigport
* \param state non-NULL
*/
int HAMLIB_API ser_get_dtr(hamlib_port_t *p, int *state)
{
2017-08-05 14:09:12 +00:00
int retcode;
unsigned int y;
// cannot do this for the RADIO port, return PTT state for the PTT port
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd)
{
*state = uh_get_ptt();
return (RIG_OK);
}
2019-11-30 16:19:08 +00:00
if (p->fd == uh_radio_fd)
{
return (-RIG_ENIMPL);
}
2017-08-05 14:09:12 +00:00
retcode = IOCTL(p->fd, TIOCMGET, &y);
*state = (y & TIOCM_DTR) == TIOCM_DTR;
return (retcode < 0 ? -RIG_EIO : RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Set Break
* \param p
* \param state (ignored?)
* \return RIG_OK or < 0
*/
int HAMLIB_API ser_set_brk(hamlib_port_t *p, int state)
{
// ignore this for microHam ports
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd)
{
return (RIG_OK);
}
#if defined(TIOCSBRK) && defined(TIOCCBRK)
return (IOCTL(p->fd, state ? TIOCSBRK : TIOCCBRK, 0) < 0 ?
-RIG_EIO : RIG_OK);
#else
return (-RIG_ENIMPL);
#endif
}
2017-08-05 14:09:12 +00:00
/**
* \brief Get Carrier (CI?) bit
* \param p supposed to be &rig->state.rigport
* \param state non-NULL
*/
int HAMLIB_API ser_get_car(hamlib_port_t *p, int *state)
{
2017-08-05 14:09:12 +00:00
int retcode;
unsigned int y;
// cannot do this for microHam ports
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd)
{
return (-RIG_ENIMPL);
}
2017-08-05 14:09:12 +00:00
retcode = IOCTL(p->fd, TIOCMGET, &y);
*state = (y & TIOCM_CAR) == TIOCM_CAR;
return (retcode < 0 ? -RIG_EIO : RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Get Clear to Send (CTS) bit
* \param p supposed to be &rig->state.rigport
* \param state non-NULL
*/
int HAMLIB_API ser_get_cts(hamlib_port_t *p, int *state)
{
2017-08-05 14:09:12 +00:00
int retcode;
unsigned int y;
// cannot do this for microHam ports
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd)
{
return (-RIG_ENIMPL);
}
2017-08-05 14:09:12 +00:00
retcode = IOCTL(p->fd, TIOCMGET, &y);
*state = (y & TIOCM_CTS) == TIOCM_CTS;
return (retcode < 0 ? -RIG_EIO : RIG_OK);
}
2017-08-05 14:09:12 +00:00
/**
* \brief Get Data Set Ready (DSR) bit
* \param p supposed to be &rig->state.rigport
* \param state non-NULL
*/
int HAMLIB_API ser_get_dsr(hamlib_port_t *p, int *state)
{
2017-08-05 14:09:12 +00:00
int retcode;
unsigned int y;
// cannot do this for microHam ports
2019-11-30 16:19:08 +00:00
if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd)
{
return (-RIG_ENIMPL);
}
2019-11-30 16:19:08 +00:00
2017-08-05 14:09:12 +00:00
retcode = IOCTL(p->fd, TIOCMGET, &y);
*state = (y & TIOCM_DSR) == TIOCM_DSR;
return (retcode < 0 ? -RIG_EIO : RIG_OK);
}
/** @} */