From edc6afc5496875a640bef0913604be7550c1795d Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 8 Dec 2006 02:38:44 -0800 Subject: [PATCH] tty: switch to ktermios and new framework This is the core of the switch to the new framework. I've split it from the driver patches which are mostly search/replace and would encourage people to give this one a good hard stare. The references to BOTHER and ISHIFT are the termios values that must be defined by a platform once it wants to turn on "new style" ioctl support. The code patches here ensure that providing 1. The termios overlays the ktermios in memory 2. The only new kernel only fields are c_ispeed/c_ospeed (or none) the existing behaviour is retained. This is true for the patches at this point in time. Future patches will define BOTHER, ISHIFT and enable newer termios structures for each architecture, and once they are all done some of the ifdefs also vanish. [akpm@osdl.org: warning fix] [akpm@osdl.org: IRDA fix] Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_ioctl.c | 258 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 244 insertions(+), 14 deletions(-) (limited to 'drivers/char/tty_ioctl.c') diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 3b6fa7b0be8..0ffba4e911c 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -36,6 +36,7 @@ #define TERMIOS_FLUSH 1 #define TERMIOS_WAIT 2 #define TERMIOS_TERMIO 4 +#define TERMIOS_OLD 8 /** @@ -84,9 +85,9 @@ stop_waiting: EXPORT_SYMBOL(tty_wait_until_sent); -static void unset_locked_termios(struct termios *termios, - struct termios *old, - struct termios *locked) +static void unset_locked_termios(struct ktermios *termios, + struct ktermios *old, + struct ktermios *locked) { int i; @@ -105,8 +106,204 @@ static void unset_locked_termios(struct termios *termios, for (i=0; i < NCCS; i++) termios->c_cc[i] = locked->c_cc[i] ? old->c_cc[i] : termios->c_cc[i]; + /* FIXME: What should we do for i/ospeed */ } +/* + * 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[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, + 9600, 19200, 38400, 57600, 115200, 230400, 460800, +#ifdef __sparc__ + 76800, 153600, 307200, 614400, 921600 +#else + 500000, 576000, 921600, 1000000, 1152000, 1500000, 2000000, + 2500000, 3000000, 3500000, 4000000 +#endif +}; + +#ifndef __sparc__ +static const tcflag_t baud_bits[] = { + B0, B50, B75, B110, B134, B150, B200, B300, B600, + B1200, B1800, B2400, B4800, B9600, B19200, B38400, + B57600, B115200, B230400, B460800, B500000, B576000, + B921600, B1000000, B1152000, B1500000, B2000000, B2500000, + B3000000, B3500000, B4000000 +}; +#else +static const tcflag_t baud_bits[] = { + B0, B50, B75, B110, B134, B150, B200, B300, B600, + B1200, B1800, B2400, B4800, B9600, B19200, B38400, + B57600, B115200, B230400, B460800, B76800, B153600, + B307200, B614400, B921600 +}; +#endif + +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 arbitary 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; + } + return baud_table[cbaud]; +} + +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); + + /* Magic token for arbitary speed via c_ispeed*/ + if (cbaud == BOTHER) + return termios->c_ispeed; + + if (cbaud & CBAUDEX) { + cbaud &= ~CBAUDEX; + + if (cbaud < 1 || cbaud + 15 > n_baud_table) + termios->c_cflag &= ~(CBAUDEX << IBSHIFT); + else + cbaud += 15; + } + return baud_table[cbaud]; +#else + return tty_termios_baud_rate(termios); +#endif +} + +EXPORT_SYMBOL(tty_termios_input_baud_rate); + +#ifdef BOTHER + +/** + * tty_termios_encode_baud_rate + * @termios: termios structure + * @ispeed: input speed + * @ospeed: output speed + * + * Encode the speeds set into the passed termios structure. This is + * used as a library helper for drivers os that they can report back + * the actual speed selected when it differs from the speed requested + * + * For now input and output speed must agree. + * + * Locking: Caller should hold termios lock. This is already held + * when calling this function from the driver termios handler. + */ + +void tty_termios_encode_baud_rate(struct ktermios *termios, speed_t ibaud, speed_t obaud) +{ + int i = 0; + int ifound = 0, ofound = 0; + + termios->c_ispeed = ibaud; + termios->c_ospeed = obaud; + + termios->c_cflag &= ~CBAUD; + /* Identical speed means no input encoding (ie B0 << IBSHIFT)*/ + if (termios->c_ispeed == termios->c_ospeed) + ifound = 1; + + do { + if (obaud == baud_table[i]) { + termios->c_cflag |= baud_bits[i]; + ofound = 1; + /* So that if ibaud == obaud we don't set it */ + continue; + } + if (ibaud == baud_table[i]) { + termios->c_cflag |= (baud_bits[i] << IBSHIFT); + ifound = 1; + } + } + while(++i < n_baud_table); + if (!ofound) + termios->c_cflag |= BOTHER; + if (!ifound) + termios->c_cflag |= (BOTHER << IBSHIFT); +} + +EXPORT_SYMBOL_GPL(tty_termios_encode_baud_rate); + +#endif + +/** + * tty_get_baud_rate - get tty bit rates + * @tty: tty to query + * + * Returns the baud rate as an integer for this terminal. The + * termios lock must be held by the caller and the terminal bit + * flags may be updated. + * + * Locking: none + */ + +speed_t tty_get_baud_rate(struct tty_struct *tty) +{ + speed_t baud = tty_termios_baud_rate(tty->termios); + + if (baud == 38400 && tty->alt_speed) { + if (!tty->warned) { + printk(KERN_WARNING "Use of setserial/setrocket to " + "set SPD_* flags is deprecated\n"); + tty->warned = 1; + } + baud = tty->alt_speed; + } + + return baud; +} + +EXPORT_SYMBOL(tty_get_baud_rate); + /** * change_termios - update termios values * @tty: tty to update @@ -119,10 +316,10 @@ static void unset_locked_termios(struct termios *termios, * Locking: termios_sem */ -static void change_termios(struct tty_struct * tty, struct termios * new_termios) +static void change_termios(struct tty_struct * tty, struct ktermios * new_termios) { int canon_change; - struct termios old_termios = *tty->termios; + struct ktermios old_termios = *tty->termios; struct tty_ldisc *ld; /* @@ -195,7 +392,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios static int set_termios(struct tty_struct * tty, void __user *arg, int opt) { - struct termios tmp_termios; + struct ktermios tmp_termios; struct tty_ldisc *ld; int retval = tty_check_change(tty); @@ -203,16 +400,28 @@ static int set_termios(struct tty_struct * tty, void __user *arg, int opt) return retval; if (opt & TERMIOS_TERMIO) { - memcpy(&tmp_termios, tty->termios, sizeof(struct termios)); + memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); if (user_termio_to_kernel_termios(&tmp_termios, (struct termio __user *)arg)) return -EFAULT; +#ifdef TCGETS2 + } else if (opt & TERMIOS_OLD) { + memcpy(&tmp_termios, tty->termios, sizeof(struct termios)); + if (user_termios_to_kernel_termios_1(&tmp_termios, + (struct termios_v1 __user *)arg)) + return -EFAULT; +#endif } else { if (user_termios_to_kernel_termios(&tmp_termios, (struct termios __user *)arg)) return -EFAULT; } + /* If old style Bfoo values are used then load c_ispeed/c_ospeed with the real speed + so its unconditionally usable */ + tmp_termios.c_ispeed = tty_termios_input_baud_rate(&tmp_termios); + tmp_termios.c_ospeed = tty_termios_baud_rate(&tmp_termios); + ld = tty_ldisc_ref(tty); if (ld != NULL) { @@ -286,8 +495,8 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) struct sgttyb tmp; mutex_lock(&tty->termios_mutex); - tmp.sg_ispeed = 0; - tmp.sg_ospeed = 0; + tmp.sg_ispeed = tty->c_ispeed; + tmp.sg_ospeed = tty->c_ospeed; tmp.sg_erase = tty->termios->c_cc[VERASE]; tmp.sg_kill = tty->termios->c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); @@ -296,7 +505,7 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0; } -static void set_sgflags(struct termios * termios, int flags) +static void set_sgflags(struct ktermios * termios, int flags) { termios->c_iflag = ICRNL | IXON; termios->c_oflag = 0; @@ -337,7 +546,7 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) { int retval; struct sgttyb tmp; - struct termios termios; + struct ktermios termios; retval = tty_check_change(tty); if (retval) @@ -351,6 +560,10 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) termios.c_cc[VERASE] = tmp.sg_erase; termios.c_cc[VKILL] = tmp.sg_kill; set_sgflags(&termios, tmp.sg_flags); + /* Try and encode into Bfoo format */ +#ifdef BOTHER + tty_termios_encode_baud_rate(&termios, termios.c_ispeed, termios.c_ospeed); +#endif mutex_unlock(&tty->termios_mutex); change_termios(tty, &termios); return 0; @@ -481,16 +694,33 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, case TIOCSLTC: return set_ltchars(real_tty, p); #endif + case TCSETSF: + return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD); + case TCSETSW: + return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD); + case TCSETS: + return set_termios(real_tty, p, TERMIOS_OLD); +#ifndef TCGETS2 case TCGETS: if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios)) return -EFAULT; return 0; - case TCSETSF: +#else + case TCGETS: + if (kernel_termios_to_user_termios_1((struct termios_v1 __user *)arg, real_tty->termios)) + return -EFAULT; + return 0; + case TCGETS2: + if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios)) + return -EFAULT; + return 0; + case TCSETSF2: return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT); - case TCSETSW: + case TCSETSW2: return set_termios(real_tty, p, TERMIOS_WAIT); - case TCSETS: + case TCSETS2: return set_termios(real_tty, p, 0); +#endif case TCGETA: return get_termio(real_tty, p); case TCSETAF: -- cgit v1.2.3 From 606d099cdd1080bbb50ea50dc52d98252f8f10a1 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 8 Dec 2006 02:38:45 -0800 Subject: [PATCH] tty: switch to ktermios This is the grungy swap all the occurrences in the right places patch that goes with the updates. At this point we have the same functionality as before (except that sgttyb() returns speeds not zero) and are ready to begin turning new stuff on providing nobody reports lots of bugs If you are a tty driver author converting an out of tree driver the only impact should be termios->ktermios name changes for the speed/property setting functions from your upper layers. If you are implementing your own TCGETS function before then your driver was broken already and its about to get a whole lot more painful for you so please fix it 8) Also fill in c_ispeed/ospeed on init for most devices, although the current code will do this for you anyway but I'd like eventually to lose that extra paranoia [akpm@osdl.org: bluetooth fix] [mp3@de.ibm.com: sclp fix] [mp3@de.ibm.com: warning fix for tty3270] [hugh@veritas.com: fix tty_ioctl powerpc build] [jdike@addtoit.com: uml: fix ->set_termios declaration] Signed-off-by: Alan Cox Signed-off-by: Martin Peschke Acked-by: Peter Oberparleiter Cc: Cornelia Huck Signed-off-by: Hugh Dickins Signed-off-by: Jeff Dike Cc: Paolo 'Blaisorblade' Giarrusso Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/char/tty_ioctl.c') diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 0ffba4e911c..30486df9fd3 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -495,8 +495,8 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb) struct sgttyb tmp; mutex_lock(&tty->termios_mutex); - tmp.sg_ispeed = tty->c_ispeed; - tmp.sg_ospeed = tty->c_ospeed; + tmp.sg_ispeed = tty->termios->c_ispeed; + tmp.sg_ospeed = tty->termios->c_ospeed; tmp.sg_erase = tty->termios->c_cc[VERASE]; tmp.sg_kill = tty->termios->c_cc[VKILL]; tmp.sg_flags = get_sgflags(tty); -- cgit v1.2.3 From 64bb6c5e1ddcd47c951740485026ef08975ee2e6 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 8 Dec 2006 02:38:47 -0800 Subject: [PATCH] tty_ioctl: use termios for the old structure and termios2 for the new Having split out the user and kernel structures it turns out that some non glibc C libraries pull their termios struct from the kernel headers directly or indirectly. This means we must keep "struct termios" as the library sees it correct for the old ioctls. Not a big problem just shuffle the names and ifdef around a bit [akpm@osdl.org: build fix] Signed-off-by: Alan Cox Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/char/tty_ioctl.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/char/tty_ioctl.c') diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 30486df9fd3..dee47f40c6a 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -399,23 +399,27 @@ static int set_termios(struct tty_struct * tty, void __user *arg, int opt) if (retval) return retval; + memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); + if (opt & TERMIOS_TERMIO) { - memcpy(&tmp_termios, tty->termios, sizeof(struct ktermios)); if (user_termio_to_kernel_termios(&tmp_termios, (struct termio __user *)arg)) return -EFAULT; #ifdef TCGETS2 } else if (opt & TERMIOS_OLD) { - memcpy(&tmp_termios, tty->termios, sizeof(struct termios)); if (user_termios_to_kernel_termios_1(&tmp_termios, - (struct termios_v1 __user *)arg)) + (struct termios __user *)arg)) return -EFAULT; -#endif } else { if (user_termios_to_kernel_termios(&tmp_termios, - (struct termios __user *)arg)) + (struct termios2 __user *)arg)) return -EFAULT; } +#else + } else if (user_termios_to_kernel_termios(&tmp_termios, + (struct termios __user *)arg)) + return -EFAULT; +#endif /* If old style Bfoo values are used then load c_ispeed/c_ospeed with the real speed so its unconditionally usable */ @@ -707,11 +711,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file, return 0; #else case TCGETS: - if (kernel_termios_to_user_termios_1((struct termios_v1 __user *)arg, real_tty->termios)) + if (kernel_termios_to_user_termios_1((struct termios __user *)arg, real_tty->termios)) return -EFAULT; return 0; case TCGETS2: - if (kernel_termios_to_user_termios((struct termios __user *)arg, real_tty->termios)) + if (kernel_termios_to_user_termios((struct termios2 __user *)arg, real_tty->termios)) return -EFAULT; return 0; case TCSETSF2: -- cgit v1.2.3