/* * 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 */ #include #include #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include #include #ifdef HAVE_SYS_IOCTL_H # include #endif #ifdef HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_TERMIOS_H # include /* POSIX terminal control definitions */ #else # ifdef HAVE_TERMIO_H # include # else /* sgtty */ # ifdef HAVE_SGTTY_H # include # endif # endif #endif #include //! @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 //! @endcond #include "serial.h" #include "misc.h" #ifdef HAVE_SYS_IOCCOM_H # include #endif #include "microham.h" static int uh_ptt_fd = -1; static int uh_radio_fd = -1; //! @cond Doxygen_Suppress 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; //! @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. */ //! @cond Doxygen_Suppress int is_uh_radio_fd(int fd) { if (uh_radio_fd >= 0 && uh_radio_fd == fd) { return 1; } else { return 0; } } //! @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 */ int HAMLIB_API serial_open(hamlib_port_t *rp) { int fd; /* File descriptor for the port */ int err; if (!rp) { return (-RIG_EINVAL); } rig_debug(RIG_DEBUG_VERBOSE, "%s: %s\n", __func__, rp->pathname); 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. */ if (rp->parm.serial.parity != RIG_PARITY_NONE) { return (-RIG_EIO); } if ((rp->parm.serial.handshake != RIG_HANDSHAKE_HARDWARE) && (rp->parm.serial.handshake != RIG_HANDSHAKE_NONE)) { return (-RIG_EIO); } /* * Note that serial setup is also don in uh_open_radio. * So we need to dig into serial_setup(). */ 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) { return (-RIG_EIO); } 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. * 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. * 4) Router is designed to be transparent as far as the applications * 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. */ uh_radio_fd = fd; return (RIG_OK); } /* * Open in Non-blocking mode. Watch for EAGAIN errors! */ int i = 1; do // some serial ports fail to open 1st time for some unknown reason { fd = OPEN(rp->pathname, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) // some serial ports fail to open 1st time for some unknown reason { rig_debug(RIG_DEBUG_WARN, "%s(%d): open failed#%d\n", __func__, __LINE__, i); hl_usleep(500 * 1000); fd = OPEN(rp->pathname, O_RDWR | O_NOCTTY | O_NDELAY); } } while (++i <= 4 && fd == -1 && errno != ENOENT && errno != EPERM); if (fd == -1) { /* Could not open the port. */ rig_debug(RIG_DEBUG_ERR, "%s: Unable to open %s - %s\n", __func__, rp->pathname, strerror(errno)); return (-RIG_EIO); } rp->fd = fd; err = serial_setup(rp); if (err != RIG_OK) { CLOSE(fd); return (err); } serial_flush(rp); // ensure nothing is there when we open hl_usleep(50 * 1000); // give a little time for MicroKeyer to finish return (RIG_OK); } /** * \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) { int fd; /* There's a lib replacement for termios under Mingw */ #if defined(HAVE_TERMIOS_H) 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; if (!rp) { return (-RIG_EINVAL); } fd = rp->fd; // Linux sets pins high so we force them low once // This fails on Linux and MacOS with hardware flow control // Seems setting low disables hardware flow setting later // ser_set_rts(rp, 0); // ser_set_dtr(rp, 0); /* * Get the current options for the port... */ #if defined(HAVE_TERMIOS_H) rig_debug(RIG_DEBUG_TRACE, "%s: tcgetattr\n", __func__); tcgetattr(fd, &options); memcpy(&orig_options, &options, sizeof(orig_options)); #elif defined(HAVE_TERMIO_H) rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TCGETA\n", __func__); IOCTL(fd, TCGETA, &options); memcpy(&orig_options, &options, sizeof(orig_options)); #else /* sgtty */ rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TIOCGETP\n", __func__); IOCTL(fd, TIOCGETP, &sg); memcpy(&orig_sg, &sg, sizeof(orig_sg)); #endif #ifdef HAVE_CFMAKERAW /* Set serial port to RAW mode by default. */ rig_debug(RIG_DEBUG_TRACE, "%s: cfmakeraw\n", __func__); cfmakeraw(&options); #endif /* * Set the baud rates to requested values */ switch (rp->parm.serial.rate) { case 150: speed = B150; /* yikes... */ break; case 300: speed = B300; /* yikes... */ break; case 600: speed = B600; break; case 1200: speed = B1200; break; case 2400: speed = B2400; break; case 4800: speed = B4800; break; case 9600: speed = B9600; break; case 19200: speed = B19200; break; case 38400: speed = B38400; break; case 57600: speed = B57600; /* cool.. */ break; case 115200: speed = B115200; /* awesome! */ break; #ifdef B230400 case 230400: speed = B230400; /* super awesome! */ break; #endif #ifdef B460800 case 460800: speed = B460800; /* extra super awesome! */ break; #endif #ifdef B500000 case 500000: speed = B500000; /* extra super awesome! */ break; #endif #ifdef B576000 case 576000: speed = B576000; /* out of adverbs */ break; #endif #ifdef B921600 case 921600: speed = B921600; break; #endif #ifdef B1000000 case 1000000: speed = B1000000; break; #endif #ifdef B1152000 case 1152000: speed = B1152000; break; #endif #ifdef B1500000 case 1500000: speed = B1500000; break; #endif #ifdef B2000000 case 2000000: speed = B2000000; break; #endif #ifdef B2500000 case 2500000: speed = B2500000; break; #endif #ifdef B3000000 case 3000000: speed = B3000000; break; #endif #ifdef B3500000 case 3500000: speed = B3500000; break; #endif #ifdef B4000000 case 4000000: speed = B4000000; break; #endif default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported rate specified: %d\n", __func__, rp->parm.serial.rate); CLOSE(fd); return (-RIG_ECONF); } /* TODO */ rig_debug(RIG_DEBUG_TRACE, "%s: cfsetispeed=%d,0x%04x\n", __func__, (int)rp->parm.serial.rate, (int)speed); cfsetispeed(&options, speed); rig_debug(RIG_DEBUG_TRACE, "%s: cfsetospeed=%d,0x%04x\n", __func__, (int)rp->parm.serial.rate, (int)speed); 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. * */ rig_debug(RIG_DEBUG_TRACE, "%s: data_bits=%d\n", __func__, rp->parm.serial.data_bits); switch (rp->parm.serial.data_bits) { 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); return (-RIG_ECONF); break; } /* * Set stop bits to requested values. * */ switch (rp->parm.serial.stop_bits) { 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); return (-RIG_ECONF); break; } /* * Set parity to requested values. * */ rig_debug(RIG_DEBUG_TRACE, "%s: parity=%d\n", __func__, rp->parm.serial.parity); switch (rp->parm.serial.parity) { 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 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 default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported serial_parity specified: %d\n", __func__, rp->parm.serial.parity); CLOSE(fd); return (-RIG_ECONF); break; } /* * Set flow control to requested mode * */ switch (rp->parm.serial.handshake) { case RIG_HANDSHAKE_NONE: rig_debug(RIG_DEBUG_TRACE, "%s: Handshake=None\n", __func__); options.c_cflag &= ~CRTSCTS; options.c_iflag &= ~IXON; break; case RIG_HANDSHAKE_XONXOFF: rig_debug(RIG_DEBUG_TRACE, "%s: Handshake=Xon/Xoff\n", __func__); options.c_cflag &= ~CRTSCTS; options.c_iflag |= IXON; /* Enable Xon/Xoff software handshaking */ break; case RIG_HANDSHAKE_HARDWARE: rig_debug(RIG_DEBUG_TRACE, "%s: Handshake=Hardware\n", __func__); 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); return (-RIG_ECONF); break; } /* * Choose raw input, no preprocessing please .. */ #if defined(HAVE_TERMIOS_H) || defined(HAVE_TERMIO_H) options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* * Choose raw output, no preprocessing please .. */ options.c_oflag &= ~OPOST; #else /* sgtty */ sg.sg_flags = RAW; #endif /* * VTIME in deciseconds, rp->timeout in milliseconds */ options.c_cc[VTIME] = (rp->timeout + 99) / 100; options.c_cc[VMIN] = 1; /* * Flush serial port */ tcflush(fd, TCIFLUSH); /* * Finally, set the new options for the port... */ #if defined(HAVE_TERMIOS_H) rig_debug(RIG_DEBUG_TRACE, "%s: tcsetattr TCSANOW\n", __func__); if (tcsetattr(fd, TCSANOW, &options) == -1) { rig_debug(RIG_DEBUG_ERR, "%s: tcsetattr failed: %s\n", __func__, strerror(errno)); CLOSE(fd); return (-RIG_ECONF); /* arg, so close! */ } #elif defined(HAVE_TERMIO_H) rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TCSETA\n", __func__); if (IOCTL(fd, TCSETA, &options) == -1) { rig_debug(RIG_DEBUG_ERR, "%s: ioctl(TCSETA) failed: %s\n", __func__, strerror(errno)); CLOSE(fd); return (-RIG_ECONF); /* arg, so close! */ } #else rig_debug(RIG_DEBUG_TRACE, "%s: IOCTL TIOCSETP\n", __func__); /* sgtty */ if (IOCTL(fd, TIOCSETP, &sg) == -1) { rig_debug(RIG_DEBUG_ERR, "%s: ioctl(TIOCSETP) failed: %s\n", __func__, strerror(errno)); CLOSE(fd); return (-RIG_ECONF); /* arg, so close! */ } #endif // Store a copy of the original options for this FD, to be restored on close. term_backup = calloc(1, 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; return (RIG_OK); } /** * \brief Flush all characters waiting in RX buffer. * \param p * \return RIG_OK */ int HAMLIB_API serial_flush(hamlib_port_t *p) { int len; int timeout_save; unsigned char buf[4096]; if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd || p->flushx) { /* * Catch microHam case: * if fd corresponds to a microHam device drain the line * (which is a socket) by reading until it is empty. */ int n, nbytes = 0; rig_debug(RIG_DEBUG_TRACE, "%s: flushing\n", __func__); while ((n = read(p->fd, buf, sizeof(buf))) > 0) { nbytes += n; //int i; //for (i = 0; i < n; ++i) { printf("0x%02x(%c) ", buf[i], isprint(buf[i]) ? buf[i] : '~'); } /* do nothing */ } rig_debug(RIG_DEBUG_TRACE, "read flushed %d bytes\n", nbytes); return (RIG_OK); } timeout_save = p->timeout; p->timeout = 1; do { // we pass an empty stopset so read_string can determine // the appropriate stopset for async data char stopset[1]; len = read_string(p, buf, sizeof(buf) - 1, stopset, 0, 1, 1); if (len > 0) { int i, binary = 0; for (i = 0; i < len; ++i) { if (!isprint(buf[i])) { binary = 1; } } if (binary) { int bytes = len * 3 + 1; char *hbuf = calloc(bytes, 1); for (i = 0; i < len; ++i) { SNPRINTF(&hbuf[i * 3], bytes - (i * 3), "%02X ", buf[i]); } rig_debug(RIG_DEBUG_WARN, "%s: flush hex:%s\n", __func__, hbuf); free(hbuf); } else { rig_debug(RIG_DEBUG_WARN, "%s: flush string:%s\n", __func__, buf); } } } while (len > 0); p->timeout = timeout_save; rig_debug(RIG_DEBUG_VERBOSE, "tcflush%s\n", ""); tcflush(p->fd, TCIFLUSH); // we also do this flush https://github.com/Hamlib/Hamlib/issues/1241 return (RIG_OK); } /** * \brief Open serial port * \param p * \return fd */ int ser_open(hamlib_port_t *p) { int ret; rig_debug(RIG_DEBUG_VERBOSE, "%s called port=%s\n", __func__, p->pathname); 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). */ 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. */ ret = uh_open_ptt(); uh_ptt_fd = ret; } else { /* * pathname is not uh_rig or uh_ptt: simply open() */ int i = 1; do // some serial ports fail to open 1st time { ret = OPEN(p->pathname, O_RDWR | O_NOCTTY | O_NDELAY); if (ret == -1) // some serial ports fail to open 1st time { rig_debug(RIG_DEBUG_WARN, "%s(%d): open failed#%d\n", __func__, __LINE__, i); hl_usleep(500 * 1000); ret = OPEN(p->pathname, O_RDWR | O_NOCTTY | O_NDELAY); } } while (++i <= 4 && ret == -1); } } p->fd = ret; return (ret); } /** * \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; //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. */ if (p->fd == uh_ptt_fd) { uh_close_ptt(); uh_ptt_fd = -1; p->fd = -1; return (0); } if (p->fd == uh_radio_fd) { uh_close_radio(); uh_radio_fd = -1; p->fd = -1; return (0); } // Find backup termios options to restore before closing term_backup = term_options_backup_head; term_backup_prev = term_options_backup_head; while (term_backup) { if (term_backup->fd == p->fd) { // Found matching. Remove from list if (term_backup == term_options_backup_head) { term_options_backup_head = term_backup->next; } else { term_backup_prev->next = term_backup->next; } break; } term_backup_prev = term_backup; term_backup = term_backup->next; } // Restore backup termios 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, "%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, "%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, "%s: ioctl(TIOCSETP) restore failed: %s\n", __func__, strerror(errno)); } #endif free(term_backup); } else { rig_debug(RIG_DEBUG_TRACE, "%s: no options for fd to restore\n", __func__); } rc = CLOSE(p->fd); p->fd = -1; return (rc); } /** * \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) { unsigned int y = TIOCM_RTS; int rc; rig_debug(RIG_DEBUG_VERBOSE, "%s: RTS=%d\n", __func__, state); // ignore this for microHam ports if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd) { return (RIG_OK); } #if defined(TIOCMBIS) && defined(TIOCMBIC) rc = IOCTL(p->fd, state ? TIOCMBIS : TIOCMBIC, &y); #else rc = IOCTL(p->fd, TIOCMGET, &y); if (rc >= 0) { if (state) { y |= TIOCM_RTS; } else { y &= ~TIOCM_RTS; } rc = IOCTL(p->fd, TIOCMSET, &y); } #endif if (rc < 0) { rig_debug(RIG_DEBUG_ERR, "%s: Cannot change RTS - %s\n", __func__, strerror(errno)); return (-RIG_EIO); } return (RIG_OK); } /** * \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) { int retcode; unsigned int y; // cannot do this for microHam ports if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd) { return (-RIG_ENIMPL); } retcode = IOCTL(p->fd, TIOCMGET, &y); *state = (y & TIOCM_RTS) == TIOCM_RTS; return (retcode < 0 ? -RIG_EIO : RIG_OK); } /** * \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) { unsigned int y = TIOCM_DTR; int rc; 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. if (p->fd == uh_radio_fd) { return (RIG_OK); } if (p->fd == uh_ptt_fd) { uh_set_ptt(state); return (RIG_OK); } #if defined(TIOCMBIS) && defined(TIOCMBIC) rc = IOCTL(p->fd, state ? TIOCMBIS : TIOCMBIC, &y); #else rc = IOCTL(p->fd, TIOCMGET, &y); if (rc >= 0) { if (state) { y |= TIOCM_DTR; } else { y &= ~TIOCM_DTR; } rc = IOCTL(p->fd, TIOCMSET, &y); } #endif if (rc < 0) { rig_debug(RIG_DEBUG_ERR, "%s: Cannot change DTR - %s\n", __func__, strerror(errno)); return (-RIG_EIO); } return (RIG_OK); } /** * \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) { int retcode; unsigned int y; // cannot do this for the RADIO port, return PTT state for the PTT port if (p->fd == uh_ptt_fd) { *state = uh_get_ptt(); return (RIG_OK); } if (p->fd == uh_radio_fd) { return (-RIG_ENIMPL); } retcode = IOCTL(p->fd, TIOCMGET, &y); *state = (y & TIOCM_DTR) == TIOCM_DTR; return (retcode < 0 ? -RIG_EIO : RIG_OK); } /** * \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 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 } /** * \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) { int retcode; unsigned int y; // cannot do this for microHam ports if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd) { return (-RIG_ENIMPL); } retcode = IOCTL(p->fd, TIOCMGET, &y); *state = (y & TIOCM_CAR) == TIOCM_CAR; return (retcode < 0 ? -RIG_EIO : RIG_OK); } /** * \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) { int retcode; unsigned int y; // cannot do this for microHam ports if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd) { return (-RIG_ENIMPL); } retcode = IOCTL(p->fd, TIOCMGET, &y); *state = (y & TIOCM_CTS) == TIOCM_CTS; return (retcode < 0 ? -RIG_EIO : RIG_OK); } /** * \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) { int retcode; unsigned int y; // cannot do this for microHam ports if (p->fd == uh_ptt_fd || p->fd == uh_radio_fd) { return (-RIG_ENIMPL); } retcode = IOCTL(p->fd, TIOCMGET, &y); *state = (y & TIOCM_DSR) == TIOCM_DSR; return (retcode < 0 ? -RIG_EIO : RIG_OK); } /** @} */