xref: /openbmc/linux/drivers/tty/tty_baudrate.c (revision fada18c4)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2fff0a2caSNicolas Pitre /*
3fff0a2caSNicolas Pitre  *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
4fff0a2caSNicolas Pitre  */
5fff0a2caSNicolas Pitre 
6fff0a2caSNicolas Pitre #include <linux/types.h>
7fff0a2caSNicolas Pitre #include <linux/kernel.h>
8fff0a2caSNicolas Pitre #include <linux/termios.h>
9fff0a2caSNicolas Pitre #include <linux/tty.h>
10fff0a2caSNicolas Pitre #include <linux/export.h>
11fff0a2caSNicolas Pitre 
12fff0a2caSNicolas Pitre 
13fff0a2caSNicolas Pitre /*
14fff0a2caSNicolas Pitre  * Routine which returns the baud rate of the tty
15fff0a2caSNicolas Pitre  *
16fff0a2caSNicolas Pitre  * Note that the baud_table needs to be kept in sync with the
17fff0a2caSNicolas Pitre  * include/asm/termbits.h file.
18fff0a2caSNicolas Pitre  */
19fff0a2caSNicolas Pitre static const speed_t baud_table[] = {
20fff0a2caSNicolas Pitre 	0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
21fff0a2caSNicolas Pitre 	9600, 19200, 38400, 57600, 115200, 230400, 460800,
22fff0a2caSNicolas Pitre #ifdef __sparc__
23fff0a2caSNicolas Pitre 	76800, 153600, 307200, 614400, 921600
24fff0a2caSNicolas Pitre #else
25fff0a2caSNicolas Pitre 	500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000,
26fff0a2caSNicolas Pitre 	2500000, 3000000, 3500000, 4000000
27fff0a2caSNicolas Pitre #endif
28fff0a2caSNicolas Pitre };
29fff0a2caSNicolas Pitre 
30fff0a2caSNicolas Pitre #ifndef __sparc__
31fff0a2caSNicolas Pitre static const tcflag_t baud_bits[] = {
32fff0a2caSNicolas Pitre 	B0, B50, B75, B110, B134, B150, B200, B300, B600,
33fff0a2caSNicolas Pitre 	B1200, B1800, B2400, B4800, B9600, B19200, B38400,
34fff0a2caSNicolas Pitre 	B57600, B115200, B230400, B460800, B500000, B576000,
35fff0a2caSNicolas Pitre 	B921600, B1000000, B1152000, B1500000, B2000000, B2500000,
36fff0a2caSNicolas Pitre 	B3000000, B3500000, B4000000
37fff0a2caSNicolas Pitre };
38fff0a2caSNicolas Pitre #else
39fff0a2caSNicolas Pitre static const tcflag_t baud_bits[] = {
40fff0a2caSNicolas Pitre 	B0, B50, B75, B110, B134, B150, B200, B300, B600,
41fff0a2caSNicolas Pitre 	B1200, B1800, B2400, B4800, B9600, B19200, B38400,
42fff0a2caSNicolas Pitre 	B57600, B115200, B230400, B460800, B76800, B153600,
43fff0a2caSNicolas Pitre 	B307200, B614400, B921600
44fff0a2caSNicolas Pitre };
45fff0a2caSNicolas Pitre #endif
46fff0a2caSNicolas Pitre 
47fff0a2caSNicolas Pitre static int n_baud_table = ARRAY_SIZE(baud_table);
48fff0a2caSNicolas Pitre 
49fff0a2caSNicolas Pitre /**
50fff0a2caSNicolas Pitre  *	tty_termios_baud_rate
51fff0a2caSNicolas Pitre  *	@termios: termios structure
52fff0a2caSNicolas Pitre  *
53fff0a2caSNicolas Pitre  *	Convert termios baud rate data into a speed. This should be called
54fff0a2caSNicolas Pitre  *	with the termios lock held if this termios is a terminal termios
55fff0a2caSNicolas Pitre  *	structure. May change the termios data. Device drivers can call this
56fff0a2caSNicolas Pitre  *	function but should use ->c_[io]speed directly as they are updated.
57fff0a2caSNicolas Pitre  *
58fff0a2caSNicolas Pitre  *	Locking: none
59fff0a2caSNicolas Pitre  */
60fff0a2caSNicolas Pitre 
61fff0a2caSNicolas Pitre speed_t tty_termios_baud_rate(struct ktermios *termios)
62fff0a2caSNicolas Pitre {
63fff0a2caSNicolas Pitre 	unsigned int cbaud;
64fff0a2caSNicolas Pitre 
65fff0a2caSNicolas Pitre 	cbaud = termios->c_cflag & CBAUD;
66fff0a2caSNicolas Pitre 
67fff0a2caSNicolas Pitre #ifdef BOTHER
68fff0a2caSNicolas Pitre 	/* Magic token for arbitrary speed via c_ispeed/c_ospeed */
69fff0a2caSNicolas Pitre 	if (cbaud == BOTHER)
70fff0a2caSNicolas Pitre 		return termios->c_ospeed;
71fff0a2caSNicolas Pitre #endif
72fff0a2caSNicolas Pitre 	if (cbaud & CBAUDEX) {
73fff0a2caSNicolas Pitre 		cbaud &= ~CBAUDEX;
74fff0a2caSNicolas Pitre 
75fff0a2caSNicolas Pitre 		if (cbaud < 1 || cbaud + 15 > n_baud_table)
76fff0a2caSNicolas Pitre 			termios->c_cflag &= ~CBAUDEX;
77fff0a2caSNicolas Pitre 		else
78fff0a2caSNicolas Pitre 			cbaud += 15;
79fff0a2caSNicolas Pitre 	}
80fff0a2caSNicolas Pitre 	return baud_table[cbaud];
81fff0a2caSNicolas Pitre }
82fff0a2caSNicolas Pitre EXPORT_SYMBOL(tty_termios_baud_rate);
83fff0a2caSNicolas Pitre 
84fff0a2caSNicolas Pitre /**
85fff0a2caSNicolas Pitre  *	tty_termios_input_baud_rate
86fff0a2caSNicolas Pitre  *	@termios: termios structure
87fff0a2caSNicolas Pitre  *
88fff0a2caSNicolas Pitre  *	Convert termios baud rate data into a speed. This should be called
89fff0a2caSNicolas Pitre  *	with the termios lock held if this termios is a terminal termios
90fff0a2caSNicolas Pitre  *	structure. May change the termios data. Device drivers can call this
91fff0a2caSNicolas Pitre  *	function but should use ->c_[io]speed directly as they are updated.
92fff0a2caSNicolas Pitre  *
93fff0a2caSNicolas Pitre  *	Locking: none
94fff0a2caSNicolas Pitre  */
95fff0a2caSNicolas Pitre 
96fff0a2caSNicolas Pitre speed_t tty_termios_input_baud_rate(struct ktermios *termios)
97fff0a2caSNicolas Pitre {
98fff0a2caSNicolas Pitre #ifdef IBSHIFT
99fff0a2caSNicolas Pitre 	unsigned int cbaud = (termios->c_cflag >> IBSHIFT) & CBAUD;
100fff0a2caSNicolas Pitre 
101fff0a2caSNicolas Pitre 	if (cbaud == B0)
102fff0a2caSNicolas Pitre 		return tty_termios_baud_rate(termios);
103fff0a2caSNicolas Pitre 
104fff0a2caSNicolas Pitre 	/* Magic token for arbitrary speed via c_ispeed*/
105fff0a2caSNicolas Pitre 	if (cbaud == BOTHER)
106fff0a2caSNicolas Pitre 		return termios->c_ispeed;
107fff0a2caSNicolas Pitre 
108fff0a2caSNicolas Pitre 	if (cbaud & CBAUDEX) {
109fff0a2caSNicolas Pitre 		cbaud &= ~CBAUDEX;
110fff0a2caSNicolas Pitre 
111fff0a2caSNicolas Pitre 		if (cbaud < 1 || cbaud + 15 > n_baud_table)
112fff0a2caSNicolas Pitre 			termios->c_cflag &= ~(CBAUDEX << IBSHIFT);
113fff0a2caSNicolas Pitre 		else
114fff0a2caSNicolas Pitre 			cbaud += 15;
115fff0a2caSNicolas Pitre 	}
116fff0a2caSNicolas Pitre 	return baud_table[cbaud];
117fff0a2caSNicolas Pitre #else
118fff0a2caSNicolas Pitre 	return tty_termios_baud_rate(termios);
119fff0a2caSNicolas Pitre #endif
120fff0a2caSNicolas Pitre }
121fff0a2caSNicolas Pitre EXPORT_SYMBOL(tty_termios_input_baud_rate);
122fff0a2caSNicolas Pitre 
123fff0a2caSNicolas Pitre /**
124fff0a2caSNicolas Pitre  *	tty_termios_encode_baud_rate
125fff0a2caSNicolas Pitre  *	@termios: ktermios structure holding user requested state
126fff0a2caSNicolas Pitre  *	@ispeed: input speed
127fff0a2caSNicolas Pitre  *	@ospeed: output speed
128fff0a2caSNicolas Pitre  *
129fff0a2caSNicolas Pitre  *	Encode the speeds set into the passed termios structure. This is
130fff0a2caSNicolas Pitre  *	used as a library helper for drivers so that they can report back
131fff0a2caSNicolas Pitre  *	the actual speed selected when it differs from the speed requested
132fff0a2caSNicolas Pitre  *
133fff0a2caSNicolas Pitre  *	For maximal back compatibility with legacy SYS5/POSIX *nix behaviour
134fff0a2caSNicolas Pitre  *	we need to carefully set the bits when the user does not get the
135fff0a2caSNicolas Pitre  *	desired speed. We allow small margins and preserve as much of possible
136fff0a2caSNicolas Pitre  *	of the input intent to keep compatibility.
137fff0a2caSNicolas Pitre  *
138fff0a2caSNicolas Pitre  *	Locking: Caller should hold termios lock. This is already held
139fff0a2caSNicolas Pitre  *	when calling this function from the driver termios handler.
140fff0a2caSNicolas Pitre  *
141fff0a2caSNicolas Pitre  *	The ifdefs deal with platforms whose owners have yet to update them
142fff0a2caSNicolas Pitre  *	and will all go away once this is done.
143fff0a2caSNicolas Pitre  */
144fff0a2caSNicolas Pitre 
145fff0a2caSNicolas Pitre void tty_termios_encode_baud_rate(struct ktermios *termios,
146fff0a2caSNicolas Pitre 				  speed_t ibaud, speed_t obaud)
147fff0a2caSNicolas Pitre {
148fff0a2caSNicolas Pitre 	int i = 0;
149fff0a2caSNicolas Pitre 	int ifound = -1, ofound = -1;
150fff0a2caSNicolas Pitre 	int iclose = ibaud/50, oclose = obaud/50;
151fff0a2caSNicolas Pitre 	int ibinput = 0;
152fff0a2caSNicolas Pitre 
153fff0a2caSNicolas Pitre 	if (obaud == 0)			/* CD dropped 		  */
154fff0a2caSNicolas Pitre 		ibaud = 0;		/* Clear ibaud to be sure */
155fff0a2caSNicolas Pitre 
156fff0a2caSNicolas Pitre 	termios->c_ispeed = ibaud;
157fff0a2caSNicolas Pitre 	termios->c_ospeed = obaud;
158fff0a2caSNicolas Pitre 
159fff0a2caSNicolas Pitre #ifdef BOTHER
160fff0a2caSNicolas Pitre 	/* If the user asked for a precise weird speed give a precise weird
161fff0a2caSNicolas Pitre 	   answer. If they asked for a Bfoo speed they may have problems
162fff0a2caSNicolas Pitre 	   digesting non-exact replies so fuzz a bit */
163fff0a2caSNicolas Pitre 
164fff0a2caSNicolas Pitre 	if ((termios->c_cflag & CBAUD) == BOTHER)
165fff0a2caSNicolas Pitre 		oclose = 0;
166fff0a2caSNicolas Pitre 	if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
167fff0a2caSNicolas Pitre 		iclose = 0;
168fff0a2caSNicolas Pitre 	if ((termios->c_cflag >> IBSHIFT) & CBAUD)
169fff0a2caSNicolas Pitre 		ibinput = 1;	/* An input speed was specified */
170fff0a2caSNicolas Pitre #endif
171fff0a2caSNicolas Pitre 	termios->c_cflag &= ~CBAUD;
172fada18c4SJohan Hovold #ifdef IBSHIFT
173fada18c4SJohan Hovold 	termios->c_cflag &= ~(CBAUD << IBSHIFT);
174fada18c4SJohan Hovold #endif
175fff0a2caSNicolas Pitre 
176fff0a2caSNicolas Pitre 	/*
177fff0a2caSNicolas Pitre 	 *	Our goal is to find a close match to the standard baud rate
178fff0a2caSNicolas Pitre 	 *	returned. Walk the baud rate table and if we get a very close
179fff0a2caSNicolas Pitre 	 *	match then report back the speed as a POSIX Bxxxx value by
180fff0a2caSNicolas Pitre 	 *	preference
181fff0a2caSNicolas Pitre 	 */
182fff0a2caSNicolas Pitre 
183fff0a2caSNicolas Pitre 	do {
184fff0a2caSNicolas Pitre 		if (obaud - oclose <= baud_table[i] &&
185fff0a2caSNicolas Pitre 		    obaud + oclose >= baud_table[i]) {
186fff0a2caSNicolas Pitre 			termios->c_cflag |= baud_bits[i];
187fff0a2caSNicolas Pitre 			ofound = i;
188fff0a2caSNicolas Pitre 		}
189fff0a2caSNicolas Pitre 		if (ibaud - iclose <= baud_table[i] &&
190fff0a2caSNicolas Pitre 		    ibaud + iclose >= baud_table[i]) {
191fff0a2caSNicolas Pitre 			/* For the case input == output don't set IBAUD bits
192fff0a2caSNicolas Pitre 			   if the user didn't do so */
193fff0a2caSNicolas Pitre 			if (ofound == i && !ibinput)
194fff0a2caSNicolas Pitre 				ifound  = i;
195fff0a2caSNicolas Pitre #ifdef IBSHIFT
196fff0a2caSNicolas Pitre 			else {
197fff0a2caSNicolas Pitre 				ifound = i;
198fff0a2caSNicolas Pitre 				termios->c_cflag |= (baud_bits[i] << IBSHIFT);
199fff0a2caSNicolas Pitre 			}
200fff0a2caSNicolas Pitre #endif
201fff0a2caSNicolas Pitre 		}
202fff0a2caSNicolas Pitre 	} while (++i < n_baud_table);
203fff0a2caSNicolas Pitre 
204fff0a2caSNicolas Pitre 	/*
205fff0a2caSNicolas Pitre 	 *	If we found no match then use BOTHER if provided or warn
206fff0a2caSNicolas Pitre 	 *	the user their platform maintainer needs to wake up if not.
207fff0a2caSNicolas Pitre 	 */
208fff0a2caSNicolas Pitre #ifdef BOTHER
209fff0a2caSNicolas Pitre 	if (ofound == -1)
210fff0a2caSNicolas Pitre 		termios->c_cflag |= BOTHER;
211fff0a2caSNicolas Pitre 	/* Set exact input bits only if the input and output differ or the
212fff0a2caSNicolas Pitre 	   user already did */
213fff0a2caSNicolas Pitre 	if (ifound == -1 && (ibaud != obaud || ibinput))
214fff0a2caSNicolas Pitre 		termios->c_cflag |= (BOTHER << IBSHIFT);
215fff0a2caSNicolas Pitre #else
216fff0a2caSNicolas Pitre 	if (ifound == -1 || ofound == -1)
217fff0a2caSNicolas Pitre 		pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n");
218fff0a2caSNicolas Pitre #endif
219fff0a2caSNicolas Pitre }
220fff0a2caSNicolas Pitre EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
221fff0a2caSNicolas Pitre 
222fff0a2caSNicolas Pitre /**
223fff0a2caSNicolas Pitre  *	tty_encode_baud_rate		-	set baud rate of the tty
224fff0a2caSNicolas Pitre  *	@ibaud: input baud rate
225fff0a2caSNicolas Pitre  *	@obad: output baud rate
226fff0a2caSNicolas Pitre  *
227fff0a2caSNicolas Pitre  *	Update the current termios data for the tty with the new speed
228fff0a2caSNicolas Pitre  *	settings. The caller must hold the termios_rwsem for the tty in
229fff0a2caSNicolas Pitre  *	question.
230fff0a2caSNicolas Pitre  */
231fff0a2caSNicolas Pitre 
232fff0a2caSNicolas Pitre void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
233fff0a2caSNicolas Pitre {
234fff0a2caSNicolas Pitre 	tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud);
235fff0a2caSNicolas Pitre }
236fff0a2caSNicolas Pitre EXPORT_SYMBOL_GPL(tty_encode_baud_rate);
237