-
Notifications
You must be signed in to change notification settings - Fork 127
/
termios2.txt
174 lines (136 loc) · 7.52 KB
/
termios2.txt
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
Linux and custom serial-port baudrates (the gory details)
=========================================================
Support for custom baudrate setting and reading in Linux is done
through the "new" "termios2" terminal-attributes structure, and the
respective ioctls: TCSETS2, TCSETSW2, TCSETSF2, and TCGETS2.
The "termios2" structure is defined in:
<linux-kernel>/arch/<arch>/include/uapi/asm/termbits.h
or <linux-kernel>/include/uapi/asm-generic/termbits.h
which may have been coppied in your system headers directories as:
/usr/include/<arch>/asm/termbits.h
or /usr/include/asm-generic/termbits.h
The termios2 structure looks like this:
#define NCCS 19
struct termios2 {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
In the same files you will also find defined some relevant macros
(constants, flags, and bit-fields, for the "termios2" "c_*flag"
fields).
Important aside: Unfortunatelly, we cannot include the above-mentioned
files in our code, since they clash badly with stuff defined in the
LIBC-provided <termios.h> header files (where the user-visible
"termios" structure is defined). Because of this clash between LIBC
and the linux headers, we have to manually copy the "termios2"
definition (and a few relevant constants) into our sources for the
whole thing to work. This is definitely very klugy, but I can see no
better way to make it work (after all, GLIBC does the same
thing---replicates the kernel definitions itself---for the older
"termios" interface).
End aside, on with it...
The new ioctls TCSETS2, TCSETSW2, TCSETSF2 pass a "termios2" structure
to the kernel in order to set the tty attributes (among which the
serial port's baudrate). The corresponding TCGETS2 ioctl retrieves a
"termios2" structure from the kernel corresponding to the actual,
effective tty settings.
These ioctls are used by the "tc2setattr()" and "tc2getattr()"
functions (see file "termios2.c"). These functions are passed a
userspace, LIBC-defined "termios" structure (which is very similar,
but not necessarily identical to the kernel's "termios2"), call the
respective ioctls, and copy the relevant information to or from the
"termios2" structure (as expected or returned by the kernel).
The game between the kernel and the "termios2" structure, regarding
how baudrate-related information is interpretted, is played like this:
Bits "c_cflags & (CBAUD | CBAUDEX)" (they are sometimes called: "the
CBAUD field"), together with field "c_ospeed" control the output
baudrate.
Bits "c_cflags & ((CBAUD | CBAUDEX) << IBSHIFT)" (they are sometimes
called: "the CIBAUD field"), together with field "c_ispeed" control
the input baudrate.
BTW: Usually CBAUD & CBAUDEX == CBAUD. That is, CBAUDEX *is*
one of the CBAUD bits. See end of this file for more on the usual
relationship of CBAUD, CBAUDEX, BOTHER
When issuing one of the TCSETS*2 ioctls, everything contained in the
"termios2" structure is copied to a kernel-resident structure and the
respective serial driver is notified. Upon the serial driver's
request, the kernel determines the output baudrate. If the kernel sees
that:
c_cflag & CBAUD == BOTHER
then "c_ospeed" is passed to the serial driver as the output
baudrate. Otherwise "c_cflag & (CBAUD | CBAUDEX)" is matched against a
table of standard baudrates (coresponding to the "Bxxxx" macros). The
matching baudrate is located and passed to the serial driver as the
output baudrate.
You can see the respective code in "drivers/tty/tty_ioctl.c" (all
files form now on relative to the linux-kernel source tree base),
function "tty_termios_baud_rate()", which is what the serial drivers
call to determine the output baudrate.
If the driver requests it, the kernel also determines the input
baudrate by checking (c_cflag >> IBSHIFT) & CBAUD. If it sees that:
(c_cflag >> IBSHIFT) & CBAUD == B0
then the *output* baudrate is passed to the driver (as the input
baudrate) determined as described above. If, on the other hand the
kernel sees that:
(c_cflag >> IBSHIFT) & CBAUD == BOTHER
then "c_ispeed" is passed to the serial driver as the input
baudrate. If, finaly, neither is true (i.e. the input baudate bits in
"c_cflag" are neither B0, nor BOTHER), then "(c_cflag >> IBSHIFT) &
(CBAUD|CBAUDEX)" is mached against the table of standard baudrates.
The matching baudrate is located and passed to the serial driver as
the input baudrate.
You can see all these happen in "drivers/tty/tty_ioctl.c", function
"tty_termios_input_baud_rate()", which is what the serial drivers call
to determine the input baudrate.
The serial driver, once it receives the requested baudrate values, it
may choose to alter them (e.g because the requested values are not
supported by the hardware). If it does so, it then passes-back to the
kernel the actual, effective, baudrate values, so that the kernel can
update its internal structure. As a result, the user will read the
*effective* baudrate values with the next TCGETS2 ioctl (which may be
different than the requested ones).
The kernel updates the "termios2" structure with the effective
baudrate values supplied by the serial driver by following a rather
complicated procedure, which I will not describe in full detail
here. The gist of it is this: If the user has requested a baudrate
using one of the standard "Bxxx" values (i.e. by setting the CBAUD /
CIBAUD fields in "c_cflag"), then the kernel will also try to
report-back the effective baudrate as a standard "Bxxx" value (by
setting the CBAUF / CIBAUD fields in "c_cflag"), *even* if it has to
lie a little about the baudrate value. If lying "a little" is not
enough, or if the user has requested a non-standard baudrate through
"c_ispeed / c_ospeed", then the kernel will set the CBAUD / CIBAUD
fields in "c_cflag" to BOTHER, and report the effective baudrate
(numerically) using "c_ispeed" / "c_ospeed". Actually, the "c_ispeed"
/ "c_ospeed" fields are *always* updated by the kernel with the
effective baudrate values, even if these values are also reported by
setting the CBAUD / CIBAUD fields in "c_cflag" to one of the "Bxxx"
values.
The details of how the kernel updates the termios2 structure with the
baudrate values supplied by the serial drivers can be seen in file
"drivers/tty/tty_ioctl.c", or "drivers/tty/tty_baudrate.c" function
"tty_termios_encode_baud_rate()" which is what the serial drivers call
to notify the kernel about the effective baudrate.
Baud-codes, CBAUD, CBAUDEX, and BOTHER
--------------------------------------
CBAUD = 0010017 ---> 0001 0000 0000 1111 mask
CBUADEX = 0010000 ---> 0001 0000 0000 0000 extender bit
BOTHER = 0010000 ---> 0001 0000 0000 0000 a reserved code
Usually CBAUD includes CBAUDEX. In any case the mask is always CBAUD |
CBAUDEX (which is usually == CBAUD)
Basic codes (Bxxx & CBAUDEX == 0)
B0 > ---0 ---- ---- 0000
B50 > ---0 ---- ---- 0001
... 16 in total ...
B38400 > ---0 ---- ---- 1111
Extended codes (Bxxx & CBAUDEX == 1)
CBAUDEX / BOTHER > ---1 ---- ---- 0000
B57600 > ---1 ---- ---- 0001
... 16 in total ...
B4000000 > ---1 ---- ---- 1111