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