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