Blame view

drivers/tty/tty_baudrate.c 6.53 KB
e3b3d0f54   Greg Kroah-Hartman   tty: add SPDX ide...
1
  // SPDX-License-Identifier: GPL-2.0
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
   */
  
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/termios.h>
  #include <linux/tty.h>
  #include <linux/export.h>
  
  
  /*
   * Routine which returns the baud rate of the tty
   *
   * Note that the baud_table needs to be kept in sync with the
   * include/asm/termbits.h file.
   */
  static const speed_t baud_table[] = {
6ada6064b   Andy Shevchenko   tty: baudrate: Sy...
20
21
  	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400,
  	4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800,
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
22
  #ifdef __sparc__
1ddeb5a74   Andy Shevchenko   tty: baudrate: SP...
23
24
  	76800, 153600, 307200, 614400, 921600, 500000, 576000,
  	1000000, 1152000, 1500000, 2000000
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
25
26
27
28
29
  #else
  	500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
  	2500000, 3000000, 3500000, 4000000
  #endif
  };
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
30
  static const tcflag_t baud_bits[] = {
6ada6064b   Andy Shevchenko   tty: baudrate: Sy...
31
32
33
  	B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400,
  	B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800,
  #ifdef __sparc__
1ddeb5a74   Andy Shevchenko   tty: baudrate: SP...
34
35
  	B76800, B153600, B307200, B614400, B921600, B500000, B576000,
  	B1000000, B1152000, B1500000, B2000000
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
36
  #else
6ada6064b   Andy Shevchenko   tty: baudrate: Sy...
37
38
  	B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000,
  	B2500000, B3000000, B3500000, B4000000
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
39
  #endif
6ada6064b   Andy Shevchenko   tty: baudrate: Sy...
40
  };
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
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
  
  static int n_baud_table = ARRAY_SIZE(baud_table);
  
  /**
   *	tty_termios_baud_rate
   *	@termios: termios structure
   *
   *	Convert termios baud rate data into a speed. This should be called
   *	with the termios lock held if this termios is a terminal termios
   *	structure. May change the termios data. Device drivers can call this
   *	function but should use ->c_[io]speed directly as they are updated.
   *
   *	Locking: none
   */
  
  speed_t tty_termios_baud_rate(struct ktermios *termios)
  {
  	unsigned int cbaud;
  
  	cbaud = termios->c_cflag & CBAUD;
  
  #ifdef BOTHER
  	/* Magic token for arbitrary speed via c_ispeed/c_ospeed */
  	if (cbaud == BOTHER)
  		return termios->c_ospeed;
  #endif
  	if (cbaud & CBAUDEX) {
  		cbaud &= ~CBAUDEX;
  
  		if (cbaud < 1 || cbaud + 15 > n_baud_table)
  			termios->c_cflag &= ~CBAUDEX;
  		else
  			cbaud += 15;
  	}
991a25194   H. Peter Anvin   termios, tty/tty_...
75
  	return cbaud >= n_baud_table ? 0 : baud_table[cbaud];
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  }
  EXPORT_SYMBOL(tty_termios_baud_rate);
  
  /**
   *	tty_termios_input_baud_rate
   *	@termios: termios structure
   *
   *	Convert termios baud rate data into a speed. This should be called
   *	with the termios lock held if this termios is a terminal termios
   *	structure. May change the termios data. Device drivers can call this
   *	function but should use ->c_[io]speed directly as they are updated.
   *
   *	Locking: none
   */
  
  speed_t tty_termios_input_baud_rate(struct ktermios *termios)
  {
  #ifdef IBSHIFT
  	unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD;
  
  	if (cbaud == B0)
  		return tty_termios_baud_rate(termios);
fefe287e4   Johan Hovold   tty: support CIBA...
98
  #ifdef BOTHER
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
99
100
101
  	/* Magic token for arbitrary speed via c_ispeed*/
  	if (cbaud == BOTHER)
  		return termios->c_ispeed;
fefe287e4   Johan Hovold   tty: support CIBA...
102
  #endif
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
103
104
105
106
107
108
109
110
  	if (cbaud & CBAUDEX) {
  		cbaud &= ~CBAUDEX;
  
  		if (cbaud < 1 || cbaud + 15 > n_baud_table)
  			termios->c_cflag &= ~(CBAUDEX << IBSHIFT);
  		else
  			cbaud += 15;
  	}
991a25194   H. Peter Anvin   termios, tty/tty_...
111
  	return cbaud >= n_baud_table ? 0 : baud_table[cbaud];
fefe287e4   Johan Hovold   tty: support CIBA...
112
  #else	/* IBSHIFT */
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
113
  	return tty_termios_baud_rate(termios);
fefe287e4   Johan Hovold   tty: support CIBA...
114
  #endif	/* IBSHIFT */
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
115
116
117
118
119
120
  }
  EXPORT_SYMBOL(tty_termios_input_baud_rate);
  
  /**
   *	tty_termios_encode_baud_rate
   *	@termios: ktermios structure holding user requested state
fa4419545   Jiri Slaby   tty: fix kernel-doc
121
122
   *	@ibaud: input speed
   *	@obaud: output speed
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
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
   *
   *	Encode the speeds set into the passed termios structure. This is
   *	used as a library helper for drivers so that they can report back
   *	the actual speed selected when it differs from the speed requested
   *
   *	For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
   *	we need to carefully set the bits when the user does not get the
   *	desired speed. We allow small margins and preserve as much of possible
   *	of the input intent to keep compatibility.
   *
   *	Locking: Caller should hold termios lock. This is already held
   *	when calling this function from the driver termios handler.
   *
   *	The ifdefs deal with platforms whose owners have yet to update them
   *	and will all go away once this is done.
   */
  
  void tty_termios_encode_baud_rate(struct ktermios *termios,
  				  speed_t ibaud, speed_t obaud)
  {
  	int i = 0;
  	int ifound = -1, ofound = -1;
  	int iclose = ibaud/50, oclose = obaud/50;
  	int ibinput = 0;
  
  	if (obaud == 0)			/* CD dropped 		  */
  		ibaud = 0;		/* Clear ibaud to be sure */
  
  	termios->c_ispeed = ibaud;
  	termios->c_ospeed = obaud;
fefe287e4   Johan Hovold   tty: support CIBA...
153
  #ifdef IBSHIFT
1cee38f03   Johan Hovold   tty: fix termios ...
154
155
  	if ((termios->c_cflag >> IBSHIFT) & CBAUD)
  		ibinput = 1;	/* An input speed was specified */
fefe287e4   Johan Hovold   tty: support CIBA...
156
157
  #endif
  #ifdef BOTHER
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
158
159
160
  	/* If the user asked for a precise weird speed give a precise weird
  	   answer. If they asked for a Bfoo speed they may have problems
  	   digesting non-exact replies so fuzz a bit */
1cee38f03   Johan Hovold   tty: fix termios ...
161
  	if ((termios->c_cflag & CBAUD) == BOTHER) {
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
162
  		oclose = 0;
1cee38f03   Johan Hovold   tty: fix termios ...
163
164
165
  		if (!ibinput)
  			iclose = 0;
  	}
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
166
167
  	if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
  		iclose = 0;
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
168
169
  #endif
  	termios->c_cflag &= ~CBAUD;
fada18c48   Johan Hovold   tty: fix termios ...
170
171
172
  #ifdef IBSHIFT
  	termios->c_cflag &= ~(CBAUD << IBSHIFT);
  #endif
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  
  	/*
  	 *	Our goal is to find a close match to the standard baud rate
  	 *	returned. Walk the baud rate table and if we get a very close
  	 *	match then report back the speed as a POSIX Bxxxx value by
  	 *	preference
  	 */
  
  	do {
  		if (obaud - oclose <= baud_table[i] &&
  		    obaud + oclose >= baud_table[i]) {
  			termios->c_cflag |= baud_bits[i];
  			ofound = i;
  		}
  		if (ibaud - iclose <= baud_table[i] &&
  		    ibaud + iclose >= baud_table[i]) {
  			/* For the case input == output don't set IBAUD bits
  			   if the user didn't do so */
  			if (ofound == i && !ibinput)
  				ifound  = i;
  #ifdef IBSHIFT
  			else {
  				ifound = i;
  				termios->c_cflag |= (baud_bits[i] << IBSHIFT);
  			}
  #endif
  		}
  	} while (++i < n_baud_table);
  
  	/*
  	 *	If we found no match then use BOTHER if provided or warn
  	 *	the user their platform maintainer needs to wake up if not.
  	 */
  #ifdef BOTHER
  	if (ofound == -1)
  		termios->c_cflag |= BOTHER;
  	/* Set exact input bits only if the input and output differ or the
  	   user already did */
  	if (ifound == -1 && (ibaud != obaud || ibinput))
  		termios->c_cflag |= (BOTHER << IBSHIFT);
  #else
  	if (ifound == -1 || ofound == -1)
  		pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.
  ");
  #endif
  }
  EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
  
  /**
   *	tty_encode_baud_rate		-	set baud rate of the tty
   *	@ibaud: input baud rate
fa4419545   Jiri Slaby   tty: fix kernel-doc
224
   *	@obaud: output baud rate
fff0a2ca3   Nicolas Pitre   tty: move baudrat...
225
226
227
228
229
230
231
232
233
234
235
   *
   *	Update the current termios data for the tty with the new speed
   *	settings. The caller must hold the termios_rwsem for the tty in
   *	question.
   */
  
  void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
  {
  	tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud);
  }
  EXPORT_SYMBOL_GPL(tty_encode_baud_rate);