198 lines
4.7 KiB
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;
|
|
}
|