1 /*
2  * termios2.c
3  *
4  * Use termios2 interface to set custom baud rates to serial ports.
5  *
6  * by Nick Patavalis (npat@efault.net)
7  *
8  * ATTENTION: Linux-specific kludge!
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23  * USA
24  */
25 
26 #include "custbaud.h"
27 
28 #if defined(__linux__) && defined(USE_CUSTOM_BAUD)
29 
30 #include <linux/version.h>
31 #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,0)
32 #error "This code requires Linux kernel > 2.6!"
33 #endif
34 
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <termios.h>
39 #include <sys/ioctl.h>
40 
41 /* Contains the definition of the termios2 structure and some related
42    constants that we should normally include from system
43    headers. Unfortunatelly, we can't. See comments in "termbits2.h"
44    for more. */
45 #include "termbits2.h"
46 
47 /* GLIBC termios use an (otherwise unused) bit in c_iflags to
48    internally record the fact that ispeed was set to zero (which is
49    special behavior and means "same as ospeed". We want to clear this
50    bit before passing c_iflags back to the kernel. See:
51 
52        <glibc-source>/sysdeps/unix/sysv/linux/speed.c
53 */
54 #define IBAUD0 020000000000
55 
56 int
tc2setattr(int fd,int optional_actions,const struct termios * tios)57 tc2setattr(int fd, int optional_actions, const struct termios *tios)
58 {
59     struct termios2 t2;
60     int cmd;
61 
62     switch (optional_actions) {
63     case TCSANOW:
64         cmd = IOCTL_SETS;
65         break;
66     case TCSADRAIN:
67         cmd = IOCTL_SETSW;
68         break;
69     case TCSAFLUSH:
70         cmd = IOCTL_SETSF;
71         break;
72     default:
73         errno = EINVAL;
74         return -1;
75     }
76 
77     t2.c_iflag = tios->c_iflag & ~IBAUD0;
78     t2.c_oflag = tios->c_oflag;
79     t2.c_cflag = tios->c_cflag;
80     t2.c_lflag = tios->c_lflag;
81     t2.c_line = tios->c_line;
82     t2.c_ispeed = tios->c_ispeed;
83     t2.c_ospeed = tios->c_ospeed;
84     memcpy(&t2.c_cc[0], &tios->c_cc[0], K_NCCS * sizeof (cc_t));
85 
86     return ioctl(fd, cmd, &t2);
87 }
88 
89 int
tc2getattr(int fd,struct termios * tios)90 tc2getattr(int fd, struct termios *tios)
91 {
92     struct termios2 t2;
93     size_t i;
94     int r;
95 
96     r = ioctl(fd, IOCTL_GETS, &t2);
97     if (r < 0) return r;
98 
99     tios->c_iflag = t2.c_iflag;
100     tios->c_oflag = t2.c_oflag;
101     tios->c_cflag = t2.c_cflag;
102     tios->c_lflag = t2.c_lflag;
103     tios->c_line = t2.c_line;
104     tios->c_ispeed = t2.c_ispeed;
105     tios->c_ospeed = t2.c_ospeed;
106     memcpy(&tios->c_cc[0], &t2.c_cc[0], K_NCCS * sizeof (cc_t));
107 
108     for (i = K_NCCS; i < NCCS; i++)
109         tios->c_cc[i] = _POSIX_VDISABLE;
110 
111     return 0;
112 }
113 
114 /* The termios2 interface supports separate input and output
115    speeds. GLIBC's termios support only one terminal speed. So the
116    standard tcsetispeed(3), actually sets the output-speed field, not
117    the input-speed field (or does nothing if speed == B0). Use
118    cf2setispeed if you want to set a *standard* input speed (one of
119    the Bxxxxx speeds) that may be different from the output
120    speed. Also if someone, somehow, has set the input speed to
121    something other than B0, then you *must* use cf2setispeed() to
122    change it. Using the standard cfsetispeed() obviously won't do
123    (since it affects only the output-speed field).
124 */
125 
126 int
cf2setispeed(struct termios * tios,speed_t speed)127 cf2setispeed(struct termios *tios, speed_t speed)
128 {
129     if ( (speed & ~CBAUD) != 0
130          && (speed < B57600 || speed > __MAX_BAUD) ) {
131         errno = EINVAL;
132         return -1;
133     }
134     tios->c_ispeed = speed;
135     tios->c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT);
136     tios->c_cflag |= (speed << IBSHIFT);
137 
138     return 0;
139 }
140 
141 speed_t
cf2getispeed(struct termios * tios)142 cf2getispeed(struct termios *tios)
143 {
144     return (tios->c_cflag >> IBSHIFT) & (CBAUD | CBAUDEX);
145 }
146 
147 /* Use these to set custom input or output speeds (i.e. speeds that do
148    not necessarily correspond to one of the Bxxx macros. */
149 
150 int
cf2setospeed_custom(struct termios * tios,int speed)151 cf2setospeed_custom(struct termios *tios, int speed)
152 {
153     if ( speed <= 0 ) {
154         errno = EINVAL;
155         return -1;
156     }
157     tios->c_cflag &= ~(CBAUD | CBAUDEX);
158     tios->c_cflag |= BOTHER;
159     tios->c_ospeed = speed;
160 
161     return 0;
162 }
163 
164 int
cf2setispeed_custom(struct termios * tios,int speed)165 cf2setispeed_custom(struct termios *tios, int speed)
166 {
167     if ( speed < 0 ) {
168         errno = EINVAL;
169         return -1;
170     }
171     if ( speed == 0 ) {
172         /* Special case: ispeed == 0 means "same as ospeed". Kernel
173            does this if it sees B0 in the "CIBAUD" field (i.e. in
174            CBAUD << IBSHIFT) */
175         tios->c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT);
176         tios->c_cflag |= (B0 << IBSHIFT);
177     } else {
178         tios->c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT);
179         tios->c_cflag |= (BOTHER << IBSHIFT);
180         tios->c_ispeed = speed;
181     }
182 
183     return 0;
184 }
185 
186 /***************************************************************************/
187 
188 #endif /* __linux__ && USE_CUSTOM_BAUD */
189 
190 /*
191  * Local Variables:
192  * mode:c
193  * tab-width: 4
194  * c-basic-offset: 4
195  * End:
196  */
197