mbusd/src/tty.c

519 lines
10 KiB
C

/*
* OpenMODBUS/TCP to RS-232/485 MODBUS RTU gateway
*
* tty.c - terminal I/O related procedures
*
* Copyright (c) 2002-2003, 2013, Victor Antonovich (v.antonovich@gmail.com)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: tty.c,v 1.4 2015/02/25 10:33:58 kapyar Exp $
*/
#include "tty.h"
extern cfg_t cfg;
static int tty_break;
/*
* Flag signal SIG
*/
void
tty_sighup(void)
{
tty_break = TRUE;
return;
}
/*
* Init serial link parameters
*/
void
tty_init(ttydata_t *mod)
{
mod->fd = -1;
mod->port = cfg.ttyport;
mod->speed = cfg.ttyspeed;
mod->bpc = DEFAULT_BITS_PER_CHAR;
if (toupper(cfg.ttymode[1]) != 'N')
{
mod->bpc++;
}
if (cfg.ttymode[2] == '2')
{
mod->bpc++;
}
#ifdef HAVE_TIOCRS485
mod->rs485 = cfg.rs485;
#endif
#ifdef TRXCTL
mod->trxcntl = cfg.trxcntl;
#endif
}
#ifdef HAVE_LIBUTIL
char *tty_get_name(char *ttyfullname);
char *
tty_get_name(char *ttyfullname)
{
static char ttynamebuf[INTBUFSIZE + 1];
char *ttyname = ttynamebuf, *ttynameptr = ttyname;
strncpy(ttynamebuf, ttyfullname, INTBUFSIZE);
for (ttynameptr = strtok(ttynamebuf, "/");
ttynameptr;
ttynameptr = strtok(NULL, "/"))
{
ttyname = ttynameptr;
}
return ttyname;
}
#endif
/*
* Opening serial link whith parameters in MOD
*/
int
tty_open(ttydata_t *mod)
{
#ifdef HAVE_LIBUTIL
int buferr, uuerr;
char *ttyname = tty_get_name(mod->port);
#endif
if (mod->fd > 0)
return RC_AOPEN; /* if already open... */
tty_break = FALSE;
#ifdef HAVE_LIBUTIL
if ((uuerr = uu_lock(ttyname)) != UU_LOCK_OK)
{
buferr = errno;
#ifdef LOG
logw(0, "uu_lock(): can't lock tty device %s (%s)",
ttyname, uu_lockerr(uuerr));
#endif
errno = buferr;
return RC_ERR;
}
#endif
#ifdef LOG
logw(2, "tty: trying to open %s (speed %d mode %s)", mod->port, mod->speed, cfg.ttymode);
#endif
mod->fd = open(mod->port, O_RDWR | O_NONBLOCK | O_NOCTTY);
if (mod->fd < 0)
return RC_ERR; /* attempt failed */
return tty_set_attr(mod);
}
/*
* Setting up tty device MOD attributes
*/
int
tty_set_attr(ttydata_t *mod)
{
int flag;
speed_t tspeed = tty_transpeed(mod->speed);
memset(&mod->savedtios, 0, sizeof(struct termios));
if (tcgetattr(mod->fd, &mod->savedtios))
return RC_ERR;
memcpy(&mod->tios, &mod->savedtios, sizeof(mod->tios));
#ifdef HAVE_CFMAKERAW
cfmakeraw(&mod->tios);
#else
mod->tios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON);
mod->tios.c_oflag &= ~OPOST;
mod->tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
#endif
mod->tios.c_cflag |= (CREAD | CLOCAL);
mod->tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS);
switch (cfg.ttymode[0])
{
case '5':
mod->tios.c_cflag |= CS5;
break;
case '6':
mod->tios.c_cflag |= CS6;
break;
case '7':
mod->tios.c_cflag |= CS7;
break;
case '8':
mod->tios.c_cflag |= CS8;
break;
}
switch (toupper(cfg.ttymode[1]))
{
case 'E':
mod->tios.c_cflag |= PARENB;
break;
case 'O':
mod->tios.c_cflag |= (PARENB | PARODD);
break;
}
if (cfg.ttymode[2] == '2')
{
mod->tios.c_cflag |= CSTOPB;
}
mod->tios.c_cc[VTIME] = 0;
mod->tios.c_cc[VMIN] = 1;
#ifdef HAVE_CFSETSPEED
cfsetspeed(&mod->tios, tspeed);
#else
cfsetispeed(&mod->tios, tspeed);
cfsetospeed(&mod->tios, tspeed);
#endif
if (tcsetattr(mod->fd, TCSANOW, &mod->tios))
return RC_ERR;
tcflush(mod->fd, TCIOFLUSH);
#ifdef TRXCTL
tty_set_rx(mod->fd);
#endif
#ifdef HAVE_TIOCRS485
if (mod->rs485)
{
struct serial_rs485 rs485conf;
#ifdef LOG
logw(2, "tty: trying to enable RS-485 support for %s", mod->port);
#endif
if (ioctl(mod->fd, TIOCGRS485, &rs485conf) < 0) {
return RC_ERR;
}
rs485conf.flags |= SER_RS485_ENABLED;
if (ioctl(mod->fd, TIOCSRS485, &rs485conf) < 0) {
return RC_ERR;
}
#ifdef LOG
logw(2, "tty: enabled RS-485 support for %s", mod->port);
#endif
}
#endif
flag = fcntl(mod->fd, F_GETFL, 0);
if (flag < 0)
return RC_ERR;
return fcntl(mod->fd, F_SETFL, flag | O_NONBLOCK);
}
/*
* Translate integer SPEED value to speed_t constant
*/
speed_t
tty_transpeed(int speed)
{
speed_t tspeed;
switch (speed)
{
#if defined(B50)
case 50:
tspeed = B50;
break;
#endif
#if defined(B75)
case 75:
tspeed = B75;
break;
#endif
#if defined(B110)
case 110:
tspeed = B110;
break;
#endif
#if defined(B134)
case 134:
tspeed = B134;
break;
#endif
#if defined(B150)
case 150:
tspeed = B150;
break;
#endif
#if defined(B200)
case 200:
tspeed = B200;
break;
#endif
#if defined(B300)
case 300:
tspeed = B300;
break;
#endif
#if defined(B600)
case 600:
tspeed = B600;
break;
#endif
#if defined(B1200)
case 1200:
tspeed = B1200;
break;
#endif
#if defined(B1800)
case 1800:
tspeed = B1800;
break;
#endif
#if defined(B2400)
case 2400:
tspeed = B2400;
break;
#endif
#if defined(B4800)
case 4800:
tspeed = B4800;
break;
#endif
#if defined(B7200)
case 7200:
tspeed = B7200;
break;
#endif
#if defined(B9600)
case 9600:
tspeed = B9600;
break;
#endif
#if defined(B12000)
case 12000:
tspeed = B12000;
break;
#endif
#if defined(B14400)
case 14400:
tspeed = B14400;
break;
#endif
#if defined(B19200)
case 19200:
tspeed = B19200;
break;
#elif defined(EXTA)
case 19200:
tspeed = EXTA;
break;
#endif
#if defined(B38400)
case 38400:
tspeed = B38400;
break;
#elif defined(EXTB)
case 38400:
tspeed = EXTB;
break;
#endif
#if defined(B57600)
case 57600:
tspeed = B57600;
break;
#endif
#if defined(B115200)
case 115200:
tspeed = B115200;
break;
#endif
#if defined(B230400)
case 230400:
tspeed = B230400;
break;
#endif
#if defined(B460800)
case 460800:
tspeed = B460800;
break;
#endif
#if defined(B500000)
case 500000:
tspeed = B500000;
break;
#endif
#if defined(B576000)
case 576000:
tspeed = B576000;
break;
#endif
#if defined(B921600)
case 921600:
tspeed = B921600;
break;
#endif
#if defined(B1000000)
case 1000000:
tspeed = B1000000;
break;
#endif
#if defined(B1152000)
case 1152000:
tspeed = B1152000;
break;
#endif
#if defined(B1500000)
case 1500000:
tspeed = B1500000;
break;
#endif
#if defined(B2000000)
case 2000000:
tspeed = B2000000;
break;
#endif
#if defined(B2500000)
case 2500000:
tspeed = B2500000;
break;
#endif
#if defined(B3000000)
case 3000000:
tspeed = B3000000;
break;
#endif
#if defined(B3500000)
case 3500000:
tspeed = B3500000;
break;
#endif
#if defined(B4000000)
case 4000000:
tspeed = B4000000;
break;
#endif
default:
#ifdef LOG
logw(0, "tty: unsupported speed: %d", speed);
#endif
exit (-1);
}
return tspeed;
}
/*
* Prepare tty device MOD to closing
*/
int
tty_cooked(ttydata_t *mod)
{
if (!isatty(mod->fd))
return RC_ERR;
if (tcsetattr(mod->fd, TCSAFLUSH, &mod->savedtios))
return RC_ERR;
return RC_OK;
}
/*
* Closing tty device MOD
*/
int
tty_close(ttydata_t *mod)
{
#ifdef HAVE_LIBUTIL
int buferr;
char *ttyname = tty_get_name(mod->port);
#endif
if (mod->fd < 0)
return RC_ACLOSE; /* already closed */
tty_cooked(mod);
#ifdef HAVE_LIBUTIL
if (close(mod->fd))
return RC_ERR;
if (uu_unlock(ttyname))
{
buferr = errno;
#ifdef LOG
logw(0, "uu_lock(): can't unlock tty device %s",
ttyname);
#endif
errno = buferr;
return RC_ERR;
}
return RC_OK;
#else
return close(mod->fd);
#endif
}
#ifdef TRXCTL
void sysfs_gpio_set(char *filename, char *value) {
int fd;
fd = open(filename, O_WRONLY);
/* write first byte. Should only be 0 or 1 */
write(fd, value, 1);
close(fd);
logw(9, "tty: sysfs_gpio_set(%s,%s)\n",filename,value);
}
/* Set tty device into transmit mode */
void
tty_set_tx(int fd)
{
if ( TRX_RTS_1 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIS, &mstat);
} else if ( TRX_RTS_0 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIC, &mstat);
} else if ( TRX_SYSFS_1 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"1");
} else if ( TRX_SYSFS_0 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"0");
}
}
/* Set tty device into receive mode */
void
tty_set_rx(int fd)
{
if ( TRX_RTS_1 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIC, &mstat);
} else if ( TRX_RTS_0 == cfg.trxcntl ) {
int mstat = TIOCM_RTS;
ioctl(fd, TIOCMBIS, &mstat);
} else if ( TRX_SYSFS_1 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"0");
} else if ( TRX_SYSFS_0 == cfg.trxcntl) {
sysfs_gpio_set(cfg.trxcntl_file,"1");
}
}
#endif
/*
* Delay for USEC microsecs
*/
void
tty_delay(int usec)
{
struct timeval tv, ttv;
long ts;
gettimeofday(&tv, NULL);
do
{
(void)gettimeofday(&ttv, NULL);
ts = 1000000l * (ttv.tv_sec - tv.tv_sec) + (ttv.tv_usec - tv.tv_usec);
} while (ts < usec && !tty_break);
}