/* * 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 #include /* Standard input/output definitions */ #include /* String function definitions */ #include /* UNIX standard function definitions */ #include /* File control definitions */ #include /* Error number definitions */ #include #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 #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 #include #include "serial.h" #include "misc.h" #ifdef HAVE_SYS_IOCCOM_H #include #endif /** * \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; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rp) { return -RIG_EINVAL; } /* * Open in Non-blocking mode. Watch for EAGAIN errors! */ fd = OPEN(rp->pathname, O_RDWR | O_NOCTTY | O_NDELAY); 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; } 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; #elif defined(HAVE_TERMIO_H) struct termio options; #elif defined(HAVE_SGTTY_H) struct sgttyb sg; #else #error "No term control supported!" #endif rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); if (!rp) { return -RIG_EINVAL; } fd = rp->fd; /* * Get the current options for the port... */ #if defined(HAVE_TERMIOS_H) tcgetattr(fd, &options); #elif defined(HAVE_TERMIO_H) IOCTL(fd, TCGETA, &options); #else /* sgtty */ IOCTL(fd, TIOCGETP, &sg); #endif #ifdef HAVE_CFMAKERAW /* Set serial port to RAW mode by default. */ 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; default: rig_debug(RIG_DEBUG_ERR, "%s: unsupported rate specified: %d\n", __func__, rp->parm.serial.rate); CLOSE(fd); return -RIG_ECONF; } /* TODO */ cfsetispeed(&options, 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. * */ 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. * */ 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: 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); 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 miliseconds */ 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) 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) 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 /* 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 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) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); tcflush(p->fd, TCIFLUSH); return RIG_OK; } /** * \brief Open serial port * \param p * \return fd */ int ser_open(hamlib_port_t *p) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); return (p->fd = OPEN(p->pathname, O_RDWR | O_NOCTTY | O_NDELAY)); } /** * \brief Close serial port * \param p fd * \return RIG_OK or < 0 */ int ser_close(hamlib_port_t *p) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); int 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 called\n", __func__); rig_debug(RIG_DEBUG_VERBOSE, "%s: RTS=%d\n", __func__, state); #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; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); 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 called\n", __func__); rig_debug(RIG_DEBUG_VERBOSE, "%s: DTR=%d\n", __func__, state); #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; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); 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) { rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); #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; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); 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; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); 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; rig_debug(RIG_DEBUG_VERBOSE, "%s called\n", __func__); retcode = IOCTL(p->fd, TIOCMGET, &y); *state = (y & TIOCM_DSR) == TIOCM_DSR; return retcode < 0 ? -RIG_EIO : RIG_OK; } /** @} */