sobredirect/common.c

198 lines
4.7 KiB
C

/*
Copyright (C) 2006 AbsoluteValue Systems, Inc. All Rights Reserved.
Originally written and maintained by: Solomon Peachy <pizza@shaftnet.org>
Local redirection & Debugging code by: Mark Mathews <mark@linux-wlan.com>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <termios.h>
#include <sys/ioctl.h>
#include <linux/tty.h>
#include <linux/serial.h>
int setserial(struct termios *cfg, int ser_fd, int baud,
int databits, char parity, int stopbits,
int hw_flow, int sw_flow, int modemlines)
{
int rval = 0;
int custom = 0;
rval = tcgetattr(ser_fd, cfg);
if (rval) goto done;
cfmakeraw(cfg);
cfg->c_cc[VMIN] = 1; /* 1 byte minimum */
cfg->c_cc[VTIME] = 0; /* No timeout */
switch(baud) {
SERIAL_BAUD_CASE(50);
SERIAL_BAUD_CASE(75);
SERIAL_BAUD_CASE(110);
SERIAL_BAUD_CASE(134);
SERIAL_BAUD_CASE(150);
SERIAL_BAUD_CASE(200);
SERIAL_BAUD_CASE(300);
SERIAL_BAUD_CASE(600);
SERIAL_BAUD_CASE(1200);
SERIAL_BAUD_CASE(1800);
SERIAL_BAUD_CASE(2400);
SERIAL_BAUD_CASE(4800);
SERIAL_BAUD_CASE(9600);
SERIAL_BAUD_CASE(19200);
SERIAL_BAUD_CASE(38400);
SERIAL_BAUD_CASE(57600);
SERIAL_BAUD_CASE(115200);
SERIAL_BAUD_CASE(230400);
SERIAL_BAUD_CASE(460800);
SERIAL_BAUD_CASE(576000);
SERIAL_BAUD_CASE(921600);
SERIAL_BAUD_CASE(1000000);
SERIAL_BAUD_CASE(1152000);
SERIAL_BAUD_CASE(1500000);
SERIAL_BAUD_CASE(2000000);
SERIAL_BAUD_CASE(2500000);
SERIAL_BAUD_CASE(3000000);
SERIAL_BAUD_CASE(3500000);
SERIAL_BAUD_CASE(4000000);
default:
fprintf(stderr, "Non-standard baud rate specified; may not function (%d)\n",
baud);
cfsetispeed(cfg, B38400);
cfsetospeed(cfg, B38400);
custom = 1;
}
parity &= ~0x20; /* make things case insensitive */
switch (parity) {
case 'N':
cfg->c_cflag &= ~PARENB;
break;
case 'E':
cfg->c_cflag |= PARENB;
cfg->c_cflag &= ~PARODD;
break;
case 'O':
cfg->c_cflag |= PARENB;
cfg->c_cflag |= PARODD;
break;
default:
fprintf(stderr, "Invalid serial parity specified (%c), should be E/O/N\n",
parity);
rval = 2;
goto done;
}
cfg->c_cflag &= ~CSIZE;
switch(databits) {
SERIAL_DATA_CASE(5);
SERIAL_DATA_CASE(6);
SERIAL_DATA_CASE(7);
SERIAL_DATA_CASE(8);
default:
fprintf(stderr, "Invalid number of data bits specified (%d), should be between 5 and 8.\n",
databits);
rval = 3;
goto done;
}
switch (stopbits) {
case 1:
cfg->c_cflag &= ~CSTOPB;
break;
case 2:
cfg->c_cflag |= CSTOPB;
break;
default:
fprintf(stderr, "Invalid number of stop bits specified (%d), should be 1 or 2.\n",
stopbits);
rval = 4;
goto done;
}
cfg->c_iflag &= ~CRTSCTS;
cfg->c_iflag &= ~(IXON|IXOFF|IXANY);
if (hw_flow) {
cfg->c_iflag |= CRTSCTS;
}
if (sw_flow) {
cfg->c_iflag |= (IXON|IXOFF|IXANY);
cfg->c_cc[VSTART] = 17;
cfg->c_cc[VSTOP] = 19;
}
if (modemlines) {
cfg->c_cflag |= HUPCL;
cfg->c_cflag &= ~CLOCAL;
} else {
cfg->c_cflag |= CLOCAL;
cfg->c_cflag &= ~HUPCL;
}
cfg->c_cflag |= CREAD;
cfg->c_iflag |= PARMRK;
cfg->c_iflag &= ~(IGNPAR | BRKINT | IGNBRK);
rval = tcsetattr(ser_fd, TCSANOW, cfg);
if (rval) goto done;
if (custom) {
struct serial_struct serinfo;
if (ioctl(ser_fd, TIOCGSERIAL, &serinfo) < 0) {
perror("Can't get serial info:");
goto done;
}
serinfo.flags &= ~ASYNC_SPD_MASK;
serinfo.flags |= ASYNC_SPD_CUST;
serinfo.custom_divisor = serinfo.baud_base / baud;
if (ioctl(ser_fd, TIOCSSERIAL, &serinfo) < 0) {
perror("Can't set serial info:");
goto done;
}
}
done:
if (rval) fprintf(stderr, "ERROR.. %d\n", rval);
{
struct termios cfg2;
tcgetattr(ser_fd, &cfg2);
if (cfg->c_iflag != cfg2.c_iflag) {
fprintf(stderr, "ERROR! c_iflag %x vs %x\n",
cfg->c_iflag, cfg2.c_iflag);
}
if (cfg->c_oflag != cfg2.c_oflag) {
fprintf(stderr, "ERROR! c_oflag %x vs %x\n",
cfg->c_oflag, cfg2.c_oflag);
}
if (cfg->c_cflag != cfg2.c_cflag) {
fprintf(stderr, "ERROR! c_cflag %x vs %x\n",
cfg->c_cflag, cfg2.c_cflag);
}
if (cfg->c_lflag != cfg2.c_lflag) {
fprintf(stderr, "ERROR! c_lflag %x vs %x\n",
cfg->c_lflag, cfg2.c_lflag);
}
}
return rval;
}