xref: /openbmc/linux/drivers/tty/tty_baudrate.c (revision fefe287e)
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);
103fefe287eSJohan Hovold #ifdef BOTHER
104fff0a2caSNicolas Pitre 	/* Magic token for arbitrary speed via c_ispeed*/
105fff0a2caSNicolas Pitre 	if (cbaud == BOTHER)
106fff0a2caSNicolas Pitre 		return termios->c_ispeed;
107fefe287eSJohan Hovold #endif
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];
117fefe287eSJohan Hovold #else	/* IBSHIFT */
118fff0a2caSNicolas Pitre 	return tty_termios_baud_rate(termios);
119fefe287eSJohan Hovold #endif	/* IBSHIFT */
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 
159fefe287eSJohan Hovold #ifdef IBSHIFT
1601cee38f0SJohan Hovold 	if ((termios->c_cflag >> IBSHIFT) & CBAUD)
1611cee38f0SJohan Hovold 		ibinput = 1;	/* An input speed was specified */
162fefe287eSJohan Hovold #endif
163fefe287eSJohan Hovold #ifdef BOTHER
164fff0a2caSNicolas Pitre 	/* If the user asked for a precise weird speed give a precise weird
165fff0a2caSNicolas Pitre 	   answer. If they asked for a Bfoo speed they may have problems
166fff0a2caSNicolas Pitre 	   digesting non-exact replies so fuzz a bit */
167fff0a2caSNicolas Pitre 
1681cee38f0SJohan Hovold 	if ((termios->c_cflag & CBAUD) == BOTHER) {
169fff0a2caSNicolas Pitre 		oclose = 0;
1701cee38f0SJohan Hovold 		if (!ibinput)
1711cee38f0SJohan Hovold 			iclose = 0;
1721cee38f0SJohan Hovold 	}
173fff0a2caSNicolas Pitre 	if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
174fff0a2caSNicolas Pitre 		iclose = 0;
175fff0a2caSNicolas Pitre #endif
176fff0a2caSNicolas Pitre 	termios->c_cflag &= ~CBAUD;
177fada18c4SJohan Hovold #ifdef IBSHIFT
178fada18c4SJohan Hovold 	termios->c_cflag &= ~(CBAUD << IBSHIFT);
179fada18c4SJohan Hovold #endif
180fff0a2caSNicolas Pitre 
181fff0a2caSNicolas Pitre 	/*
182fff0a2caSNicolas Pitre 	 *	Our goal is to find a close match to the standard baud rate
183fff0a2caSNicolas Pitre 	 *	returned. Walk the baud rate table and if we get a very close
184fff0a2caSNicolas Pitre 	 *	match then report back the speed as a POSIX Bxxxx value by
185fff0a2caSNicolas Pitre 	 *	preference
186fff0a2caSNicolas Pitre 	 */
187fff0a2caSNicolas Pitre 
188fff0a2caSNicolas Pitre 	do {
189fff0a2caSNicolas Pitre 		if (obaud - oclose <= baud_table[i] &&
190fff0a2caSNicolas Pitre 		    obaud + oclose >= baud_table[i]) {
191fff0a2caSNicolas Pitre 			termios->c_cflag |= baud_bits[i];
192fff0a2caSNicolas Pitre 			ofound = i;
193fff0a2caSNicolas Pitre 		}
194fff0a2caSNicolas Pitre 		if (ibaud - iclose <= baud_table[i] &&
195fff0a2caSNicolas Pitre 		    ibaud + iclose >= baud_table[i]) {
196fff0a2caSNicolas Pitre 			/* For the case input == output don't set IBAUD bits
197fff0a2caSNicolas Pitre 			   if the user didn't do so */
198fff0a2caSNicolas Pitre 			if (ofound == i && !ibinput)
199fff0a2caSNicolas Pitre 				ifound  = i;
200fff0a2caSNicolas Pitre #ifdef IBSHIFT
201fff0a2caSNicolas Pitre 			else {
202fff0a2caSNicolas Pitre 				ifound = i;
203fff0a2caSNicolas Pitre 				termios->c_cflag |= (baud_bits[i] << IBSHIFT);
204fff0a2caSNicolas Pitre 			}
205fff0a2caSNicolas Pitre #endif
206fff0a2caSNicolas Pitre 		}
207fff0a2caSNicolas Pitre 	} while (++i < n_baud_table);
208fff0a2caSNicolas Pitre 
209fff0a2caSNicolas Pitre 	/*
210fff0a2caSNicolas Pitre 	 *	If we found no match then use BOTHER if provided or warn
211fff0a2caSNicolas Pitre 	 *	the user their platform maintainer needs to wake up if not.
212fff0a2caSNicolas Pitre 	 */
213fff0a2caSNicolas Pitre #ifdef BOTHER
214fff0a2caSNicolas Pitre 	if (ofound == -1)
215fff0a2caSNicolas Pitre 		termios->c_cflag |= BOTHER;
216fff0a2caSNicolas Pitre 	/* Set exact input bits only if the input and output differ or the
217fff0a2caSNicolas Pitre 	   user already did */
218fff0a2caSNicolas Pitre 	if (ifound == -1 && (ibaud != obaud || ibinput))
219fff0a2caSNicolas Pitre 		termios->c_cflag |= (BOTHER << IBSHIFT);
220fff0a2caSNicolas Pitre #else
221fff0a2caSNicolas Pitre 	if (ifound == -1 || ofound == -1)
222fff0a2caSNicolas Pitre 		pr_warn_once("tty: Unable to return correct speed data as your architecture needs updating.\n");
223fff0a2caSNicolas Pitre #endif
224fff0a2caSNicolas Pitre }
225fff0a2caSNicolas Pitre EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate);
226fff0a2caSNicolas Pitre 
227fff0a2caSNicolas Pitre /**
228fff0a2caSNicolas Pitre  *	tty_encode_baud_rate		-	set baud rate of the tty
229fff0a2caSNicolas Pitre  *	@ibaud: input baud rate
230fff0a2caSNicolas Pitre  *	@obad: output baud rate
231fff0a2caSNicolas Pitre  *
232fff0a2caSNicolas Pitre  *	Update the current termios data for the tty with the new speed
233fff0a2caSNicolas Pitre  *	settings. The caller must hold the termios_rwsem for the tty in
234fff0a2caSNicolas Pitre  *	question.
235fff0a2caSNicolas Pitre  */
236fff0a2caSNicolas Pitre 
237fff0a2caSNicolas Pitre void tty_encode_baud_rate(struct tty_struct *tty, speed_t ibaud, speed_t obaud)
238fff0a2caSNicolas Pitre {
239fff0a2caSNicolas Pitre 	tty_termios_encode_baud_rate(&tty->termios, ibaud, obaud);
240fff0a2caSNicolas Pitre }
241fff0a2caSNicolas Pitre EXPORT_SYMBOL_GPL(tty_encode_baud_rate);
242