/* Copyright (C) 2006 AbsoluteValue Systems, Inc. All Rights Reserved. Originally written and maintained by: Solomon Peachy Local redirection & Debugging code by: Mark Mathews 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 . */ #include "common.h" #include #include #include #include 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; }