196fd7ce5SGreg Kroah-Hartman #include <linux/types.h> 296fd7ce5SGreg Kroah-Hartman #include <linux/errno.h> 38b3ffa17SJiri Slaby #include <linux/kmod.h> 496fd7ce5SGreg Kroah-Hartman #include <linux/sched.h> 596fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h> 696fd7ce5SGreg Kroah-Hartman #include <linux/tty.h> 796fd7ce5SGreg Kroah-Hartman #include <linux/tty_driver.h> 896fd7ce5SGreg Kroah-Hartman #include <linux/file.h> 996fd7ce5SGreg Kroah-Hartman #include <linux/mm.h> 1096fd7ce5SGreg Kroah-Hartman #include <linux/string.h> 1196fd7ce5SGreg Kroah-Hartman #include <linux/slab.h> 1296fd7ce5SGreg Kroah-Hartman #include <linux/poll.h> 1396fd7ce5SGreg Kroah-Hartman #include <linux/proc_fs.h> 1496fd7ce5SGreg Kroah-Hartman #include <linux/module.h> 1596fd7ce5SGreg Kroah-Hartman #include <linux/device.h> 1696fd7ce5SGreg Kroah-Hartman #include <linux/wait.h> 1796fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h> 1896fd7ce5SGreg Kroah-Hartman #include <linux/seq_file.h> 1996fd7ce5SGreg Kroah-Hartman #include <linux/uaccess.h> 200c73c08eSJiri Slaby #include <linux/ratelimit.h> 2196fd7ce5SGreg Kroah-Hartman 22fc575ee6SPeter Hurley #undef LDISC_DEBUG_HANGUP 23fc575ee6SPeter Hurley 24fc575ee6SPeter Hurley #ifdef LDISC_DEBUG_HANGUP 25fc575ee6SPeter Hurley #define tty_ldisc_debug(tty, f, args...) ({ \ 26fc575ee6SPeter Hurley char __b[64]; \ 27fc575ee6SPeter Hurley printk(KERN_DEBUG "%s: %s: " f, __func__, tty_name(tty, __b), ##args); \ 28fc575ee6SPeter Hurley }) 29fc575ee6SPeter Hurley #else 30fc575ee6SPeter Hurley #define tty_ldisc_debug(tty, f, args...) 31fc575ee6SPeter Hurley #endif 32fc575ee6SPeter Hurley 33d2c43890SPeter Hurley /* lockdep nested classes for tty->ldisc_sem */ 34d2c43890SPeter Hurley enum { 35d2c43890SPeter Hurley LDISC_SEM_NORMAL, 36d2c43890SPeter Hurley LDISC_SEM_OTHER, 37d2c43890SPeter Hurley }; 38d2c43890SPeter Hurley 39d2c43890SPeter Hurley 4096fd7ce5SGreg Kroah-Hartman /* 4196fd7ce5SGreg Kroah-Hartman * This guards the refcounted line discipline lists. The lock 4296fd7ce5SGreg Kroah-Hartman * must be taken with irqs off because there are hangup path 4396fd7ce5SGreg Kroah-Hartman * callers who will do ldisc lookups and cannot sleep. 4496fd7ce5SGreg Kroah-Hartman */ 4596fd7ce5SGreg Kroah-Hartman 46137084bbSPeter Hurley static DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); 4796fd7ce5SGreg Kroah-Hartman /* Line disc dispatch table */ 4896fd7ce5SGreg Kroah-Hartman static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; 4996fd7ce5SGreg Kroah-Hartman 5096fd7ce5SGreg Kroah-Hartman /** 5196fd7ce5SGreg Kroah-Hartman * tty_register_ldisc - install a line discipline 5296fd7ce5SGreg Kroah-Hartman * @disc: ldisc number 5396fd7ce5SGreg Kroah-Hartman * @new_ldisc: pointer to the ldisc object 5496fd7ce5SGreg Kroah-Hartman * 5596fd7ce5SGreg Kroah-Hartman * Installs a new line discipline into the kernel. The discipline 5696fd7ce5SGreg Kroah-Hartman * is set up as unreferenced and then made available to the kernel 5796fd7ce5SGreg Kroah-Hartman * from this point onwards. 5896fd7ce5SGreg Kroah-Hartman * 5996fd7ce5SGreg Kroah-Hartman * Locking: 60137084bbSPeter Hurley * takes tty_ldiscs_lock to guard against ldisc races 6196fd7ce5SGreg Kroah-Hartman */ 6296fd7ce5SGreg Kroah-Hartman 6396fd7ce5SGreg Kroah-Hartman int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) 6496fd7ce5SGreg Kroah-Hartman { 6596fd7ce5SGreg Kroah-Hartman unsigned long flags; 6696fd7ce5SGreg Kroah-Hartman int ret = 0; 6796fd7ce5SGreg Kroah-Hartman 6896fd7ce5SGreg Kroah-Hartman if (disc < N_TTY || disc >= NR_LDISCS) 6996fd7ce5SGreg Kroah-Hartman return -EINVAL; 7096fd7ce5SGreg Kroah-Hartman 71137084bbSPeter Hurley raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 7296fd7ce5SGreg Kroah-Hartman tty_ldiscs[disc] = new_ldisc; 7396fd7ce5SGreg Kroah-Hartman new_ldisc->num = disc; 7496fd7ce5SGreg Kroah-Hartman new_ldisc->refcount = 0; 75137084bbSPeter Hurley raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 7696fd7ce5SGreg Kroah-Hartman 7796fd7ce5SGreg Kroah-Hartman return ret; 7896fd7ce5SGreg Kroah-Hartman } 7996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_register_ldisc); 8096fd7ce5SGreg Kroah-Hartman 8196fd7ce5SGreg Kroah-Hartman /** 8296fd7ce5SGreg Kroah-Hartman * tty_unregister_ldisc - unload a line discipline 8396fd7ce5SGreg Kroah-Hartman * @disc: ldisc number 8496fd7ce5SGreg Kroah-Hartman * @new_ldisc: pointer to the ldisc object 8596fd7ce5SGreg Kroah-Hartman * 8696fd7ce5SGreg Kroah-Hartman * Remove a line discipline from the kernel providing it is not 8796fd7ce5SGreg Kroah-Hartman * currently in use. 8896fd7ce5SGreg Kroah-Hartman * 8996fd7ce5SGreg Kroah-Hartman * Locking: 90137084bbSPeter Hurley * takes tty_ldiscs_lock to guard against ldisc races 9196fd7ce5SGreg Kroah-Hartman */ 9296fd7ce5SGreg Kroah-Hartman 9396fd7ce5SGreg Kroah-Hartman int tty_unregister_ldisc(int disc) 9496fd7ce5SGreg Kroah-Hartman { 9596fd7ce5SGreg Kroah-Hartman unsigned long flags; 9696fd7ce5SGreg Kroah-Hartman int ret = 0; 9796fd7ce5SGreg Kroah-Hartman 9896fd7ce5SGreg Kroah-Hartman if (disc < N_TTY || disc >= NR_LDISCS) 9996fd7ce5SGreg Kroah-Hartman return -EINVAL; 10096fd7ce5SGreg Kroah-Hartman 101137084bbSPeter Hurley raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 10296fd7ce5SGreg Kroah-Hartman if (tty_ldiscs[disc]->refcount) 10396fd7ce5SGreg Kroah-Hartman ret = -EBUSY; 10496fd7ce5SGreg Kroah-Hartman else 10596fd7ce5SGreg Kroah-Hartman tty_ldiscs[disc] = NULL; 106137084bbSPeter Hurley raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 10796fd7ce5SGreg Kroah-Hartman 10896fd7ce5SGreg Kroah-Hartman return ret; 10996fd7ce5SGreg Kroah-Hartman } 11096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_unregister_ldisc); 11196fd7ce5SGreg Kroah-Hartman 11296fd7ce5SGreg Kroah-Hartman static struct tty_ldisc_ops *get_ldops(int disc) 11396fd7ce5SGreg Kroah-Hartman { 11496fd7ce5SGreg Kroah-Hartman unsigned long flags; 11596fd7ce5SGreg Kroah-Hartman struct tty_ldisc_ops *ldops, *ret; 11696fd7ce5SGreg Kroah-Hartman 117137084bbSPeter Hurley raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 11896fd7ce5SGreg Kroah-Hartman ret = ERR_PTR(-EINVAL); 11996fd7ce5SGreg Kroah-Hartman ldops = tty_ldiscs[disc]; 12096fd7ce5SGreg Kroah-Hartman if (ldops) { 12196fd7ce5SGreg Kroah-Hartman ret = ERR_PTR(-EAGAIN); 12296fd7ce5SGreg Kroah-Hartman if (try_module_get(ldops->owner)) { 12396fd7ce5SGreg Kroah-Hartman ldops->refcount++; 12496fd7ce5SGreg Kroah-Hartman ret = ldops; 12596fd7ce5SGreg Kroah-Hartman } 12696fd7ce5SGreg Kroah-Hartman } 127137084bbSPeter Hurley raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 12896fd7ce5SGreg Kroah-Hartman return ret; 12996fd7ce5SGreg Kroah-Hartman } 13096fd7ce5SGreg Kroah-Hartman 13196fd7ce5SGreg Kroah-Hartman static void put_ldops(struct tty_ldisc_ops *ldops) 13296fd7ce5SGreg Kroah-Hartman { 13396fd7ce5SGreg Kroah-Hartman unsigned long flags; 13496fd7ce5SGreg Kroah-Hartman 135137084bbSPeter Hurley raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 13696fd7ce5SGreg Kroah-Hartman ldops->refcount--; 13796fd7ce5SGreg Kroah-Hartman module_put(ldops->owner); 138137084bbSPeter Hurley raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 13996fd7ce5SGreg Kroah-Hartman } 14096fd7ce5SGreg Kroah-Hartman 14196fd7ce5SGreg Kroah-Hartman /** 14296fd7ce5SGreg Kroah-Hartman * tty_ldisc_get - take a reference to an ldisc 14396fd7ce5SGreg Kroah-Hartman * @disc: ldisc number 14496fd7ce5SGreg Kroah-Hartman * 14596fd7ce5SGreg Kroah-Hartman * Takes a reference to a line discipline. Deals with refcounts and 14696fd7ce5SGreg Kroah-Hartman * module locking counts. Returns NULL if the discipline is not available. 14796fd7ce5SGreg Kroah-Hartman * Returns a pointer to the discipline and bumps the ref count if it is 14896fd7ce5SGreg Kroah-Hartman * available 14996fd7ce5SGreg Kroah-Hartman * 15096fd7ce5SGreg Kroah-Hartman * Locking: 151137084bbSPeter Hurley * takes tty_ldiscs_lock to guard against ldisc races 15296fd7ce5SGreg Kroah-Hartman */ 15396fd7ce5SGreg Kroah-Hartman 15436697529SPeter Hurley static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) 15596fd7ce5SGreg Kroah-Hartman { 15696fd7ce5SGreg Kroah-Hartman struct tty_ldisc *ld; 15796fd7ce5SGreg Kroah-Hartman struct tty_ldisc_ops *ldops; 15896fd7ce5SGreg Kroah-Hartman 15996fd7ce5SGreg Kroah-Hartman if (disc < N_TTY || disc >= NR_LDISCS) 16096fd7ce5SGreg Kroah-Hartman return ERR_PTR(-EINVAL); 16196fd7ce5SGreg Kroah-Hartman 16296fd7ce5SGreg Kroah-Hartman /* 16396fd7ce5SGreg Kroah-Hartman * Get the ldisc ops - we may need to request them to be loaded 16496fd7ce5SGreg Kroah-Hartman * dynamically and try again. 16596fd7ce5SGreg Kroah-Hartman */ 16696fd7ce5SGreg Kroah-Hartman ldops = get_ldops(disc); 16796fd7ce5SGreg Kroah-Hartman if (IS_ERR(ldops)) { 16896fd7ce5SGreg Kroah-Hartman request_module("tty-ldisc-%d", disc); 16996fd7ce5SGreg Kroah-Hartman ldops = get_ldops(disc); 17096fd7ce5SGreg Kroah-Hartman if (IS_ERR(ldops)) 17196fd7ce5SGreg Kroah-Hartman return ERR_CAST(ldops); 17296fd7ce5SGreg Kroah-Hartman } 17396fd7ce5SGreg Kroah-Hartman 17496fd7ce5SGreg Kroah-Hartman ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL); 17596fd7ce5SGreg Kroah-Hartman if (ld == NULL) { 17696fd7ce5SGreg Kroah-Hartman put_ldops(ldops); 17796fd7ce5SGreg Kroah-Hartman return ERR_PTR(-ENOMEM); 17896fd7ce5SGreg Kroah-Hartman } 17996fd7ce5SGreg Kroah-Hartman 18096fd7ce5SGreg Kroah-Hartman ld->ops = ldops; 18136697529SPeter Hurley ld->tty = tty; 1821541f845SIvo Sieben 18396fd7ce5SGreg Kroah-Hartman return ld; 18496fd7ce5SGreg Kroah-Hartman } 18596fd7ce5SGreg Kroah-Hartman 186734de249SPeter Hurley /** 187734de249SPeter Hurley * tty_ldisc_put - release the ldisc 188734de249SPeter Hurley * 189734de249SPeter Hurley * Complement of tty_ldisc_get(). 190734de249SPeter Hurley */ 191734de249SPeter Hurley static inline void tty_ldisc_put(struct tty_ldisc *ld) 192734de249SPeter Hurley { 193734de249SPeter Hurley if (WARN_ON_ONCE(!ld)) 194734de249SPeter Hurley return; 195734de249SPeter Hurley 19636697529SPeter Hurley put_ldops(ld->ops); 197734de249SPeter Hurley kfree(ld); 198734de249SPeter Hurley } 199734de249SPeter Hurley 20096fd7ce5SGreg Kroah-Hartman static void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) 20196fd7ce5SGreg Kroah-Hartman { 20296fd7ce5SGreg Kroah-Hartman return (*pos < NR_LDISCS) ? pos : NULL; 20396fd7ce5SGreg Kroah-Hartman } 20496fd7ce5SGreg Kroah-Hartman 20596fd7ce5SGreg Kroah-Hartman static void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) 20696fd7ce5SGreg Kroah-Hartman { 20796fd7ce5SGreg Kroah-Hartman (*pos)++; 20896fd7ce5SGreg Kroah-Hartman return (*pos < NR_LDISCS) ? pos : NULL; 20996fd7ce5SGreg Kroah-Hartman } 21096fd7ce5SGreg Kroah-Hartman 21196fd7ce5SGreg Kroah-Hartman static void tty_ldiscs_seq_stop(struct seq_file *m, void *v) 21296fd7ce5SGreg Kroah-Hartman { 21396fd7ce5SGreg Kroah-Hartman } 21496fd7ce5SGreg Kroah-Hartman 21596fd7ce5SGreg Kroah-Hartman static int tty_ldiscs_seq_show(struct seq_file *m, void *v) 21696fd7ce5SGreg Kroah-Hartman { 21796fd7ce5SGreg Kroah-Hartman int i = *(loff_t *)v; 21896fd7ce5SGreg Kroah-Hartman struct tty_ldisc_ops *ldops; 21996fd7ce5SGreg Kroah-Hartman 22096fd7ce5SGreg Kroah-Hartman ldops = get_ldops(i); 22196fd7ce5SGreg Kroah-Hartman if (IS_ERR(ldops)) 22296fd7ce5SGreg Kroah-Hartman return 0; 22396fd7ce5SGreg Kroah-Hartman seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i); 22496fd7ce5SGreg Kroah-Hartman put_ldops(ldops); 22596fd7ce5SGreg Kroah-Hartman return 0; 22696fd7ce5SGreg Kroah-Hartman } 22796fd7ce5SGreg Kroah-Hartman 22896fd7ce5SGreg Kroah-Hartman static const struct seq_operations tty_ldiscs_seq_ops = { 22996fd7ce5SGreg Kroah-Hartman .start = tty_ldiscs_seq_start, 23096fd7ce5SGreg Kroah-Hartman .next = tty_ldiscs_seq_next, 23196fd7ce5SGreg Kroah-Hartman .stop = tty_ldiscs_seq_stop, 23296fd7ce5SGreg Kroah-Hartman .show = tty_ldiscs_seq_show, 23396fd7ce5SGreg Kroah-Hartman }; 23496fd7ce5SGreg Kroah-Hartman 23596fd7ce5SGreg Kroah-Hartman static int proc_tty_ldiscs_open(struct inode *inode, struct file *file) 23696fd7ce5SGreg Kroah-Hartman { 23796fd7ce5SGreg Kroah-Hartman return seq_open(file, &tty_ldiscs_seq_ops); 23896fd7ce5SGreg Kroah-Hartman } 23996fd7ce5SGreg Kroah-Hartman 24096fd7ce5SGreg Kroah-Hartman const struct file_operations tty_ldiscs_proc_fops = { 24196fd7ce5SGreg Kroah-Hartman .owner = THIS_MODULE, 24296fd7ce5SGreg Kroah-Hartman .open = proc_tty_ldiscs_open, 24396fd7ce5SGreg Kroah-Hartman .read = seq_read, 24496fd7ce5SGreg Kroah-Hartman .llseek = seq_lseek, 24596fd7ce5SGreg Kroah-Hartman .release = seq_release, 24696fd7ce5SGreg Kroah-Hartman }; 24796fd7ce5SGreg Kroah-Hartman 24896fd7ce5SGreg Kroah-Hartman /** 24996fd7ce5SGreg Kroah-Hartman * tty_ldisc_ref_wait - wait for the tty ldisc 25096fd7ce5SGreg Kroah-Hartman * @tty: tty device 25196fd7ce5SGreg Kroah-Hartman * 25296fd7ce5SGreg Kroah-Hartman * Dereference the line discipline for the terminal and take a 25396fd7ce5SGreg Kroah-Hartman * reference to it. If the line discipline is in flux then 25496fd7ce5SGreg Kroah-Hartman * wait patiently until it changes. 25596fd7ce5SGreg Kroah-Hartman * 25696fd7ce5SGreg Kroah-Hartman * Note: Must not be called from an IRQ/timer context. The caller 25796fd7ce5SGreg Kroah-Hartman * must also be careful not to hold other locks that will deadlock 25896fd7ce5SGreg Kroah-Hartman * against a discipline change, such as an existing ldisc reference 25996fd7ce5SGreg Kroah-Hartman * (which we check for) 26096fd7ce5SGreg Kroah-Hartman * 26136697529SPeter Hurley * Note: only callable from a file_operations routine (which 26236697529SPeter Hurley * guarantees tty->ldisc != NULL when the lock is acquired). 26396fd7ce5SGreg Kroah-Hartman */ 26496fd7ce5SGreg Kroah-Hartman 26596fd7ce5SGreg Kroah-Hartman struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) 26696fd7ce5SGreg Kroah-Hartman { 26736697529SPeter Hurley ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); 26836697529SPeter Hurley WARN_ON(!tty->ldisc); 26936697529SPeter Hurley return tty->ldisc; 27096fd7ce5SGreg Kroah-Hartman } 27196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); 27296fd7ce5SGreg Kroah-Hartman 27396fd7ce5SGreg Kroah-Hartman /** 27496fd7ce5SGreg Kroah-Hartman * tty_ldisc_ref - get the tty ldisc 27596fd7ce5SGreg Kroah-Hartman * @tty: tty device 27696fd7ce5SGreg Kroah-Hartman * 27796fd7ce5SGreg Kroah-Hartman * Dereference the line discipline for the terminal and take a 27896fd7ce5SGreg Kroah-Hartman * reference to it. If the line discipline is in flux then 27996fd7ce5SGreg Kroah-Hartman * return NULL. Can be called from IRQ and timer functions. 28096fd7ce5SGreg Kroah-Hartman */ 28196fd7ce5SGreg Kroah-Hartman 28296fd7ce5SGreg Kroah-Hartman struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) 28396fd7ce5SGreg Kroah-Hartman { 28436697529SPeter Hurley struct tty_ldisc *ld = NULL; 28536697529SPeter Hurley 28636697529SPeter Hurley if (ldsem_down_read_trylock(&tty->ldisc_sem)) { 28736697529SPeter Hurley ld = tty->ldisc; 28836697529SPeter Hurley if (!ld) 28936697529SPeter Hurley ldsem_up_read(&tty->ldisc_sem); 29036697529SPeter Hurley } 29136697529SPeter Hurley return ld; 29296fd7ce5SGreg Kroah-Hartman } 29396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_ldisc_ref); 29496fd7ce5SGreg Kroah-Hartman 29596fd7ce5SGreg Kroah-Hartman /** 29696fd7ce5SGreg Kroah-Hartman * tty_ldisc_deref - free a tty ldisc reference 29796fd7ce5SGreg Kroah-Hartman * @ld: reference to free up 29896fd7ce5SGreg Kroah-Hartman * 29996fd7ce5SGreg Kroah-Hartman * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May 30096fd7ce5SGreg Kroah-Hartman * be called in IRQ context. 30196fd7ce5SGreg Kroah-Hartman */ 30296fd7ce5SGreg Kroah-Hartman 30396fd7ce5SGreg Kroah-Hartman void tty_ldisc_deref(struct tty_ldisc *ld) 30496fd7ce5SGreg Kroah-Hartman { 30536697529SPeter Hurley ldsem_up_read(&ld->tty->ldisc_sem); 30696fd7ce5SGreg Kroah-Hartman } 30796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_ldisc_deref); 30896fd7ce5SGreg Kroah-Hartman 309d2c43890SPeter Hurley 310d2c43890SPeter Hurley static inline int __lockfunc 311d2c43890SPeter Hurley tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) 312d2c43890SPeter Hurley { 313d2c43890SPeter Hurley return ldsem_down_write(&tty->ldisc_sem, timeout); 314d2c43890SPeter Hurley } 315d2c43890SPeter Hurley 316d2c43890SPeter Hurley static inline int __lockfunc 317d2c43890SPeter Hurley tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) 318d2c43890SPeter Hurley { 319d2c43890SPeter Hurley return ldsem_down_write_nested(&tty->ldisc_sem, 320d2c43890SPeter Hurley LDISC_SEM_OTHER, timeout); 321d2c43890SPeter Hurley } 322d2c43890SPeter Hurley 323d2c43890SPeter Hurley static inline void tty_ldisc_unlock(struct tty_struct *tty) 324d2c43890SPeter Hurley { 325d2c43890SPeter Hurley return ldsem_up_write(&tty->ldisc_sem); 326d2c43890SPeter Hurley } 327d2c43890SPeter Hurley 328d2c43890SPeter Hurley static int __lockfunc 329d2c43890SPeter Hurley tty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, 330d2c43890SPeter Hurley unsigned long timeout) 331d2c43890SPeter Hurley { 332d2c43890SPeter Hurley int ret; 333d2c43890SPeter Hurley 334d2c43890SPeter Hurley if (tty < tty2) { 335d2c43890SPeter Hurley ret = tty_ldisc_lock(tty, timeout); 336d2c43890SPeter Hurley if (ret) { 337d2c43890SPeter Hurley ret = tty_ldisc_lock_nested(tty2, timeout); 338d2c43890SPeter Hurley if (!ret) 339d2c43890SPeter Hurley tty_ldisc_unlock(tty); 340d2c43890SPeter Hurley } 341d2c43890SPeter Hurley } else { 342d2c43890SPeter Hurley /* if this is possible, it has lots of implications */ 343d2c43890SPeter Hurley WARN_ON_ONCE(tty == tty2); 344d2c43890SPeter Hurley if (tty2 && tty != tty2) { 345d2c43890SPeter Hurley ret = tty_ldisc_lock(tty2, timeout); 346d2c43890SPeter Hurley if (ret) { 347d2c43890SPeter Hurley ret = tty_ldisc_lock_nested(tty, timeout); 348d2c43890SPeter Hurley if (!ret) 349d2c43890SPeter Hurley tty_ldisc_unlock(tty2); 350d2c43890SPeter Hurley } 351d2c43890SPeter Hurley } else 352d2c43890SPeter Hurley ret = tty_ldisc_lock(tty, timeout); 353d2c43890SPeter Hurley } 354d2c43890SPeter Hurley 355d2c43890SPeter Hurley if (!ret) 356d2c43890SPeter Hurley return -EBUSY; 357d2c43890SPeter Hurley 358d2c43890SPeter Hurley set_bit(TTY_LDISC_HALTED, &tty->flags); 359d2c43890SPeter Hurley if (tty2) 360d2c43890SPeter Hurley set_bit(TTY_LDISC_HALTED, &tty2->flags); 361d2c43890SPeter Hurley return 0; 362d2c43890SPeter Hurley } 363d2c43890SPeter Hurley 364d2c43890SPeter Hurley static void __lockfunc 365d2c43890SPeter Hurley tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) 366d2c43890SPeter Hurley { 367d2c43890SPeter Hurley tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT); 368d2c43890SPeter Hurley } 369d2c43890SPeter Hurley 370d2c43890SPeter Hurley static void __lockfunc tty_ldisc_unlock_pair(struct tty_struct *tty, 371d2c43890SPeter Hurley struct tty_struct *tty2) 372d2c43890SPeter Hurley { 373d2c43890SPeter Hurley tty_ldisc_unlock(tty); 374d2c43890SPeter Hurley if (tty2) 375d2c43890SPeter Hurley tty_ldisc_unlock(tty2); 376d2c43890SPeter Hurley } 377d2c43890SPeter Hurley 378d2c43890SPeter Hurley static void __lockfunc tty_ldisc_enable_pair(struct tty_struct *tty, 379d2c43890SPeter Hurley struct tty_struct *tty2) 380d2c43890SPeter Hurley { 381d2c43890SPeter Hurley clear_bit(TTY_LDISC_HALTED, &tty->flags); 382d2c43890SPeter Hurley if (tty2) 383d2c43890SPeter Hurley clear_bit(TTY_LDISC_HALTED, &tty2->flags); 384d2c43890SPeter Hurley 385d2c43890SPeter Hurley tty_ldisc_unlock_pair(tty, tty2); 386d2c43890SPeter Hurley } 387d2c43890SPeter Hurley 38896fd7ce5SGreg Kroah-Hartman /** 38996fd7ce5SGreg Kroah-Hartman * tty_ldisc_flush - flush line discipline queue 39096fd7ce5SGreg Kroah-Hartman * @tty: tty 39196fd7ce5SGreg Kroah-Hartman * 39296fd7ce5SGreg Kroah-Hartman * Flush the line discipline queue (if any) for this tty. If there 39396fd7ce5SGreg Kroah-Hartman * is no line discipline active this is a no-op. 39496fd7ce5SGreg Kroah-Hartman */ 39596fd7ce5SGreg Kroah-Hartman 39696fd7ce5SGreg Kroah-Hartman void tty_ldisc_flush(struct tty_struct *tty) 39796fd7ce5SGreg Kroah-Hartman { 39896fd7ce5SGreg Kroah-Hartman struct tty_ldisc *ld = tty_ldisc_ref(tty); 39996fd7ce5SGreg Kroah-Hartman if (ld) { 40096fd7ce5SGreg Kroah-Hartman if (ld->ops->flush_buffer) 40196fd7ce5SGreg Kroah-Hartman ld->ops->flush_buffer(tty); 40296fd7ce5SGreg Kroah-Hartman tty_ldisc_deref(ld); 40396fd7ce5SGreg Kroah-Hartman } 40496fd7ce5SGreg Kroah-Hartman tty_buffer_flush(tty); 40596fd7ce5SGreg Kroah-Hartman } 40696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_ldisc_flush); 40796fd7ce5SGreg Kroah-Hartman 40896fd7ce5SGreg Kroah-Hartman /** 40996fd7ce5SGreg Kroah-Hartman * tty_set_termios_ldisc - set ldisc field 41096fd7ce5SGreg Kroah-Hartman * @tty: tty structure 41196fd7ce5SGreg Kroah-Hartman * @num: line discipline number 41296fd7ce5SGreg Kroah-Hartman * 41396fd7ce5SGreg Kroah-Hartman * This is probably overkill for real world processors but 41496fd7ce5SGreg Kroah-Hartman * they are not on hot paths so a little discipline won't do 41596fd7ce5SGreg Kroah-Hartman * any harm. 41696fd7ce5SGreg Kroah-Hartman * 4176a1c0680SPeter Hurley * Locking: takes termios_rwsem 41896fd7ce5SGreg Kroah-Hartman */ 41996fd7ce5SGreg Kroah-Hartman 42096fd7ce5SGreg Kroah-Hartman static void tty_set_termios_ldisc(struct tty_struct *tty, int num) 42196fd7ce5SGreg Kroah-Hartman { 4226a1c0680SPeter Hurley down_write(&tty->termios_rwsem); 423adc8d746SAlan Cox tty->termios.c_line = num; 4246a1c0680SPeter Hurley up_write(&tty->termios_rwsem); 42596fd7ce5SGreg Kroah-Hartman } 42696fd7ce5SGreg Kroah-Hartman 42796fd7ce5SGreg Kroah-Hartman /** 42896fd7ce5SGreg Kroah-Hartman * tty_ldisc_open - open a line discipline 42996fd7ce5SGreg Kroah-Hartman * @tty: tty we are opening the ldisc on 43096fd7ce5SGreg Kroah-Hartman * @ld: discipline to open 43196fd7ce5SGreg Kroah-Hartman * 43296fd7ce5SGreg Kroah-Hartman * A helper opening method. Also a convenient debugging and check 43396fd7ce5SGreg Kroah-Hartman * point. 43496fd7ce5SGreg Kroah-Hartman * 43596fd7ce5SGreg Kroah-Hartman * Locking: always called with BTM already held. 43696fd7ce5SGreg Kroah-Hartman */ 43796fd7ce5SGreg Kroah-Hartman 43896fd7ce5SGreg Kroah-Hartman static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) 43996fd7ce5SGreg Kroah-Hartman { 44096fd7ce5SGreg Kroah-Hartman WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); 44196fd7ce5SGreg Kroah-Hartman if (ld->ops->open) { 44296fd7ce5SGreg Kroah-Hartman int ret; 44396fd7ce5SGreg Kroah-Hartman /* BTM here locks versus a hangup event */ 44496fd7ce5SGreg Kroah-Hartman ret = ld->ops->open(tty); 4457f90cfc5SJiri Slaby if (ret) 4467f90cfc5SJiri Slaby clear_bit(TTY_LDISC_OPEN, &tty->flags); 44796fd7ce5SGreg Kroah-Hartman return ret; 44896fd7ce5SGreg Kroah-Hartman } 44996fd7ce5SGreg Kroah-Hartman return 0; 45096fd7ce5SGreg Kroah-Hartman } 45196fd7ce5SGreg Kroah-Hartman 45296fd7ce5SGreg Kroah-Hartman /** 45396fd7ce5SGreg Kroah-Hartman * tty_ldisc_close - close a line discipline 45496fd7ce5SGreg Kroah-Hartman * @tty: tty we are opening the ldisc on 45596fd7ce5SGreg Kroah-Hartman * @ld: discipline to close 45696fd7ce5SGreg Kroah-Hartman * 45796fd7ce5SGreg Kroah-Hartman * A helper close method. Also a convenient debugging and check 45896fd7ce5SGreg Kroah-Hartman * point. 45996fd7ce5SGreg Kroah-Hartman */ 46096fd7ce5SGreg Kroah-Hartman 46196fd7ce5SGreg Kroah-Hartman static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) 46296fd7ce5SGreg Kroah-Hartman { 46396fd7ce5SGreg Kroah-Hartman WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags)); 46496fd7ce5SGreg Kroah-Hartman clear_bit(TTY_LDISC_OPEN, &tty->flags); 46596fd7ce5SGreg Kroah-Hartman if (ld->ops->close) 46696fd7ce5SGreg Kroah-Hartman ld->ops->close(tty); 46796fd7ce5SGreg Kroah-Hartman } 46896fd7ce5SGreg Kroah-Hartman 46996fd7ce5SGreg Kroah-Hartman /** 47096fd7ce5SGreg Kroah-Hartman * tty_ldisc_restore - helper for tty ldisc change 47196fd7ce5SGreg Kroah-Hartman * @tty: tty to recover 47296fd7ce5SGreg Kroah-Hartman * @old: previous ldisc 47396fd7ce5SGreg Kroah-Hartman * 47496fd7ce5SGreg Kroah-Hartman * Restore the previous line discipline or N_TTY when a line discipline 47596fd7ce5SGreg Kroah-Hartman * change fails due to an open error 47696fd7ce5SGreg Kroah-Hartman */ 47796fd7ce5SGreg Kroah-Hartman 47896fd7ce5SGreg Kroah-Hartman static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) 47996fd7ce5SGreg Kroah-Hartman { 48096fd7ce5SGreg Kroah-Hartman char buf[64]; 48196fd7ce5SGreg Kroah-Hartman struct tty_ldisc *new_ldisc; 48296fd7ce5SGreg Kroah-Hartman int r; 48396fd7ce5SGreg Kroah-Hartman 48496fd7ce5SGreg Kroah-Hartman /* There is an outstanding reference here so this is safe */ 48536697529SPeter Hurley old = tty_ldisc_get(tty, old->ops->num); 48696fd7ce5SGreg Kroah-Hartman WARN_ON(IS_ERR(old)); 487f4807045SPeter Hurley tty->ldisc = old; 48896fd7ce5SGreg Kroah-Hartman tty_set_termios_ldisc(tty, old->ops->num); 48996fd7ce5SGreg Kroah-Hartman if (tty_ldisc_open(tty, old) < 0) { 49096fd7ce5SGreg Kroah-Hartman tty_ldisc_put(old); 49196fd7ce5SGreg Kroah-Hartman /* This driver is always present */ 49236697529SPeter Hurley new_ldisc = tty_ldisc_get(tty, N_TTY); 49396fd7ce5SGreg Kroah-Hartman if (IS_ERR(new_ldisc)) 49496fd7ce5SGreg Kroah-Hartman panic("n_tty: get"); 495f4807045SPeter Hurley tty->ldisc = new_ldisc; 49696fd7ce5SGreg Kroah-Hartman tty_set_termios_ldisc(tty, N_TTY); 49796fd7ce5SGreg Kroah-Hartman r = tty_ldisc_open(tty, new_ldisc); 49896fd7ce5SGreg Kroah-Hartman if (r < 0) 49996fd7ce5SGreg Kroah-Hartman panic("Couldn't open N_TTY ldisc for " 50096fd7ce5SGreg Kroah-Hartman "%s --- error %d.", 50196fd7ce5SGreg Kroah-Hartman tty_name(tty, buf), r); 50296fd7ce5SGreg Kroah-Hartman } 50396fd7ce5SGreg Kroah-Hartman } 50496fd7ce5SGreg Kroah-Hartman 50596fd7ce5SGreg Kroah-Hartman /** 50696fd7ce5SGreg Kroah-Hartman * tty_set_ldisc - set line discipline 50796fd7ce5SGreg Kroah-Hartman * @tty: the terminal to set 50896fd7ce5SGreg Kroah-Hartman * @ldisc: the line discipline 50996fd7ce5SGreg Kroah-Hartman * 51096fd7ce5SGreg Kroah-Hartman * Set the discipline of a tty line. Must be called from a process 51196fd7ce5SGreg Kroah-Hartman * context. The ldisc change logic has to protect itself against any 51296fd7ce5SGreg Kroah-Hartman * overlapping ldisc change (including on the other end of pty pairs), 51396fd7ce5SGreg Kroah-Hartman * the close of one side of a tty/pty pair, and eventually hangup. 51496fd7ce5SGreg Kroah-Hartman */ 51596fd7ce5SGreg Kroah-Hartman 51696fd7ce5SGreg Kroah-Hartman int tty_set_ldisc(struct tty_struct *tty, int ldisc) 51796fd7ce5SGreg Kroah-Hartman { 51896fd7ce5SGreg Kroah-Hartman int retval; 5199fbfa34cSPeter Hurley struct tty_ldisc *old_ldisc, *new_ldisc; 52036697529SPeter Hurley struct tty_struct *o_tty = tty->link; 52196fd7ce5SGreg Kroah-Hartman 52236697529SPeter Hurley new_ldisc = tty_ldisc_get(tty, ldisc); 52396fd7ce5SGreg Kroah-Hartman if (IS_ERR(new_ldisc)) 52496fd7ce5SGreg Kroah-Hartman return PTR_ERR(new_ldisc); 52596fd7ce5SGreg Kroah-Hartman 52636697529SPeter Hurley retval = tty_ldisc_lock_pair_timeout(tty, o_tty, 5 * HZ); 52736697529SPeter Hurley if (retval) { 52836697529SPeter Hurley tty_ldisc_put(new_ldisc); 52936697529SPeter Hurley return retval; 53036697529SPeter Hurley } 53196fd7ce5SGreg Kroah-Hartman 53296fd7ce5SGreg Kroah-Hartman /* 53396fd7ce5SGreg Kroah-Hartman * Check the no-op case 53496fd7ce5SGreg Kroah-Hartman */ 53596fd7ce5SGreg Kroah-Hartman 53696fd7ce5SGreg Kroah-Hartman if (tty->ldisc->ops->num == ldisc) { 53736697529SPeter Hurley tty_ldisc_enable_pair(tty, o_tty); 53896fd7ce5SGreg Kroah-Hartman tty_ldisc_put(new_ldisc); 53996fd7ce5SGreg Kroah-Hartman return 0; 54096fd7ce5SGreg Kroah-Hartman } 54196fd7ce5SGreg Kroah-Hartman 5429fbfa34cSPeter Hurley old_ldisc = tty->ldisc; 54389c8d91eSAlan Cox tty_lock(tty); 544100eeae2SJiri Slaby 545e97733caSPeter Hurley if (test_bit(TTY_HUPPING, &tty->flags) || 546e97733caSPeter Hurley test_bit(TTY_HUPPED, &tty->flags)) { 54796fd7ce5SGreg Kroah-Hartman /* We were raced by the hangup method. It will have stomped 54896fd7ce5SGreg Kroah-Hartman the ldisc data and closed the ldisc down */ 54936697529SPeter Hurley tty_ldisc_enable_pair(tty, o_tty); 55096fd7ce5SGreg Kroah-Hartman tty_ldisc_put(new_ldisc); 55189c8d91eSAlan Cox tty_unlock(tty); 55296fd7ce5SGreg Kroah-Hartman return -EIO; 55396fd7ce5SGreg Kroah-Hartman } 55496fd7ce5SGreg Kroah-Hartman 5559fbfa34cSPeter Hurley /* Shutdown the old discipline. */ 5569fbfa34cSPeter Hurley tty_ldisc_close(tty, old_ldisc); 55796fd7ce5SGreg Kroah-Hartman 55896fd7ce5SGreg Kroah-Hartman /* Now set up the new line discipline. */ 559f4807045SPeter Hurley tty->ldisc = new_ldisc; 56096fd7ce5SGreg Kroah-Hartman tty_set_termios_ldisc(tty, ldisc); 56196fd7ce5SGreg Kroah-Hartman 56296fd7ce5SGreg Kroah-Hartman retval = tty_ldisc_open(tty, new_ldisc); 56396fd7ce5SGreg Kroah-Hartman if (retval < 0) { 56496fd7ce5SGreg Kroah-Hartman /* Back to the old one or N_TTY if we can't */ 56596fd7ce5SGreg Kroah-Hartman tty_ldisc_put(new_ldisc); 5669fbfa34cSPeter Hurley tty_ldisc_restore(tty, old_ldisc); 56796fd7ce5SGreg Kroah-Hartman } 56896fd7ce5SGreg Kroah-Hartman 5699fbfa34cSPeter Hurley if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) 57096fd7ce5SGreg Kroah-Hartman tty->ops->set_ldisc(tty); 57196fd7ce5SGreg Kroah-Hartman 572b0e95858SPeter Hurley /* At this point we hold a reference to the new ldisc and a 573b0e95858SPeter Hurley reference to the old ldisc, or we hold two references to 574b0e95858SPeter Hurley the old ldisc (if it was restored as part of error cleanup 575b0e95858SPeter Hurley above). In either case, releasing a single reference from 576b0e95858SPeter Hurley the old ldisc is correct. */ 577b0e95858SPeter Hurley 5789fbfa34cSPeter Hurley tty_ldisc_put(old_ldisc); 57996fd7ce5SGreg Kroah-Hartman 58096fd7ce5SGreg Kroah-Hartman /* 58196fd7ce5SGreg Kroah-Hartman * Allow ldisc referencing to occur again 58296fd7ce5SGreg Kroah-Hartman */ 58336697529SPeter Hurley tty_ldisc_enable_pair(tty, o_tty); 58496fd7ce5SGreg Kroah-Hartman 58596fd7ce5SGreg Kroah-Hartman /* Restart the work queue in case no characters kick it off. Safe if 58696fd7ce5SGreg Kroah-Hartman already running */ 587ecbbfd44SJiri Slaby schedule_work(&tty->port->buf.work); 5884f98d467SPeter Hurley if (o_tty) 589ecbbfd44SJiri Slaby schedule_work(&o_tty->port->buf.work); 5904f98d467SPeter Hurley 59189c8d91eSAlan Cox tty_unlock(tty); 59296fd7ce5SGreg Kroah-Hartman return retval; 59396fd7ce5SGreg Kroah-Hartman } 59496fd7ce5SGreg Kroah-Hartman 59596fd7ce5SGreg Kroah-Hartman /** 59696fd7ce5SGreg Kroah-Hartman * tty_reset_termios - reset terminal state 59796fd7ce5SGreg Kroah-Hartman * @tty: tty to reset 59896fd7ce5SGreg Kroah-Hartman * 59996fd7ce5SGreg Kroah-Hartman * Restore a terminal to the driver default state. 60096fd7ce5SGreg Kroah-Hartman */ 60196fd7ce5SGreg Kroah-Hartman 60296fd7ce5SGreg Kroah-Hartman static void tty_reset_termios(struct tty_struct *tty) 60396fd7ce5SGreg Kroah-Hartman { 6046a1c0680SPeter Hurley down_write(&tty->termios_rwsem); 605adc8d746SAlan Cox tty->termios = tty->driver->init_termios; 606adc8d746SAlan Cox tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); 607adc8d746SAlan Cox tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); 6086a1c0680SPeter Hurley up_write(&tty->termios_rwsem); 60996fd7ce5SGreg Kroah-Hartman } 61096fd7ce5SGreg Kroah-Hartman 61196fd7ce5SGreg Kroah-Hartman 61296fd7ce5SGreg Kroah-Hartman /** 61396fd7ce5SGreg Kroah-Hartman * tty_ldisc_reinit - reinitialise the tty ldisc 61496fd7ce5SGreg Kroah-Hartman * @tty: tty to reinit 61596fd7ce5SGreg Kroah-Hartman * @ldisc: line discipline to reinitialize 61696fd7ce5SGreg Kroah-Hartman * 61796fd7ce5SGreg Kroah-Hartman * Switch the tty to a line discipline and leave the ldisc 61896fd7ce5SGreg Kroah-Hartman * state closed 61996fd7ce5SGreg Kroah-Hartman */ 62096fd7ce5SGreg Kroah-Hartman 6211c95ba1eSPhilippe Rétornaz static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) 62296fd7ce5SGreg Kroah-Hartman { 62336697529SPeter Hurley struct tty_ldisc *ld = tty_ldisc_get(tty, ldisc); 6241c95ba1eSPhilippe Rétornaz 6251c95ba1eSPhilippe Rétornaz if (IS_ERR(ld)) 6261c95ba1eSPhilippe Rétornaz return -1; 62796fd7ce5SGreg Kroah-Hartman 62896fd7ce5SGreg Kroah-Hartman tty_ldisc_close(tty, tty->ldisc); 62996fd7ce5SGreg Kroah-Hartman tty_ldisc_put(tty->ldisc); 63096fd7ce5SGreg Kroah-Hartman /* 63196fd7ce5SGreg Kroah-Hartman * Switch the line discipline back 63296fd7ce5SGreg Kroah-Hartman */ 633f4807045SPeter Hurley tty->ldisc = ld; 63496fd7ce5SGreg Kroah-Hartman tty_set_termios_ldisc(tty, ldisc); 6351c95ba1eSPhilippe Rétornaz 6361c95ba1eSPhilippe Rétornaz return 0; 63796fd7ce5SGreg Kroah-Hartman } 63896fd7ce5SGreg Kroah-Hartman 63996fd7ce5SGreg Kroah-Hartman /** 64096fd7ce5SGreg Kroah-Hartman * tty_ldisc_hangup - hangup ldisc reset 64196fd7ce5SGreg Kroah-Hartman * @tty: tty being hung up 64296fd7ce5SGreg Kroah-Hartman * 64396fd7ce5SGreg Kroah-Hartman * Some tty devices reset their termios when they receive a hangup 64496fd7ce5SGreg Kroah-Hartman * event. In that situation we must also switch back to N_TTY properly 64596fd7ce5SGreg Kroah-Hartman * before we reset the termios data. 64696fd7ce5SGreg Kroah-Hartman * 64796fd7ce5SGreg Kroah-Hartman * Locking: We can take the ldisc mutex as the rest of the code is 64896fd7ce5SGreg Kroah-Hartman * careful to allow for this. 64996fd7ce5SGreg Kroah-Hartman * 65096fd7ce5SGreg Kroah-Hartman * In the pty pair case this occurs in the close() path of the 65196fd7ce5SGreg Kroah-Hartman * tty itself so we must be careful about locking rules. 65296fd7ce5SGreg Kroah-Hartman */ 65396fd7ce5SGreg Kroah-Hartman 65496fd7ce5SGreg Kroah-Hartman void tty_ldisc_hangup(struct tty_struct *tty) 65596fd7ce5SGreg Kroah-Hartman { 65696fd7ce5SGreg Kroah-Hartman struct tty_ldisc *ld; 65796fd7ce5SGreg Kroah-Hartman int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; 65896fd7ce5SGreg Kroah-Hartman int err = 0; 65996fd7ce5SGreg Kroah-Hartman 660fc575ee6SPeter Hurley tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); 661fc575ee6SPeter Hurley 66296fd7ce5SGreg Kroah-Hartman ld = tty_ldisc_ref(tty); 66396fd7ce5SGreg Kroah-Hartman if (ld != NULL) { 66496fd7ce5SGreg Kroah-Hartman if (ld->ops->flush_buffer) 66596fd7ce5SGreg Kroah-Hartman ld->ops->flush_buffer(tty); 66696fd7ce5SGreg Kroah-Hartman tty_driver_flush_buffer(tty); 66796fd7ce5SGreg Kroah-Hartman if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && 66896fd7ce5SGreg Kroah-Hartman ld->ops->write_wakeup) 66996fd7ce5SGreg Kroah-Hartman ld->ops->write_wakeup(tty); 67096fd7ce5SGreg Kroah-Hartman if (ld->ops->hangup) 67196fd7ce5SGreg Kroah-Hartman ld->ops->hangup(tty); 67296fd7ce5SGreg Kroah-Hartman tty_ldisc_deref(ld); 67396fd7ce5SGreg Kroah-Hartman } 67436697529SPeter Hurley 67596fd7ce5SGreg Kroah-Hartman wake_up_interruptible_poll(&tty->write_wait, POLLOUT); 67696fd7ce5SGreg Kroah-Hartman wake_up_interruptible_poll(&tty->read_wait, POLLIN); 67736697529SPeter Hurley 67836697529SPeter Hurley tty_unlock(tty); 67936697529SPeter Hurley 68096fd7ce5SGreg Kroah-Hartman /* 68196fd7ce5SGreg Kroah-Hartman * Shutdown the current line discipline, and reset it to 68296fd7ce5SGreg Kroah-Hartman * N_TTY if need be. 68396fd7ce5SGreg Kroah-Hartman * 68496fd7ce5SGreg Kroah-Hartman * Avoid racing set_ldisc or tty_ldisc_release 68596fd7ce5SGreg Kroah-Hartman */ 68636697529SPeter Hurley tty_ldisc_lock_pair(tty, tty->link); 68736697529SPeter Hurley tty_lock(tty); 68896fd7ce5SGreg Kroah-Hartman 68936697529SPeter Hurley if (tty->ldisc) { 690c8785241SPeter Hurley 691c8785241SPeter Hurley /* At this point we have a halted ldisc; we want to close it and 692c8785241SPeter Hurley reopen a new ldisc. We could defer the reopen to the next 693c8785241SPeter Hurley open but it means auditing a lot of other paths so this is 694c8785241SPeter Hurley a FIXME */ 69596fd7ce5SGreg Kroah-Hartman if (reset == 0) { 6961c95ba1eSPhilippe Rétornaz 697adc8d746SAlan Cox if (!tty_ldisc_reinit(tty, tty->termios.c_line)) 69896fd7ce5SGreg Kroah-Hartman err = tty_ldisc_open(tty, tty->ldisc); 6991c95ba1eSPhilippe Rétornaz else 7001c95ba1eSPhilippe Rétornaz err = 1; 70196fd7ce5SGreg Kroah-Hartman } 70296fd7ce5SGreg Kroah-Hartman /* If the re-open fails or we reset then go to N_TTY. The 70396fd7ce5SGreg Kroah-Hartman N_TTY open cannot fail */ 70496fd7ce5SGreg Kroah-Hartman if (reset || err) { 7051c95ba1eSPhilippe Rétornaz BUG_ON(tty_ldisc_reinit(tty, N_TTY)); 70696fd7ce5SGreg Kroah-Hartman WARN_ON(tty_ldisc_open(tty, tty->ldisc)); 70796fd7ce5SGreg Kroah-Hartman } 70896fd7ce5SGreg Kroah-Hartman } 70936697529SPeter Hurley tty_ldisc_enable_pair(tty, tty->link); 71096fd7ce5SGreg Kroah-Hartman if (reset) 71196fd7ce5SGreg Kroah-Hartman tty_reset_termios(tty); 712fc575ee6SPeter Hurley 713fc575ee6SPeter Hurley tty_ldisc_debug(tty, "re-opened ldisc: %p\n", tty->ldisc); 71496fd7ce5SGreg Kroah-Hartman } 71596fd7ce5SGreg Kroah-Hartman 71696fd7ce5SGreg Kroah-Hartman /** 71796fd7ce5SGreg Kroah-Hartman * tty_ldisc_setup - open line discipline 71896fd7ce5SGreg Kroah-Hartman * @tty: tty being shut down 71996fd7ce5SGreg Kroah-Hartman * @o_tty: pair tty for pty/tty pairs 72096fd7ce5SGreg Kroah-Hartman * 72196fd7ce5SGreg Kroah-Hartman * Called during the initial open of a tty/pty pair in order to set up the 72296fd7ce5SGreg Kroah-Hartman * line disciplines and bind them to the tty. This has no locking issues 72396fd7ce5SGreg Kroah-Hartman * as the device isn't yet active. 72496fd7ce5SGreg Kroah-Hartman */ 72596fd7ce5SGreg Kroah-Hartman 72696fd7ce5SGreg Kroah-Hartman int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) 72796fd7ce5SGreg Kroah-Hartman { 72896fd7ce5SGreg Kroah-Hartman struct tty_ldisc *ld = tty->ldisc; 72996fd7ce5SGreg Kroah-Hartman int retval; 73096fd7ce5SGreg Kroah-Hartman 73196fd7ce5SGreg Kroah-Hartman retval = tty_ldisc_open(tty, ld); 73296fd7ce5SGreg Kroah-Hartman if (retval) 73396fd7ce5SGreg Kroah-Hartman return retval; 73496fd7ce5SGreg Kroah-Hartman 73596fd7ce5SGreg Kroah-Hartman if (o_tty) { 73696fd7ce5SGreg Kroah-Hartman retval = tty_ldisc_open(o_tty, o_tty->ldisc); 73796fd7ce5SGreg Kroah-Hartman if (retval) { 73896fd7ce5SGreg Kroah-Hartman tty_ldisc_close(tty, ld); 73996fd7ce5SGreg Kroah-Hartman return retval; 74096fd7ce5SGreg Kroah-Hartman } 74196fd7ce5SGreg Kroah-Hartman } 74296fd7ce5SGreg Kroah-Hartman return 0; 74396fd7ce5SGreg Kroah-Hartman } 74489c8d91eSAlan Cox 74589c8d91eSAlan Cox static void tty_ldisc_kill(struct tty_struct *tty) 74689c8d91eSAlan Cox { 74789c8d91eSAlan Cox /* 74889c8d91eSAlan Cox * Now kill off the ldisc 74989c8d91eSAlan Cox */ 75089c8d91eSAlan Cox tty_ldisc_close(tty, tty->ldisc); 75189c8d91eSAlan Cox tty_ldisc_put(tty->ldisc); 75289c8d91eSAlan Cox /* Force an oops if we mess this up */ 75389c8d91eSAlan Cox tty->ldisc = NULL; 75489c8d91eSAlan Cox 75589c8d91eSAlan Cox /* Ensure the next open requests the N_TTY ldisc */ 75689c8d91eSAlan Cox tty_set_termios_ldisc(tty, N_TTY); 75789c8d91eSAlan Cox } 75889c8d91eSAlan Cox 75996fd7ce5SGreg Kroah-Hartman /** 76096fd7ce5SGreg Kroah-Hartman * tty_ldisc_release - release line discipline 76196fd7ce5SGreg Kroah-Hartman * @tty: tty being shut down 76296fd7ce5SGreg Kroah-Hartman * @o_tty: pair tty for pty/tty pairs 76396fd7ce5SGreg Kroah-Hartman * 76496fd7ce5SGreg Kroah-Hartman * Called during the final close of a tty/pty pair in order to shut down 76596fd7ce5SGreg Kroah-Hartman * the line discpline layer. On exit the ldisc assigned is N_TTY and the 76696fd7ce5SGreg Kroah-Hartman * ldisc has not been opened. 7673ee175d9SPeter Hurley * 7683ee175d9SPeter Hurley * Holding ldisc_sem write lock serializes tty->ldisc changes. 76996fd7ce5SGreg Kroah-Hartman */ 77096fd7ce5SGreg Kroah-Hartman 77196fd7ce5SGreg Kroah-Hartman void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) 77296fd7ce5SGreg Kroah-Hartman { 77396fd7ce5SGreg Kroah-Hartman /* 774a2965b7bSPeter Hurley * Shutdown this line discipline. As this is the final close, 775a2965b7bSPeter Hurley * it does not race with the set_ldisc code path. 77696fd7ce5SGreg Kroah-Hartman */ 77796fd7ce5SGreg Kroah-Hartman 778fc575ee6SPeter Hurley tty_ldisc_debug(tty, "closing ldisc: %p\n", tty->ldisc); 779fc575ee6SPeter Hurley 78036697529SPeter Hurley tty_ldisc_lock_pair(tty, o_tty); 78189c8d91eSAlan Cox tty_ldisc_kill(tty); 78289c8d91eSAlan Cox if (o_tty) 78389c8d91eSAlan Cox tty_ldisc_kill(o_tty); 78436697529SPeter Hurley tty_ldisc_unlock_pair(tty, o_tty); 78536697529SPeter Hurley 78696fd7ce5SGreg Kroah-Hartman /* And the memory resources remaining (buffers, termios) will be 78796fd7ce5SGreg Kroah-Hartman disposed of when the kref hits zero */ 788fc575ee6SPeter Hurley 789fc575ee6SPeter Hurley tty_ldisc_debug(tty, "ldisc closed\n"); 79096fd7ce5SGreg Kroah-Hartman } 79196fd7ce5SGreg Kroah-Hartman 79296fd7ce5SGreg Kroah-Hartman /** 79396fd7ce5SGreg Kroah-Hartman * tty_ldisc_init - ldisc setup for new tty 79496fd7ce5SGreg Kroah-Hartman * @tty: tty being allocated 79596fd7ce5SGreg Kroah-Hartman * 79696fd7ce5SGreg Kroah-Hartman * Set up the line discipline objects for a newly allocated tty. Note that 79796fd7ce5SGreg Kroah-Hartman * the tty structure is not completely set up when this call is made. 79896fd7ce5SGreg Kroah-Hartman */ 79996fd7ce5SGreg Kroah-Hartman 80096fd7ce5SGreg Kroah-Hartman void tty_ldisc_init(struct tty_struct *tty) 80196fd7ce5SGreg Kroah-Hartman { 80236697529SPeter Hurley struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); 80396fd7ce5SGreg Kroah-Hartman if (IS_ERR(ld)) 80496fd7ce5SGreg Kroah-Hartman panic("n_tty: init_tty"); 805f4807045SPeter Hurley tty->ldisc = ld; 80696fd7ce5SGreg Kroah-Hartman } 80796fd7ce5SGreg Kroah-Hartman 8086716671dSJiri Slaby /** 8096716671dSJiri Slaby * tty_ldisc_init - ldisc cleanup for new tty 8106716671dSJiri Slaby * @tty: tty that was allocated recently 8116716671dSJiri Slaby * 8126716671dSJiri Slaby * The tty structure must not becompletely set up (tty_ldisc_setup) when 8136716671dSJiri Slaby * this call is made. 8146716671dSJiri Slaby */ 8156716671dSJiri Slaby void tty_ldisc_deinit(struct tty_struct *tty) 8166716671dSJiri Slaby { 817ebc9baedSPeter Hurley tty_ldisc_put(tty->ldisc); 818f4807045SPeter Hurley tty->ldisc = NULL; 8196716671dSJiri Slaby } 8206716671dSJiri Slaby 82196fd7ce5SGreg Kroah-Hartman void tty_ldisc_begin(void) 82296fd7ce5SGreg Kroah-Hartman { 82396fd7ce5SGreg Kroah-Hartman /* Setup the default TTY line discipline. */ 82496fd7ce5SGreg Kroah-Hartman (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); 82596fd7ce5SGreg Kroah-Hartman } 826