1429 lines
32 KiB
C
1429 lines
32 KiB
C
/*
|
|
* ser2net - A program for allowing telnet connection to serial ports
|
|
* Copyright (C) 2001 Corey Minyard <minyard@acm.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* This code handles generating the configuration for the serial port. */
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <syslog.h>
|
|
|
|
#include "ser2net.h"
|
|
#include "selector.h"
|
|
#include "utils.h"
|
|
#include "dataxfer.h"
|
|
#include "devio.h"
|
|
|
|
#include <assert.h>
|
|
|
|
struct devcfg_data {
|
|
/* Information about the terminal device. */
|
|
char *devname; /* The full path to the device */
|
|
int devfd; /* The file descriptor for the
|
|
device, only valid if the
|
|
TCP port is open. */
|
|
struct termios termctl;
|
|
|
|
void (*shutdown_done)(struct devio *);
|
|
|
|
int default_bps;
|
|
int default_data_size;
|
|
int default_stop_bits;
|
|
int default_parity_on;
|
|
int current_bps;
|
|
int current_data_size;
|
|
int current_stop_bits;
|
|
int current_parity_on;
|
|
|
|
/* Holds whether break is on or not. */
|
|
int break_set;
|
|
|
|
/* Disable break-commands */
|
|
int disablebreak;
|
|
|
|
#if HAVE_DECL_TIOCSRS485
|
|
struct serial_rs485 *conf;
|
|
#endif
|
|
};
|
|
|
|
static struct baud_rates_s {
|
|
int real_rate;
|
|
int val;
|
|
int cisco_ios_val;
|
|
} baud_rates[] =
|
|
{
|
|
{ 50, B50, -1 },
|
|
{ 75, B75, -1 },
|
|
{ 110, B110, -1 },
|
|
{ 134, B134, -1 },
|
|
{ 150, B150, -1 },
|
|
{ 200, B200, -1 },
|
|
{ 300, B300, 3 },
|
|
{ 600, B600 , 4},
|
|
{ 1200, B1200, 5 },
|
|
{ 1800, B1800, -1 },
|
|
{ 2400, B2400, 6 },
|
|
{ 4800, B4800, 7 },
|
|
{ 9600, B9600, 8 },
|
|
/* We don't support 14400 baud */
|
|
{ 19200, B19200, 10 },
|
|
/* We don't support 28800 baud */
|
|
{ 38400, B38400, 12 },
|
|
{ 57600, B57600, 13 },
|
|
{ 115200, B115200, 14 },
|
|
{ 230400, B230400, 15 },
|
|
/* We don't support 460800 baud */
|
|
};
|
|
#define BAUD_RATES_LEN ((sizeof(baud_rates) / sizeof(struct baud_rates_s)))
|
|
|
|
static int
|
|
get_baud_rate(int rate, int *val, int cisco, int *bps)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < BAUD_RATES_LEN; i++) {
|
|
if (cisco) {
|
|
if (rate == baud_rates[i].cisco_ios_val) {
|
|
*val = baud_rates[i].val;
|
|
*bps = baud_rates[i].real_rate;
|
|
return 1;
|
|
}
|
|
} else {
|
|
if (rate == baud_rates[i].real_rate) {
|
|
*val = baud_rates[i].val;
|
|
*bps = baud_rates[i].real_rate;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
get_rate_from_baud_rate(int baud_rate, int *val, int cisco)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < BAUD_RATES_LEN; i++) {
|
|
if (baud_rate == baud_rates[i].val) {
|
|
if (cisco) {
|
|
if (baud_rates[i].cisco_ios_val < 0)
|
|
/* We are at a baud rate unsupported by the
|
|
enumeration, just return zero. */
|
|
*val = 0;
|
|
else
|
|
*val = baud_rates[i].cisco_ios_val;
|
|
} else {
|
|
*val = baud_rates[i].real_rate;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef USE_UUCP_LOCKING
|
|
static char *uucp_lck_dir = "/var/lock/";
|
|
static char *dev_prefix = "/dev/";
|
|
|
|
static int
|
|
uucp_fname_lock_size(char *devname)
|
|
{
|
|
int dev_prefix_len = strlen(dev_prefix);
|
|
|
|
if (strncmp(dev_prefix, devname, dev_prefix_len) == 0)
|
|
devname += dev_prefix_len;
|
|
|
|
/*
|
|
* Format is "/var/lock/LCK..<devname>". The 6 is for
|
|
* the "LCK.." and the final nil char.
|
|
*/
|
|
return 6 + strlen(uucp_lck_dir) + strlen(devname);
|
|
}
|
|
|
|
static void
|
|
uucp_fname_lock(char *buf, char *devname)
|
|
{
|
|
int i, dev_prefix_len = strlen(dev_prefix);
|
|
|
|
if (strncmp(dev_prefix, devname, dev_prefix_len) == 0)
|
|
devname += dev_prefix_len;
|
|
|
|
sprintf(buf, "%sLCK..%s", uucp_lck_dir, devname);
|
|
for (i = strlen(uucp_lck_dir); buf[i]; i++) {
|
|
if (buf[i] == '/')
|
|
buf[i] = '_';
|
|
}
|
|
}
|
|
|
|
static void
|
|
uucp_rm_lock(char *devname)
|
|
{
|
|
char *lck_file;
|
|
|
|
if (!uucp_locking_enabled) return;
|
|
|
|
lck_file = malloc(uucp_fname_lock_size(devname));
|
|
if (lck_file == NULL) {
|
|
return;
|
|
}
|
|
uucp_fname_lock(lck_file, devname);
|
|
unlink(lck_file);
|
|
free(lck_file);
|
|
}
|
|
|
|
/* return 0=OK, -1=error, 1=locked by other proces */
|
|
static int
|
|
uucp_mk_lock(char *devname)
|
|
{
|
|
struct stat stt;
|
|
int pid = -1;
|
|
|
|
if (!uucp_locking_enabled)
|
|
return 0;
|
|
|
|
if (stat(uucp_lck_dir, &stt) == 0) { /* is lock file directory present? */
|
|
char *lck_file;
|
|
union {
|
|
uint32_t ival;
|
|
char str[64];
|
|
} buf;
|
|
int fd;
|
|
|
|
lck_file = malloc(uucp_fname_lock_size(devname));
|
|
if (lck_file == NULL)
|
|
return -1;
|
|
|
|
uucp_fname_lock(lck_file, devname);
|
|
|
|
pid = 0;
|
|
if ((fd = open(lck_file, O_RDONLY)) >= 0) {
|
|
int n;
|
|
|
|
n = read(fd, &buf, sizeof(buf) - 1);
|
|
close(fd);
|
|
if (n == 4) /* Kermit-style lockfile. */
|
|
pid = buf.ival;
|
|
else if (n > 0) { /* Ascii lockfile. */
|
|
buf.str[n] = '\0';
|
|
sscanf(buf.str, "%10d", &pid);
|
|
}
|
|
|
|
if (pid > 0 && kill((pid_t)pid, 0) < 0 && errno == ESRCH) {
|
|
/* death lockfile - remove it */
|
|
unlink(lck_file);
|
|
pid = 0;
|
|
} else
|
|
pid = 1;
|
|
|
|
}
|
|
|
|
if (pid == 0) {
|
|
int mask;
|
|
|
|
mask = umask(022);
|
|
fd = open(lck_file, O_WRONLY | O_CREAT | O_EXCL, 0666);
|
|
umask(mask);
|
|
if (fd >= 0) {
|
|
ssize_t rv;
|
|
|
|
snprintf(buf.str, sizeof(buf), "%10ld\n",
|
|
(long)getpid());
|
|
rv = write_full(fd, buf.str, strlen(buf.str));
|
|
close(fd);
|
|
if (rv < 0) {
|
|
pid = -1;
|
|
unlink(lck_file);
|
|
}
|
|
} else {
|
|
pid = -1;
|
|
}
|
|
}
|
|
|
|
free(lck_file);
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
#else
|
|
static void uucp_rm_lock(char *devname) { }
|
|
static int uucp_mk_lock(char *devname)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif /* USE_UUCP_LOCKING */
|
|
|
|
#ifdef __CYGWIN__
|
|
static void cfmakeraw(struct termios *termios_p) {
|
|
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
|
|
termios_p->c_oflag &= ~OPOST;
|
|
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
|
|
termios_p->c_cflag &= ~(CSIZE|PARENB);
|
|
termios_p->c_cflag |= CS8;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
set_termios_from_speed(struct devcfg_data *d, struct termios *termctl,
|
|
int speed)
|
|
{
|
|
switch (speed) {
|
|
case 300:
|
|
cfsetospeed(termctl, B300);
|
|
cfsetispeed(termctl, B300);
|
|
break;
|
|
|
|
case 600:
|
|
cfsetospeed(termctl, B600);
|
|
cfsetispeed(termctl, B600);
|
|
break;
|
|
|
|
case 1200:
|
|
cfsetospeed(termctl, B1200);
|
|
cfsetispeed(termctl, B1200);
|
|
break;
|
|
|
|
case 2400:
|
|
cfsetospeed(termctl, B2400);
|
|
cfsetispeed(termctl, B2400);
|
|
break;
|
|
|
|
case 4800:
|
|
cfsetospeed(termctl, B4800);
|
|
cfsetispeed(termctl, B4800);
|
|
break;
|
|
|
|
case 9600:
|
|
cfsetospeed(termctl, B9600);
|
|
cfsetispeed(termctl, B9600);
|
|
break;
|
|
|
|
case 19200:
|
|
cfsetospeed(termctl, B19200);
|
|
cfsetispeed(termctl, B19200);
|
|
break;
|
|
|
|
case 38400:
|
|
cfsetospeed(termctl, B38400);
|
|
cfsetispeed(termctl, B38400);
|
|
break;
|
|
|
|
case 57600:
|
|
cfsetospeed(termctl, B57600);
|
|
cfsetispeed(termctl, B57600);
|
|
break;
|
|
|
|
case 115200:
|
|
cfsetospeed(termctl, B115200);
|
|
cfsetispeed(termctl, B115200);
|
|
break;
|
|
|
|
#ifdef B230400
|
|
case 230400:
|
|
cfsetospeed(termctl, B230400);
|
|
cfsetispeed(termctl, B230400);
|
|
break;
|
|
#endif
|
|
#ifdef B460800
|
|
case 460800:
|
|
cfsetospeed(termctl, B460800);
|
|
cfsetispeed(termctl, B460800);
|
|
break;
|
|
#endif
|
|
#ifdef B500000
|
|
case 500000:
|
|
cfsetospeed(termctl, B500000);
|
|
cfsetispeed(termctl, B500000);
|
|
break;
|
|
#endif
|
|
#ifdef B576000
|
|
case 576000:
|
|
cfsetospeed(termctl, B576000);
|
|
cfsetispeed(termctl, B576000);
|
|
break;
|
|
#endif
|
|
#ifdef B921600
|
|
case 921600:
|
|
cfsetospeed(termctl, B921600);
|
|
cfsetispeed(termctl, B921600);
|
|
break;
|
|
#endif
|
|
#ifdef B1000000
|
|
case 1000000:
|
|
cfsetospeed(termctl, B1000000);
|
|
cfsetispeed(termctl, B1000000);
|
|
break;
|
|
#endif
|
|
#ifdef B1152000
|
|
case 1152000:
|
|
cfsetospeed(termctl, B1152000);
|
|
cfsetispeed(termctl, B1152000);
|
|
break;
|
|
#endif
|
|
#ifdef B1500000
|
|
case 1500000:
|
|
cfsetospeed(termctl, B1500000);
|
|
cfsetispeed(termctl, B1500000);
|
|
break;
|
|
#endif
|
|
#ifdef B2000000
|
|
case 2000000:
|
|
cfsetospeed(termctl, B2000000);
|
|
cfsetispeed(termctl, B2000000);
|
|
break;
|
|
#endif
|
|
#ifdef B2500000
|
|
case 2500000:
|
|
cfsetospeed(termctl, B2500000);
|
|
cfsetispeed(termctl, B2500000);
|
|
break;
|
|
#endif
|
|
#ifdef B3000000
|
|
case 3000000:
|
|
cfsetospeed(termctl, B3000000);
|
|
cfsetispeed(termctl, B3000000);
|
|
break;
|
|
#endif
|
|
#ifdef B3500000
|
|
case 3500000:
|
|
cfsetospeed(termctl, B3500000);
|
|
cfsetispeed(termctl, B3500000);
|
|
break;
|
|
#endif
|
|
#ifdef B4000000
|
|
case 4000000:
|
|
cfsetospeed(termctl, B4000000);
|
|
cfsetispeed(termctl, B4000000);
|
|
break;
|
|
#endif
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
d->default_bps = speed;
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
set_termios_parity(struct devcfg_data *d, struct termios *termctl,
|
|
enum parity_vals val)
|
|
{
|
|
switch (val) {
|
|
case PARITY_NONE:
|
|
termctl->c_cflag &= ~(PARENB);
|
|
break;
|
|
case PARITY_EVEN:
|
|
case PARITY_SPACE:
|
|
termctl->c_cflag |= PARENB;
|
|
termctl->c_cflag &= ~(PARODD);
|
|
#ifdef CMSPAR
|
|
if (val == PARITY_SPACE)
|
|
termctl->c_cflag |= CMSPAR;
|
|
#endif
|
|
break;
|
|
case PARITY_ODD:
|
|
case PARITY_MARK:
|
|
termctl->c_cflag |= PARENB | PARODD;
|
|
#ifdef CMSPAR
|
|
if (val == PARITY_MARK)
|
|
termctl->c_cflag |= CMSPAR;
|
|
#endif
|
|
break;
|
|
}
|
|
d->default_parity_on = val != PARITY_NONE;
|
|
}
|
|
|
|
static void
|
|
set_termios_xonoff(struct termios *termctl, int enabled)
|
|
{
|
|
if (enabled) {
|
|
termctl->c_iflag |= (IXON | IXOFF | IXANY);
|
|
termctl->c_cc[VSTART] = 17;
|
|
termctl->c_cc[VSTOP] = 19;
|
|
} else {
|
|
termctl->c_iflag &= ~(IXON | IXOFF | IXANY);
|
|
}
|
|
}
|
|
|
|
static void
|
|
set_termios_rtscts(struct termios *termctl, int enabled)
|
|
{
|
|
if (enabled)
|
|
termctl->c_cflag |= CRTSCTS;
|
|
else
|
|
termctl->c_cflag &= ~CRTSCTS;
|
|
}
|
|
|
|
static void
|
|
set_termios_datasize(struct devcfg_data *d, struct termios *termctl, int size)
|
|
{
|
|
termctl->c_cflag &= ~CSIZE;
|
|
switch (size) {
|
|
case 5: termctl->c_cflag |= CS5; break;
|
|
case 6: termctl->c_cflag |= CS6; break;
|
|
case 7: termctl->c_cflag |= CS7; break;
|
|
case 8: termctl->c_cflag |= CS8; break;
|
|
}
|
|
d->default_data_size = size;
|
|
}
|
|
|
|
/* Initialize a serial port control structure for the first time.
|
|
This should only be called when the port is created. It sets the
|
|
port to the default 9600N81. */
|
|
static void
|
|
devinit(struct devcfg_data *d, struct termios *termctl)
|
|
{
|
|
cfmakeraw(termctl);
|
|
|
|
set_termios_from_speed(d, termctl, find_default_int("speed"));
|
|
set_termios_datasize(d, termctl, find_default_int("databits"));
|
|
d->default_stop_bits = find_default_int("stopbits");
|
|
if (d->default_stop_bits == 1)
|
|
termctl->c_cflag &= ~(CSTOPB);
|
|
else
|
|
termctl->c_cflag |= CSTOPB;
|
|
|
|
set_termios_parity(d, termctl, find_default_int("parity"));
|
|
set_termios_xonoff(termctl, find_default_int("xonxoff"));
|
|
set_termios_rtscts(termctl, find_default_int("rtscts"));
|
|
|
|
if (find_default_int("local"))
|
|
termctl->c_cflag |= CLOCAL;
|
|
else
|
|
termctl->c_cflag &= ~CLOCAL;
|
|
|
|
if (find_default_int("hangup_when_done"))
|
|
termctl->c_cflag &= HUPCL;
|
|
else
|
|
termctl->c_cflag &= ~HUPCL;
|
|
|
|
d->disablebreak = find_default_int("nobreak");
|
|
|
|
termctl->c_cflag |= CREAD;
|
|
termctl->c_iflag |= IGNBRK;
|
|
}
|
|
|
|
/* Configure a serial port control structure based upon input strings
|
|
in instr. These strings are described in the man page for this
|
|
program. */
|
|
static int
|
|
devconfig(struct devcfg_data *d, struct absout *eout, const char *instr,
|
|
int (*otherconfig)(void *data, struct absout *eout, const char *item),
|
|
void *data)
|
|
{
|
|
struct termios *termctl = &d->termctl;
|
|
char *str;
|
|
char *pos;
|
|
char *strtok_data;
|
|
int rv = 0, val;
|
|
|
|
devinit(d, termctl);
|
|
|
|
str = strdup(instr);
|
|
if (str == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
pos = strtok_r(str, " \t", &strtok_data);
|
|
while (pos != NULL) {
|
|
if ((val = speedstr_to_speed(pos)) != -1) {
|
|
rv = set_termios_from_speed(d, termctl, val);
|
|
if (rv) {
|
|
eout->out(eout, "Invalid baud rate: %d\n", d->default_bps);
|
|
goto out;
|
|
}
|
|
} else if (strcmp(pos, "1STOPBIT") == 0) {
|
|
termctl->c_cflag &= ~(CSTOPB);
|
|
d->default_stop_bits = 1;
|
|
} else if (strcmp(pos, "2STOPBITS") == 0) {
|
|
termctl->c_cflag |= CSTOPB;
|
|
d->default_stop_bits = 2;
|
|
} else if (strcmp(pos, "5DATABITS") == 0) {
|
|
set_termios_datasize(d, termctl, 5);
|
|
} else if (strcmp(pos, "6DATABITS") == 0) {
|
|
set_termios_datasize(d, termctl, 6);
|
|
} else if (strcmp(pos, "7DATABITS") == 0) {
|
|
set_termios_datasize(d, termctl, 7);
|
|
} else if (strcmp(pos, "8DATABITS") == 0) {
|
|
set_termios_datasize(d, termctl, 8);
|
|
} else if ((val = lookup_parity(pos)) != -1) {
|
|
set_termios_parity(d, termctl, val);
|
|
} else if (strcmp(pos, "XONXOFF") == 0) {
|
|
set_termios_xonoff(termctl, 1);
|
|
} else if (strcmp(pos, "-XONXOFF") == 0) {
|
|
set_termios_xonoff(termctl, 0);
|
|
} else if (strcmp(pos, "RTSCTS") == 0) {
|
|
set_termios_rtscts(termctl, 1);
|
|
} else if (strcmp(pos, "-RTSCTS") == 0) {
|
|
set_termios_rtscts(termctl, 0);
|
|
} else if (strcmp(pos, "LOCAL") == 0) {
|
|
termctl->c_cflag |= CLOCAL;
|
|
} else if (strcmp(pos, "-LOCAL") == 0) {
|
|
termctl->c_cflag &= ~CLOCAL;
|
|
} else if (strcmp(pos, "HANGUP_WHEN_DONE") == 0) {
|
|
termctl->c_cflag |= HUPCL;
|
|
} else if (strcmp(pos, "-HANGUP_WHEN_DONE") == 0) {
|
|
termctl->c_cflag &= ~HUPCL;
|
|
} else if (strcmp(pos, "NOBREAK") == 0) {
|
|
d->disablebreak = 1;
|
|
} else if (strcmp(pos, "-NOBREAK") == 0) {
|
|
d->disablebreak = 0;
|
|
} else {
|
|
if (otherconfig(data, eout, pos) == -1)
|
|
goto out;
|
|
}
|
|
|
|
pos = strtok_r(NULL, " \t", &strtok_data);
|
|
}
|
|
|
|
#if HAVE_DECL_TIOCSRS485
|
|
d->conf = get_rs485_conf(data);
|
|
#endif
|
|
out:
|
|
free(str);
|
|
return rv;
|
|
}
|
|
|
|
static char *
|
|
baud_string(int speed)
|
|
{
|
|
char *str;
|
|
switch (speed) {
|
|
case B300: str = "300"; break;
|
|
case B600: str = "600"; break;
|
|
case B1200: str = "1200"; break;
|
|
case B2400: str = "2400"; break;
|
|
case B4800: str = "4800"; break;
|
|
case B9600: str = "9600"; break;
|
|
case B19200: str = "19200"; break;
|
|
case B38400: str = "38400"; break;
|
|
case B57600: str = "57600"; break;
|
|
case B115200: str = "115200"; break;
|
|
#ifdef B230400
|
|
case B230400: str = "230400"; break;
|
|
#endif
|
|
#ifdef B460800
|
|
case B460800: str = "460800"; break;
|
|
#endif
|
|
#ifdef B500000
|
|
case B500000: str = "500000"; break;
|
|
#endif
|
|
#ifdef B576000
|
|
case B576000: str = "576000"; break;
|
|
#endif
|
|
#ifdef B921600
|
|
case B921600: str = "921600"; break;
|
|
#endif
|
|
#ifdef B1000000
|
|
case B1000000: str = "1000000"; break;
|
|
#endif
|
|
#ifdef B1152000
|
|
case B1152000: str = "1152000"; break;
|
|
#endif
|
|
#ifdef B1500000
|
|
case B1500000: str = "1500000"; break;
|
|
#endif
|
|
#ifdef B2000000
|
|
case B2000000: str = "2000000"; break;
|
|
#endif
|
|
#ifdef B2500000
|
|
case B2500000: str = "2500000"; break;
|
|
#endif
|
|
#ifdef B3000000
|
|
case B3000000: str = "3000000"; break;
|
|
#endif
|
|
#ifdef B3500000
|
|
case B3500000: str = "3500000"; break;
|
|
#endif
|
|
#ifdef B4000000
|
|
case B4000000: str = "4000000"; break;
|
|
#endif
|
|
default: str = "unknown speed";
|
|
}
|
|
return str;
|
|
}
|
|
|
|
static void
|
|
devcfg_serparm_to_str(struct devio *io, char *str, int strlen)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios *termctl = &d->termctl;
|
|
speed_t speed = cfgetospeed(termctl);
|
|
int stopbits = termctl->c_cflag & CSTOPB;
|
|
int databits = termctl->c_cflag & CSIZE;
|
|
int parity_enabled = termctl->c_cflag & PARENB;
|
|
int parity = termctl->c_cflag & PARODD;
|
|
char *sstr;
|
|
char pchar, schar, dchar;
|
|
|
|
sstr = baud_string(speed);
|
|
|
|
if (stopbits)
|
|
schar = '2';
|
|
else
|
|
schar = '1';
|
|
|
|
switch (databits) {
|
|
case CS7: dchar = '7'; break;
|
|
case CS8: dchar = '8'; break;
|
|
default: dchar = '?';
|
|
}
|
|
|
|
if (parity_enabled) {
|
|
if (parity) {
|
|
pchar = 'O';
|
|
} else {
|
|
pchar = 'E';
|
|
}
|
|
} else {
|
|
pchar = 'N';
|
|
}
|
|
|
|
snprintf(str, strlen, "%s %c%c%c", sstr, pchar, dchar, schar);
|
|
}
|
|
|
|
/* Send the serial port device configuration to the control port. */
|
|
static void
|
|
devcfg_show_devcfg(struct devio *io, struct absout *out)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios *termctl = &d->termctl;
|
|
|
|
speed_t speed = cfgetospeed(termctl);
|
|
int stopbits = termctl->c_cflag & CSTOPB;
|
|
int databits = termctl->c_cflag & CSIZE;
|
|
int parity_enabled = termctl->c_cflag & PARENB;
|
|
int parity = termctl->c_cflag & PARODD;
|
|
int xon = termctl->c_iflag & IXON;
|
|
int xoff = termctl->c_iflag & IXOFF;
|
|
int xany = termctl->c_iflag & IXANY;
|
|
int flow_rtscts = termctl->c_cflag & CRTSCTS;
|
|
int clocal = termctl->c_cflag & CLOCAL;
|
|
int hangup_when_done = termctl->c_cflag & HUPCL;
|
|
char *str;
|
|
|
|
out->out(out, "%s ", baud_string(speed));
|
|
|
|
if (xon && xoff && xany) {
|
|
out->out(out, "XONXOFF ");
|
|
}
|
|
|
|
if (flow_rtscts) {
|
|
out->out(out, "RTSCTS ");
|
|
}
|
|
|
|
if (clocal) {
|
|
out->out(out, "LOCAL ");
|
|
}
|
|
|
|
if (hangup_when_done) {
|
|
out->out(out, "HANGUP_WHEN_DONE ");
|
|
}
|
|
|
|
if (stopbits) {
|
|
str = "2STOPBITS";
|
|
} else {
|
|
str = "1STOPBIT";
|
|
}
|
|
out->out(out, "%s ", str);
|
|
|
|
switch (databits) {
|
|
case CS7: str = "7DATABITS"; break;
|
|
case CS8: str = "8DATABITS"; break;
|
|
default: str = "unknown databits";
|
|
}
|
|
out->out(out, "%s ", str);
|
|
|
|
if (parity_enabled) {
|
|
if (parity) {
|
|
str = "ODD";
|
|
} else {
|
|
str = "EVEN";
|
|
}
|
|
} else {
|
|
str = "NONE";
|
|
}
|
|
out->out(out, "%s", str);
|
|
}
|
|
|
|
static int
|
|
devcfg_set_devcontrol(struct devio *io, const char *instr)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
int fd = d->devfd;
|
|
int rv = 0;
|
|
char *str;
|
|
char *pos;
|
|
int status;
|
|
char *strtok_data;
|
|
|
|
str = malloc(strlen(instr) + 1);
|
|
if (str == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
strcpy(str, instr);
|
|
|
|
pos = strtok_r(str, " \t", &strtok_data);
|
|
while (pos != NULL) {
|
|
if (strcmp(pos, "RTSHI") == 0) {
|
|
ioctl(fd, TIOCMGET, &status);
|
|
status |= TIOCM_RTS;
|
|
ioctl(fd, TIOCMSET, &status);
|
|
} else if (strcmp(pos, "RTSLO") == 0) {
|
|
ioctl(fd, TIOCMGET, &status);
|
|
status &= ~TIOCM_RTS;
|
|
ioctl(fd, TIOCMSET, &status);
|
|
} else if (strcmp(pos, "DTRHI") == 0) {
|
|
ioctl(fd, TIOCMGET, &status);
|
|
status |= TIOCM_DTR;
|
|
ioctl(fd, TIOCMSET, &status);
|
|
} else if (strcmp(pos, "DTRLO") == 0) {
|
|
ioctl(fd, TIOCMGET, &status);
|
|
status &= ~TIOCM_DTR; /* AKA drop DTR */
|
|
ioctl(fd, TIOCMSET, &status);
|
|
} else {
|
|
rv = -1;
|
|
goto out;
|
|
}
|
|
|
|
pos = strtok_r(NULL, " \t", &strtok_data);
|
|
}
|
|
|
|
out:
|
|
free(str);
|
|
return rv;
|
|
}
|
|
|
|
static void
|
|
devcfg_show_devcontrol(struct devio *io, struct absout *out)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
char *str;
|
|
int status;
|
|
|
|
ioctl(d->devfd, TIOCMGET, &status);
|
|
|
|
if (status & TIOCM_RTS) {
|
|
str = "RTSHI";
|
|
} else {
|
|
str = "RTSLO";
|
|
}
|
|
out->out(out, "%s ", str);
|
|
|
|
if (status & TIOCM_DTR) {
|
|
str = "DTRHI";
|
|
} else {
|
|
str = "DTRLO";
|
|
}
|
|
out->out(out, "%s ", str);
|
|
}
|
|
|
|
static void
|
|
do_read(int fd, void *data)
|
|
{
|
|
struct devio *io = data;
|
|
io->read_handler(io);
|
|
}
|
|
|
|
static void
|
|
do_write(int fd, void *data)
|
|
{
|
|
struct devio *io = data;
|
|
io->write_handler(io);
|
|
}
|
|
|
|
static void
|
|
do_except(int fd, void *data)
|
|
{
|
|
struct devio *io = data;
|
|
io->except_handler(io);
|
|
}
|
|
|
|
static int calc_bpc(struct devcfg_data *d)
|
|
{
|
|
return d->current_data_size + d->current_stop_bits +
|
|
d->current_parity_on + 1;
|
|
}
|
|
|
|
static void
|
|
devfd_fd_cleared(int fd, void *cb_data)
|
|
{
|
|
struct devio *io = cb_data;
|
|
struct devcfg_data *d = io->my_data;
|
|
void (*shutdown_done)(struct devio *) = d->shutdown_done;
|
|
struct termios termio;
|
|
|
|
/* Disable flow control to avoid a long shutdown. */
|
|
if (tcgetattr(d->devfd, &termio) != -1) {
|
|
termio.c_iflag &= ~(IXON | IXOFF);
|
|
termio.c_cflag &= ~CRTSCTS;
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
tcflush(d->devfd, TCOFLUSH);
|
|
close(d->devfd);
|
|
d->devfd = -1;
|
|
uucp_rm_lock(io->devname);
|
|
shutdown_done(io);
|
|
}
|
|
|
|
static int devcfg_setup(struct devio *io, const char *name, const char **errstr,
|
|
int *bps, int *bpc)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
int options;
|
|
int rv;
|
|
|
|
rv = uucp_mk_lock(io->devname);
|
|
if (rv > 0 ) {
|
|
*errstr = "Port already in use by another process\r\n";
|
|
return -1;
|
|
} else if (rv < 0) {
|
|
*errstr = "Error creating port lock file\r\n";
|
|
return -1;
|
|
}
|
|
|
|
*bps = d->current_bps = d->default_bps;
|
|
d->current_data_size = d->default_data_size;
|
|
d->current_stop_bits = d->default_stop_bits;
|
|
d->current_parity_on = d->default_parity_on;
|
|
*bpc = calc_bpc(d);
|
|
|
|
/* Oct 05 2001 druzus: NOCTTY - don't make
|
|
device control tty for our process */
|
|
options = O_NONBLOCK | O_NOCTTY;
|
|
if (io->read_disabled) {
|
|
options |= O_WRONLY;
|
|
} else {
|
|
options |= O_RDWR;
|
|
}
|
|
d->devfd = open(io->devname, options);
|
|
if (d->devfd == -1) {
|
|
syslog(LOG_ERR, "Could not open device %s for port %s: %m",
|
|
io->devname,
|
|
name);
|
|
uucp_rm_lock(io->devname);
|
|
return -1;
|
|
}
|
|
|
|
if (!io->read_disabled && tcsetattr(d->devfd, TCSANOW, &d->termctl) == -1)
|
|
{
|
|
close(d->devfd);
|
|
d->devfd = -1;
|
|
syslog(LOG_ERR, "Could not set up device %s for port %s: %m",
|
|
io->devname,
|
|
name);
|
|
uucp_rm_lock(io->devname);
|
|
return -1;
|
|
}
|
|
|
|
/* Turn off BREAK. */
|
|
if (!io->read_disabled && !d->disablebreak
|
|
&& ioctl(d->devfd, TIOCCBRK) == -1) {
|
|
/* Probably not critical, but we should at least log something. */
|
|
syslog(LOG_ERR, "Could not turn off break for device %s port %s: %m",
|
|
io->devname,
|
|
name);
|
|
}
|
|
|
|
#if HAVE_DECL_TIOCSRS485
|
|
if (d->conf) {
|
|
if (d->conf->flags & SER_RS485_ENABLED) {
|
|
if (ioctl(d->devfd , TIOCSRS485, d->conf ) < 0) {
|
|
syslog(LOG_ERR, "Could not set RS485 config for device %s port %s: %m",
|
|
io->devname,
|
|
name);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
sel_set_fd_handlers(ser2net_sel, d->devfd, io,
|
|
io->read_disabled ? NULL : do_read,
|
|
do_write, do_except, devfd_fd_cleared);
|
|
return 0;
|
|
}
|
|
|
|
static void devcfg_shutdown(struct devio *io,
|
|
void (*shutdown_done)(struct devio *))
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
/* To avoid blocking on close if we have written bytes and are in
|
|
flow-control, we flush the output queue. */
|
|
if (d->devfd != -1) {
|
|
d->shutdown_done = shutdown_done;
|
|
sel_clear_fd_handlers(ser2net_sel, d->devfd);
|
|
} else {
|
|
shutdown_done(io);
|
|
}
|
|
}
|
|
|
|
static int devcfg_read(struct devio *io, void *buf, size_t size)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
return read(d->devfd, buf, size);
|
|
}
|
|
|
|
static int devcfg_write(struct devio *io, void *buf, size_t size)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
return write(d->devfd, buf, size);
|
|
}
|
|
|
|
static void devcfg_read_handler_enable(struct devio *io, int enabled)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
sel_set_fd_read_handler(ser2net_sel, d->devfd,
|
|
enabled ? SEL_FD_HANDLER_ENABLED :
|
|
SEL_FD_HANDLER_DISABLED);
|
|
}
|
|
|
|
static void devcfg_write_handler_enable(struct devio *io, int enabled)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
sel_set_fd_write_handler(ser2net_sel, d->devfd,
|
|
enabled ? SEL_FD_HANDLER_ENABLED :
|
|
SEL_FD_HANDLER_DISABLED);
|
|
}
|
|
|
|
static void devcfg_except_handler_enable(struct devio *io, int enabled)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
sel_set_fd_except_handler(ser2net_sel, d->devfd,
|
|
enabled ? SEL_FD_HANDLER_ENABLED :
|
|
SEL_FD_HANDLER_DISABLED);
|
|
}
|
|
|
|
static int devcfg_send_break(struct devio *io)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
tcsendbreak(d->devfd, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_get_modem_state(struct devio *io, unsigned char *modemstate)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
int val;
|
|
|
|
if (ioctl(d->devfd, TIOCMGET, &val) != 0)
|
|
return -1;
|
|
|
|
*modemstate = 0;
|
|
if (val & TIOCM_CD)
|
|
*modemstate |= 0x80;
|
|
if (val & TIOCM_RI)
|
|
*modemstate |= 0x40;
|
|
if (val & TIOCM_DSR)
|
|
*modemstate |= 0x20;
|
|
if (val & TIOCM_CTS)
|
|
*modemstate |= 0x10;
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_baud_rate(struct devio *io, int *val, int cisco, int *bps)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios termio;
|
|
|
|
if (tcgetattr(d->devfd, &termio) == -1) {
|
|
*val = 0;
|
|
return -1;
|
|
}
|
|
|
|
if ((*val != 0) && (get_baud_rate(*val, val, cisco, bps))) {
|
|
/* We have a valid baud rate. */
|
|
cfsetispeed(&termio, *val);
|
|
cfsetospeed(&termio, *val);
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
|
|
tcgetattr(d->devfd, &termio);
|
|
*val = cfgetispeed(&termio);
|
|
get_rate_from_baud_rate(*val, val, cisco);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_data_size(struct devio *io, unsigned char *val, int *bpc)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios termio;
|
|
|
|
if (tcgetattr(d->devfd, &termio) == -1) {
|
|
*val = 0;
|
|
return -1;
|
|
}
|
|
|
|
if ((*val >= 5) && (*val <= 8)) {
|
|
termio.c_cflag &= ~CSIZE;
|
|
switch (*val) {
|
|
case 5: termio.c_cflag |= CS5; d->current_data_size = 5; break;
|
|
case 6: termio.c_cflag |= CS6; d->current_data_size = 6; break;
|
|
case 7: termio.c_cflag |= CS7; d->current_data_size = 7; break;
|
|
case 8: termio.c_cflag |= CS8; d->current_data_size = 8; break;
|
|
}
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
|
|
switch (termio.c_cflag & CSIZE) {
|
|
case CS5: *val = 5; break;
|
|
case CS6: *val = 6; break;
|
|
case CS7: *val = 7; break;
|
|
case CS8: *val = 8; break;
|
|
default: *val = 0;
|
|
}
|
|
|
|
*bpc = calc_bpc(d);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_parity(struct devio *io, unsigned char *val, int *bpc)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios termio;
|
|
|
|
if (tcgetattr(d->devfd, &termio) == -1) {
|
|
*val = 0;
|
|
return -1;
|
|
}
|
|
|
|
/* We don't support MARK or SPACE parity. */
|
|
if ((*val >= 1) && (*val <= 3)) {
|
|
termio.c_cflag &= ~(PARENB | PARODD);
|
|
switch (*val) {
|
|
case 1: d->current_parity_on = 0; break; /* NONE */
|
|
case 2: termio.c_cflag |= PARENB | PARODD; /* ODD */
|
|
d->current_parity_on = 1;
|
|
break;
|
|
case 3: termio.c_cflag |= PARENB; /* EVEN */
|
|
d->current_parity_on = 1;
|
|
break;
|
|
}
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
|
|
if (termio.c_cflag & PARENB) {
|
|
if (termio.c_cflag & PARODD)
|
|
*val = 2; /* ODD */
|
|
else
|
|
*val = 3; /* EVEN */
|
|
} else
|
|
*val = 1; /* NONE */
|
|
|
|
*bpc = calc_bpc(d);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_stop_size(struct devio *io, unsigned char *val, int *bpc)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios termio;
|
|
|
|
if (tcgetattr(d->devfd, &termio) == -1) {
|
|
*val = 0;
|
|
return -1;
|
|
}
|
|
|
|
if ((*val >= 1) && (*val <= 2)) {
|
|
termio.c_cflag &= ~CSTOPB;
|
|
switch (*val) {
|
|
case 1: d->current_stop_bits = 1; break; /* 1 stop bit */
|
|
case 2: d->current_stop_bits = 2; /* 2 stop bits */
|
|
termio.c_cflag |= CSTOPB;
|
|
break;
|
|
}
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
|
|
if (termio.c_cflag & CSTOPB)
|
|
*val = 2; /* 2 stop bits. */
|
|
else
|
|
*val = 1; /* 1 stop bit. */
|
|
|
|
*bpc = calc_bpc(d);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_flow_control(struct devio *io, unsigned char val)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
tcflow(d->devfd, val ? TCIOFF : TCION);
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_control(struct devio *io, unsigned char *val)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
struct termios termio;
|
|
int ival;
|
|
|
|
if (tcgetattr(d->devfd, &termio) == -1) {
|
|
*val = 0;
|
|
return -1;
|
|
}
|
|
|
|
switch (*val) {
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
/* Outbound/both flow control */
|
|
if (tcgetattr(d->devfd, &termio) != -1) {
|
|
if (*val != 0) {
|
|
termio.c_iflag &= ~(IXON | IXOFF);
|
|
termio.c_cflag &= ~CRTSCTS;
|
|
switch (*val) {
|
|
case 1: break; /* NONE */
|
|
case 2: termio.c_iflag |= IXON | IXOFF; break;
|
|
case 3: termio.c_cflag |= CRTSCTS; break;
|
|
}
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
if (termio.c_cflag & CRTSCTS)
|
|
*val = 3;
|
|
else if (termio.c_iflag & IXON)
|
|
*val = 2;
|
|
else
|
|
*val = 1;
|
|
}
|
|
break;
|
|
|
|
case 13:
|
|
case 14:
|
|
case 15:
|
|
case 16:
|
|
case 17:
|
|
case 18:
|
|
case 19:
|
|
/* Inbound flow-control */
|
|
if (tcgetattr(d->devfd, &termio) != -1) {
|
|
if (*val == 15) {
|
|
/* We can only set XON/XOFF independently */
|
|
termio.c_iflag |= IXOFF;
|
|
tcsetattr(d->devfd, TCSANOW, &termio);
|
|
}
|
|
if (termio.c_cflag & CRTSCTS)
|
|
*val = 16;
|
|
else if (termio.c_iflag & IXOFF)
|
|
*val = 15;
|
|
else
|
|
*val = 14;
|
|
}
|
|
break;
|
|
|
|
/* Handle BREAK stuff. */
|
|
case 6:
|
|
if (ioctl(d->devfd, TIOCCBRK) != -1)
|
|
d->break_set = 0;
|
|
goto read_break_val;
|
|
|
|
case 5:
|
|
if (ioctl(d->devfd, TIOCSBRK) != -1)
|
|
d->break_set = 1;
|
|
goto read_break_val;
|
|
|
|
case 4:
|
|
read_break_val:
|
|
if (d->break_set)
|
|
*val = 5;
|
|
else
|
|
*val = 6;
|
|
break;
|
|
|
|
/* DTR handling */
|
|
case 8:
|
|
#ifndef __CYGWIN__
|
|
ival = TIOCM_DTR;
|
|
ioctl(d->devfd, TIOCMBIS, &ival);
|
|
#else
|
|
ioctl(d->devfd, TIOCMGET, &ival);
|
|
ival |= TIOCM_DTR;
|
|
ioctl(d->devfd, TIOCMSET, &ival);
|
|
#endif
|
|
goto read_dtr_val;
|
|
|
|
case 9:
|
|
#ifndef __CYGWIN__
|
|
ival = TIOCM_DTR;
|
|
ioctl(d->devfd, TIOCMBIC, &ival);
|
|
#else
|
|
ioctl(d->devfd, TIOCMGET, &ival);
|
|
ival &= ~TIOCM_DTR;
|
|
ioctl(d->devfd, TIOCMSET, &ival);
|
|
#endif
|
|
goto read_dtr_val;
|
|
|
|
case 7:
|
|
read_dtr_val:
|
|
if (ioctl(d->devfd, TIOCMGET, &ival) == -1)
|
|
*val = 7;
|
|
else if (ival & TIOCM_DTR)
|
|
*val = 8;
|
|
else
|
|
*val = 9;
|
|
break;
|
|
|
|
/* RTS handling */
|
|
case 11:
|
|
#ifndef __CYGWIN__
|
|
ival = TIOCM_RTS;
|
|
ioctl(d->devfd, TIOCMBIS, &ival);
|
|
#else
|
|
ioctl(d->devfd, TIOCMGET, &ival);
|
|
ival |= TIOCM_RTS;
|
|
ioctl(d->devfd, TIOCMSET, &ival);
|
|
#endif
|
|
goto read_rts_val;
|
|
|
|
case 12:
|
|
#ifndef __CYGWIN__
|
|
ival = TIOCM_RTS;
|
|
ioctl(d->devfd, TIOCMBIC, &ival);
|
|
#else
|
|
ioctl(d->devfd, TIOCMGET, &ival);
|
|
ival &= ~TIOCM_RTS;
|
|
ioctl(d->devfd, TIOCMSET, &ival);
|
|
#endif
|
|
goto read_rts_val;
|
|
|
|
case 10:
|
|
read_rts_val:
|
|
if (ioctl(d->devfd, TIOCMGET, &ival) == -1)
|
|
*val = 10;
|
|
else if (ival & TIOCM_RTS)
|
|
*val = 11;
|
|
else
|
|
*val = 12;
|
|
break;
|
|
|
|
default:
|
|
*val = 0;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int devcfg_flush(struct devio *io, int *val)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
int ival;
|
|
|
|
switch (*val) {
|
|
case DEVIO_FLUSH_INPUT: ival = TCIFLUSH; goto purge_found;
|
|
case DEVIO_FLUSH_OUTPUT: ival = TCOFLUSH; goto purge_found;
|
|
case DEVIO_FLUSH_INPUT | DEVIO_FLUSH_OUTPUT:
|
|
ival = TCIOFLUSH; goto purge_found;
|
|
}
|
|
*val = 0;
|
|
return -1;
|
|
purge_found:
|
|
tcflush(d->devfd, ival);
|
|
return 0;
|
|
}
|
|
|
|
static void devcfg_free(struct devio *io)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
if (d->devfd != -1)
|
|
close(d->devfd);
|
|
io->my_data = NULL;
|
|
free(d);
|
|
}
|
|
|
|
static int
|
|
devcfg_reconfig(struct devio *io, struct absout *eout, const char *instr,
|
|
int (*otherconfig)(void *data, struct absout *eout,
|
|
const char *item),
|
|
void *data)
|
|
{
|
|
struct devcfg_data *d = io->my_data;
|
|
|
|
return devconfig(d, eout, instr, otherconfig, data);
|
|
}
|
|
|
|
static struct devio_f devcfg_io_f = {
|
|
.setup = devcfg_setup,
|
|
.shutdown = devcfg_shutdown,
|
|
.reconfig = devcfg_reconfig,
|
|
.read = devcfg_read,
|
|
.write = devcfg_write,
|
|
.read_handler_enable = devcfg_read_handler_enable,
|
|
.write_handler_enable = devcfg_write_handler_enable,
|
|
.except_handler_enable = devcfg_except_handler_enable,
|
|
.send_break = devcfg_send_break,
|
|
.get_modem_state = devcfg_get_modem_state,
|
|
.set_devcontrol = devcfg_set_devcontrol,
|
|
.show_devcontrol = devcfg_show_devcontrol,
|
|
.show_devcfg = devcfg_show_devcfg,
|
|
.baud_rate = devcfg_baud_rate,
|
|
.data_size = devcfg_data_size,
|
|
.parity = devcfg_parity,
|
|
.stop_size = devcfg_stop_size,
|
|
.control = devcfg_control,
|
|
.flow_control = devcfg_flow_control,
|
|
.flush = devcfg_flush,
|
|
.free = devcfg_free,
|
|
.serparm_to_str = devcfg_serparm_to_str
|
|
};
|
|
|
|
int
|
|
devcfg_init(struct devio *io, struct absout *eout, const char *instr,
|
|
int (*otherconfig)(void *data, struct absout *eout,
|
|
const char *item),
|
|
void *data)
|
|
{
|
|
struct devcfg_data *d;
|
|
|
|
d = malloc(sizeof(*d));
|
|
if (!d)
|
|
return -1;
|
|
memset(d, 0, sizeof(*d));
|
|
d->devfd = -1;
|
|
|
|
if (devconfig(d, eout, instr, otherconfig, data) == -1) {
|
|
free(d);
|
|
return -1;
|
|
}
|
|
|
|
io->my_data = d;
|
|
io->f = &devcfg_io_f;
|
|
return 0;
|
|
}
|
|
|