18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/types.h> 38c2ecf20Sopenharmony_ci#include <linux/errno.h> 48c2ecf20Sopenharmony_ci#include <linux/kmod.h> 58c2ecf20Sopenharmony_ci#include <linux/sched.h> 68c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 78c2ecf20Sopenharmony_ci#include <linux/tty.h> 88c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 98c2ecf20Sopenharmony_ci#include <linux/file.h> 108c2ecf20Sopenharmony_ci#include <linux/mm.h> 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/poll.h> 148c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/device.h> 178c2ecf20Sopenharmony_ci#include <linux/wait.h> 188c2ecf20Sopenharmony_ci#include <linux/bitops.h> 198c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 208c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 218c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 228c2ecf20Sopenharmony_ci#include "tty.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#undef LDISC_DEBUG_HANGUP 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#ifdef LDISC_DEBUG_HANGUP 278c2ecf20Sopenharmony_ci#define tty_ldisc_debug(tty, f, args...) tty_debug(tty, f, ##args) 288c2ecf20Sopenharmony_ci#else 298c2ecf20Sopenharmony_ci#define tty_ldisc_debug(tty, f, args...) 308c2ecf20Sopenharmony_ci#endif 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* lockdep nested classes for tty->ldisc_sem */ 338c2ecf20Sopenharmony_cienum { 348c2ecf20Sopenharmony_ci LDISC_SEM_NORMAL, 358c2ecf20Sopenharmony_ci LDISC_SEM_OTHER, 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* 408c2ecf20Sopenharmony_ci * This guards the refcounted line discipline lists. The lock 418c2ecf20Sopenharmony_ci * must be taken with irqs off because there are hangup path 428c2ecf20Sopenharmony_ci * callers who will do ldisc lookups and cannot sleep. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(tty_ldiscs_lock); 468c2ecf20Sopenharmony_ci/* Line disc dispatch table */ 478c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/** 508c2ecf20Sopenharmony_ci * tty_register_ldisc - install a line discipline 518c2ecf20Sopenharmony_ci * @disc: ldisc number 528c2ecf20Sopenharmony_ci * @new_ldisc: pointer to the ldisc object 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * Installs a new line discipline into the kernel. The discipline 558c2ecf20Sopenharmony_ci * is set up as unreferenced and then made available to the kernel 568c2ecf20Sopenharmony_ci * from this point onwards. 578c2ecf20Sopenharmony_ci * 588c2ecf20Sopenharmony_ci * Locking: 598c2ecf20Sopenharmony_ci * takes tty_ldiscs_lock to guard against ldisc races 608c2ecf20Sopenharmony_ci */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ciint tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci unsigned long flags; 658c2ecf20Sopenharmony_ci int ret = 0; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (disc < N_TTY || disc >= NR_LDISCS) 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 718c2ecf20Sopenharmony_ci tty_ldiscs[disc] = new_ldisc; 728c2ecf20Sopenharmony_ci new_ldisc->num = disc; 738c2ecf20Sopenharmony_ci new_ldisc->refcount = 0; 748c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return ret; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_register_ldisc); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/** 818c2ecf20Sopenharmony_ci * tty_unregister_ldisc - unload a line discipline 828c2ecf20Sopenharmony_ci * @disc: ldisc number 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * Remove a line discipline from the kernel providing it is not 858c2ecf20Sopenharmony_ci * currently in use. 868c2ecf20Sopenharmony_ci * 878c2ecf20Sopenharmony_ci * Locking: 888c2ecf20Sopenharmony_ci * takes tty_ldiscs_lock to guard against ldisc races 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ciint tty_unregister_ldisc(int disc) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci unsigned long flags; 948c2ecf20Sopenharmony_ci int ret = 0; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (disc < N_TTY || disc >= NR_LDISCS) 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 1008c2ecf20Sopenharmony_ci if (tty_ldiscs[disc]->refcount) 1018c2ecf20Sopenharmony_ci ret = -EBUSY; 1028c2ecf20Sopenharmony_ci else 1038c2ecf20Sopenharmony_ci tty_ldiscs[disc] = NULL; 1048c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return ret; 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_unregister_ldisc); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic struct tty_ldisc_ops *get_ldops(int disc) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci unsigned long flags; 1138c2ecf20Sopenharmony_ci struct tty_ldisc_ops *ldops, *ret; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 1168c2ecf20Sopenharmony_ci ret = ERR_PTR(-EINVAL); 1178c2ecf20Sopenharmony_ci ldops = tty_ldiscs[disc]; 1188c2ecf20Sopenharmony_ci if (ldops) { 1198c2ecf20Sopenharmony_ci ret = ERR_PTR(-EAGAIN); 1208c2ecf20Sopenharmony_ci if (try_module_get(ldops->owner)) { 1218c2ecf20Sopenharmony_ci ldops->refcount++; 1228c2ecf20Sopenharmony_ci ret = ldops; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void put_ldops(struct tty_ldisc_ops *ldops) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned long flags; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&tty_ldiscs_lock, flags); 1348c2ecf20Sopenharmony_ci ldops->refcount--; 1358c2ecf20Sopenharmony_ci module_put(ldops->owner); 1368c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&tty_ldiscs_lock, flags); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/** 1408c2ecf20Sopenharmony_ci * tty_ldisc_get - take a reference to an ldisc 1418c2ecf20Sopenharmony_ci * @disc: ldisc number 1428c2ecf20Sopenharmony_ci * 1438c2ecf20Sopenharmony_ci * Takes a reference to a line discipline. Deals with refcounts and 1448c2ecf20Sopenharmony_ci * module locking counts. 1458c2ecf20Sopenharmony_ci * 1468c2ecf20Sopenharmony_ci * Returns: -EINVAL if the discipline index is not [N_TTY..NR_LDISCS] or 1478c2ecf20Sopenharmony_ci * if the discipline is not registered 1488c2ecf20Sopenharmony_ci * -EAGAIN if request_module() failed to load or register the 1498c2ecf20Sopenharmony_ci * the discipline 1508c2ecf20Sopenharmony_ci * -ENOMEM if allocation failure 1518c2ecf20Sopenharmony_ci * 1528c2ecf20Sopenharmony_ci * Otherwise, returns a pointer to the discipline and bumps the 1538c2ecf20Sopenharmony_ci * ref count 1548c2ecf20Sopenharmony_ci * 1558c2ecf20Sopenharmony_ci * Locking: 1568c2ecf20Sopenharmony_ci * takes tty_ldiscs_lock to guard against ldisc races 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int tty_ldisc_autoload = IS_BUILTIN(CONFIG_LDISC_AUTOLOAD); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) 1628c2ecf20Sopenharmony_ci{ 1638c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 1648c2ecf20Sopenharmony_ci struct tty_ldisc_ops *ldops; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (disc < N_TTY || disc >= NR_LDISCS) 1678c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Get the ldisc ops - we may need to request them to be loaded 1718c2ecf20Sopenharmony_ci * dynamically and try again. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci ldops = get_ldops(disc); 1748c2ecf20Sopenharmony_ci if (IS_ERR(ldops)) { 1758c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_MODULE) && !tty_ldisc_autoload) 1768c2ecf20Sopenharmony_ci return ERR_PTR(-EPERM); 1778c2ecf20Sopenharmony_ci request_module("tty-ldisc-%d", disc); 1788c2ecf20Sopenharmony_ci ldops = get_ldops(disc); 1798c2ecf20Sopenharmony_ci if (IS_ERR(ldops)) 1808c2ecf20Sopenharmony_ci return ERR_CAST(ldops); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* 1848c2ecf20Sopenharmony_ci * There is no way to handle allocation failure of only 16 bytes. 1858c2ecf20Sopenharmony_ci * Let's simplify error handling and save more memory. 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL | __GFP_NOFAIL); 1888c2ecf20Sopenharmony_ci ld->ops = ldops; 1898c2ecf20Sopenharmony_ci ld->tty = tty; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return ld; 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/** 1958c2ecf20Sopenharmony_ci * tty_ldisc_put - release the ldisc 1968c2ecf20Sopenharmony_ci * 1978c2ecf20Sopenharmony_ci * Complement of tty_ldisc_get(). 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_cistatic void tty_ldisc_put(struct tty_ldisc *ld) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(!ld)) 2028c2ecf20Sopenharmony_ci return; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci put_ldops(ld->ops); 2058c2ecf20Sopenharmony_ci kfree(ld); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void *tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci return (*pos < NR_LDISCS) ? pos : NULL; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void *tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci (*pos)++; 2168c2ecf20Sopenharmony_ci return (*pos < NR_LDISCS) ? pos : NULL; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic void tty_ldiscs_seq_stop(struct seq_file *m, void *v) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic int tty_ldiscs_seq_show(struct seq_file *m, void *v) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci int i = *(loff_t *)v; 2268c2ecf20Sopenharmony_ci struct tty_ldisc_ops *ldops; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci ldops = get_ldops(i); 2298c2ecf20Sopenharmony_ci if (IS_ERR(ldops)) 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci seq_printf(m, "%-10s %2d\n", ldops->name ? ldops->name : "???", i); 2328c2ecf20Sopenharmony_ci put_ldops(ldops); 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ciconst struct seq_operations tty_ldiscs_seq_ops = { 2378c2ecf20Sopenharmony_ci .start = tty_ldiscs_seq_start, 2388c2ecf20Sopenharmony_ci .next = tty_ldiscs_seq_next, 2398c2ecf20Sopenharmony_ci .stop = tty_ldiscs_seq_stop, 2408c2ecf20Sopenharmony_ci .show = tty_ldiscs_seq_show, 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/** 2448c2ecf20Sopenharmony_ci * tty_ldisc_ref_wait - wait for the tty ldisc 2458c2ecf20Sopenharmony_ci * @tty: tty device 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * Dereference the line discipline for the terminal and take a 2488c2ecf20Sopenharmony_ci * reference to it. If the line discipline is in flux then 2498c2ecf20Sopenharmony_ci * wait patiently until it changes. 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * Returns: NULL if the tty has been hungup and not re-opened with 2528c2ecf20Sopenharmony_ci * a new file descriptor, otherwise valid ldisc reference 2538c2ecf20Sopenharmony_ci * 2548c2ecf20Sopenharmony_ci * Note: Must not be called from an IRQ/timer context. The caller 2558c2ecf20Sopenharmony_ci * must also be careful not to hold other locks that will deadlock 2568c2ecf20Sopenharmony_ci * against a discipline change, such as an existing ldisc reference 2578c2ecf20Sopenharmony_ci * (which we check for) 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * Note: a file_operations routine (read/poll/write) should use this 2608c2ecf20Sopenharmony_ci * function to wait for any ldisc lifetime events to finish. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistruct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); 2688c2ecf20Sopenharmony_ci ld = tty->ldisc; 2698c2ecf20Sopenharmony_ci if (!ld) 2708c2ecf20Sopenharmony_ci ldsem_up_read(&tty->ldisc_sem); 2718c2ecf20Sopenharmony_ci return ld; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/** 2768c2ecf20Sopenharmony_ci * tty_ldisc_ref - get the tty ldisc 2778c2ecf20Sopenharmony_ci * @tty: tty device 2788c2ecf20Sopenharmony_ci * 2798c2ecf20Sopenharmony_ci * Dereference the line discipline for the terminal and take a 2808c2ecf20Sopenharmony_ci * reference to it. If the line discipline is in flux then 2818c2ecf20Sopenharmony_ci * return NULL. Can be called from IRQ and timer functions. 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cistruct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct tty_ldisc *ld = NULL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (ldsem_down_read_trylock(&tty->ldisc_sem)) { 2898c2ecf20Sopenharmony_ci ld = tty->ldisc; 2908c2ecf20Sopenharmony_ci if (!ld) 2918c2ecf20Sopenharmony_ci ldsem_up_read(&tty->ldisc_sem); 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ci return ld; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_ldisc_ref); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/** 2988c2ecf20Sopenharmony_ci * tty_ldisc_deref - free a tty ldisc reference 2998c2ecf20Sopenharmony_ci * @ld: reference to free up 3008c2ecf20Sopenharmony_ci * 3018c2ecf20Sopenharmony_ci * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May 3028c2ecf20Sopenharmony_ci * be called in IRQ context. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_civoid tty_ldisc_deref(struct tty_ldisc *ld) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci ldsem_up_read(&ld->tty->ldisc_sem); 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_ldisc_deref); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic inline int 3138c2ecf20Sopenharmony_ci__tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci return ldsem_down_write(&tty->ldisc_sem, timeout); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic inline int 3198c2ecf20Sopenharmony_ci__tty_ldisc_lock_nested(struct tty_struct *tty, unsigned long timeout) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci return ldsem_down_write_nested(&tty->ldisc_sem, 3228c2ecf20Sopenharmony_ci LDISC_SEM_OTHER, timeout); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cistatic inline void __tty_ldisc_unlock(struct tty_struct *tty) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci ldsem_up_write(&tty->ldisc_sem); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ciint tty_ldisc_lock(struct tty_struct *tty, unsigned long timeout) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci int ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Kindly asking blocked readers to release the read side */ 3358c2ecf20Sopenharmony_ci set_bit(TTY_LDISC_CHANGING, &tty->flags); 3368c2ecf20Sopenharmony_ci wake_up_interruptible_all(&tty->read_wait); 3378c2ecf20Sopenharmony_ci wake_up_interruptible_all(&tty->write_wait); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci ret = __tty_ldisc_lock(tty, timeout); 3408c2ecf20Sopenharmony_ci if (!ret) 3418c2ecf20Sopenharmony_ci return -EBUSY; 3428c2ecf20Sopenharmony_ci set_bit(TTY_LDISC_HALTED, &tty->flags); 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_civoid tty_ldisc_unlock(struct tty_struct *tty) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci clear_bit(TTY_LDISC_HALTED, &tty->flags); 3498c2ecf20Sopenharmony_ci /* Can be cleared here - ldisc_unlock will wake up writers firstly */ 3508c2ecf20Sopenharmony_ci clear_bit(TTY_LDISC_CHANGING, &tty->flags); 3518c2ecf20Sopenharmony_ci __tty_ldisc_unlock(tty); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int 3558c2ecf20Sopenharmony_citty_ldisc_lock_pair_timeout(struct tty_struct *tty, struct tty_struct *tty2, 3568c2ecf20Sopenharmony_ci unsigned long timeout) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci int ret; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (tty < tty2) { 3618c2ecf20Sopenharmony_ci ret = __tty_ldisc_lock(tty, timeout); 3628c2ecf20Sopenharmony_ci if (ret) { 3638c2ecf20Sopenharmony_ci ret = __tty_ldisc_lock_nested(tty2, timeout); 3648c2ecf20Sopenharmony_ci if (!ret) 3658c2ecf20Sopenharmony_ci __tty_ldisc_unlock(tty); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci } else { 3688c2ecf20Sopenharmony_ci /* if this is possible, it has lots of implications */ 3698c2ecf20Sopenharmony_ci WARN_ON_ONCE(tty == tty2); 3708c2ecf20Sopenharmony_ci if (tty2 && tty != tty2) { 3718c2ecf20Sopenharmony_ci ret = __tty_ldisc_lock(tty2, timeout); 3728c2ecf20Sopenharmony_ci if (ret) { 3738c2ecf20Sopenharmony_ci ret = __tty_ldisc_lock_nested(tty, timeout); 3748c2ecf20Sopenharmony_ci if (!ret) 3758c2ecf20Sopenharmony_ci __tty_ldisc_unlock(tty2); 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci } else 3788c2ecf20Sopenharmony_ci ret = __tty_ldisc_lock(tty, timeout); 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!ret) 3828c2ecf20Sopenharmony_ci return -EBUSY; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci set_bit(TTY_LDISC_HALTED, &tty->flags); 3858c2ecf20Sopenharmony_ci if (tty2) 3868c2ecf20Sopenharmony_ci set_bit(TTY_LDISC_HALTED, &tty2->flags); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci} 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_cistatic void tty_ldisc_lock_pair(struct tty_struct *tty, struct tty_struct *tty2) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci tty_ldisc_lock_pair_timeout(tty, tty2, MAX_SCHEDULE_TIMEOUT); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_cistatic void tty_ldisc_unlock_pair(struct tty_struct *tty, 3968c2ecf20Sopenharmony_ci struct tty_struct *tty2) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci __tty_ldisc_unlock(tty); 3998c2ecf20Sopenharmony_ci if (tty2) 4008c2ecf20Sopenharmony_ci __tty_ldisc_unlock(tty2); 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci/** 4048c2ecf20Sopenharmony_ci * tty_ldisc_flush - flush line discipline queue 4058c2ecf20Sopenharmony_ci * @tty: tty 4068c2ecf20Sopenharmony_ci * 4078c2ecf20Sopenharmony_ci * Flush the line discipline queue (if any) and the tty flip buffers 4088c2ecf20Sopenharmony_ci * for this tty. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_civoid tty_ldisc_flush(struct tty_struct *tty) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct tty_ldisc *ld = tty_ldisc_ref(tty); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci tty_buffer_flush(tty, ld); 4168c2ecf20Sopenharmony_ci if (ld) 4178c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_ldisc_flush); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci/** 4228c2ecf20Sopenharmony_ci * tty_set_termios_ldisc - set ldisc field 4238c2ecf20Sopenharmony_ci * @tty: tty structure 4248c2ecf20Sopenharmony_ci * @disc: line discipline number 4258c2ecf20Sopenharmony_ci * 4268c2ecf20Sopenharmony_ci * This is probably overkill for real world processors but 4278c2ecf20Sopenharmony_ci * they are not on hot paths so a little discipline won't do 4288c2ecf20Sopenharmony_ci * any harm. 4298c2ecf20Sopenharmony_ci * 4308c2ecf20Sopenharmony_ci * The line discipline-related tty_struct fields are reset to 4318c2ecf20Sopenharmony_ci * prevent the ldisc driver from re-using stale information for 4328c2ecf20Sopenharmony_ci * the new ldisc instance. 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Locking: takes termios_rwsem 4358c2ecf20Sopenharmony_ci */ 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void tty_set_termios_ldisc(struct tty_struct *tty, int disc) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci down_write(&tty->termios_rwsem); 4408c2ecf20Sopenharmony_ci tty->termios.c_line = disc; 4418c2ecf20Sopenharmony_ci up_write(&tty->termios_rwsem); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci tty->disc_data = NULL; 4448c2ecf20Sopenharmony_ci tty->receive_room = 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/** 4488c2ecf20Sopenharmony_ci * tty_ldisc_open - open a line discipline 4498c2ecf20Sopenharmony_ci * @tty: tty we are opening the ldisc on 4508c2ecf20Sopenharmony_ci * @ld: discipline to open 4518c2ecf20Sopenharmony_ci * 4528c2ecf20Sopenharmony_ci * A helper opening method. Also a convenient debugging and check 4538c2ecf20Sopenharmony_ci * point. 4548c2ecf20Sopenharmony_ci * 4558c2ecf20Sopenharmony_ci * Locking: always called with BTM already held. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); 4618c2ecf20Sopenharmony_ci if (ld->ops->open) { 4628c2ecf20Sopenharmony_ci int ret; 4638c2ecf20Sopenharmony_ci /* BTM here locks versus a hangup event */ 4648c2ecf20Sopenharmony_ci ret = ld->ops->open(tty); 4658c2ecf20Sopenharmony_ci if (ret) 4668c2ecf20Sopenharmony_ci clear_bit(TTY_LDISC_OPEN, &tty->flags); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci tty_ldisc_debug(tty, "%p: opened\n", ld); 4698c2ecf20Sopenharmony_ci return ret; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/** 4758c2ecf20Sopenharmony_ci * tty_ldisc_close - close a line discipline 4768c2ecf20Sopenharmony_ci * @tty: tty we are opening the ldisc on 4778c2ecf20Sopenharmony_ci * @ld: discipline to close 4788c2ecf20Sopenharmony_ci * 4798c2ecf20Sopenharmony_ci * A helper close method. Also a convenient debugging and check 4808c2ecf20Sopenharmony_ci * point. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci lockdep_assert_held_write(&tty->ldisc_sem); 4868c2ecf20Sopenharmony_ci WARN_ON(!test_bit(TTY_LDISC_OPEN, &tty->flags)); 4878c2ecf20Sopenharmony_ci clear_bit(TTY_LDISC_OPEN, &tty->flags); 4888c2ecf20Sopenharmony_ci if (ld->ops->close) 4898c2ecf20Sopenharmony_ci ld->ops->close(tty); 4908c2ecf20Sopenharmony_ci tty_ldisc_debug(tty, "%p: closed\n", ld); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci/** 4948c2ecf20Sopenharmony_ci * tty_ldisc_failto - helper for ldisc failback 4958c2ecf20Sopenharmony_ci * @tty: tty to open the ldisc on 4968c2ecf20Sopenharmony_ci * @ld: ldisc we are trying to fail back to 4978c2ecf20Sopenharmony_ci * 4988c2ecf20Sopenharmony_ci * Helper to try and recover a tty when switching back to the old 4998c2ecf20Sopenharmony_ci * ldisc fails and we need something attached. 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic int tty_ldisc_failto(struct tty_struct *tty, int ld) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct tty_ldisc *disc = tty_ldisc_get(tty, ld); 5058c2ecf20Sopenharmony_ci int r; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci lockdep_assert_held_write(&tty->ldisc_sem); 5088c2ecf20Sopenharmony_ci if (IS_ERR(disc)) 5098c2ecf20Sopenharmony_ci return PTR_ERR(disc); 5108c2ecf20Sopenharmony_ci tty->ldisc = disc; 5118c2ecf20Sopenharmony_ci tty_set_termios_ldisc(tty, ld); 5128c2ecf20Sopenharmony_ci if ((r = tty_ldisc_open(tty, disc)) < 0) 5138c2ecf20Sopenharmony_ci tty_ldisc_put(disc); 5148c2ecf20Sopenharmony_ci return r; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci/** 5188c2ecf20Sopenharmony_ci * tty_ldisc_restore - helper for tty ldisc change 5198c2ecf20Sopenharmony_ci * @tty: tty to recover 5208c2ecf20Sopenharmony_ci * @old: previous ldisc 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Restore the previous line discipline or N_TTY when a line discipline 5238c2ecf20Sopenharmony_ci * change fails due to an open error 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci /* There is an outstanding reference here so this is safe */ 5298c2ecf20Sopenharmony_ci if (tty_ldisc_failto(tty, old->ops->num) < 0) { 5308c2ecf20Sopenharmony_ci const char *name = tty_name(tty); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci pr_warn("Falling back ldisc for %s.\n", name); 5338c2ecf20Sopenharmony_ci /* The traditional behaviour is to fall back to N_TTY, we 5348c2ecf20Sopenharmony_ci want to avoid falling back to N_NULL unless we have no 5358c2ecf20Sopenharmony_ci choice to avoid the risk of breaking anything */ 5368c2ecf20Sopenharmony_ci if (tty_ldisc_failto(tty, N_TTY) < 0 && 5378c2ecf20Sopenharmony_ci tty_ldisc_failto(tty, N_NULL) < 0) 5388c2ecf20Sopenharmony_ci panic("Couldn't open N_NULL ldisc for %s.", name); 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci/** 5438c2ecf20Sopenharmony_ci * tty_set_ldisc - set line discipline 5448c2ecf20Sopenharmony_ci * @tty: the terminal to set 5458c2ecf20Sopenharmony_ci * @disc: the line discipline number 5468c2ecf20Sopenharmony_ci * 5478c2ecf20Sopenharmony_ci * Set the discipline of a tty line. Must be called from a process 5488c2ecf20Sopenharmony_ci * context. The ldisc change logic has to protect itself against any 5498c2ecf20Sopenharmony_ci * overlapping ldisc change (including on the other end of pty pairs), 5508c2ecf20Sopenharmony_ci * the close of one side of a tty/pty pair, and eventually hangup. 5518c2ecf20Sopenharmony_ci */ 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ciint tty_set_ldisc(struct tty_struct *tty, int disc) 5548c2ecf20Sopenharmony_ci{ 5558c2ecf20Sopenharmony_ci int retval; 5568c2ecf20Sopenharmony_ci struct tty_ldisc *old_ldisc, *new_ldisc; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci new_ldisc = tty_ldisc_get(tty, disc); 5598c2ecf20Sopenharmony_ci if (IS_ERR(new_ldisc)) 5608c2ecf20Sopenharmony_ci return PTR_ERR(new_ldisc); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci tty_lock(tty); 5638c2ecf20Sopenharmony_ci retval = tty_ldisc_lock(tty, 5 * HZ); 5648c2ecf20Sopenharmony_ci if (retval) 5658c2ecf20Sopenharmony_ci goto err; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (!tty->ldisc) { 5688c2ecf20Sopenharmony_ci retval = -EIO; 5698c2ecf20Sopenharmony_ci goto out; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Check the no-op case */ 5738c2ecf20Sopenharmony_ci if (tty->ldisc->ops->num == disc) 5748c2ecf20Sopenharmony_ci goto out; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (test_bit(TTY_HUPPED, &tty->flags)) { 5778c2ecf20Sopenharmony_ci /* We were raced by hangup */ 5788c2ecf20Sopenharmony_ci retval = -EIO; 5798c2ecf20Sopenharmony_ci goto out; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci old_ldisc = tty->ldisc; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Shutdown the old discipline. */ 5858c2ecf20Sopenharmony_ci tty_ldisc_close(tty, old_ldisc); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* Now set up the new line discipline. */ 5888c2ecf20Sopenharmony_ci tty->ldisc = new_ldisc; 5898c2ecf20Sopenharmony_ci tty_set_termios_ldisc(tty, disc); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci retval = tty_ldisc_open(tty, new_ldisc); 5928c2ecf20Sopenharmony_ci if (retval < 0) { 5938c2ecf20Sopenharmony_ci /* Back to the old one or N_TTY if we can't */ 5948c2ecf20Sopenharmony_ci tty_ldisc_put(new_ldisc); 5958c2ecf20Sopenharmony_ci tty_ldisc_restore(tty, old_ldisc); 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { 5998c2ecf20Sopenharmony_ci down_read(&tty->termios_rwsem); 6008c2ecf20Sopenharmony_ci tty->ops->set_ldisc(tty); 6018c2ecf20Sopenharmony_ci up_read(&tty->termios_rwsem); 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* At this point we hold a reference to the new ldisc and a 6058c2ecf20Sopenharmony_ci reference to the old ldisc, or we hold two references to 6068c2ecf20Sopenharmony_ci the old ldisc (if it was restored as part of error cleanup 6078c2ecf20Sopenharmony_ci above). In either case, releasing a single reference from 6088c2ecf20Sopenharmony_ci the old ldisc is correct. */ 6098c2ecf20Sopenharmony_ci new_ldisc = old_ldisc; 6108c2ecf20Sopenharmony_ciout: 6118c2ecf20Sopenharmony_ci tty_ldisc_unlock(tty); 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci /* Restart the work queue in case no characters kick it off. Safe if 6148c2ecf20Sopenharmony_ci already running */ 6158c2ecf20Sopenharmony_ci tty_buffer_restart_work(tty->port); 6168c2ecf20Sopenharmony_cierr: 6178c2ecf20Sopenharmony_ci tty_ldisc_put(new_ldisc); /* drop the extra reference */ 6188c2ecf20Sopenharmony_ci tty_unlock(tty); 6198c2ecf20Sopenharmony_ci return retval; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_set_ldisc); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci/** 6248c2ecf20Sopenharmony_ci * tty_ldisc_kill - teardown ldisc 6258c2ecf20Sopenharmony_ci * @tty: tty being released 6268c2ecf20Sopenharmony_ci * 6278c2ecf20Sopenharmony_ci * Perform final close of the ldisc and reset tty->ldisc 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_cistatic void tty_ldisc_kill(struct tty_struct *tty) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci lockdep_assert_held_write(&tty->ldisc_sem); 6328c2ecf20Sopenharmony_ci if (!tty->ldisc) 6338c2ecf20Sopenharmony_ci return; 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * Now kill off the ldisc 6368c2ecf20Sopenharmony_ci */ 6378c2ecf20Sopenharmony_ci tty_ldisc_close(tty, tty->ldisc); 6388c2ecf20Sopenharmony_ci tty_ldisc_put(tty->ldisc); 6398c2ecf20Sopenharmony_ci /* Force an oops if we mess this up */ 6408c2ecf20Sopenharmony_ci tty->ldisc = NULL; 6418c2ecf20Sopenharmony_ci} 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci/** 6448c2ecf20Sopenharmony_ci * tty_reset_termios - reset terminal state 6458c2ecf20Sopenharmony_ci * @tty: tty to reset 6468c2ecf20Sopenharmony_ci * 6478c2ecf20Sopenharmony_ci * Restore a terminal to the driver default state. 6488c2ecf20Sopenharmony_ci */ 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic void tty_reset_termios(struct tty_struct *tty) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci down_write(&tty->termios_rwsem); 6538c2ecf20Sopenharmony_ci tty->termios = tty->driver->init_termios; 6548c2ecf20Sopenharmony_ci tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); 6558c2ecf20Sopenharmony_ci tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); 6568c2ecf20Sopenharmony_ci up_write(&tty->termios_rwsem); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci/** 6618c2ecf20Sopenharmony_ci * tty_ldisc_reinit - reinitialise the tty ldisc 6628c2ecf20Sopenharmony_ci * @tty: tty to reinit 6638c2ecf20Sopenharmony_ci * @disc: line discipline to reinitialize 6648c2ecf20Sopenharmony_ci * 6658c2ecf20Sopenharmony_ci * Completely reinitialize the line discipline state, by closing the 6668c2ecf20Sopenharmony_ci * current instance, if there is one, and opening a new instance. If 6678c2ecf20Sopenharmony_ci * an error occurs opening the new non-N_TTY instance, the instance 6688c2ecf20Sopenharmony_ci * is dropped and tty->ldisc reset to NULL. The caller can then retry 6698c2ecf20Sopenharmony_ci * with N_TTY instead. 6708c2ecf20Sopenharmony_ci * 6718c2ecf20Sopenharmony_ci * Returns 0 if successful, otherwise error code < 0 6728c2ecf20Sopenharmony_ci */ 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ciint tty_ldisc_reinit(struct tty_struct *tty, int disc) 6758c2ecf20Sopenharmony_ci{ 6768c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 6778c2ecf20Sopenharmony_ci int retval; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci lockdep_assert_held_write(&tty->ldisc_sem); 6808c2ecf20Sopenharmony_ci ld = tty_ldisc_get(tty, disc); 6818c2ecf20Sopenharmony_ci if (IS_ERR(ld)) { 6828c2ecf20Sopenharmony_ci BUG_ON(disc == N_TTY); 6838c2ecf20Sopenharmony_ci return PTR_ERR(ld); 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (tty->ldisc) { 6878c2ecf20Sopenharmony_ci tty_ldisc_close(tty, tty->ldisc); 6888c2ecf20Sopenharmony_ci tty_ldisc_put(tty->ldisc); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* switch the line discipline */ 6928c2ecf20Sopenharmony_ci tty->ldisc = ld; 6938c2ecf20Sopenharmony_ci tty_set_termios_ldisc(tty, disc); 6948c2ecf20Sopenharmony_ci retval = tty_ldisc_open(tty, tty->ldisc); 6958c2ecf20Sopenharmony_ci if (retval) { 6968c2ecf20Sopenharmony_ci tty_ldisc_put(tty->ldisc); 6978c2ecf20Sopenharmony_ci tty->ldisc = NULL; 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci return retval; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * tty_ldisc_hangup - hangup ldisc reset 7048c2ecf20Sopenharmony_ci * @tty: tty being hung up 7058c2ecf20Sopenharmony_ci * 7068c2ecf20Sopenharmony_ci * Some tty devices reset their termios when they receive a hangup 7078c2ecf20Sopenharmony_ci * event. In that situation we must also switch back to N_TTY properly 7088c2ecf20Sopenharmony_ci * before we reset the termios data. 7098c2ecf20Sopenharmony_ci * 7108c2ecf20Sopenharmony_ci * Locking: We can take the ldisc mutex as the rest of the code is 7118c2ecf20Sopenharmony_ci * careful to allow for this. 7128c2ecf20Sopenharmony_ci * 7138c2ecf20Sopenharmony_ci * In the pty pair case this occurs in the close() path of the 7148c2ecf20Sopenharmony_ci * tty itself so we must be careful about locking rules. 7158c2ecf20Sopenharmony_ci */ 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_civoid tty_ldisc_hangup(struct tty_struct *tty, bool reinit) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci tty_ldisc_debug(tty, "%p: hangup\n", tty->ldisc); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ld = tty_ldisc_ref(tty); 7248c2ecf20Sopenharmony_ci if (ld != NULL) { 7258c2ecf20Sopenharmony_ci if (ld->ops->flush_buffer) 7268c2ecf20Sopenharmony_ci ld->ops->flush_buffer(tty); 7278c2ecf20Sopenharmony_ci tty_driver_flush_buffer(tty); 7288c2ecf20Sopenharmony_ci if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && 7298c2ecf20Sopenharmony_ci ld->ops->write_wakeup) 7308c2ecf20Sopenharmony_ci ld->ops->write_wakeup(tty); 7318c2ecf20Sopenharmony_ci if (ld->ops->hangup) 7328c2ecf20Sopenharmony_ci ld->ops->hangup(tty); 7338c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT); 7378c2ecf20Sopenharmony_ci wake_up_interruptible_poll(&tty->read_wait, EPOLLIN); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * Shutdown the current line discipline, and reset it to 7418c2ecf20Sopenharmony_ci * N_TTY if need be. 7428c2ecf20Sopenharmony_ci * 7438c2ecf20Sopenharmony_ci * Avoid racing set_ldisc or tty_ldisc_release 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci tty_ldisc_lock(tty, MAX_SCHEDULE_TIMEOUT); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) 7488c2ecf20Sopenharmony_ci tty_reset_termios(tty); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (tty->ldisc) { 7518c2ecf20Sopenharmony_ci if (reinit) { 7528c2ecf20Sopenharmony_ci if (tty_ldisc_reinit(tty, tty->termios.c_line) < 0 && 7538c2ecf20Sopenharmony_ci tty_ldisc_reinit(tty, N_TTY) < 0) 7548c2ecf20Sopenharmony_ci WARN_ON(tty_ldisc_reinit(tty, N_NULL) < 0); 7558c2ecf20Sopenharmony_ci } else 7568c2ecf20Sopenharmony_ci tty_ldisc_kill(tty); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci tty_ldisc_unlock(tty); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci/** 7628c2ecf20Sopenharmony_ci * tty_ldisc_setup - open line discipline 7638c2ecf20Sopenharmony_ci * @tty: tty being shut down 7648c2ecf20Sopenharmony_ci * @o_tty: pair tty for pty/tty pairs 7658c2ecf20Sopenharmony_ci * 7668c2ecf20Sopenharmony_ci * Called during the initial open of a tty/pty pair in order to set up the 7678c2ecf20Sopenharmony_ci * line disciplines and bind them to the tty. This has no locking issues 7688c2ecf20Sopenharmony_ci * as the device isn't yet active. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ciint tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci int retval = tty_ldisc_open(tty, tty->ldisc); 7748c2ecf20Sopenharmony_ci if (retval) 7758c2ecf20Sopenharmony_ci return retval; 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci if (o_tty) { 7788c2ecf20Sopenharmony_ci /* 7798c2ecf20Sopenharmony_ci * Called without o_tty->ldisc_sem held, as o_tty has been 7808c2ecf20Sopenharmony_ci * just allocated and no one has a reference to it. 7818c2ecf20Sopenharmony_ci */ 7828c2ecf20Sopenharmony_ci retval = tty_ldisc_open(o_tty, o_tty->ldisc); 7838c2ecf20Sopenharmony_ci if (retval) { 7848c2ecf20Sopenharmony_ci tty_ldisc_close(tty, tty->ldisc); 7858c2ecf20Sopenharmony_ci return retval; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci/** 7928c2ecf20Sopenharmony_ci * tty_ldisc_release - release line discipline 7938c2ecf20Sopenharmony_ci * @tty: tty being shut down (or one end of pty pair) 7948c2ecf20Sopenharmony_ci * 7958c2ecf20Sopenharmony_ci * Called during the final close of a tty or a pty pair in order to shut 7968c2ecf20Sopenharmony_ci * down the line discpline layer. On exit, each tty's ldisc is NULL. 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_civoid tty_ldisc_release(struct tty_struct *tty) 8008c2ecf20Sopenharmony_ci{ 8018c2ecf20Sopenharmony_ci struct tty_struct *o_tty = tty->link; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* 8048c2ecf20Sopenharmony_ci * Shutdown this line discipline. As this is the final close, 8058c2ecf20Sopenharmony_ci * it does not race with the set_ldisc code path. 8068c2ecf20Sopenharmony_ci */ 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci tty_ldisc_lock_pair(tty, o_tty); 8098c2ecf20Sopenharmony_ci tty_ldisc_kill(tty); 8108c2ecf20Sopenharmony_ci if (o_tty) 8118c2ecf20Sopenharmony_ci tty_ldisc_kill(o_tty); 8128c2ecf20Sopenharmony_ci tty_ldisc_unlock_pair(tty, o_tty); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* And the memory resources remaining (buffers, termios) will be 8158c2ecf20Sopenharmony_ci disposed of when the kref hits zero */ 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci tty_ldisc_debug(tty, "released\n"); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_ldisc_release); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/** 8228c2ecf20Sopenharmony_ci * tty_ldisc_init - ldisc setup for new tty 8238c2ecf20Sopenharmony_ci * @tty: tty being allocated 8248c2ecf20Sopenharmony_ci * 8258c2ecf20Sopenharmony_ci * Set up the line discipline objects for a newly allocated tty. Note that 8268c2ecf20Sopenharmony_ci * the tty structure is not completely set up when this call is made. 8278c2ecf20Sopenharmony_ci */ 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ciint tty_ldisc_init(struct tty_struct *tty) 8308c2ecf20Sopenharmony_ci{ 8318c2ecf20Sopenharmony_ci struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); 8328c2ecf20Sopenharmony_ci if (IS_ERR(ld)) 8338c2ecf20Sopenharmony_ci return PTR_ERR(ld); 8348c2ecf20Sopenharmony_ci tty->ldisc = ld; 8358c2ecf20Sopenharmony_ci return 0; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci/** 8398c2ecf20Sopenharmony_ci * tty_ldisc_deinit - ldisc cleanup for new tty 8408c2ecf20Sopenharmony_ci * @tty: tty that was allocated recently 8418c2ecf20Sopenharmony_ci * 8428c2ecf20Sopenharmony_ci * The tty structure must not becompletely set up (tty_ldisc_setup) when 8438c2ecf20Sopenharmony_ci * this call is made. 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_civoid tty_ldisc_deinit(struct tty_struct *tty) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci /* no ldisc_sem, tty is being destroyed */ 8488c2ecf20Sopenharmony_ci if (tty->ldisc) 8498c2ecf20Sopenharmony_ci tty_ldisc_put(tty->ldisc); 8508c2ecf20Sopenharmony_ci tty->ldisc = NULL; 8518c2ecf20Sopenharmony_ci} 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_cistatic struct ctl_table tty_table[] = { 8548c2ecf20Sopenharmony_ci { 8558c2ecf20Sopenharmony_ci .procname = "ldisc_autoload", 8568c2ecf20Sopenharmony_ci .data = &tty_ldisc_autoload, 8578c2ecf20Sopenharmony_ci .maxlen = sizeof(tty_ldisc_autoload), 8588c2ecf20Sopenharmony_ci .mode = 0644, 8598c2ecf20Sopenharmony_ci .proc_handler = proc_dointvec, 8608c2ecf20Sopenharmony_ci .extra1 = SYSCTL_ZERO, 8618c2ecf20Sopenharmony_ci .extra2 = SYSCTL_ONE, 8628c2ecf20Sopenharmony_ci }, 8638c2ecf20Sopenharmony_ci { } 8648c2ecf20Sopenharmony_ci}; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_cistatic struct ctl_table tty_dir_table[] = { 8678c2ecf20Sopenharmony_ci { 8688c2ecf20Sopenharmony_ci .procname = "tty", 8698c2ecf20Sopenharmony_ci .mode = 0555, 8708c2ecf20Sopenharmony_ci .child = tty_table, 8718c2ecf20Sopenharmony_ci }, 8728c2ecf20Sopenharmony_ci { } 8738c2ecf20Sopenharmony_ci}; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic struct ctl_table tty_root_table[] = { 8768c2ecf20Sopenharmony_ci { 8778c2ecf20Sopenharmony_ci .procname = "dev", 8788c2ecf20Sopenharmony_ci .mode = 0555, 8798c2ecf20Sopenharmony_ci .child = tty_dir_table, 8808c2ecf20Sopenharmony_ci }, 8818c2ecf20Sopenharmony_ci { } 8828c2ecf20Sopenharmony_ci}; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_civoid tty_sysctl_init(void) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci register_sysctl_table(tty_root_table); 8878c2ecf20Sopenharmony_ci} 888