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