18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 1991, 1992 Linus Torvalds 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/* 78c2ecf20Sopenharmony_ci * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles 88c2ecf20Sopenharmony_ci * or rs-channels. It also implements echoing, cooked mode etc. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the 138c2ecf20Sopenharmony_ci * tty_struct and tty_queue structures. Previously there was an array 148c2ecf20Sopenharmony_ci * of 256 tty_struct's which was statically allocated, and the 158c2ecf20Sopenharmony_ci * tty_queue structures were allocated at boot time. Both are now 168c2ecf20Sopenharmony_ci * dynamically allocated only when the tty is open. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * Also restructured routines so that there is more of a separation 198c2ecf20Sopenharmony_ci * between the high-level tty routines (tty_io.c and tty_ioctl.c) and 208c2ecf20Sopenharmony_ci * the low-level tty routines (serial.c, pty.c, console.c). This 218c2ecf20Sopenharmony_ci * makes for cleaner and more compact code. -TYT, 9/17/92 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines 248c2ecf20Sopenharmony_ci * which can be dynamically activated and de-activated by the line 258c2ecf20Sopenharmony_ci * discipline handling modules (like SLIP). 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * NOTE: pay no attention to the line discipline code (yet); its 288c2ecf20Sopenharmony_ci * interface is still subject to change in this version... 298c2ecf20Sopenharmony_ci * -- TYT, 1/31/92 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * Added functionality to the OPOST tty handling. No delays, but all 328c2ecf20Sopenharmony_ci * other bits should be there. 338c2ecf20Sopenharmony_ci * -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993. 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * Rewrote canonical mode and added more termios flags. 368c2ecf20Sopenharmony_ci * -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Reorganized FASYNC support so mouse code can share it. 398c2ecf20Sopenharmony_ci * -- ctm@ardi.com, 9Sep95 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * New TIOCLINUX variants added. 428c2ecf20Sopenharmony_ci * -- mj@k332.feld.cvut.cz, 19-Nov-95 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Restrict vt switching via ioctl() 458c2ecf20Sopenharmony_ci * -- grif@cs.ucr.edu, 5-Dec-95 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Move console and virtual terminal code to more appropriate files, 488c2ecf20Sopenharmony_ci * implement CONFIG_VT and generalize console device interface. 498c2ecf20Sopenharmony_ci * -- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 508c2ecf20Sopenharmony_ci * 518c2ecf20Sopenharmony_ci * Rewrote tty_init_dev and tty_release_dev to eliminate races. 528c2ecf20Sopenharmony_ci * -- Bill Hawes <whawes@star.net>, June 97 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * Added devfs support. 558c2ecf20Sopenharmony_ci * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * Added support for a Unix98-style ptmx device. 588c2ecf20Sopenharmony_ci * -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * Reduced memory usage for older ARM systems 618c2ecf20Sopenharmony_ci * -- Russell King <rmk@arm.linux.org.uk> 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci * Move do_SAK() into process context. Less stack use in devfs functions. 648c2ecf20Sopenharmony_ci * alloc_tty_struct() always uses kmalloc() 658c2ecf20Sopenharmony_ci * -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#include <linux/types.h> 698c2ecf20Sopenharmony_ci#include <linux/major.h> 708c2ecf20Sopenharmony_ci#include <linux/errno.h> 718c2ecf20Sopenharmony_ci#include <linux/signal.h> 728c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 738c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 748c2ecf20Sopenharmony_ci#include <linux/sched/task.h> 758c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 768c2ecf20Sopenharmony_ci#include <linux/tty.h> 778c2ecf20Sopenharmony_ci#include <linux/tty_driver.h> 788c2ecf20Sopenharmony_ci#include <linux/tty_flip.h> 798c2ecf20Sopenharmony_ci#include <linux/devpts_fs.h> 808c2ecf20Sopenharmony_ci#include <linux/file.h> 818c2ecf20Sopenharmony_ci#include <linux/fdtable.h> 828c2ecf20Sopenharmony_ci#include <linux/console.h> 838c2ecf20Sopenharmony_ci#include <linux/timer.h> 848c2ecf20Sopenharmony_ci#include <linux/ctype.h> 858c2ecf20Sopenharmony_ci#include <linux/kd.h> 868c2ecf20Sopenharmony_ci#include <linux/mm.h> 878c2ecf20Sopenharmony_ci#include <linux/string.h> 888c2ecf20Sopenharmony_ci#include <linux/slab.h> 898c2ecf20Sopenharmony_ci#include <linux/poll.h> 908c2ecf20Sopenharmony_ci#include <linux/ppp-ioctl.h> 918c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 928c2ecf20Sopenharmony_ci#include <linux/init.h> 938c2ecf20Sopenharmony_ci#include <linux/module.h> 948c2ecf20Sopenharmony_ci#include <linux/device.h> 958c2ecf20Sopenharmony_ci#include <linux/wait.h> 968c2ecf20Sopenharmony_ci#include <linux/bitops.h> 978c2ecf20Sopenharmony_ci#include <linux/delay.h> 988c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 998c2ecf20Sopenharmony_ci#include <linux/serial.h> 1008c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 1018c2ecf20Sopenharmony_ci#include <linux/compat.h> 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci#include <linux/kbd_kern.h> 1068c2ecf20Sopenharmony_ci#include <linux/vt_kern.h> 1078c2ecf20Sopenharmony_ci#include <linux/selection.h> 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci#include <linux/kmod.h> 1108c2ecf20Sopenharmony_ci#include <linux/nsproxy.h> 1118c2ecf20Sopenharmony_ci#include "tty.h" 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#undef TTY_DEBUG_HANGUP 1148c2ecf20Sopenharmony_ci#ifdef TTY_DEBUG_HANGUP 1158c2ecf20Sopenharmony_ci# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args) 1168c2ecf20Sopenharmony_ci#else 1178c2ecf20Sopenharmony_ci# define tty_debug_hangup(tty, f, args...) do { } while (0) 1188c2ecf20Sopenharmony_ci#endif 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci#define TTY_PARANOIA_CHECK 1 1218c2ecf20Sopenharmony_ci#define CHECK_TTY_COUNT 1 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistruct ktermios tty_std_termios = { /* for the benefit of tty drivers */ 1248c2ecf20Sopenharmony_ci .c_iflag = ICRNL | IXON, 1258c2ecf20Sopenharmony_ci .c_oflag = OPOST | ONLCR, 1268c2ecf20Sopenharmony_ci .c_cflag = B38400 | CS8 | CREAD | HUPCL, 1278c2ecf20Sopenharmony_ci .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | 1288c2ecf20Sopenharmony_ci ECHOCTL | ECHOKE | IEXTEN, 1298c2ecf20Sopenharmony_ci .c_cc = INIT_C_CC, 1308c2ecf20Sopenharmony_ci .c_ispeed = 38400, 1318c2ecf20Sopenharmony_ci .c_ospeed = 38400, 1328c2ecf20Sopenharmony_ci /* .c_line = N_TTY, */ 1338c2ecf20Sopenharmony_ci}; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_std_termios); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci/* This list gets poked at by procfs and various bits of boot up code. This 1388c2ecf20Sopenharmony_ci could do with some rationalisation such as pulling the tty proc function 1398c2ecf20Sopenharmony_ci into this file */ 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ciLIST_HEAD(tty_drivers); /* linked list of tty drivers */ 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* Mutex to protect creating and releasing a tty */ 1448c2ecf20Sopenharmony_ciDEFINE_MUTEX(tty_mutex); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic ssize_t tty_read(struct kiocb *, struct iov_iter *); 1478c2ecf20Sopenharmony_cistatic ssize_t tty_write(struct kiocb *, struct iov_iter *); 1488c2ecf20Sopenharmony_cistatic __poll_t tty_poll(struct file *, poll_table *); 1498c2ecf20Sopenharmony_cistatic int tty_open(struct inode *, struct file *); 1508c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1518c2ecf20Sopenharmony_cistatic long tty_compat_ioctl(struct file *file, unsigned int cmd, 1528c2ecf20Sopenharmony_ci unsigned long arg); 1538c2ecf20Sopenharmony_ci#else 1548c2ecf20Sopenharmony_ci#define tty_compat_ioctl NULL 1558c2ecf20Sopenharmony_ci#endif 1568c2ecf20Sopenharmony_cistatic int __tty_fasync(int fd, struct file *filp, int on); 1578c2ecf20Sopenharmony_cistatic int tty_fasync(int fd, struct file *filp, int on); 1588c2ecf20Sopenharmony_cistatic void release_tty(struct tty_struct *tty, int idx); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * free_tty_struct - free a disused tty 1628c2ecf20Sopenharmony_ci * @tty: tty struct to free 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Free the write buffers, tty queue and tty memory itself. 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Locking: none. Must be called after tty is definitely unused 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void free_tty_struct(struct tty_struct *tty) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci tty_ldisc_deinit(tty); 1728c2ecf20Sopenharmony_ci put_device(tty->dev); 1738c2ecf20Sopenharmony_ci kfree(tty->write_buf); 1748c2ecf20Sopenharmony_ci tty->magic = 0xDEADDEAD; 1758c2ecf20Sopenharmony_ci kfree(tty); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic inline struct tty_struct *file_tty(struct file *file) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci return ((struct tty_file_private *)file->private_data)->tty; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciint tty_alloc_file(struct file *file) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct tty_file_private *priv; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci priv = kmalloc(sizeof(*priv), GFP_KERNEL); 1888c2ecf20Sopenharmony_ci if (!priv) 1898c2ecf20Sopenharmony_ci return -ENOMEM; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci file->private_data = priv; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* Associate a new file with the tty structure */ 1978c2ecf20Sopenharmony_civoid tty_add_file(struct tty_struct *tty, struct file *file) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct tty_file_private *priv = file->private_data; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci priv->tty = tty; 2028c2ecf20Sopenharmony_ci priv->file = file; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci spin_lock(&tty->files_lock); 2058c2ecf20Sopenharmony_ci list_add(&priv->list, &tty->tty_files); 2068c2ecf20Sopenharmony_ci spin_unlock(&tty->files_lock); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci/** 2108c2ecf20Sopenharmony_ci * tty_free_file - free file->private_data 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * This shall be used only for fail path handling when tty_add_file was not 2138c2ecf20Sopenharmony_ci * called yet. 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_civoid tty_free_file(struct file *file) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct tty_file_private *priv = file->private_data; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci file->private_data = NULL; 2208c2ecf20Sopenharmony_ci kfree(priv); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* Delete file from its tty */ 2248c2ecf20Sopenharmony_cistatic void tty_del_file(struct file *file) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct tty_file_private *priv = file->private_data; 2278c2ecf20Sopenharmony_ci struct tty_struct *tty = priv->tty; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci spin_lock(&tty->files_lock); 2308c2ecf20Sopenharmony_ci list_del(&priv->list); 2318c2ecf20Sopenharmony_ci spin_unlock(&tty->files_lock); 2328c2ecf20Sopenharmony_ci tty_free_file(file); 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/** 2368c2ecf20Sopenharmony_ci * tty_name - return tty naming 2378c2ecf20Sopenharmony_ci * @tty: tty structure 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * Convert a tty structure into a name. The name reflects the kernel 2408c2ecf20Sopenharmony_ci * naming policy and if udev is in use may not reflect user space 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * Locking: none 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ciconst char *tty_name(const struct tty_struct *tty) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (!tty) /* Hmm. NULL pointer. That's fun. */ 2488c2ecf20Sopenharmony_ci return "NULL tty"; 2498c2ecf20Sopenharmony_ci return tty->name; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_name); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ciconst char *tty_driver_name(const struct tty_struct *tty) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci if (!tty || !tty->driver) 2578c2ecf20Sopenharmony_ci return ""; 2588c2ecf20Sopenharmony_ci return tty->driver->name; 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic int tty_paranoia_check(struct tty_struct *tty, struct inode *inode, 2628c2ecf20Sopenharmony_ci const char *routine) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci#ifdef TTY_PARANOIA_CHECK 2658c2ecf20Sopenharmony_ci if (!tty) { 2668c2ecf20Sopenharmony_ci pr_warn("(%d:%d): %s: NULL tty\n", 2678c2ecf20Sopenharmony_ci imajor(inode), iminor(inode), routine); 2688c2ecf20Sopenharmony_ci return 1; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci if (tty->magic != TTY_MAGIC) { 2718c2ecf20Sopenharmony_ci pr_warn("(%d:%d): %s: bad magic number\n", 2728c2ecf20Sopenharmony_ci imajor(inode), iminor(inode), routine); 2738c2ecf20Sopenharmony_ci return 1; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci#endif 2768c2ecf20Sopenharmony_ci return 0; 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* Caller must hold tty_lock */ 2808c2ecf20Sopenharmony_cistatic int check_tty_count(struct tty_struct *tty, const char *routine) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci#ifdef CHECK_TTY_COUNT 2838c2ecf20Sopenharmony_ci struct list_head *p; 2848c2ecf20Sopenharmony_ci int count = 0, kopen_count = 0; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci spin_lock(&tty->files_lock); 2878c2ecf20Sopenharmony_ci list_for_each(p, &tty->tty_files) { 2888c2ecf20Sopenharmony_ci count++; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci spin_unlock(&tty->files_lock); 2918c2ecf20Sopenharmony_ci if (tty->driver->type == TTY_DRIVER_TYPE_PTY && 2928c2ecf20Sopenharmony_ci tty->driver->subtype == PTY_TYPE_SLAVE && 2938c2ecf20Sopenharmony_ci tty->link && tty->link->count) 2948c2ecf20Sopenharmony_ci count++; 2958c2ecf20Sopenharmony_ci if (tty_port_kopened(tty->port)) 2968c2ecf20Sopenharmony_ci kopen_count++; 2978c2ecf20Sopenharmony_ci if (tty->count != (count + kopen_count)) { 2988c2ecf20Sopenharmony_ci tty_warn(tty, "%s: tty->count(%d) != (#fd's(%d) + #kopen's(%d))\n", 2998c2ecf20Sopenharmony_ci routine, tty->count, count, kopen_count); 3008c2ecf20Sopenharmony_ci return (count + kopen_count); 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci#endif 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/** 3078c2ecf20Sopenharmony_ci * get_tty_driver - find device of a tty 3088c2ecf20Sopenharmony_ci * @device: device identifier 3098c2ecf20Sopenharmony_ci * @index: returns the index of the tty 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * This routine returns a tty driver structure, given a device number 3128c2ecf20Sopenharmony_ci * and also passes back the index number. 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * Locking: caller must hold tty_mutex 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic struct tty_driver *get_tty_driver(dev_t device, int *index) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct tty_driver *p; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci list_for_each_entry(p, &tty_drivers, tty_drivers) { 3228c2ecf20Sopenharmony_ci dev_t base = MKDEV(p->major, p->minor_start); 3238c2ecf20Sopenharmony_ci if (device < base || device >= base + p->num) 3248c2ecf20Sopenharmony_ci continue; 3258c2ecf20Sopenharmony_ci *index = device - base; 3268c2ecf20Sopenharmony_ci return tty_driver_kref_get(p); 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci return NULL; 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci/** 3328c2ecf20Sopenharmony_ci * tty_dev_name_to_number - return dev_t for device name 3338c2ecf20Sopenharmony_ci * @name: user space name of device under /dev 3348c2ecf20Sopenharmony_ci * @number: pointer to dev_t that this function will populate 3358c2ecf20Sopenharmony_ci * 3368c2ecf20Sopenharmony_ci * This function converts device names like ttyS0 or ttyUSB1 into dev_t 3378c2ecf20Sopenharmony_ci * like (4, 64) or (188, 1). If no corresponding driver is registered then 3388c2ecf20Sopenharmony_ci * the function returns -ENODEV. 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Locking: this acquires tty_mutex to protect the tty_drivers list from 3418c2ecf20Sopenharmony_ci * being modified while we are traversing it, and makes sure to 3428c2ecf20Sopenharmony_ci * release it before exiting. 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ciint tty_dev_name_to_number(const char *name, dev_t *number) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct tty_driver *p; 3478c2ecf20Sopenharmony_ci int ret; 3488c2ecf20Sopenharmony_ci int index, prefix_length = 0; 3498c2ecf20Sopenharmony_ci const char *str; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (str = name; *str && !isdigit(*str); str++) 3528c2ecf20Sopenharmony_ci ; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (!*str) 3558c2ecf20Sopenharmony_ci return -EINVAL; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci ret = kstrtoint(str, 10, &index); 3588c2ecf20Sopenharmony_ci if (ret) 3598c2ecf20Sopenharmony_ci return ret; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci prefix_length = str - name; 3628c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci list_for_each_entry(p, &tty_drivers, tty_drivers) 3658c2ecf20Sopenharmony_ci if (prefix_length == strlen(p->name) && strncmp(name, 3668c2ecf20Sopenharmony_ci p->name, prefix_length) == 0) { 3678c2ecf20Sopenharmony_ci if (index < p->num) { 3688c2ecf20Sopenharmony_ci *number = MKDEV(p->major, p->minor_start + index); 3698c2ecf20Sopenharmony_ci goto out; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* if here then driver wasn't found */ 3748c2ecf20Sopenharmony_ci ret = -ENODEV; 3758c2ecf20Sopenharmony_ciout: 3768c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_dev_name_to_number); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci#ifdef CONFIG_CONSOLE_POLL 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci/** 3848c2ecf20Sopenharmony_ci * tty_find_polling_driver - find device of a polled tty 3858c2ecf20Sopenharmony_ci * @name: name string to match 3868c2ecf20Sopenharmony_ci * @line: pointer to resulting tty line nr 3878c2ecf20Sopenharmony_ci * 3888c2ecf20Sopenharmony_ci * This routine returns a tty driver structure, given a name 3898c2ecf20Sopenharmony_ci * and the condition that the tty driver is capable of polled 3908c2ecf20Sopenharmony_ci * operation. 3918c2ecf20Sopenharmony_ci */ 3928c2ecf20Sopenharmony_cistruct tty_driver *tty_find_polling_driver(char *name, int *line) 3938c2ecf20Sopenharmony_ci{ 3948c2ecf20Sopenharmony_ci struct tty_driver *p, *res = NULL; 3958c2ecf20Sopenharmony_ci int tty_line = 0; 3968c2ecf20Sopenharmony_ci int len; 3978c2ecf20Sopenharmony_ci char *str, *stp; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci for (str = name; *str; str++) 4008c2ecf20Sopenharmony_ci if ((*str >= '0' && *str <= '9') || *str == ',') 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci if (!*str) 4038c2ecf20Sopenharmony_ci return NULL; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci len = str - name; 4068c2ecf20Sopenharmony_ci tty_line = simple_strtoul(str, &str, 10); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 4098c2ecf20Sopenharmony_ci /* Search through the tty devices to look for a match */ 4108c2ecf20Sopenharmony_ci list_for_each_entry(p, &tty_drivers, tty_drivers) { 4118c2ecf20Sopenharmony_ci if (!len || strncmp(name, p->name, len) != 0) 4128c2ecf20Sopenharmony_ci continue; 4138c2ecf20Sopenharmony_ci stp = str; 4148c2ecf20Sopenharmony_ci if (*stp == ',') 4158c2ecf20Sopenharmony_ci stp++; 4168c2ecf20Sopenharmony_ci if (*stp == '\0') 4178c2ecf20Sopenharmony_ci stp = NULL; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (tty_line >= 0 && tty_line < p->num && p->ops && 4208c2ecf20Sopenharmony_ci p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) { 4218c2ecf20Sopenharmony_ci res = tty_driver_kref_get(p); 4228c2ecf20Sopenharmony_ci *line = tty_line; 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return res; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_find_polling_driver); 4318c2ecf20Sopenharmony_ci#endif 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic ssize_t hung_up_tty_read(struct kiocb *iocb, struct iov_iter *to) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci return -EIO; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* No kernel lock held - none needed ;) */ 4448c2ecf20Sopenharmony_cistatic __poll_t hung_up_tty_poll(struct file *filp, poll_table *wait) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci return EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | EPOLLWRNORM; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic long hung_up_tty_ioctl(struct file *file, unsigned int cmd, 4508c2ecf20Sopenharmony_ci unsigned long arg) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci return cmd == TIOCSPGRP ? -ENOTTY : -EIO; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic long hung_up_tty_compat_ioctl(struct file *file, 4568c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci return cmd == TIOCSPGRP ? -ENOTTY : -EIO; 4598c2ecf20Sopenharmony_ci} 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic int hung_up_tty_fasync(int fd, struct file *file, int on) 4628c2ecf20Sopenharmony_ci{ 4638c2ecf20Sopenharmony_ci return -ENOTTY; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cistatic void tty_show_fdinfo(struct seq_file *m, struct file *file) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(file); 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (tty && tty->ops && tty->ops->show_fdinfo) 4718c2ecf20Sopenharmony_ci tty->ops->show_fdinfo(tty, m); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct file_operations tty_fops = { 4758c2ecf20Sopenharmony_ci .llseek = no_llseek, 4768c2ecf20Sopenharmony_ci .read_iter = tty_read, 4778c2ecf20Sopenharmony_ci .write_iter = tty_write, 4788c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 4798c2ecf20Sopenharmony_ci .splice_write = iter_file_splice_write, 4808c2ecf20Sopenharmony_ci .poll = tty_poll, 4818c2ecf20Sopenharmony_ci .unlocked_ioctl = tty_ioctl, 4828c2ecf20Sopenharmony_ci .compat_ioctl = tty_compat_ioctl, 4838c2ecf20Sopenharmony_ci .open = tty_open, 4848c2ecf20Sopenharmony_ci .release = tty_release, 4858c2ecf20Sopenharmony_ci .fasync = tty_fasync, 4868c2ecf20Sopenharmony_ci .show_fdinfo = tty_show_fdinfo, 4878c2ecf20Sopenharmony_ci}; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct file_operations console_fops = { 4908c2ecf20Sopenharmony_ci .llseek = no_llseek, 4918c2ecf20Sopenharmony_ci .read_iter = tty_read, 4928c2ecf20Sopenharmony_ci .write_iter = redirected_tty_write, 4938c2ecf20Sopenharmony_ci .splice_read = generic_file_splice_read, 4948c2ecf20Sopenharmony_ci .splice_write = iter_file_splice_write, 4958c2ecf20Sopenharmony_ci .poll = tty_poll, 4968c2ecf20Sopenharmony_ci .unlocked_ioctl = tty_ioctl, 4978c2ecf20Sopenharmony_ci .compat_ioctl = tty_compat_ioctl, 4988c2ecf20Sopenharmony_ci .open = tty_open, 4998c2ecf20Sopenharmony_ci .release = tty_release, 5008c2ecf20Sopenharmony_ci .fasync = tty_fasync, 5018c2ecf20Sopenharmony_ci}; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic const struct file_operations hung_up_tty_fops = { 5048c2ecf20Sopenharmony_ci .llseek = no_llseek, 5058c2ecf20Sopenharmony_ci .read_iter = hung_up_tty_read, 5068c2ecf20Sopenharmony_ci .write_iter = hung_up_tty_write, 5078c2ecf20Sopenharmony_ci .poll = hung_up_tty_poll, 5088c2ecf20Sopenharmony_ci .unlocked_ioctl = hung_up_tty_ioctl, 5098c2ecf20Sopenharmony_ci .compat_ioctl = hung_up_tty_compat_ioctl, 5108c2ecf20Sopenharmony_ci .release = tty_release, 5118c2ecf20Sopenharmony_ci .fasync = hung_up_tty_fasync, 5128c2ecf20Sopenharmony_ci}; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(redirect_lock); 5158c2ecf20Sopenharmony_cistatic struct file *redirect; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciextern void tty_sysctl_init(void); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci/** 5208c2ecf20Sopenharmony_ci * tty_wakeup - request more data 5218c2ecf20Sopenharmony_ci * @tty: terminal 5228c2ecf20Sopenharmony_ci * 5238c2ecf20Sopenharmony_ci * Internal and external helper for wakeups of tty. This function 5248c2ecf20Sopenharmony_ci * informs the line discipline if present that the driver is ready 5258c2ecf20Sopenharmony_ci * to receive more output data. 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_civoid tty_wakeup(struct tty_struct *tty) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { 5338c2ecf20Sopenharmony_ci ld = tty_ldisc_ref(tty); 5348c2ecf20Sopenharmony_ci if (ld) { 5358c2ecf20Sopenharmony_ci if (ld->ops->write_wakeup) 5368c2ecf20Sopenharmony_ci ld->ops->write_wakeup(tty); 5378c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci } 5408c2ecf20Sopenharmony_ci wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT); 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_wakeup); 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci/** 5468c2ecf20Sopenharmony_ci * __tty_hangup - actual handler for hangup events 5478c2ecf20Sopenharmony_ci * @tty: tty device 5488c2ecf20Sopenharmony_ci * 5498c2ecf20Sopenharmony_ci * This can be called by a "kworker" kernel thread. That is process 5508c2ecf20Sopenharmony_ci * synchronous but doesn't hold any locks, so we need to make sure we 5518c2ecf20Sopenharmony_ci * have the appropriate locks for what we're doing. 5528c2ecf20Sopenharmony_ci * 5538c2ecf20Sopenharmony_ci * The hangup event clears any pending redirections onto the hung up 5548c2ecf20Sopenharmony_ci * device. It ensures future writes will error and it does the needed 5558c2ecf20Sopenharmony_ci * line discipline hangup and signal delivery. The tty object itself 5568c2ecf20Sopenharmony_ci * remains intact. 5578c2ecf20Sopenharmony_ci * 5588c2ecf20Sopenharmony_ci * Locking: 5598c2ecf20Sopenharmony_ci * BTM 5608c2ecf20Sopenharmony_ci * redirect lock for undoing redirection 5618c2ecf20Sopenharmony_ci * file list lock for manipulating list of ttys 5628c2ecf20Sopenharmony_ci * tty_ldiscs_lock from called functions 5638c2ecf20Sopenharmony_ci * termios_rwsem resetting termios data 5648c2ecf20Sopenharmony_ci * tasklist_lock to walk task list for hangup event 5658c2ecf20Sopenharmony_ci * ->siglock to protect ->signal/->sighand 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_cistatic void __tty_hangup(struct tty_struct *tty, int exit_session) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci struct file *cons_filp = NULL; 5708c2ecf20Sopenharmony_ci struct file *filp, *f = NULL; 5718c2ecf20Sopenharmony_ci struct tty_file_private *priv; 5728c2ecf20Sopenharmony_ci int closecount = 0, n; 5738c2ecf20Sopenharmony_ci int refs; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (!tty) 5768c2ecf20Sopenharmony_ci return; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci spin_lock(&redirect_lock); 5808c2ecf20Sopenharmony_ci if (redirect && file_tty(redirect) == tty) { 5818c2ecf20Sopenharmony_ci f = redirect; 5828c2ecf20Sopenharmony_ci redirect = NULL; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci spin_unlock(&redirect_lock); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci tty_lock(tty); 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (test_bit(TTY_HUPPED, &tty->flags)) { 5898c2ecf20Sopenharmony_ci tty_unlock(tty); 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* 5948c2ecf20Sopenharmony_ci * Some console devices aren't actually hung up for technical and 5958c2ecf20Sopenharmony_ci * historical reasons, which can lead to indefinite interruptible 5968c2ecf20Sopenharmony_ci * sleep in n_tty_read(). The following explicitly tells 5978c2ecf20Sopenharmony_ci * n_tty_read() to abort readers. 5988c2ecf20Sopenharmony_ci */ 5998c2ecf20Sopenharmony_ci set_bit(TTY_HUPPING, &tty->flags); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci /* inuse_filps is protected by the single tty lock, 6028c2ecf20Sopenharmony_ci this really needs to change if we want to flush the 6038c2ecf20Sopenharmony_ci workqueue with the lock held */ 6048c2ecf20Sopenharmony_ci check_tty_count(tty, "tty_hangup"); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci spin_lock(&tty->files_lock); 6078c2ecf20Sopenharmony_ci /* This breaks for file handles being sent over AF_UNIX sockets ? */ 6088c2ecf20Sopenharmony_ci list_for_each_entry(priv, &tty->tty_files, list) { 6098c2ecf20Sopenharmony_ci filp = priv->file; 6108c2ecf20Sopenharmony_ci if (filp->f_op->write_iter == redirected_tty_write) 6118c2ecf20Sopenharmony_ci cons_filp = filp; 6128c2ecf20Sopenharmony_ci if (filp->f_op->write_iter != tty_write) 6138c2ecf20Sopenharmony_ci continue; 6148c2ecf20Sopenharmony_ci closecount++; 6158c2ecf20Sopenharmony_ci __tty_fasync(-1, filp, 0); /* can't block */ 6168c2ecf20Sopenharmony_ci filp->f_op = &hung_up_tty_fops; 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci spin_unlock(&tty->files_lock); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci refs = tty_signal_session_leader(tty, exit_session); 6218c2ecf20Sopenharmony_ci /* Account for the p->signal references we killed */ 6228c2ecf20Sopenharmony_ci while (refs--) 6238c2ecf20Sopenharmony_ci tty_kref_put(tty); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci tty_ldisc_hangup(tty, cons_filp != NULL); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci spin_lock_irq(&tty->ctrl_lock); 6288c2ecf20Sopenharmony_ci clear_bit(TTY_THROTTLED, &tty->flags); 6298c2ecf20Sopenharmony_ci clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); 6308c2ecf20Sopenharmony_ci put_pid(tty->session); 6318c2ecf20Sopenharmony_ci put_pid(tty->pgrp); 6328c2ecf20Sopenharmony_ci tty->session = NULL; 6338c2ecf20Sopenharmony_ci tty->pgrp = NULL; 6348c2ecf20Sopenharmony_ci tty->ctrl_status = 0; 6358c2ecf20Sopenharmony_ci spin_unlock_irq(&tty->ctrl_lock); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* 6388c2ecf20Sopenharmony_ci * If one of the devices matches a console pointer, we 6398c2ecf20Sopenharmony_ci * cannot just call hangup() because that will cause 6408c2ecf20Sopenharmony_ci * tty->count and state->count to go out of sync. 6418c2ecf20Sopenharmony_ci * So we just call close() the right number of times. 6428c2ecf20Sopenharmony_ci */ 6438c2ecf20Sopenharmony_ci if (cons_filp) { 6448c2ecf20Sopenharmony_ci if (tty->ops->close) 6458c2ecf20Sopenharmony_ci for (n = 0; n < closecount; n++) 6468c2ecf20Sopenharmony_ci tty->ops->close(tty, cons_filp); 6478c2ecf20Sopenharmony_ci } else if (tty->ops->hangup) 6488c2ecf20Sopenharmony_ci tty->ops->hangup(tty); 6498c2ecf20Sopenharmony_ci /* 6508c2ecf20Sopenharmony_ci * We don't want to have driver/ldisc interactions beyond the ones 6518c2ecf20Sopenharmony_ci * we did here. The driver layer expects no calls after ->hangup() 6528c2ecf20Sopenharmony_ci * from the ldisc side, which is now guaranteed. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_ci set_bit(TTY_HUPPED, &tty->flags); 6558c2ecf20Sopenharmony_ci clear_bit(TTY_HUPPING, &tty->flags); 6568c2ecf20Sopenharmony_ci tty_unlock(tty); 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (f) 6598c2ecf20Sopenharmony_ci fput(f); 6608c2ecf20Sopenharmony_ci} 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_cistatic void do_tty_hangup(struct work_struct *work) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct tty_struct *tty = 6658c2ecf20Sopenharmony_ci container_of(work, struct tty_struct, hangup_work); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci __tty_hangup(tty, 0); 6688c2ecf20Sopenharmony_ci} 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci/** 6718c2ecf20Sopenharmony_ci * tty_hangup - trigger a hangup event 6728c2ecf20Sopenharmony_ci * @tty: tty to hangup 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * A carrier loss (virtual or otherwise) has occurred on this like 6758c2ecf20Sopenharmony_ci * schedule a hangup sequence to run after this event. 6768c2ecf20Sopenharmony_ci */ 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_civoid tty_hangup(struct tty_struct *tty) 6798c2ecf20Sopenharmony_ci{ 6808c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "hangup\n"); 6818c2ecf20Sopenharmony_ci schedule_work(&tty->hangup_work); 6828c2ecf20Sopenharmony_ci} 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_hangup); 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci/** 6878c2ecf20Sopenharmony_ci * tty_vhangup - process vhangup 6888c2ecf20Sopenharmony_ci * @tty: tty to hangup 6898c2ecf20Sopenharmony_ci * 6908c2ecf20Sopenharmony_ci * The user has asked via system call for the terminal to be hung up. 6918c2ecf20Sopenharmony_ci * We do this synchronously so that when the syscall returns the process 6928c2ecf20Sopenharmony_ci * is complete. That guarantee is necessary for security reasons. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_civoid tty_vhangup(struct tty_struct *tty) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "vhangup\n"); 6988c2ecf20Sopenharmony_ci __tty_hangup(tty, 0); 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_vhangup); 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/** 7058c2ecf20Sopenharmony_ci * tty_vhangup_self - process vhangup for own ctty 7068c2ecf20Sopenharmony_ci * 7078c2ecf20Sopenharmony_ci * Perform a vhangup on the current controlling tty 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_civoid tty_vhangup_self(void) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct tty_struct *tty; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci tty = get_current_tty(); 7158c2ecf20Sopenharmony_ci if (tty) { 7168c2ecf20Sopenharmony_ci tty_vhangup(tty); 7178c2ecf20Sopenharmony_ci tty_kref_put(tty); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci/** 7228c2ecf20Sopenharmony_ci * tty_vhangup_session - hangup session leader exit 7238c2ecf20Sopenharmony_ci * @tty: tty to hangup 7248c2ecf20Sopenharmony_ci * 7258c2ecf20Sopenharmony_ci * The session leader is exiting and hanging up its controlling terminal. 7268c2ecf20Sopenharmony_ci * Every process in the foreground process group is signalled SIGHUP. 7278c2ecf20Sopenharmony_ci * 7288c2ecf20Sopenharmony_ci * We do this synchronously so that when the syscall returns the process 7298c2ecf20Sopenharmony_ci * is complete. That guarantee is necessary for security reasons. 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_civoid tty_vhangup_session(struct tty_struct *tty) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "session hangup\n"); 7358c2ecf20Sopenharmony_ci __tty_hangup(tty, 1); 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci/** 7398c2ecf20Sopenharmony_ci * tty_hung_up_p - was tty hung up 7408c2ecf20Sopenharmony_ci * @filp: file pointer of tty 7418c2ecf20Sopenharmony_ci * 7428c2ecf20Sopenharmony_ci * Return true if the tty has been subject to a vhangup or a carrier 7438c2ecf20Sopenharmony_ci * loss 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ciint tty_hung_up_p(struct file *filp) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci return (filp && filp->f_op == &hung_up_tty_fops); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_hung_up_p); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci/** 7548c2ecf20Sopenharmony_ci * stop_tty - propagate flow control 7558c2ecf20Sopenharmony_ci * @tty: tty to stop 7568c2ecf20Sopenharmony_ci * 7578c2ecf20Sopenharmony_ci * Perform flow control to the driver. May be called 7588c2ecf20Sopenharmony_ci * on an already stopped device and will not re-call the driver 7598c2ecf20Sopenharmony_ci * method. 7608c2ecf20Sopenharmony_ci * 7618c2ecf20Sopenharmony_ci * This functionality is used by both the line disciplines for 7628c2ecf20Sopenharmony_ci * halting incoming flow and by the driver. It may therefore be 7638c2ecf20Sopenharmony_ci * called from any context, may be under the tty atomic_write_lock 7648c2ecf20Sopenharmony_ci * but not always. 7658c2ecf20Sopenharmony_ci * 7668c2ecf20Sopenharmony_ci * Locking: 7678c2ecf20Sopenharmony_ci * flow_lock 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_civoid __stop_tty(struct tty_struct *tty) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci if (tty->stopped) 7738c2ecf20Sopenharmony_ci return; 7748c2ecf20Sopenharmony_ci tty->stopped = 1; 7758c2ecf20Sopenharmony_ci if (tty->ops->stop) 7768c2ecf20Sopenharmony_ci tty->ops->stop(tty); 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_civoid stop_tty(struct tty_struct *tty) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci unsigned long flags; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci spin_lock_irqsave(&tty->flow_lock, flags); 7848c2ecf20Sopenharmony_ci __stop_tty(tty); 7858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tty->flow_lock, flags); 7868c2ecf20Sopenharmony_ci} 7878c2ecf20Sopenharmony_ciEXPORT_SYMBOL(stop_tty); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci/** 7908c2ecf20Sopenharmony_ci * start_tty - propagate flow control 7918c2ecf20Sopenharmony_ci * @tty: tty to start 7928c2ecf20Sopenharmony_ci * 7938c2ecf20Sopenharmony_ci * Start a tty that has been stopped if at all possible. If this 7948c2ecf20Sopenharmony_ci * tty was previous stopped and is now being started, the driver 7958c2ecf20Sopenharmony_ci * start method is invoked and the line discipline woken. 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * Locking: 7988c2ecf20Sopenharmony_ci * flow_lock 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_civoid __start_tty(struct tty_struct *tty) 8028c2ecf20Sopenharmony_ci{ 8038c2ecf20Sopenharmony_ci if (!tty->stopped || tty->flow_stopped) 8048c2ecf20Sopenharmony_ci return; 8058c2ecf20Sopenharmony_ci tty->stopped = 0; 8068c2ecf20Sopenharmony_ci if (tty->ops->start) 8078c2ecf20Sopenharmony_ci tty->ops->start(tty); 8088c2ecf20Sopenharmony_ci tty_wakeup(tty); 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_civoid start_tty(struct tty_struct *tty) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci unsigned long flags; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci spin_lock_irqsave(&tty->flow_lock, flags); 8168c2ecf20Sopenharmony_ci __start_tty(tty); 8178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tty->flow_lock, flags); 8188c2ecf20Sopenharmony_ci} 8198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(start_tty); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic void tty_update_time(struct timespec64 *time) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci time64_t sec = ktime_get_real_seconds(); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* 8268c2ecf20Sopenharmony_ci * We only care if the two values differ in anything other than the 8278c2ecf20Sopenharmony_ci * lower three bits (i.e every 8 seconds). If so, then we can update 8288c2ecf20Sopenharmony_ci * the time of the tty device, otherwise it could be construded as a 8298c2ecf20Sopenharmony_ci * security leak to let userspace know the exact timing of the tty. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci if ((sec ^ time->tv_sec) & ~7) 8328c2ecf20Sopenharmony_ci time->tv_sec = sec; 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci/* 8368c2ecf20Sopenharmony_ci * Iterate on the ldisc ->read() function until we've gotten all 8378c2ecf20Sopenharmony_ci * the data the ldisc has for us. 8388c2ecf20Sopenharmony_ci * 8398c2ecf20Sopenharmony_ci * The "cookie" is something that the ldisc read function can fill 8408c2ecf20Sopenharmony_ci * in to let us know that there is more data to be had. 8418c2ecf20Sopenharmony_ci * 8428c2ecf20Sopenharmony_ci * We promise to continue to call the ldisc until it stops returning 8438c2ecf20Sopenharmony_ci * data or clears the cookie. The cookie may be something that the 8448c2ecf20Sopenharmony_ci * ldisc maintains state for and needs to free. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_cistatic int iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty, 8478c2ecf20Sopenharmony_ci struct file *file, struct iov_iter *to) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci int retval = 0; 8508c2ecf20Sopenharmony_ci void *cookie = NULL; 8518c2ecf20Sopenharmony_ci unsigned long offset = 0; 8528c2ecf20Sopenharmony_ci char kernel_buf[64]; 8538c2ecf20Sopenharmony_ci size_t count = iov_iter_count(to); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci do { 8568c2ecf20Sopenharmony_ci int size, copied; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci size = count > sizeof(kernel_buf) ? sizeof(kernel_buf) : count; 8598c2ecf20Sopenharmony_ci size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset); 8608c2ecf20Sopenharmony_ci if (!size) 8618c2ecf20Sopenharmony_ci break; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci if (size < 0) { 8648c2ecf20Sopenharmony_ci /* Did we have an earlier error (ie -EFAULT)? */ 8658c2ecf20Sopenharmony_ci if (retval) 8668c2ecf20Sopenharmony_ci break; 8678c2ecf20Sopenharmony_ci retval = size; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci /* 8708c2ecf20Sopenharmony_ci * -EOVERFLOW means we didn't have enough space 8718c2ecf20Sopenharmony_ci * for a whole packet, and we shouldn't return 8728c2ecf20Sopenharmony_ci * a partial result. 8738c2ecf20Sopenharmony_ci */ 8748c2ecf20Sopenharmony_ci if (retval == -EOVERFLOW) 8758c2ecf20Sopenharmony_ci offset = 0; 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci copied = copy_to_iter(kernel_buf, size, to); 8808c2ecf20Sopenharmony_ci offset += copied; 8818c2ecf20Sopenharmony_ci count -= copied; 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci /* 8848c2ecf20Sopenharmony_ci * If the user copy failed, we still need to do another ->read() 8858c2ecf20Sopenharmony_ci * call if we had a cookie to let the ldisc clear up. 8868c2ecf20Sopenharmony_ci * 8878c2ecf20Sopenharmony_ci * But make sure size is zeroed. 8888c2ecf20Sopenharmony_ci */ 8898c2ecf20Sopenharmony_ci if (unlikely(copied != size)) { 8908c2ecf20Sopenharmony_ci count = 0; 8918c2ecf20Sopenharmony_ci retval = -EFAULT; 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci } while (cookie); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci /* We always clear tty buffer in case they contained passwords */ 8968c2ecf20Sopenharmony_ci memzero_explicit(kernel_buf, sizeof(kernel_buf)); 8978c2ecf20Sopenharmony_ci return offset ? offset : retval; 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci/** 9028c2ecf20Sopenharmony_ci * tty_read - read method for tty device files 9038c2ecf20Sopenharmony_ci * @file: pointer to tty file 9048c2ecf20Sopenharmony_ci * @buf: user buffer 9058c2ecf20Sopenharmony_ci * @count: size of user buffer 9068c2ecf20Sopenharmony_ci * @ppos: unused 9078c2ecf20Sopenharmony_ci * 9088c2ecf20Sopenharmony_ci * Perform the read system call function on this terminal device. Checks 9098c2ecf20Sopenharmony_ci * for hung up devices before calling the line discipline method. 9108c2ecf20Sopenharmony_ci * 9118c2ecf20Sopenharmony_ci * Locking: 9128c2ecf20Sopenharmony_ci * Locks the line discipline internally while needed. Multiple 9138c2ecf20Sopenharmony_ci * read calls may be outstanding in parallel. 9148c2ecf20Sopenharmony_ci */ 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci int i; 9198c2ecf20Sopenharmony_ci struct file *file = iocb->ki_filp; 9208c2ecf20Sopenharmony_ci struct inode *inode = file_inode(file); 9218c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(file); 9228c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, inode, "tty_read")) 9258c2ecf20Sopenharmony_ci return -EIO; 9268c2ecf20Sopenharmony_ci if (!tty || tty_io_error(tty)) 9278c2ecf20Sopenharmony_ci return -EIO; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci /* We want to wait for the line discipline to sort out in this 9308c2ecf20Sopenharmony_ci situation */ 9318c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 9328c2ecf20Sopenharmony_ci if (!ld) 9338c2ecf20Sopenharmony_ci return hung_up_tty_read(iocb, to); 9348c2ecf20Sopenharmony_ci i = -EIO; 9358c2ecf20Sopenharmony_ci if (ld->ops->read) 9368c2ecf20Sopenharmony_ci i = iterate_tty_read(ld, tty, file, to); 9378c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (i > 0) 9408c2ecf20Sopenharmony_ci tty_update_time(&inode->i_atime); 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci return i; 9438c2ecf20Sopenharmony_ci} 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_civoid tty_write_unlock(struct tty_struct *tty) 9468c2ecf20Sopenharmony_ci{ 9478c2ecf20Sopenharmony_ci mutex_unlock(&tty->atomic_write_lock); 9488c2ecf20Sopenharmony_ci wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ciint tty_write_lock(struct tty_struct *tty, bool ndelay) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci if (!mutex_trylock(&tty->atomic_write_lock)) { 9548c2ecf20Sopenharmony_ci if (ndelay) 9558c2ecf20Sopenharmony_ci return -EAGAIN; 9568c2ecf20Sopenharmony_ci if (mutex_lock_interruptible(&tty->atomic_write_lock)) 9578c2ecf20Sopenharmony_ci return -ERESTARTSYS; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci return 0; 9608c2ecf20Sopenharmony_ci} 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci/* 9638c2ecf20Sopenharmony_ci * Split writes up in sane blocksizes to avoid 9648c2ecf20Sopenharmony_ci * denial-of-service type attacks 9658c2ecf20Sopenharmony_ci */ 9668c2ecf20Sopenharmony_cistatic inline ssize_t do_tty_write( 9678c2ecf20Sopenharmony_ci ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t), 9688c2ecf20Sopenharmony_ci struct tty_struct *tty, 9698c2ecf20Sopenharmony_ci struct file *file, 9708c2ecf20Sopenharmony_ci struct iov_iter *from) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci size_t count = iov_iter_count(from); 9738c2ecf20Sopenharmony_ci ssize_t ret, written = 0; 9748c2ecf20Sopenharmony_ci unsigned int chunk; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci ret = tty_write_lock(tty, file->f_flags & O_NDELAY); 9778c2ecf20Sopenharmony_ci if (ret < 0) 9788c2ecf20Sopenharmony_ci return ret; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci /* 9818c2ecf20Sopenharmony_ci * We chunk up writes into a temporary buffer. This 9828c2ecf20Sopenharmony_ci * simplifies low-level drivers immensely, since they 9838c2ecf20Sopenharmony_ci * don't have locking issues and user mode accesses. 9848c2ecf20Sopenharmony_ci * 9858c2ecf20Sopenharmony_ci * But if TTY_NO_WRITE_SPLIT is set, we should use a 9868c2ecf20Sopenharmony_ci * big chunk-size.. 9878c2ecf20Sopenharmony_ci * 9888c2ecf20Sopenharmony_ci * The default chunk-size is 2kB, because the NTTY 9898c2ecf20Sopenharmony_ci * layer has problems with bigger chunks. It will 9908c2ecf20Sopenharmony_ci * claim to be able to handle more characters than 9918c2ecf20Sopenharmony_ci * it actually does. 9928c2ecf20Sopenharmony_ci * 9938c2ecf20Sopenharmony_ci * FIXME: This can probably go away now except that 64K chunks 9948c2ecf20Sopenharmony_ci * are too likely to fail unless switched to vmalloc... 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci chunk = 2048; 9978c2ecf20Sopenharmony_ci if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags)) 9988c2ecf20Sopenharmony_ci chunk = 65536; 9998c2ecf20Sopenharmony_ci if (count < chunk) 10008c2ecf20Sopenharmony_ci chunk = count; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* write_buf/write_cnt is protected by the atomic_write_lock mutex */ 10038c2ecf20Sopenharmony_ci if (tty->write_cnt < chunk) { 10048c2ecf20Sopenharmony_ci unsigned char *buf_chunk; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci if (chunk < 1024) 10078c2ecf20Sopenharmony_ci chunk = 1024; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci buf_chunk = kmalloc(chunk, GFP_KERNEL); 10108c2ecf20Sopenharmony_ci if (!buf_chunk) { 10118c2ecf20Sopenharmony_ci ret = -ENOMEM; 10128c2ecf20Sopenharmony_ci goto out; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci kfree(tty->write_buf); 10158c2ecf20Sopenharmony_ci tty->write_cnt = chunk; 10168c2ecf20Sopenharmony_ci tty->write_buf = buf_chunk; 10178c2ecf20Sopenharmony_ci } 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* Do the write .. */ 10208c2ecf20Sopenharmony_ci for (;;) { 10218c2ecf20Sopenharmony_ci size_t size = count; 10228c2ecf20Sopenharmony_ci if (size > chunk) 10238c2ecf20Sopenharmony_ci size = chunk; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci ret = -EFAULT; 10268c2ecf20Sopenharmony_ci if (copy_from_iter(tty->write_buf, size, from) != size) 10278c2ecf20Sopenharmony_ci break; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci ret = write(tty, file, tty->write_buf, size); 10308c2ecf20Sopenharmony_ci if (ret <= 0) 10318c2ecf20Sopenharmony_ci break; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci written += ret; 10348c2ecf20Sopenharmony_ci if (ret > size) 10358c2ecf20Sopenharmony_ci break; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci /* FIXME! Have Al check this! */ 10388c2ecf20Sopenharmony_ci if (ret != size) 10398c2ecf20Sopenharmony_ci iov_iter_revert(from, size-ret); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci count -= ret; 10428c2ecf20Sopenharmony_ci if (!count) 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci ret = -ERESTARTSYS; 10458c2ecf20Sopenharmony_ci if (signal_pending(current)) 10468c2ecf20Sopenharmony_ci break; 10478c2ecf20Sopenharmony_ci cond_resched(); 10488c2ecf20Sopenharmony_ci } 10498c2ecf20Sopenharmony_ci if (written) { 10508c2ecf20Sopenharmony_ci tty_update_time(&file_inode(file)->i_mtime); 10518c2ecf20Sopenharmony_ci ret = written; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ciout: 10548c2ecf20Sopenharmony_ci tty_write_unlock(tty); 10558c2ecf20Sopenharmony_ci return ret; 10568c2ecf20Sopenharmony_ci} 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci/** 10598c2ecf20Sopenharmony_ci * tty_write_message - write a message to a certain tty, not just the console. 10608c2ecf20Sopenharmony_ci * @tty: the destination tty_struct 10618c2ecf20Sopenharmony_ci * @msg: the message to write 10628c2ecf20Sopenharmony_ci * 10638c2ecf20Sopenharmony_ci * This is used for messages that need to be redirected to a specific tty. 10648c2ecf20Sopenharmony_ci * We don't put it into the syslog queue right now maybe in the future if 10658c2ecf20Sopenharmony_ci * really needed. 10668c2ecf20Sopenharmony_ci * 10678c2ecf20Sopenharmony_ci * We must still hold the BTM and test the CLOSING flag for the moment. 10688c2ecf20Sopenharmony_ci */ 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_civoid tty_write_message(struct tty_struct *tty, char *msg) 10718c2ecf20Sopenharmony_ci{ 10728c2ecf20Sopenharmony_ci if (tty) { 10738c2ecf20Sopenharmony_ci mutex_lock(&tty->atomic_write_lock); 10748c2ecf20Sopenharmony_ci tty_lock(tty); 10758c2ecf20Sopenharmony_ci if (tty->ops->write && tty->count > 0) 10768c2ecf20Sopenharmony_ci tty->ops->write(tty, msg, strlen(msg)); 10778c2ecf20Sopenharmony_ci tty_unlock(tty); 10788c2ecf20Sopenharmony_ci tty_write_unlock(tty); 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci return; 10818c2ecf20Sopenharmony_ci} 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci/** 10858c2ecf20Sopenharmony_ci * tty_write - write method for tty device file 10868c2ecf20Sopenharmony_ci * @file: tty file pointer 10878c2ecf20Sopenharmony_ci * @buf: user data to write 10888c2ecf20Sopenharmony_ci * @count: bytes to write 10898c2ecf20Sopenharmony_ci * @ppos: unused 10908c2ecf20Sopenharmony_ci * 10918c2ecf20Sopenharmony_ci * Write data to a tty device via the line discipline. 10928c2ecf20Sopenharmony_ci * 10938c2ecf20Sopenharmony_ci * Locking: 10948c2ecf20Sopenharmony_ci * Locks the line discipline as required 10958c2ecf20Sopenharmony_ci * Writes to the tty driver are serialized by the atomic_write_lock 10968c2ecf20Sopenharmony_ci * and are then processed in chunks to the device. The line discipline 10978c2ecf20Sopenharmony_ci * write method will not be invoked in parallel for each device. 10988c2ecf20Sopenharmony_ci */ 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_cistatic ssize_t file_tty_write(struct file *file, struct kiocb *iocb, struct iov_iter *from) 11018c2ecf20Sopenharmony_ci{ 11028c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(file); 11038c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 11048c2ecf20Sopenharmony_ci ssize_t ret; 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, file_inode(file), "tty_write")) 11078c2ecf20Sopenharmony_ci return -EIO; 11088c2ecf20Sopenharmony_ci if (!tty || !tty->ops->write || tty_io_error(tty)) 11098c2ecf20Sopenharmony_ci return -EIO; 11108c2ecf20Sopenharmony_ci /* Short term debug to catch buggy drivers */ 11118c2ecf20Sopenharmony_ci if (tty->ops->write_room == NULL) 11128c2ecf20Sopenharmony_ci tty_err(tty, "missing write_room method\n"); 11138c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 11148c2ecf20Sopenharmony_ci if (!ld) 11158c2ecf20Sopenharmony_ci return hung_up_tty_write(iocb, from); 11168c2ecf20Sopenharmony_ci if (!ld->ops->write) 11178c2ecf20Sopenharmony_ci ret = -EIO; 11188c2ecf20Sopenharmony_ci else 11198c2ecf20Sopenharmony_ci ret = do_tty_write(ld->ops->write, tty, file, from); 11208c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 11218c2ecf20Sopenharmony_ci return ret; 11228c2ecf20Sopenharmony_ci} 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_cistatic ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from) 11258c2ecf20Sopenharmony_ci{ 11268c2ecf20Sopenharmony_ci return file_tty_write(iocb->ki_filp, iocb, from); 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cissize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct file *p = NULL; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci spin_lock(&redirect_lock); 11348c2ecf20Sopenharmony_ci if (redirect) 11358c2ecf20Sopenharmony_ci p = get_file(redirect); 11368c2ecf20Sopenharmony_ci spin_unlock(&redirect_lock); 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci /* 11398c2ecf20Sopenharmony_ci * We know the redirected tty is just another tty, we can can 11408c2ecf20Sopenharmony_ci * call file_tty_write() directly with that file pointer. 11418c2ecf20Sopenharmony_ci */ 11428c2ecf20Sopenharmony_ci if (p) { 11438c2ecf20Sopenharmony_ci ssize_t res; 11448c2ecf20Sopenharmony_ci res = file_tty_write(p, iocb, iter); 11458c2ecf20Sopenharmony_ci fput(p); 11468c2ecf20Sopenharmony_ci return res; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci return tty_write(iocb, iter); 11498c2ecf20Sopenharmony_ci} 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci/** 11528c2ecf20Sopenharmony_ci * tty_send_xchar - send priority character 11538c2ecf20Sopenharmony_ci * 11548c2ecf20Sopenharmony_ci * Send a high priority character to the tty even if stopped 11558c2ecf20Sopenharmony_ci * 11568c2ecf20Sopenharmony_ci * Locking: none for xchar method, write ordering for write method. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ciint tty_send_xchar(struct tty_struct *tty, char ch) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci int was_stopped = tty->stopped; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci if (tty->ops->send_xchar) { 11648c2ecf20Sopenharmony_ci down_read(&tty->termios_rwsem); 11658c2ecf20Sopenharmony_ci tty->ops->send_xchar(tty, ch); 11668c2ecf20Sopenharmony_ci up_read(&tty->termios_rwsem); 11678c2ecf20Sopenharmony_ci return 0; 11688c2ecf20Sopenharmony_ci } 11698c2ecf20Sopenharmony_ci 11708c2ecf20Sopenharmony_ci if (tty_write_lock(tty, false) < 0) 11718c2ecf20Sopenharmony_ci return -ERESTARTSYS; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci down_read(&tty->termios_rwsem); 11748c2ecf20Sopenharmony_ci if (was_stopped) 11758c2ecf20Sopenharmony_ci start_tty(tty); 11768c2ecf20Sopenharmony_ci tty->ops->write(tty, &ch, 1); 11778c2ecf20Sopenharmony_ci if (was_stopped) 11788c2ecf20Sopenharmony_ci stop_tty(tty); 11798c2ecf20Sopenharmony_ci up_read(&tty->termios_rwsem); 11808c2ecf20Sopenharmony_ci tty_write_unlock(tty); 11818c2ecf20Sopenharmony_ci return 0; 11828c2ecf20Sopenharmony_ci} 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic char ptychar[] = "pqrstuvwxyzabcde"; 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci/** 11878c2ecf20Sopenharmony_ci * pty_line_name - generate name for a pty 11888c2ecf20Sopenharmony_ci * @driver: the tty driver in use 11898c2ecf20Sopenharmony_ci * @index: the minor number 11908c2ecf20Sopenharmony_ci * @p: output buffer of at least 6 bytes 11918c2ecf20Sopenharmony_ci * 11928c2ecf20Sopenharmony_ci * Generate a name from a driver reference and write it to the output 11938c2ecf20Sopenharmony_ci * buffer. 11948c2ecf20Sopenharmony_ci * 11958c2ecf20Sopenharmony_ci * Locking: None 11968c2ecf20Sopenharmony_ci */ 11978c2ecf20Sopenharmony_cistatic void pty_line_name(struct tty_driver *driver, int index, char *p) 11988c2ecf20Sopenharmony_ci{ 11998c2ecf20Sopenharmony_ci int i = index + driver->name_base; 12008c2ecf20Sopenharmony_ci /* ->name is initialized to "ttyp", but "tty" is expected */ 12018c2ecf20Sopenharmony_ci sprintf(p, "%s%c%x", 12028c2ecf20Sopenharmony_ci driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name, 12038c2ecf20Sopenharmony_ci ptychar[i >> 4 & 0xf], i & 0xf); 12048c2ecf20Sopenharmony_ci} 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci/** 12078c2ecf20Sopenharmony_ci * tty_line_name - generate name for a tty 12088c2ecf20Sopenharmony_ci * @driver: the tty driver in use 12098c2ecf20Sopenharmony_ci * @index: the minor number 12108c2ecf20Sopenharmony_ci * @p: output buffer of at least 7 bytes 12118c2ecf20Sopenharmony_ci * 12128c2ecf20Sopenharmony_ci * Generate a name from a driver reference and write it to the output 12138c2ecf20Sopenharmony_ci * buffer. 12148c2ecf20Sopenharmony_ci * 12158c2ecf20Sopenharmony_ci * Locking: None 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_cistatic ssize_t tty_line_name(struct tty_driver *driver, int index, char *p) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE) 12208c2ecf20Sopenharmony_ci return sprintf(p, "%s", driver->name); 12218c2ecf20Sopenharmony_ci else 12228c2ecf20Sopenharmony_ci return sprintf(p, "%s%d", driver->name, 12238c2ecf20Sopenharmony_ci index + driver->name_base); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci/** 12278c2ecf20Sopenharmony_ci * tty_driver_lookup_tty() - find an existing tty, if any 12288c2ecf20Sopenharmony_ci * @driver: the driver for the tty 12298c2ecf20Sopenharmony_ci * @idx: the minor number 12308c2ecf20Sopenharmony_ci * 12318c2ecf20Sopenharmony_ci * Return the tty, if found. If not found, return NULL or ERR_PTR() if the 12328c2ecf20Sopenharmony_ci * driver lookup() method returns an error. 12338c2ecf20Sopenharmony_ci * 12348c2ecf20Sopenharmony_ci * Locking: tty_mutex must be held. If the tty is found, bump the tty kref. 12358c2ecf20Sopenharmony_ci */ 12368c2ecf20Sopenharmony_cistatic struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver, 12378c2ecf20Sopenharmony_ci struct file *file, int idx) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct tty_struct *tty; 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (driver->ops->lookup) { 12428c2ecf20Sopenharmony_ci if (!file) 12438c2ecf20Sopenharmony_ci tty = ERR_PTR(-EIO); 12448c2ecf20Sopenharmony_ci else 12458c2ecf20Sopenharmony_ci tty = driver->ops->lookup(driver, file, idx); 12468c2ecf20Sopenharmony_ci } else { 12478c2ecf20Sopenharmony_ci if (idx >= driver->num) 12488c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 12498c2ecf20Sopenharmony_ci tty = driver->ttys[idx]; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci if (!IS_ERR(tty)) 12528c2ecf20Sopenharmony_ci tty_kref_get(tty); 12538c2ecf20Sopenharmony_ci return tty; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/** 12578c2ecf20Sopenharmony_ci * tty_init_termios - helper for termios setup 12588c2ecf20Sopenharmony_ci * @tty: the tty to set up 12598c2ecf20Sopenharmony_ci * 12608c2ecf20Sopenharmony_ci * Initialise the termios structure for this tty. This runs under 12618c2ecf20Sopenharmony_ci * the tty_mutex currently so we can be relaxed about ordering. 12628c2ecf20Sopenharmony_ci */ 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_civoid tty_init_termios(struct tty_struct *tty) 12658c2ecf20Sopenharmony_ci{ 12668c2ecf20Sopenharmony_ci struct ktermios *tp; 12678c2ecf20Sopenharmony_ci int idx = tty->index; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) 12708c2ecf20Sopenharmony_ci tty->termios = tty->driver->init_termios; 12718c2ecf20Sopenharmony_ci else { 12728c2ecf20Sopenharmony_ci /* Check for lazy saved data */ 12738c2ecf20Sopenharmony_ci tp = tty->driver->termios[idx]; 12748c2ecf20Sopenharmony_ci if (tp != NULL) { 12758c2ecf20Sopenharmony_ci tty->termios = *tp; 12768c2ecf20Sopenharmony_ci tty->termios.c_line = tty->driver->init_termios.c_line; 12778c2ecf20Sopenharmony_ci } else 12788c2ecf20Sopenharmony_ci tty->termios = tty->driver->init_termios; 12798c2ecf20Sopenharmony_ci } 12808c2ecf20Sopenharmony_ci /* Compatibility until drivers always set this */ 12818c2ecf20Sopenharmony_ci tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios); 12828c2ecf20Sopenharmony_ci tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios); 12838c2ecf20Sopenharmony_ci} 12848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_init_termios); 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ciint tty_standard_install(struct tty_driver *driver, struct tty_struct *tty) 12878c2ecf20Sopenharmony_ci{ 12888c2ecf20Sopenharmony_ci tty_init_termios(tty); 12898c2ecf20Sopenharmony_ci tty_driver_kref_get(driver); 12908c2ecf20Sopenharmony_ci tty->count++; 12918c2ecf20Sopenharmony_ci driver->ttys[tty->index] = tty; 12928c2ecf20Sopenharmony_ci return 0; 12938c2ecf20Sopenharmony_ci} 12948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_standard_install); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci/** 12978c2ecf20Sopenharmony_ci * tty_driver_install_tty() - install a tty entry in the driver 12988c2ecf20Sopenharmony_ci * @driver: the driver for the tty 12998c2ecf20Sopenharmony_ci * @tty: the tty 13008c2ecf20Sopenharmony_ci * 13018c2ecf20Sopenharmony_ci * Install a tty object into the driver tables. The tty->index field 13028c2ecf20Sopenharmony_ci * will be set by the time this is called. This method is responsible 13038c2ecf20Sopenharmony_ci * for ensuring any need additional structures are allocated and 13048c2ecf20Sopenharmony_ci * configured. 13058c2ecf20Sopenharmony_ci * 13068c2ecf20Sopenharmony_ci * Locking: tty_mutex for now 13078c2ecf20Sopenharmony_ci */ 13088c2ecf20Sopenharmony_cistatic int tty_driver_install_tty(struct tty_driver *driver, 13098c2ecf20Sopenharmony_ci struct tty_struct *tty) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci return driver->ops->install ? driver->ops->install(driver, tty) : 13128c2ecf20Sopenharmony_ci tty_standard_install(driver, tty); 13138c2ecf20Sopenharmony_ci} 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci/** 13168c2ecf20Sopenharmony_ci * tty_driver_remove_tty() - remove a tty from the driver tables 13178c2ecf20Sopenharmony_ci * @driver: the driver for the tty 13188c2ecf20Sopenharmony_ci * @tty: tty to remove 13198c2ecf20Sopenharmony_ci * 13208c2ecf20Sopenharmony_ci * Remvoe a tty object from the driver tables. The tty->index field 13218c2ecf20Sopenharmony_ci * will be set by the time this is called. 13228c2ecf20Sopenharmony_ci * 13238c2ecf20Sopenharmony_ci * Locking: tty_mutex for now 13248c2ecf20Sopenharmony_ci */ 13258c2ecf20Sopenharmony_cistatic void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty) 13268c2ecf20Sopenharmony_ci{ 13278c2ecf20Sopenharmony_ci if (driver->ops->remove) 13288c2ecf20Sopenharmony_ci driver->ops->remove(driver, tty); 13298c2ecf20Sopenharmony_ci else 13308c2ecf20Sopenharmony_ci driver->ttys[tty->index] = NULL; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci/** 13348c2ecf20Sopenharmony_ci * tty_reopen() - fast re-open of an open tty 13358c2ecf20Sopenharmony_ci * @tty: the tty to open 13368c2ecf20Sopenharmony_ci * 13378c2ecf20Sopenharmony_ci * Return 0 on success, -errno on error. 13388c2ecf20Sopenharmony_ci * Re-opens on master ptys are not allowed and return -EIO. 13398c2ecf20Sopenharmony_ci * 13408c2ecf20Sopenharmony_ci * Locking: Caller must hold tty_lock 13418c2ecf20Sopenharmony_ci */ 13428c2ecf20Sopenharmony_cistatic int tty_reopen(struct tty_struct *tty) 13438c2ecf20Sopenharmony_ci{ 13448c2ecf20Sopenharmony_ci struct tty_driver *driver = tty->driver; 13458c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 13468c2ecf20Sopenharmony_ci int retval = 0; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci if (driver->type == TTY_DRIVER_TYPE_PTY && 13498c2ecf20Sopenharmony_ci driver->subtype == PTY_TYPE_MASTER) 13508c2ecf20Sopenharmony_ci return -EIO; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci if (!tty->count) 13538c2ecf20Sopenharmony_ci return -EAGAIN; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN)) 13568c2ecf20Sopenharmony_ci return -EBUSY; 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 13598c2ecf20Sopenharmony_ci if (ld) { 13608c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 13618c2ecf20Sopenharmony_ci } else { 13628c2ecf20Sopenharmony_ci retval = tty_ldisc_lock(tty, 5 * HZ); 13638c2ecf20Sopenharmony_ci if (retval) 13648c2ecf20Sopenharmony_ci return retval; 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if (!tty->ldisc) 13678c2ecf20Sopenharmony_ci retval = tty_ldisc_reinit(tty, tty->termios.c_line); 13688c2ecf20Sopenharmony_ci tty_ldisc_unlock(tty); 13698c2ecf20Sopenharmony_ci } 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci if (retval == 0) 13728c2ecf20Sopenharmony_ci tty->count++; 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci return retval; 13758c2ecf20Sopenharmony_ci} 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci/** 13788c2ecf20Sopenharmony_ci * tty_init_dev - initialise a tty device 13798c2ecf20Sopenharmony_ci * @driver: tty driver we are opening a device on 13808c2ecf20Sopenharmony_ci * @idx: device index 13818c2ecf20Sopenharmony_ci * 13828c2ecf20Sopenharmony_ci * Prepare a tty device. This may not be a "new" clean device but 13838c2ecf20Sopenharmony_ci * could also be an active device. The pty drivers require special 13848c2ecf20Sopenharmony_ci * handling because of this. 13858c2ecf20Sopenharmony_ci * 13868c2ecf20Sopenharmony_ci * Locking: 13878c2ecf20Sopenharmony_ci * The function is called under the tty_mutex, which 13888c2ecf20Sopenharmony_ci * protects us from the tty struct or driver itself going away. 13898c2ecf20Sopenharmony_ci * 13908c2ecf20Sopenharmony_ci * On exit the tty device has the line discipline attached and 13918c2ecf20Sopenharmony_ci * a reference count of 1. If a pair was created for pty/tty use 13928c2ecf20Sopenharmony_ci * and the other was a pty master then it too has a reference count of 1. 13938c2ecf20Sopenharmony_ci * 13948c2ecf20Sopenharmony_ci * WSH 06/09/97: Rewritten to remove races and properly clean up after a 13958c2ecf20Sopenharmony_ci * failed open. The new code protects the open with a mutex, so it's 13968c2ecf20Sopenharmony_ci * really quite straightforward. The mutex locking can probably be 13978c2ecf20Sopenharmony_ci * relaxed for the (most common) case of reopening a tty. 13988c2ecf20Sopenharmony_ci * 13998c2ecf20Sopenharmony_ci * Return: returned tty structure 14008c2ecf20Sopenharmony_ci */ 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistruct tty_struct *tty_init_dev(struct tty_driver *driver, int idx) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci struct tty_struct *tty; 14058c2ecf20Sopenharmony_ci int retval; 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci /* 14088c2ecf20Sopenharmony_ci * First time open is complex, especially for PTY devices. 14098c2ecf20Sopenharmony_ci * This code guarantees that either everything succeeds and the 14108c2ecf20Sopenharmony_ci * TTY is ready for operation, or else the table slots are vacated 14118c2ecf20Sopenharmony_ci * and the allocated memory released. (Except that the termios 14128c2ecf20Sopenharmony_ci * may be retained.) 14138c2ecf20Sopenharmony_ci */ 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (!try_module_get(driver->owner)) 14168c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci tty = alloc_tty_struct(driver, idx); 14198c2ecf20Sopenharmony_ci if (!tty) { 14208c2ecf20Sopenharmony_ci retval = -ENOMEM; 14218c2ecf20Sopenharmony_ci goto err_module_put; 14228c2ecf20Sopenharmony_ci } 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci tty_lock(tty); 14258c2ecf20Sopenharmony_ci retval = tty_driver_install_tty(driver, tty); 14268c2ecf20Sopenharmony_ci if (retval < 0) 14278c2ecf20Sopenharmony_ci goto err_free_tty; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci if (!tty->port) 14308c2ecf20Sopenharmony_ci tty->port = driver->ports[idx]; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci if (WARN_RATELIMIT(!tty->port, 14338c2ecf20Sopenharmony_ci "%s: %s driver does not set tty->port. This would crash the kernel. Fix the driver!\n", 14348c2ecf20Sopenharmony_ci __func__, tty->driver->name)) { 14358c2ecf20Sopenharmony_ci retval = -EINVAL; 14368c2ecf20Sopenharmony_ci goto err_release_lock; 14378c2ecf20Sopenharmony_ci } 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci retval = tty_ldisc_lock(tty, 5 * HZ); 14408c2ecf20Sopenharmony_ci if (retval) 14418c2ecf20Sopenharmony_ci goto err_release_lock; 14428c2ecf20Sopenharmony_ci tty->port->itty = tty; 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* 14458c2ecf20Sopenharmony_ci * Structures all installed ... call the ldisc open routines. 14468c2ecf20Sopenharmony_ci * If we fail here just call release_tty to clean up. No need 14478c2ecf20Sopenharmony_ci * to decrement the use counts, as release_tty doesn't care. 14488c2ecf20Sopenharmony_ci */ 14498c2ecf20Sopenharmony_ci retval = tty_ldisc_setup(tty, tty->link); 14508c2ecf20Sopenharmony_ci if (retval) 14518c2ecf20Sopenharmony_ci goto err_release_tty; 14528c2ecf20Sopenharmony_ci tty_ldisc_unlock(tty); 14538c2ecf20Sopenharmony_ci /* Return the tty locked so that it cannot vanish under the caller */ 14548c2ecf20Sopenharmony_ci return tty; 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_cierr_free_tty: 14578c2ecf20Sopenharmony_ci tty_unlock(tty); 14588c2ecf20Sopenharmony_ci free_tty_struct(tty); 14598c2ecf20Sopenharmony_cierr_module_put: 14608c2ecf20Sopenharmony_ci module_put(driver->owner); 14618c2ecf20Sopenharmony_ci return ERR_PTR(retval); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci /* call the tty release_tty routine to clean out this slot */ 14648c2ecf20Sopenharmony_cierr_release_tty: 14658c2ecf20Sopenharmony_ci tty_ldisc_unlock(tty); 14668c2ecf20Sopenharmony_ci tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n", 14678c2ecf20Sopenharmony_ci retval, idx); 14688c2ecf20Sopenharmony_cierr_release_lock: 14698c2ecf20Sopenharmony_ci tty_unlock(tty); 14708c2ecf20Sopenharmony_ci release_tty(tty, idx); 14718c2ecf20Sopenharmony_ci return ERR_PTR(retval); 14728c2ecf20Sopenharmony_ci} 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci/** 14758c2ecf20Sopenharmony_ci * tty_save_termios() - save tty termios data in driver table 14768c2ecf20Sopenharmony_ci * @tty: tty whose termios data to save 14778c2ecf20Sopenharmony_ci * 14788c2ecf20Sopenharmony_ci * Locking: Caller guarantees serialisation with tty_init_termios(). 14798c2ecf20Sopenharmony_ci */ 14808c2ecf20Sopenharmony_civoid tty_save_termios(struct tty_struct *tty) 14818c2ecf20Sopenharmony_ci{ 14828c2ecf20Sopenharmony_ci struct ktermios *tp; 14838c2ecf20Sopenharmony_ci int idx = tty->index; 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_ci /* If the port is going to reset then it has no termios to save */ 14868c2ecf20Sopenharmony_ci if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) 14878c2ecf20Sopenharmony_ci return; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci /* Stash the termios data */ 14908c2ecf20Sopenharmony_ci tp = tty->driver->termios[idx]; 14918c2ecf20Sopenharmony_ci if (tp == NULL) { 14928c2ecf20Sopenharmony_ci tp = kmalloc(sizeof(*tp), GFP_KERNEL); 14938c2ecf20Sopenharmony_ci if (tp == NULL) 14948c2ecf20Sopenharmony_ci return; 14958c2ecf20Sopenharmony_ci tty->driver->termios[idx] = tp; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci *tp = tty->termios; 14988c2ecf20Sopenharmony_ci} 14998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_save_termios); 15008c2ecf20Sopenharmony_ci 15018c2ecf20Sopenharmony_ci/** 15028c2ecf20Sopenharmony_ci * tty_flush_works - flush all works of a tty/pty pair 15038c2ecf20Sopenharmony_ci * @tty: tty device to flush works for (or either end of a pty pair) 15048c2ecf20Sopenharmony_ci * 15058c2ecf20Sopenharmony_ci * Sync flush all works belonging to @tty (and the 'other' tty). 15068c2ecf20Sopenharmony_ci */ 15078c2ecf20Sopenharmony_cistatic void tty_flush_works(struct tty_struct *tty) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci flush_work(&tty->SAK_work); 15108c2ecf20Sopenharmony_ci flush_work(&tty->hangup_work); 15118c2ecf20Sopenharmony_ci if (tty->link) { 15128c2ecf20Sopenharmony_ci flush_work(&tty->link->SAK_work); 15138c2ecf20Sopenharmony_ci flush_work(&tty->link->hangup_work); 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci/** 15188c2ecf20Sopenharmony_ci * release_one_tty - release tty structure memory 15198c2ecf20Sopenharmony_ci * @work: work of tty we are obliterating 15208c2ecf20Sopenharmony_ci * 15218c2ecf20Sopenharmony_ci * Releases memory associated with a tty structure, and clears out the 15228c2ecf20Sopenharmony_ci * driver table slots. This function is called when a device is no longer 15238c2ecf20Sopenharmony_ci * in use. It also gets called when setup of a device fails. 15248c2ecf20Sopenharmony_ci * 15258c2ecf20Sopenharmony_ci * Locking: 15268c2ecf20Sopenharmony_ci * takes the file list lock internally when working on the list 15278c2ecf20Sopenharmony_ci * of ttys that the driver keeps. 15288c2ecf20Sopenharmony_ci * 15298c2ecf20Sopenharmony_ci * This method gets called from a work queue so that the driver private 15308c2ecf20Sopenharmony_ci * cleanup ops can sleep (needed for USB at least) 15318c2ecf20Sopenharmony_ci */ 15328c2ecf20Sopenharmony_cistatic void release_one_tty(struct work_struct *work) 15338c2ecf20Sopenharmony_ci{ 15348c2ecf20Sopenharmony_ci struct tty_struct *tty = 15358c2ecf20Sopenharmony_ci container_of(work, struct tty_struct, hangup_work); 15368c2ecf20Sopenharmony_ci struct tty_driver *driver = tty->driver; 15378c2ecf20Sopenharmony_ci struct module *owner = driver->owner; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (tty->ops->cleanup) 15408c2ecf20Sopenharmony_ci tty->ops->cleanup(tty); 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci tty->magic = 0; 15438c2ecf20Sopenharmony_ci tty_driver_kref_put(driver); 15448c2ecf20Sopenharmony_ci module_put(owner); 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci spin_lock(&tty->files_lock); 15478c2ecf20Sopenharmony_ci list_del_init(&tty->tty_files); 15488c2ecf20Sopenharmony_ci spin_unlock(&tty->files_lock); 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci put_pid(tty->pgrp); 15518c2ecf20Sopenharmony_ci put_pid(tty->session); 15528c2ecf20Sopenharmony_ci free_tty_struct(tty); 15538c2ecf20Sopenharmony_ci} 15548c2ecf20Sopenharmony_ci 15558c2ecf20Sopenharmony_cistatic void queue_release_one_tty(struct kref *kref) 15568c2ecf20Sopenharmony_ci{ 15578c2ecf20Sopenharmony_ci struct tty_struct *tty = container_of(kref, struct tty_struct, kref); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci /* The hangup queue is now free so we can reuse it rather than 15608c2ecf20Sopenharmony_ci waste a chunk of memory for each port */ 15618c2ecf20Sopenharmony_ci INIT_WORK(&tty->hangup_work, release_one_tty); 15628c2ecf20Sopenharmony_ci schedule_work(&tty->hangup_work); 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_ci/** 15668c2ecf20Sopenharmony_ci * tty_kref_put - release a tty kref 15678c2ecf20Sopenharmony_ci * @tty: tty device 15688c2ecf20Sopenharmony_ci * 15698c2ecf20Sopenharmony_ci * Release a reference to a tty device and if need be let the kref 15708c2ecf20Sopenharmony_ci * layer destruct the object for us 15718c2ecf20Sopenharmony_ci */ 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_civoid tty_kref_put(struct tty_struct *tty) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci if (tty) 15768c2ecf20Sopenharmony_ci kref_put(&tty->kref, queue_release_one_tty); 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_kref_put); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci/** 15818c2ecf20Sopenharmony_ci * release_tty - release tty structure memory 15828c2ecf20Sopenharmony_ci * 15838c2ecf20Sopenharmony_ci * Release both @tty and a possible linked partner (think pty pair), 15848c2ecf20Sopenharmony_ci * and decrement the refcount of the backing module. 15858c2ecf20Sopenharmony_ci * 15868c2ecf20Sopenharmony_ci * Locking: 15878c2ecf20Sopenharmony_ci * tty_mutex 15888c2ecf20Sopenharmony_ci * takes the file list lock internally when working on the list 15898c2ecf20Sopenharmony_ci * of ttys that the driver keeps. 15908c2ecf20Sopenharmony_ci * 15918c2ecf20Sopenharmony_ci */ 15928c2ecf20Sopenharmony_cistatic void release_tty(struct tty_struct *tty, int idx) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci /* This should always be true but check for the moment */ 15958c2ecf20Sopenharmony_ci WARN_ON(tty->index != idx); 15968c2ecf20Sopenharmony_ci WARN_ON(!mutex_is_locked(&tty_mutex)); 15978c2ecf20Sopenharmony_ci if (tty->ops->shutdown) 15988c2ecf20Sopenharmony_ci tty->ops->shutdown(tty); 15998c2ecf20Sopenharmony_ci tty_save_termios(tty); 16008c2ecf20Sopenharmony_ci tty_driver_remove_tty(tty->driver, tty); 16018c2ecf20Sopenharmony_ci if (tty->port) 16028c2ecf20Sopenharmony_ci tty->port->itty = NULL; 16038c2ecf20Sopenharmony_ci if (tty->link) 16048c2ecf20Sopenharmony_ci tty->link->port->itty = NULL; 16058c2ecf20Sopenharmony_ci if (tty->port) 16068c2ecf20Sopenharmony_ci tty_buffer_cancel_work(tty->port); 16078c2ecf20Sopenharmony_ci if (tty->link) 16088c2ecf20Sopenharmony_ci tty_buffer_cancel_work(tty->link->port); 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci tty_kref_put(tty->link); 16118c2ecf20Sopenharmony_ci tty_kref_put(tty); 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci/** 16158c2ecf20Sopenharmony_ci * tty_release_checks - check a tty before real release 16168c2ecf20Sopenharmony_ci * @tty: tty to check 16178c2ecf20Sopenharmony_ci * @idx: index of the tty 16188c2ecf20Sopenharmony_ci * 16198c2ecf20Sopenharmony_ci * Performs some paranoid checking before true release of the @tty. 16208c2ecf20Sopenharmony_ci * This is a no-op unless TTY_PARANOIA_CHECK is defined. 16218c2ecf20Sopenharmony_ci */ 16228c2ecf20Sopenharmony_cistatic int tty_release_checks(struct tty_struct *tty, int idx) 16238c2ecf20Sopenharmony_ci{ 16248c2ecf20Sopenharmony_ci#ifdef TTY_PARANOIA_CHECK 16258c2ecf20Sopenharmony_ci if (idx < 0 || idx >= tty->driver->num) { 16268c2ecf20Sopenharmony_ci tty_debug(tty, "bad idx %d\n", idx); 16278c2ecf20Sopenharmony_ci return -1; 16288c2ecf20Sopenharmony_ci } 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci /* not much to check for devpts */ 16318c2ecf20Sopenharmony_ci if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) 16328c2ecf20Sopenharmony_ci return 0; 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if (tty != tty->driver->ttys[idx]) { 16358c2ecf20Sopenharmony_ci tty_debug(tty, "bad driver table[%d] = %p\n", 16368c2ecf20Sopenharmony_ci idx, tty->driver->ttys[idx]); 16378c2ecf20Sopenharmony_ci return -1; 16388c2ecf20Sopenharmony_ci } 16398c2ecf20Sopenharmony_ci if (tty->driver->other) { 16408c2ecf20Sopenharmony_ci struct tty_struct *o_tty = tty->link; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci if (o_tty != tty->driver->other->ttys[idx]) { 16438c2ecf20Sopenharmony_ci tty_debug(tty, "bad other table[%d] = %p\n", 16448c2ecf20Sopenharmony_ci idx, tty->driver->other->ttys[idx]); 16458c2ecf20Sopenharmony_ci return -1; 16468c2ecf20Sopenharmony_ci } 16478c2ecf20Sopenharmony_ci if (o_tty->link != tty) { 16488c2ecf20Sopenharmony_ci tty_debug(tty, "bad link = %p\n", o_tty->link); 16498c2ecf20Sopenharmony_ci return -1; 16508c2ecf20Sopenharmony_ci } 16518c2ecf20Sopenharmony_ci } 16528c2ecf20Sopenharmony_ci#endif 16538c2ecf20Sopenharmony_ci return 0; 16548c2ecf20Sopenharmony_ci} 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci/** 16578c2ecf20Sopenharmony_ci * tty_kclose - closes tty opened by tty_kopen 16588c2ecf20Sopenharmony_ci * @tty: tty device 16598c2ecf20Sopenharmony_ci * 16608c2ecf20Sopenharmony_ci * Performs the final steps to release and free a tty device. It is the 16618c2ecf20Sopenharmony_ci * same as tty_release_struct except that it also resets TTY_PORT_KOPENED 16628c2ecf20Sopenharmony_ci * flag on tty->port. 16638c2ecf20Sopenharmony_ci */ 16648c2ecf20Sopenharmony_civoid tty_kclose(struct tty_struct *tty) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci /* 16678c2ecf20Sopenharmony_ci * Ask the line discipline code to release its structures 16688c2ecf20Sopenharmony_ci */ 16698c2ecf20Sopenharmony_ci tty_ldisc_release(tty); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci /* Wait for pending work before tty destruction commmences */ 16728c2ecf20Sopenharmony_ci tty_flush_works(tty); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "freeing structure\n"); 16758c2ecf20Sopenharmony_ci /* 16768c2ecf20Sopenharmony_ci * The release_tty function takes care of the details of clearing 16778c2ecf20Sopenharmony_ci * the slots and preserving the termios structure. 16788c2ecf20Sopenharmony_ci */ 16798c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 16808c2ecf20Sopenharmony_ci tty_port_set_kopened(tty->port, 0); 16818c2ecf20Sopenharmony_ci release_tty(tty, tty->index); 16828c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 16838c2ecf20Sopenharmony_ci} 16848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_kclose); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci/** 16878c2ecf20Sopenharmony_ci * tty_release_struct - release a tty struct 16888c2ecf20Sopenharmony_ci * @tty: tty device 16898c2ecf20Sopenharmony_ci * @idx: index of the tty 16908c2ecf20Sopenharmony_ci * 16918c2ecf20Sopenharmony_ci * Performs the final steps to release and free a tty device. It is 16928c2ecf20Sopenharmony_ci * roughly the reverse of tty_init_dev. 16938c2ecf20Sopenharmony_ci */ 16948c2ecf20Sopenharmony_civoid tty_release_struct(struct tty_struct *tty, int idx) 16958c2ecf20Sopenharmony_ci{ 16968c2ecf20Sopenharmony_ci /* 16978c2ecf20Sopenharmony_ci * Ask the line discipline code to release its structures 16988c2ecf20Sopenharmony_ci */ 16998c2ecf20Sopenharmony_ci tty_ldisc_release(tty); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci /* Wait for pending work before tty destruction commmences */ 17028c2ecf20Sopenharmony_ci tty_flush_works(tty); 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "freeing structure\n"); 17058c2ecf20Sopenharmony_ci /* 17068c2ecf20Sopenharmony_ci * The release_tty function takes care of the details of clearing 17078c2ecf20Sopenharmony_ci * the slots and preserving the termios structure. 17088c2ecf20Sopenharmony_ci */ 17098c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 17108c2ecf20Sopenharmony_ci release_tty(tty, idx); 17118c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 17128c2ecf20Sopenharmony_ci} 17138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_release_struct); 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci/** 17168c2ecf20Sopenharmony_ci * tty_release - vfs callback for close 17178c2ecf20Sopenharmony_ci * @inode: inode of tty 17188c2ecf20Sopenharmony_ci * @filp: file pointer for handle to tty 17198c2ecf20Sopenharmony_ci * 17208c2ecf20Sopenharmony_ci * Called the last time each file handle is closed that references 17218c2ecf20Sopenharmony_ci * this tty. There may however be several such references. 17228c2ecf20Sopenharmony_ci * 17238c2ecf20Sopenharmony_ci * Locking: 17248c2ecf20Sopenharmony_ci * Takes bkl. See tty_release_dev 17258c2ecf20Sopenharmony_ci * 17268c2ecf20Sopenharmony_ci * Even releasing the tty structures is a tricky business.. We have 17278c2ecf20Sopenharmony_ci * to be very careful that the structures are all released at the 17288c2ecf20Sopenharmony_ci * same time, as interrupts might otherwise get the wrong pointers. 17298c2ecf20Sopenharmony_ci * 17308c2ecf20Sopenharmony_ci * WSH 09/09/97: rewritten to avoid some nasty race conditions that could 17318c2ecf20Sopenharmony_ci * lead to double frees or releasing memory still in use. 17328c2ecf20Sopenharmony_ci */ 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_ciint tty_release(struct inode *inode, struct file *filp) 17358c2ecf20Sopenharmony_ci{ 17368c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(filp); 17378c2ecf20Sopenharmony_ci struct tty_struct *o_tty = NULL; 17388c2ecf20Sopenharmony_ci int do_sleep, final; 17398c2ecf20Sopenharmony_ci int idx; 17408c2ecf20Sopenharmony_ci long timeout = 0; 17418c2ecf20Sopenharmony_ci int once = 1; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, inode, __func__)) 17448c2ecf20Sopenharmony_ci return 0; 17458c2ecf20Sopenharmony_ci 17468c2ecf20Sopenharmony_ci tty_lock(tty); 17478c2ecf20Sopenharmony_ci check_tty_count(tty, __func__); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci __tty_fasync(-1, filp, 0); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci idx = tty->index; 17528c2ecf20Sopenharmony_ci if (tty->driver->type == TTY_DRIVER_TYPE_PTY && 17538c2ecf20Sopenharmony_ci tty->driver->subtype == PTY_TYPE_MASTER) 17548c2ecf20Sopenharmony_ci o_tty = tty->link; 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_ci if (tty_release_checks(tty, idx)) { 17578c2ecf20Sopenharmony_ci tty_unlock(tty); 17588c2ecf20Sopenharmony_ci return 0; 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "releasing (count=%d)\n", tty->count); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci if (tty->ops->close) 17648c2ecf20Sopenharmony_ci tty->ops->close(tty, filp); 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci /* If tty is pty master, lock the slave pty (stable lock order) */ 17678c2ecf20Sopenharmony_ci tty_lock_slave(o_tty); 17688c2ecf20Sopenharmony_ci 17698c2ecf20Sopenharmony_ci /* 17708c2ecf20Sopenharmony_ci * Sanity check: if tty->count is going to zero, there shouldn't be 17718c2ecf20Sopenharmony_ci * any waiters on tty->read_wait or tty->write_wait. We test the 17728c2ecf20Sopenharmony_ci * wait queues and kick everyone out _before_ actually starting to 17738c2ecf20Sopenharmony_ci * close. This ensures that we won't block while releasing the tty 17748c2ecf20Sopenharmony_ci * structure. 17758c2ecf20Sopenharmony_ci * 17768c2ecf20Sopenharmony_ci * The test for the o_tty closing is necessary, since the master and 17778c2ecf20Sopenharmony_ci * slave sides may close in any order. If the slave side closes out 17788c2ecf20Sopenharmony_ci * first, its count will be one, since the master side holds an open. 17798c2ecf20Sopenharmony_ci * Thus this test wouldn't be triggered at the time the slave closed, 17808c2ecf20Sopenharmony_ci * so we do it now. 17818c2ecf20Sopenharmony_ci */ 17828c2ecf20Sopenharmony_ci while (1) { 17838c2ecf20Sopenharmony_ci do_sleep = 0; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci if (tty->count <= 1) { 17868c2ecf20Sopenharmony_ci if (waitqueue_active(&tty->read_wait)) { 17878c2ecf20Sopenharmony_ci wake_up_poll(&tty->read_wait, EPOLLIN); 17888c2ecf20Sopenharmony_ci do_sleep++; 17898c2ecf20Sopenharmony_ci } 17908c2ecf20Sopenharmony_ci if (waitqueue_active(&tty->write_wait)) { 17918c2ecf20Sopenharmony_ci wake_up_poll(&tty->write_wait, EPOLLOUT); 17928c2ecf20Sopenharmony_ci do_sleep++; 17938c2ecf20Sopenharmony_ci } 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci if (o_tty && o_tty->count <= 1) { 17968c2ecf20Sopenharmony_ci if (waitqueue_active(&o_tty->read_wait)) { 17978c2ecf20Sopenharmony_ci wake_up_poll(&o_tty->read_wait, EPOLLIN); 17988c2ecf20Sopenharmony_ci do_sleep++; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci if (waitqueue_active(&o_tty->write_wait)) { 18018c2ecf20Sopenharmony_ci wake_up_poll(&o_tty->write_wait, EPOLLOUT); 18028c2ecf20Sopenharmony_ci do_sleep++; 18038c2ecf20Sopenharmony_ci } 18048c2ecf20Sopenharmony_ci } 18058c2ecf20Sopenharmony_ci if (!do_sleep) 18068c2ecf20Sopenharmony_ci break; 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci if (once) { 18098c2ecf20Sopenharmony_ci once = 0; 18108c2ecf20Sopenharmony_ci tty_warn(tty, "read/write wait queue active!\n"); 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci schedule_timeout_killable(timeout); 18138c2ecf20Sopenharmony_ci if (timeout < 120 * HZ) 18148c2ecf20Sopenharmony_ci timeout = 2 * timeout + 1; 18158c2ecf20Sopenharmony_ci else 18168c2ecf20Sopenharmony_ci timeout = MAX_SCHEDULE_TIMEOUT; 18178c2ecf20Sopenharmony_ci } 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_ci if (o_tty) { 18208c2ecf20Sopenharmony_ci if (--o_tty->count < 0) { 18218c2ecf20Sopenharmony_ci tty_warn(tty, "bad slave count (%d)\n", o_tty->count); 18228c2ecf20Sopenharmony_ci o_tty->count = 0; 18238c2ecf20Sopenharmony_ci } 18248c2ecf20Sopenharmony_ci } 18258c2ecf20Sopenharmony_ci if (--tty->count < 0) { 18268c2ecf20Sopenharmony_ci tty_warn(tty, "bad tty->count (%d)\n", tty->count); 18278c2ecf20Sopenharmony_ci tty->count = 0; 18288c2ecf20Sopenharmony_ci } 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci /* 18318c2ecf20Sopenharmony_ci * We've decremented tty->count, so we need to remove this file 18328c2ecf20Sopenharmony_ci * descriptor off the tty->tty_files list; this serves two 18338c2ecf20Sopenharmony_ci * purposes: 18348c2ecf20Sopenharmony_ci * - check_tty_count sees the correct number of file descriptors 18358c2ecf20Sopenharmony_ci * associated with this tty. 18368c2ecf20Sopenharmony_ci * - do_tty_hangup no longer sees this file descriptor as 18378c2ecf20Sopenharmony_ci * something that needs to be handled for hangups. 18388c2ecf20Sopenharmony_ci */ 18398c2ecf20Sopenharmony_ci tty_del_file(filp); 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci /* 18428c2ecf20Sopenharmony_ci * Perform some housekeeping before deciding whether to return. 18438c2ecf20Sopenharmony_ci * 18448c2ecf20Sopenharmony_ci * If _either_ side is closing, make sure there aren't any 18458c2ecf20Sopenharmony_ci * processes that still think tty or o_tty is their controlling 18468c2ecf20Sopenharmony_ci * tty. 18478c2ecf20Sopenharmony_ci */ 18488c2ecf20Sopenharmony_ci if (!tty->count) { 18498c2ecf20Sopenharmony_ci read_lock(&tasklist_lock); 18508c2ecf20Sopenharmony_ci session_clear_tty(tty->session); 18518c2ecf20Sopenharmony_ci if (o_tty) 18528c2ecf20Sopenharmony_ci session_clear_tty(o_tty->session); 18538c2ecf20Sopenharmony_ci read_unlock(&tasklist_lock); 18548c2ecf20Sopenharmony_ci } 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci /* check whether both sides are closing ... */ 18578c2ecf20Sopenharmony_ci final = !tty->count && !(o_tty && o_tty->count); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci tty_unlock_slave(o_tty); 18608c2ecf20Sopenharmony_ci tty_unlock(tty); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci /* At this point, the tty->count == 0 should ensure a dead tty 18638c2ecf20Sopenharmony_ci cannot be re-opened by a racing opener */ 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if (!final) 18668c2ecf20Sopenharmony_ci return 0; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "final close\n"); 18698c2ecf20Sopenharmony_ci 18708c2ecf20Sopenharmony_ci tty_release_struct(tty, idx); 18718c2ecf20Sopenharmony_ci return 0; 18728c2ecf20Sopenharmony_ci} 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci/** 18758c2ecf20Sopenharmony_ci * tty_open_current_tty - get locked tty of current task 18768c2ecf20Sopenharmony_ci * @device: device number 18778c2ecf20Sopenharmony_ci * @filp: file pointer to tty 18788c2ecf20Sopenharmony_ci * @return: locked tty of the current task iff @device is /dev/tty 18798c2ecf20Sopenharmony_ci * 18808c2ecf20Sopenharmony_ci * Performs a re-open of the current task's controlling tty. 18818c2ecf20Sopenharmony_ci * 18828c2ecf20Sopenharmony_ci * We cannot return driver and index like for the other nodes because 18838c2ecf20Sopenharmony_ci * devpts will not work then. It expects inodes to be from devpts FS. 18848c2ecf20Sopenharmony_ci */ 18858c2ecf20Sopenharmony_cistatic struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct tty_struct *tty; 18888c2ecf20Sopenharmony_ci int retval; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (device != MKDEV(TTYAUX_MAJOR, 0)) 18918c2ecf20Sopenharmony_ci return NULL; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci tty = get_current_tty(); 18948c2ecf20Sopenharmony_ci if (!tty) 18958c2ecf20Sopenharmony_ci return ERR_PTR(-ENXIO); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */ 18988c2ecf20Sopenharmony_ci /* noctty = 1; */ 18998c2ecf20Sopenharmony_ci tty_lock(tty); 19008c2ecf20Sopenharmony_ci tty_kref_put(tty); /* safe to drop the kref now */ 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci retval = tty_reopen(tty); 19038c2ecf20Sopenharmony_ci if (retval < 0) { 19048c2ecf20Sopenharmony_ci tty_unlock(tty); 19058c2ecf20Sopenharmony_ci tty = ERR_PTR(retval); 19068c2ecf20Sopenharmony_ci } 19078c2ecf20Sopenharmony_ci return tty; 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci/** 19118c2ecf20Sopenharmony_ci * tty_lookup_driver - lookup a tty driver for a given device file 19128c2ecf20Sopenharmony_ci * @device: device number 19138c2ecf20Sopenharmony_ci * @filp: file pointer to tty 19148c2ecf20Sopenharmony_ci * @index: index for the device in the @return driver 19158c2ecf20Sopenharmony_ci * @return: driver for this inode (with increased refcount) 19168c2ecf20Sopenharmony_ci * 19178c2ecf20Sopenharmony_ci * If @return is not erroneous, the caller is responsible to decrement the 19188c2ecf20Sopenharmony_ci * refcount by tty_driver_kref_put. 19198c2ecf20Sopenharmony_ci * 19208c2ecf20Sopenharmony_ci * Locking: tty_mutex protects get_tty_driver 19218c2ecf20Sopenharmony_ci */ 19228c2ecf20Sopenharmony_cistatic struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp, 19238c2ecf20Sopenharmony_ci int *index) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci struct tty_driver *driver = NULL; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci switch (device) { 19288c2ecf20Sopenharmony_ci#ifdef CONFIG_VT 19298c2ecf20Sopenharmony_ci case MKDEV(TTY_MAJOR, 0): { 19308c2ecf20Sopenharmony_ci extern struct tty_driver *console_driver; 19318c2ecf20Sopenharmony_ci driver = tty_driver_kref_get(console_driver); 19328c2ecf20Sopenharmony_ci *index = fg_console; 19338c2ecf20Sopenharmony_ci break; 19348c2ecf20Sopenharmony_ci } 19358c2ecf20Sopenharmony_ci#endif 19368c2ecf20Sopenharmony_ci case MKDEV(TTYAUX_MAJOR, 1): { 19378c2ecf20Sopenharmony_ci struct tty_driver *console_driver = console_device(index); 19388c2ecf20Sopenharmony_ci if (console_driver) { 19398c2ecf20Sopenharmony_ci driver = tty_driver_kref_get(console_driver); 19408c2ecf20Sopenharmony_ci if (driver && filp) { 19418c2ecf20Sopenharmony_ci /* Don't let /dev/console block */ 19428c2ecf20Sopenharmony_ci filp->f_flags |= O_NONBLOCK; 19438c2ecf20Sopenharmony_ci break; 19448c2ecf20Sopenharmony_ci } 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci if (driver) 19478c2ecf20Sopenharmony_ci tty_driver_kref_put(driver); 19488c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 19498c2ecf20Sopenharmony_ci } 19508c2ecf20Sopenharmony_ci default: 19518c2ecf20Sopenharmony_ci driver = get_tty_driver(device, index); 19528c2ecf20Sopenharmony_ci if (!driver) 19538c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 19548c2ecf20Sopenharmony_ci break; 19558c2ecf20Sopenharmony_ci } 19568c2ecf20Sopenharmony_ci return driver; 19578c2ecf20Sopenharmony_ci} 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci/** 19608c2ecf20Sopenharmony_ci * tty_kopen - open a tty device for kernel 19618c2ecf20Sopenharmony_ci * @device: dev_t of device to open 19628c2ecf20Sopenharmony_ci * 19638c2ecf20Sopenharmony_ci * Opens tty exclusively for kernel. Performs the driver lookup, 19648c2ecf20Sopenharmony_ci * makes sure it's not already opened and performs the first-time 19658c2ecf20Sopenharmony_ci * tty initialization. 19668c2ecf20Sopenharmony_ci * 19678c2ecf20Sopenharmony_ci * Returns the locked initialized &tty_struct 19688c2ecf20Sopenharmony_ci * 19698c2ecf20Sopenharmony_ci * Claims the global tty_mutex to serialize: 19708c2ecf20Sopenharmony_ci * - concurrent first-time tty initialization 19718c2ecf20Sopenharmony_ci * - concurrent tty driver removal w/ lookup 19728c2ecf20Sopenharmony_ci * - concurrent tty removal from driver table 19738c2ecf20Sopenharmony_ci */ 19748c2ecf20Sopenharmony_cistruct tty_struct *tty_kopen(dev_t device) 19758c2ecf20Sopenharmony_ci{ 19768c2ecf20Sopenharmony_ci struct tty_struct *tty; 19778c2ecf20Sopenharmony_ci struct tty_driver *driver; 19788c2ecf20Sopenharmony_ci int index = -1; 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 19818c2ecf20Sopenharmony_ci driver = tty_lookup_driver(device, NULL, &index); 19828c2ecf20Sopenharmony_ci if (IS_ERR(driver)) { 19838c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 19848c2ecf20Sopenharmony_ci return ERR_CAST(driver); 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci /* check whether we're reopening an existing tty */ 19888c2ecf20Sopenharmony_ci tty = tty_driver_lookup_tty(driver, NULL, index); 19898c2ecf20Sopenharmony_ci if (IS_ERR(tty)) 19908c2ecf20Sopenharmony_ci goto out; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (tty) { 19938c2ecf20Sopenharmony_ci /* drop kref from tty_driver_lookup_tty() */ 19948c2ecf20Sopenharmony_ci tty_kref_put(tty); 19958c2ecf20Sopenharmony_ci tty = ERR_PTR(-EBUSY); 19968c2ecf20Sopenharmony_ci } else { /* tty_init_dev returns tty with the tty_lock held */ 19978c2ecf20Sopenharmony_ci tty = tty_init_dev(driver, index); 19988c2ecf20Sopenharmony_ci if (IS_ERR(tty)) 19998c2ecf20Sopenharmony_ci goto out; 20008c2ecf20Sopenharmony_ci tty_port_set_kopened(tty->port, 1); 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ciout: 20038c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 20048c2ecf20Sopenharmony_ci tty_driver_kref_put(driver); 20058c2ecf20Sopenharmony_ci return tty; 20068c2ecf20Sopenharmony_ci} 20078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_kopen); 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci/** 20108c2ecf20Sopenharmony_ci * tty_open_by_driver - open a tty device 20118c2ecf20Sopenharmony_ci * @device: dev_t of device to open 20128c2ecf20Sopenharmony_ci * @filp: file pointer to tty 20138c2ecf20Sopenharmony_ci * 20148c2ecf20Sopenharmony_ci * Performs the driver lookup, checks for a reopen, or otherwise 20158c2ecf20Sopenharmony_ci * performs the first-time tty initialization. 20168c2ecf20Sopenharmony_ci * 20178c2ecf20Sopenharmony_ci * Returns the locked initialized or re-opened &tty_struct 20188c2ecf20Sopenharmony_ci * 20198c2ecf20Sopenharmony_ci * Claims the global tty_mutex to serialize: 20208c2ecf20Sopenharmony_ci * - concurrent first-time tty initialization 20218c2ecf20Sopenharmony_ci * - concurrent tty driver removal w/ lookup 20228c2ecf20Sopenharmony_ci * - concurrent tty removal from driver table 20238c2ecf20Sopenharmony_ci */ 20248c2ecf20Sopenharmony_cistatic struct tty_struct *tty_open_by_driver(dev_t device, 20258c2ecf20Sopenharmony_ci struct file *filp) 20268c2ecf20Sopenharmony_ci{ 20278c2ecf20Sopenharmony_ci struct tty_struct *tty; 20288c2ecf20Sopenharmony_ci struct tty_driver *driver = NULL; 20298c2ecf20Sopenharmony_ci int index = -1; 20308c2ecf20Sopenharmony_ci int retval; 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 20338c2ecf20Sopenharmony_ci driver = tty_lookup_driver(device, filp, &index); 20348c2ecf20Sopenharmony_ci if (IS_ERR(driver)) { 20358c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 20368c2ecf20Sopenharmony_ci return ERR_CAST(driver); 20378c2ecf20Sopenharmony_ci } 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci /* check whether we're reopening an existing tty */ 20408c2ecf20Sopenharmony_ci tty = tty_driver_lookup_tty(driver, filp, index); 20418c2ecf20Sopenharmony_ci if (IS_ERR(tty)) { 20428c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 20438c2ecf20Sopenharmony_ci goto out; 20448c2ecf20Sopenharmony_ci } 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci if (tty) { 20478c2ecf20Sopenharmony_ci if (tty_port_kopened(tty->port)) { 20488c2ecf20Sopenharmony_ci tty_kref_put(tty); 20498c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 20508c2ecf20Sopenharmony_ci tty = ERR_PTR(-EBUSY); 20518c2ecf20Sopenharmony_ci goto out; 20528c2ecf20Sopenharmony_ci } 20538c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 20548c2ecf20Sopenharmony_ci retval = tty_lock_interruptible(tty); 20558c2ecf20Sopenharmony_ci tty_kref_put(tty); /* drop kref from tty_driver_lookup_tty() */ 20568c2ecf20Sopenharmony_ci if (retval) { 20578c2ecf20Sopenharmony_ci if (retval == -EINTR) 20588c2ecf20Sopenharmony_ci retval = -ERESTARTSYS; 20598c2ecf20Sopenharmony_ci tty = ERR_PTR(retval); 20608c2ecf20Sopenharmony_ci goto out; 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci retval = tty_reopen(tty); 20638c2ecf20Sopenharmony_ci if (retval < 0) { 20648c2ecf20Sopenharmony_ci tty_unlock(tty); 20658c2ecf20Sopenharmony_ci tty = ERR_PTR(retval); 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci } else { /* Returns with the tty_lock held for now */ 20688c2ecf20Sopenharmony_ci tty = tty_init_dev(driver, index); 20698c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 20708c2ecf20Sopenharmony_ci } 20718c2ecf20Sopenharmony_ciout: 20728c2ecf20Sopenharmony_ci tty_driver_kref_put(driver); 20738c2ecf20Sopenharmony_ci return tty; 20748c2ecf20Sopenharmony_ci} 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci/** 20778c2ecf20Sopenharmony_ci * tty_open - open a tty device 20788c2ecf20Sopenharmony_ci * @inode: inode of device file 20798c2ecf20Sopenharmony_ci * @filp: file pointer to tty 20808c2ecf20Sopenharmony_ci * 20818c2ecf20Sopenharmony_ci * tty_open and tty_release keep up the tty count that contains the 20828c2ecf20Sopenharmony_ci * number of opens done on a tty. We cannot use the inode-count, as 20838c2ecf20Sopenharmony_ci * different inodes might point to the same tty. 20848c2ecf20Sopenharmony_ci * 20858c2ecf20Sopenharmony_ci * Open-counting is needed for pty masters, as well as for keeping 20868c2ecf20Sopenharmony_ci * track of serial lines: DTR is dropped when the last close happens. 20878c2ecf20Sopenharmony_ci * (This is not done solely through tty->count, now. - Ted 1/27/92) 20888c2ecf20Sopenharmony_ci * 20898c2ecf20Sopenharmony_ci * The termios state of a pty is reset on first open so that 20908c2ecf20Sopenharmony_ci * settings don't persist across reuse. 20918c2ecf20Sopenharmony_ci * 20928c2ecf20Sopenharmony_ci * Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev. 20938c2ecf20Sopenharmony_ci * tty->count should protect the rest. 20948c2ecf20Sopenharmony_ci * ->siglock protects ->signal/->sighand 20958c2ecf20Sopenharmony_ci * 20968c2ecf20Sopenharmony_ci * Note: the tty_unlock/lock cases without a ref are only safe due to 20978c2ecf20Sopenharmony_ci * tty_mutex 20988c2ecf20Sopenharmony_ci */ 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_cistatic int tty_open(struct inode *inode, struct file *filp) 21018c2ecf20Sopenharmony_ci{ 21028c2ecf20Sopenharmony_ci struct tty_struct *tty; 21038c2ecf20Sopenharmony_ci int noctty, retval; 21048c2ecf20Sopenharmony_ci dev_t device = inode->i_rdev; 21058c2ecf20Sopenharmony_ci unsigned saved_flags = filp->f_flags; 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_ci nonseekable_open(inode, filp); 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ciretry_open: 21108c2ecf20Sopenharmony_ci retval = tty_alloc_file(filp); 21118c2ecf20Sopenharmony_ci if (retval) 21128c2ecf20Sopenharmony_ci return -ENOMEM; 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci tty = tty_open_current_tty(device, filp); 21158c2ecf20Sopenharmony_ci if (!tty) 21168c2ecf20Sopenharmony_ci tty = tty_open_by_driver(device, filp); 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci if (IS_ERR(tty)) { 21198c2ecf20Sopenharmony_ci tty_free_file(filp); 21208c2ecf20Sopenharmony_ci retval = PTR_ERR(tty); 21218c2ecf20Sopenharmony_ci if (retval != -EAGAIN || signal_pending(current)) 21228c2ecf20Sopenharmony_ci return retval; 21238c2ecf20Sopenharmony_ci schedule(); 21248c2ecf20Sopenharmony_ci goto retry_open; 21258c2ecf20Sopenharmony_ci } 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci tty_add_file(tty, filp); 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_ci check_tty_count(tty, __func__); 21308c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "opening (count=%d)\n", tty->count); 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci if (tty->ops->open) 21338c2ecf20Sopenharmony_ci retval = tty->ops->open(tty, filp); 21348c2ecf20Sopenharmony_ci else 21358c2ecf20Sopenharmony_ci retval = -ENODEV; 21368c2ecf20Sopenharmony_ci filp->f_flags = saved_flags; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci if (retval) { 21398c2ecf20Sopenharmony_ci tty_debug_hangup(tty, "open error %d, releasing\n", retval); 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ci tty_unlock(tty); /* need to call tty_release without BTM */ 21428c2ecf20Sopenharmony_ci tty_release(inode, filp); 21438c2ecf20Sopenharmony_ci if (retval != -ERESTARTSYS) 21448c2ecf20Sopenharmony_ci return retval; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (signal_pending(current)) 21478c2ecf20Sopenharmony_ci return retval; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci schedule(); 21508c2ecf20Sopenharmony_ci /* 21518c2ecf20Sopenharmony_ci * Need to reset f_op in case a hangup happened. 21528c2ecf20Sopenharmony_ci */ 21538c2ecf20Sopenharmony_ci if (tty_hung_up_p(filp)) 21548c2ecf20Sopenharmony_ci filp->f_op = &tty_fops; 21558c2ecf20Sopenharmony_ci goto retry_open; 21568c2ecf20Sopenharmony_ci } 21578c2ecf20Sopenharmony_ci clear_bit(TTY_HUPPED, &tty->flags); 21588c2ecf20Sopenharmony_ci 21598c2ecf20Sopenharmony_ci noctty = (filp->f_flags & O_NOCTTY) || 21608c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) || 21618c2ecf20Sopenharmony_ci device == MKDEV(TTYAUX_MAJOR, 1) || 21628c2ecf20Sopenharmony_ci (tty->driver->type == TTY_DRIVER_TYPE_PTY && 21638c2ecf20Sopenharmony_ci tty->driver->subtype == PTY_TYPE_MASTER); 21648c2ecf20Sopenharmony_ci if (!noctty) 21658c2ecf20Sopenharmony_ci tty_open_proc_set_tty(filp, tty); 21668c2ecf20Sopenharmony_ci tty_unlock(tty); 21678c2ecf20Sopenharmony_ci return 0; 21688c2ecf20Sopenharmony_ci} 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci/** 21738c2ecf20Sopenharmony_ci * tty_poll - check tty status 21748c2ecf20Sopenharmony_ci * @filp: file being polled 21758c2ecf20Sopenharmony_ci * @wait: poll wait structures to update 21768c2ecf20Sopenharmony_ci * 21778c2ecf20Sopenharmony_ci * Call the line discipline polling method to obtain the poll 21788c2ecf20Sopenharmony_ci * status of the device. 21798c2ecf20Sopenharmony_ci * 21808c2ecf20Sopenharmony_ci * Locking: locks called line discipline but ldisc poll method 21818c2ecf20Sopenharmony_ci * may be re-entered freely by other callers. 21828c2ecf20Sopenharmony_ci */ 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_cistatic __poll_t tty_poll(struct file *filp, poll_table *wait) 21858c2ecf20Sopenharmony_ci{ 21868c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(filp); 21878c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 21888c2ecf20Sopenharmony_ci __poll_t ret = 0; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, file_inode(filp), "tty_poll")) 21918c2ecf20Sopenharmony_ci return 0; 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 21948c2ecf20Sopenharmony_ci if (!ld) 21958c2ecf20Sopenharmony_ci return hung_up_tty_poll(filp, wait); 21968c2ecf20Sopenharmony_ci if (ld->ops->poll) 21978c2ecf20Sopenharmony_ci ret = ld->ops->poll(tty, filp, wait); 21988c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 21998c2ecf20Sopenharmony_ci return ret; 22008c2ecf20Sopenharmony_ci} 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_cistatic int __tty_fasync(int fd, struct file *filp, int on) 22038c2ecf20Sopenharmony_ci{ 22048c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(filp); 22058c2ecf20Sopenharmony_ci unsigned long flags; 22068c2ecf20Sopenharmony_ci int retval = 0; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync")) 22098c2ecf20Sopenharmony_ci goto out; 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci retval = fasync_helper(fd, filp, on, &tty->fasync); 22128c2ecf20Sopenharmony_ci if (retval <= 0) 22138c2ecf20Sopenharmony_ci goto out; 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (on) { 22168c2ecf20Sopenharmony_ci enum pid_type type; 22178c2ecf20Sopenharmony_ci struct pid *pid; 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci spin_lock_irqsave(&tty->ctrl_lock, flags); 22208c2ecf20Sopenharmony_ci if (tty->pgrp) { 22218c2ecf20Sopenharmony_ci pid = tty->pgrp; 22228c2ecf20Sopenharmony_ci type = PIDTYPE_PGID; 22238c2ecf20Sopenharmony_ci } else { 22248c2ecf20Sopenharmony_ci pid = task_pid(current); 22258c2ecf20Sopenharmony_ci type = PIDTYPE_TGID; 22268c2ecf20Sopenharmony_ci } 22278c2ecf20Sopenharmony_ci get_pid(pid); 22288c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tty->ctrl_lock, flags); 22298c2ecf20Sopenharmony_ci __f_setown(filp, pid, type, 0); 22308c2ecf20Sopenharmony_ci put_pid(pid); 22318c2ecf20Sopenharmony_ci retval = 0; 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ciout: 22348c2ecf20Sopenharmony_ci return retval; 22358c2ecf20Sopenharmony_ci} 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_cistatic int tty_fasync(int fd, struct file *filp, int on) 22388c2ecf20Sopenharmony_ci{ 22398c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(filp); 22408c2ecf20Sopenharmony_ci int retval = -ENOTTY; 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci tty_lock(tty); 22438c2ecf20Sopenharmony_ci if (!tty_hung_up_p(filp)) 22448c2ecf20Sopenharmony_ci retval = __tty_fasync(fd, filp, on); 22458c2ecf20Sopenharmony_ci tty_unlock(tty); 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci return retval; 22488c2ecf20Sopenharmony_ci} 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci/** 22518c2ecf20Sopenharmony_ci * tiocsti - fake input character 22528c2ecf20Sopenharmony_ci * @tty: tty to fake input into 22538c2ecf20Sopenharmony_ci * @p: pointer to character 22548c2ecf20Sopenharmony_ci * 22558c2ecf20Sopenharmony_ci * Fake input to a tty device. Does the necessary locking and 22568c2ecf20Sopenharmony_ci * input management. 22578c2ecf20Sopenharmony_ci * 22588c2ecf20Sopenharmony_ci * FIXME: does not honour flow control ?? 22598c2ecf20Sopenharmony_ci * 22608c2ecf20Sopenharmony_ci * Locking: 22618c2ecf20Sopenharmony_ci * Called functions take tty_ldiscs_lock 22628c2ecf20Sopenharmony_ci * current->signal->tty check is safe without locks 22638c2ecf20Sopenharmony_ci */ 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_cistatic int tiocsti(struct tty_struct *tty, char __user *p) 22668c2ecf20Sopenharmony_ci{ 22678c2ecf20Sopenharmony_ci char ch, mbz = 0; 22688c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 22698c2ecf20Sopenharmony_ci 22708c2ecf20Sopenharmony_ci if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN)) 22718c2ecf20Sopenharmony_ci return -EPERM; 22728c2ecf20Sopenharmony_ci if (get_user(ch, p)) 22738c2ecf20Sopenharmony_ci return -EFAULT; 22748c2ecf20Sopenharmony_ci tty_audit_tiocsti(tty, ch); 22758c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 22768c2ecf20Sopenharmony_ci if (!ld) 22778c2ecf20Sopenharmony_ci return -EIO; 22788c2ecf20Sopenharmony_ci tty_buffer_lock_exclusive(tty->port); 22798c2ecf20Sopenharmony_ci if (ld->ops->receive_buf) 22808c2ecf20Sopenharmony_ci ld->ops->receive_buf(tty, &ch, &mbz, 1); 22818c2ecf20Sopenharmony_ci tty_buffer_unlock_exclusive(tty->port); 22828c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 22838c2ecf20Sopenharmony_ci return 0; 22848c2ecf20Sopenharmony_ci} 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci/** 22878c2ecf20Sopenharmony_ci * tiocgwinsz - implement window query ioctl 22888c2ecf20Sopenharmony_ci * @tty: tty 22898c2ecf20Sopenharmony_ci * @arg: user buffer for result 22908c2ecf20Sopenharmony_ci * 22918c2ecf20Sopenharmony_ci * Copies the kernel idea of the window size into the user buffer. 22928c2ecf20Sopenharmony_ci * 22938c2ecf20Sopenharmony_ci * Locking: tty->winsize_mutex is taken to ensure the winsize data 22948c2ecf20Sopenharmony_ci * is consistent. 22958c2ecf20Sopenharmony_ci */ 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_cistatic int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg) 22988c2ecf20Sopenharmony_ci{ 22998c2ecf20Sopenharmony_ci int err; 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci mutex_lock(&tty->winsize_mutex); 23028c2ecf20Sopenharmony_ci err = copy_to_user(arg, &tty->winsize, sizeof(*arg)); 23038c2ecf20Sopenharmony_ci mutex_unlock(&tty->winsize_mutex); 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci return err ? -EFAULT: 0; 23068c2ecf20Sopenharmony_ci} 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_ci/** 23098c2ecf20Sopenharmony_ci * tty_do_resize - resize event 23108c2ecf20Sopenharmony_ci * @tty: tty being resized 23118c2ecf20Sopenharmony_ci * @ws: new dimensions 23128c2ecf20Sopenharmony_ci * 23138c2ecf20Sopenharmony_ci * Update the termios variables and send the necessary signals to 23148c2ecf20Sopenharmony_ci * peform a terminal resize correctly 23158c2ecf20Sopenharmony_ci */ 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ciint tty_do_resize(struct tty_struct *tty, struct winsize *ws) 23188c2ecf20Sopenharmony_ci{ 23198c2ecf20Sopenharmony_ci struct pid *pgrp; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci /* Lock the tty */ 23228c2ecf20Sopenharmony_ci mutex_lock(&tty->winsize_mutex); 23238c2ecf20Sopenharmony_ci if (!memcmp(ws, &tty->winsize, sizeof(*ws))) 23248c2ecf20Sopenharmony_ci goto done; 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci /* Signal the foreground process group */ 23278c2ecf20Sopenharmony_ci pgrp = tty_get_pgrp(tty); 23288c2ecf20Sopenharmony_ci if (pgrp) 23298c2ecf20Sopenharmony_ci kill_pgrp(pgrp, SIGWINCH, 1); 23308c2ecf20Sopenharmony_ci put_pid(pgrp); 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_ci tty->winsize = *ws; 23338c2ecf20Sopenharmony_cidone: 23348c2ecf20Sopenharmony_ci mutex_unlock(&tty->winsize_mutex); 23358c2ecf20Sopenharmony_ci return 0; 23368c2ecf20Sopenharmony_ci} 23378c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_do_resize); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci/** 23408c2ecf20Sopenharmony_ci * tiocswinsz - implement window size set ioctl 23418c2ecf20Sopenharmony_ci * @tty: tty side of tty 23428c2ecf20Sopenharmony_ci * @arg: user buffer for result 23438c2ecf20Sopenharmony_ci * 23448c2ecf20Sopenharmony_ci * Copies the user idea of the window size to the kernel. Traditionally 23458c2ecf20Sopenharmony_ci * this is just advisory information but for the Linux console it 23468c2ecf20Sopenharmony_ci * actually has driver level meaning and triggers a VC resize. 23478c2ecf20Sopenharmony_ci * 23488c2ecf20Sopenharmony_ci * Locking: 23498c2ecf20Sopenharmony_ci * Driver dependent. The default do_resize method takes the 23508c2ecf20Sopenharmony_ci * tty termios mutex and ctrl_lock. The console takes its own lock 23518c2ecf20Sopenharmony_ci * then calls into the default method. 23528c2ecf20Sopenharmony_ci */ 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_cistatic int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg) 23558c2ecf20Sopenharmony_ci{ 23568c2ecf20Sopenharmony_ci struct winsize tmp_ws; 23578c2ecf20Sopenharmony_ci if (copy_from_user(&tmp_ws, arg, sizeof(*arg))) 23588c2ecf20Sopenharmony_ci return -EFAULT; 23598c2ecf20Sopenharmony_ci 23608c2ecf20Sopenharmony_ci if (tty->ops->resize) 23618c2ecf20Sopenharmony_ci return tty->ops->resize(tty, &tmp_ws); 23628c2ecf20Sopenharmony_ci else 23638c2ecf20Sopenharmony_ci return tty_do_resize(tty, &tmp_ws); 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci/** 23678c2ecf20Sopenharmony_ci * tioccons - allow admin to move logical console 23688c2ecf20Sopenharmony_ci * @file: the file to become console 23698c2ecf20Sopenharmony_ci * 23708c2ecf20Sopenharmony_ci * Allow the administrator to move the redirected console device 23718c2ecf20Sopenharmony_ci * 23728c2ecf20Sopenharmony_ci * Locking: uses redirect_lock to guard the redirect information 23738c2ecf20Sopenharmony_ci */ 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_cistatic int tioccons(struct file *file) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 23788c2ecf20Sopenharmony_ci return -EPERM; 23798c2ecf20Sopenharmony_ci if (file->f_op->write_iter == redirected_tty_write) { 23808c2ecf20Sopenharmony_ci struct file *f; 23818c2ecf20Sopenharmony_ci spin_lock(&redirect_lock); 23828c2ecf20Sopenharmony_ci f = redirect; 23838c2ecf20Sopenharmony_ci redirect = NULL; 23848c2ecf20Sopenharmony_ci spin_unlock(&redirect_lock); 23858c2ecf20Sopenharmony_ci if (f) 23868c2ecf20Sopenharmony_ci fput(f); 23878c2ecf20Sopenharmony_ci return 0; 23888c2ecf20Sopenharmony_ci } 23898c2ecf20Sopenharmony_ci if (file->f_op->write_iter != tty_write) 23908c2ecf20Sopenharmony_ci return -ENOTTY; 23918c2ecf20Sopenharmony_ci if (!(file->f_mode & FMODE_WRITE)) 23928c2ecf20Sopenharmony_ci return -EBADF; 23938c2ecf20Sopenharmony_ci if (!(file->f_mode & FMODE_CAN_WRITE)) 23948c2ecf20Sopenharmony_ci return -EINVAL; 23958c2ecf20Sopenharmony_ci spin_lock(&redirect_lock); 23968c2ecf20Sopenharmony_ci if (redirect) { 23978c2ecf20Sopenharmony_ci spin_unlock(&redirect_lock); 23988c2ecf20Sopenharmony_ci return -EBUSY; 23998c2ecf20Sopenharmony_ci } 24008c2ecf20Sopenharmony_ci redirect = get_file(file); 24018c2ecf20Sopenharmony_ci spin_unlock(&redirect_lock); 24028c2ecf20Sopenharmony_ci return 0; 24038c2ecf20Sopenharmony_ci} 24048c2ecf20Sopenharmony_ci 24058c2ecf20Sopenharmony_ci/** 24068c2ecf20Sopenharmony_ci * tiocsetd - set line discipline 24078c2ecf20Sopenharmony_ci * @tty: tty device 24088c2ecf20Sopenharmony_ci * @p: pointer to user data 24098c2ecf20Sopenharmony_ci * 24108c2ecf20Sopenharmony_ci * Set the line discipline according to user request. 24118c2ecf20Sopenharmony_ci * 24128c2ecf20Sopenharmony_ci * Locking: see tty_set_ldisc, this function is just a helper 24138c2ecf20Sopenharmony_ci */ 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_cistatic int tiocsetd(struct tty_struct *tty, int __user *p) 24168c2ecf20Sopenharmony_ci{ 24178c2ecf20Sopenharmony_ci int disc; 24188c2ecf20Sopenharmony_ci int ret; 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci if (get_user(disc, p)) 24218c2ecf20Sopenharmony_ci return -EFAULT; 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci ret = tty_set_ldisc(tty, disc); 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci return ret; 24268c2ecf20Sopenharmony_ci} 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_ci/** 24298c2ecf20Sopenharmony_ci * tiocgetd - get line discipline 24308c2ecf20Sopenharmony_ci * @tty: tty device 24318c2ecf20Sopenharmony_ci * @p: pointer to user data 24328c2ecf20Sopenharmony_ci * 24338c2ecf20Sopenharmony_ci * Retrieves the line discipline id directly from the ldisc. 24348c2ecf20Sopenharmony_ci * 24358c2ecf20Sopenharmony_ci * Locking: waits for ldisc reference (in case the line discipline 24368c2ecf20Sopenharmony_ci * is changing or the tty is being hungup) 24378c2ecf20Sopenharmony_ci */ 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_cistatic int tiocgetd(struct tty_struct *tty, int __user *p) 24408c2ecf20Sopenharmony_ci{ 24418c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 24428c2ecf20Sopenharmony_ci int ret; 24438c2ecf20Sopenharmony_ci 24448c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 24458c2ecf20Sopenharmony_ci if (!ld) 24468c2ecf20Sopenharmony_ci return -EIO; 24478c2ecf20Sopenharmony_ci ret = put_user(ld->ops->num, p); 24488c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 24498c2ecf20Sopenharmony_ci return ret; 24508c2ecf20Sopenharmony_ci} 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci/** 24538c2ecf20Sopenharmony_ci * send_break - performed time break 24548c2ecf20Sopenharmony_ci * @tty: device to break on 24558c2ecf20Sopenharmony_ci * @duration: timeout in mS 24568c2ecf20Sopenharmony_ci * 24578c2ecf20Sopenharmony_ci * Perform a timed break on hardware that lacks its own driver level 24588c2ecf20Sopenharmony_ci * timed break functionality. 24598c2ecf20Sopenharmony_ci * 24608c2ecf20Sopenharmony_ci * Locking: 24618c2ecf20Sopenharmony_ci * atomic_write_lock serializes 24628c2ecf20Sopenharmony_ci * 24638c2ecf20Sopenharmony_ci */ 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_cistatic int send_break(struct tty_struct *tty, unsigned int duration) 24668c2ecf20Sopenharmony_ci{ 24678c2ecf20Sopenharmony_ci int retval; 24688c2ecf20Sopenharmony_ci 24698c2ecf20Sopenharmony_ci if (tty->ops->break_ctl == NULL) 24708c2ecf20Sopenharmony_ci return 0; 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) 24738c2ecf20Sopenharmony_ci return tty->ops->break_ctl(tty, duration); 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci /* Do the work ourselves */ 24768c2ecf20Sopenharmony_ci if (tty_write_lock(tty, false) < 0) 24778c2ecf20Sopenharmony_ci return -EINTR; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci retval = tty->ops->break_ctl(tty, -1); 24808c2ecf20Sopenharmony_ci if (!retval) { 24818c2ecf20Sopenharmony_ci msleep_interruptible(duration); 24828c2ecf20Sopenharmony_ci retval = tty->ops->break_ctl(tty, 0); 24838c2ecf20Sopenharmony_ci } else if (retval == -EOPNOTSUPP) { 24848c2ecf20Sopenharmony_ci /* some drivers can tell only dynamically */ 24858c2ecf20Sopenharmony_ci retval = 0; 24868c2ecf20Sopenharmony_ci } 24878c2ecf20Sopenharmony_ci tty_write_unlock(tty); 24888c2ecf20Sopenharmony_ci 24898c2ecf20Sopenharmony_ci if (signal_pending(current)) 24908c2ecf20Sopenharmony_ci retval = -EINTR; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci return retval; 24938c2ecf20Sopenharmony_ci} 24948c2ecf20Sopenharmony_ci 24958c2ecf20Sopenharmony_ci/** 24968c2ecf20Sopenharmony_ci * tty_tiocmget - get modem status 24978c2ecf20Sopenharmony_ci * @tty: tty device 24988c2ecf20Sopenharmony_ci * @p: pointer to result 24998c2ecf20Sopenharmony_ci * 25008c2ecf20Sopenharmony_ci * Obtain the modem status bits from the tty driver if the feature 25018c2ecf20Sopenharmony_ci * is supported. Return -ENOTTY if it is not available. 25028c2ecf20Sopenharmony_ci * 25038c2ecf20Sopenharmony_ci * Locking: none (up to the driver) 25048c2ecf20Sopenharmony_ci */ 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_cistatic int tty_tiocmget(struct tty_struct *tty, int __user *p) 25078c2ecf20Sopenharmony_ci{ 25088c2ecf20Sopenharmony_ci int retval = -ENOTTY; 25098c2ecf20Sopenharmony_ci 25108c2ecf20Sopenharmony_ci if (tty->ops->tiocmget) { 25118c2ecf20Sopenharmony_ci retval = tty->ops->tiocmget(tty); 25128c2ecf20Sopenharmony_ci 25138c2ecf20Sopenharmony_ci if (retval >= 0) 25148c2ecf20Sopenharmony_ci retval = put_user(retval, p); 25158c2ecf20Sopenharmony_ci } 25168c2ecf20Sopenharmony_ci return retval; 25178c2ecf20Sopenharmony_ci} 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci/** 25208c2ecf20Sopenharmony_ci * tty_tiocmset - set modem status 25218c2ecf20Sopenharmony_ci * @tty: tty device 25228c2ecf20Sopenharmony_ci * @cmd: command - clear bits, set bits or set all 25238c2ecf20Sopenharmony_ci * @p: pointer to desired bits 25248c2ecf20Sopenharmony_ci * 25258c2ecf20Sopenharmony_ci * Set the modem status bits from the tty driver if the feature 25268c2ecf20Sopenharmony_ci * is supported. Return -ENOTTY if it is not available. 25278c2ecf20Sopenharmony_ci * 25288c2ecf20Sopenharmony_ci * Locking: none (up to the driver) 25298c2ecf20Sopenharmony_ci */ 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_cistatic int tty_tiocmset(struct tty_struct *tty, unsigned int cmd, 25328c2ecf20Sopenharmony_ci unsigned __user *p) 25338c2ecf20Sopenharmony_ci{ 25348c2ecf20Sopenharmony_ci int retval; 25358c2ecf20Sopenharmony_ci unsigned int set, clear, val; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci if (tty->ops->tiocmset == NULL) 25388c2ecf20Sopenharmony_ci return -ENOTTY; 25398c2ecf20Sopenharmony_ci 25408c2ecf20Sopenharmony_ci retval = get_user(val, p); 25418c2ecf20Sopenharmony_ci if (retval) 25428c2ecf20Sopenharmony_ci return retval; 25438c2ecf20Sopenharmony_ci set = clear = 0; 25448c2ecf20Sopenharmony_ci switch (cmd) { 25458c2ecf20Sopenharmony_ci case TIOCMBIS: 25468c2ecf20Sopenharmony_ci set = val; 25478c2ecf20Sopenharmony_ci break; 25488c2ecf20Sopenharmony_ci case TIOCMBIC: 25498c2ecf20Sopenharmony_ci clear = val; 25508c2ecf20Sopenharmony_ci break; 25518c2ecf20Sopenharmony_ci case TIOCMSET: 25528c2ecf20Sopenharmony_ci set = val; 25538c2ecf20Sopenharmony_ci clear = ~val; 25548c2ecf20Sopenharmony_ci break; 25558c2ecf20Sopenharmony_ci } 25568c2ecf20Sopenharmony_ci set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; 25578c2ecf20Sopenharmony_ci clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; 25588c2ecf20Sopenharmony_ci return tty->ops->tiocmset(tty, set, clear); 25598c2ecf20Sopenharmony_ci} 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_cistatic int tty_tiocgicount(struct tty_struct *tty, void __user *arg) 25628c2ecf20Sopenharmony_ci{ 25638c2ecf20Sopenharmony_ci int retval = -EINVAL; 25648c2ecf20Sopenharmony_ci struct serial_icounter_struct icount; 25658c2ecf20Sopenharmony_ci memset(&icount, 0, sizeof(icount)); 25668c2ecf20Sopenharmony_ci if (tty->ops->get_icount) 25678c2ecf20Sopenharmony_ci retval = tty->ops->get_icount(tty, &icount); 25688c2ecf20Sopenharmony_ci if (retval != 0) 25698c2ecf20Sopenharmony_ci return retval; 25708c2ecf20Sopenharmony_ci if (copy_to_user(arg, &icount, sizeof(icount))) 25718c2ecf20Sopenharmony_ci return -EFAULT; 25728c2ecf20Sopenharmony_ci return 0; 25738c2ecf20Sopenharmony_ci} 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_cistatic int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss) 25768c2ecf20Sopenharmony_ci{ 25778c2ecf20Sopenharmony_ci static DEFINE_RATELIMIT_STATE(depr_flags, 25788c2ecf20Sopenharmony_ci DEFAULT_RATELIMIT_INTERVAL, 25798c2ecf20Sopenharmony_ci DEFAULT_RATELIMIT_BURST); 25808c2ecf20Sopenharmony_ci char comm[TASK_COMM_LEN]; 25818c2ecf20Sopenharmony_ci struct serial_struct v; 25828c2ecf20Sopenharmony_ci int flags; 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci if (copy_from_user(&v, ss, sizeof(*ss))) 25858c2ecf20Sopenharmony_ci return -EFAULT; 25868c2ecf20Sopenharmony_ci 25878c2ecf20Sopenharmony_ci flags = v.flags & ASYNC_DEPRECATED; 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci if (flags && __ratelimit(&depr_flags)) 25908c2ecf20Sopenharmony_ci pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", 25918c2ecf20Sopenharmony_ci __func__, get_task_comm(comm, current), flags); 25928c2ecf20Sopenharmony_ci if (!tty->ops->set_serial) 25938c2ecf20Sopenharmony_ci return -ENOTTY; 25948c2ecf20Sopenharmony_ci return tty->ops->set_serial(tty, &v); 25958c2ecf20Sopenharmony_ci} 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_cistatic int tty_tiocgserial(struct tty_struct *tty, struct serial_struct __user *ss) 25988c2ecf20Sopenharmony_ci{ 25998c2ecf20Sopenharmony_ci struct serial_struct v; 26008c2ecf20Sopenharmony_ci int err; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 26038c2ecf20Sopenharmony_ci if (!tty->ops->get_serial) 26048c2ecf20Sopenharmony_ci return -ENOTTY; 26058c2ecf20Sopenharmony_ci err = tty->ops->get_serial(tty, &v); 26068c2ecf20Sopenharmony_ci if (!err && copy_to_user(ss, &v, sizeof(v))) 26078c2ecf20Sopenharmony_ci err = -EFAULT; 26088c2ecf20Sopenharmony_ci return err; 26098c2ecf20Sopenharmony_ci} 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci/* 26128c2ecf20Sopenharmony_ci * if pty, return the slave side (real_tty) 26138c2ecf20Sopenharmony_ci * otherwise, return self 26148c2ecf20Sopenharmony_ci */ 26158c2ecf20Sopenharmony_cistatic struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) 26168c2ecf20Sopenharmony_ci{ 26178c2ecf20Sopenharmony_ci if (tty->driver->type == TTY_DRIVER_TYPE_PTY && 26188c2ecf20Sopenharmony_ci tty->driver->subtype == PTY_TYPE_MASTER) 26198c2ecf20Sopenharmony_ci tty = tty->link; 26208c2ecf20Sopenharmony_ci return tty; 26218c2ecf20Sopenharmony_ci} 26228c2ecf20Sopenharmony_ci 26238c2ecf20Sopenharmony_ci/* 26248c2ecf20Sopenharmony_ci * Split this up, as gcc can choke on it otherwise.. 26258c2ecf20Sopenharmony_ci */ 26268c2ecf20Sopenharmony_cilong tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 26278c2ecf20Sopenharmony_ci{ 26288c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(file); 26298c2ecf20Sopenharmony_ci struct tty_struct *real_tty; 26308c2ecf20Sopenharmony_ci void __user *p = (void __user *)arg; 26318c2ecf20Sopenharmony_ci int retval; 26328c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 26338c2ecf20Sopenharmony_ci 26348c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl")) 26358c2ecf20Sopenharmony_ci return -EINVAL; 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci real_tty = tty_pair_get_tty(tty); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci /* 26408c2ecf20Sopenharmony_ci * Factor out some common prep work 26418c2ecf20Sopenharmony_ci */ 26428c2ecf20Sopenharmony_ci switch (cmd) { 26438c2ecf20Sopenharmony_ci case TIOCSETD: 26448c2ecf20Sopenharmony_ci case TIOCSBRK: 26458c2ecf20Sopenharmony_ci case TIOCCBRK: 26468c2ecf20Sopenharmony_ci case TCSBRK: 26478c2ecf20Sopenharmony_ci case TCSBRKP: 26488c2ecf20Sopenharmony_ci retval = tty_check_change(tty); 26498c2ecf20Sopenharmony_ci if (retval) 26508c2ecf20Sopenharmony_ci return retval; 26518c2ecf20Sopenharmony_ci if (cmd != TIOCCBRK) { 26528c2ecf20Sopenharmony_ci tty_wait_until_sent(tty, 0); 26538c2ecf20Sopenharmony_ci if (signal_pending(current)) 26548c2ecf20Sopenharmony_ci return -EINTR; 26558c2ecf20Sopenharmony_ci } 26568c2ecf20Sopenharmony_ci break; 26578c2ecf20Sopenharmony_ci } 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci /* 26608c2ecf20Sopenharmony_ci * Now do the stuff. 26618c2ecf20Sopenharmony_ci */ 26628c2ecf20Sopenharmony_ci switch (cmd) { 26638c2ecf20Sopenharmony_ci case TIOCSTI: 26648c2ecf20Sopenharmony_ci return tiocsti(tty, p); 26658c2ecf20Sopenharmony_ci case TIOCGWINSZ: 26668c2ecf20Sopenharmony_ci return tiocgwinsz(real_tty, p); 26678c2ecf20Sopenharmony_ci case TIOCSWINSZ: 26688c2ecf20Sopenharmony_ci return tiocswinsz(real_tty, p); 26698c2ecf20Sopenharmony_ci case TIOCCONS: 26708c2ecf20Sopenharmony_ci return real_tty != tty ? -EINVAL : tioccons(file); 26718c2ecf20Sopenharmony_ci case TIOCEXCL: 26728c2ecf20Sopenharmony_ci set_bit(TTY_EXCLUSIVE, &tty->flags); 26738c2ecf20Sopenharmony_ci return 0; 26748c2ecf20Sopenharmony_ci case TIOCNXCL: 26758c2ecf20Sopenharmony_ci clear_bit(TTY_EXCLUSIVE, &tty->flags); 26768c2ecf20Sopenharmony_ci return 0; 26778c2ecf20Sopenharmony_ci case TIOCGEXCL: 26788c2ecf20Sopenharmony_ci { 26798c2ecf20Sopenharmony_ci int excl = test_bit(TTY_EXCLUSIVE, &tty->flags); 26808c2ecf20Sopenharmony_ci return put_user(excl, (int __user *)p); 26818c2ecf20Sopenharmony_ci } 26828c2ecf20Sopenharmony_ci case TIOCGETD: 26838c2ecf20Sopenharmony_ci return tiocgetd(tty, p); 26848c2ecf20Sopenharmony_ci case TIOCSETD: 26858c2ecf20Sopenharmony_ci return tiocsetd(tty, p); 26868c2ecf20Sopenharmony_ci case TIOCVHANGUP: 26878c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 26888c2ecf20Sopenharmony_ci return -EPERM; 26898c2ecf20Sopenharmony_ci tty_vhangup(tty); 26908c2ecf20Sopenharmony_ci return 0; 26918c2ecf20Sopenharmony_ci case TIOCGDEV: 26928c2ecf20Sopenharmony_ci { 26938c2ecf20Sopenharmony_ci unsigned int ret = new_encode_dev(tty_devnum(real_tty)); 26948c2ecf20Sopenharmony_ci return put_user(ret, (unsigned int __user *)p); 26958c2ecf20Sopenharmony_ci } 26968c2ecf20Sopenharmony_ci /* 26978c2ecf20Sopenharmony_ci * Break handling 26988c2ecf20Sopenharmony_ci */ 26998c2ecf20Sopenharmony_ci case TIOCSBRK: /* Turn break on, unconditionally */ 27008c2ecf20Sopenharmony_ci if (tty->ops->break_ctl) 27018c2ecf20Sopenharmony_ci return tty->ops->break_ctl(tty, -1); 27028c2ecf20Sopenharmony_ci return 0; 27038c2ecf20Sopenharmony_ci case TIOCCBRK: /* Turn break off, unconditionally */ 27048c2ecf20Sopenharmony_ci if (tty->ops->break_ctl) 27058c2ecf20Sopenharmony_ci return tty->ops->break_ctl(tty, 0); 27068c2ecf20Sopenharmony_ci return 0; 27078c2ecf20Sopenharmony_ci case TCSBRK: /* SVID version: non-zero arg --> no break */ 27088c2ecf20Sopenharmony_ci /* non-zero arg means wait for all output data 27098c2ecf20Sopenharmony_ci * to be sent (performed above) but don't send break. 27108c2ecf20Sopenharmony_ci * This is used by the tcdrain() termios function. 27118c2ecf20Sopenharmony_ci */ 27128c2ecf20Sopenharmony_ci if (!arg) 27138c2ecf20Sopenharmony_ci return send_break(tty, 250); 27148c2ecf20Sopenharmony_ci return 0; 27158c2ecf20Sopenharmony_ci case TCSBRKP: /* support for POSIX tcsendbreak() */ 27168c2ecf20Sopenharmony_ci return send_break(tty, arg ? arg*100 : 250); 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci case TIOCMGET: 27198c2ecf20Sopenharmony_ci return tty_tiocmget(tty, p); 27208c2ecf20Sopenharmony_ci case TIOCMSET: 27218c2ecf20Sopenharmony_ci case TIOCMBIC: 27228c2ecf20Sopenharmony_ci case TIOCMBIS: 27238c2ecf20Sopenharmony_ci return tty_tiocmset(tty, cmd, p); 27248c2ecf20Sopenharmony_ci case TIOCGICOUNT: 27258c2ecf20Sopenharmony_ci return tty_tiocgicount(tty, p); 27268c2ecf20Sopenharmony_ci case TCFLSH: 27278c2ecf20Sopenharmony_ci switch (arg) { 27288c2ecf20Sopenharmony_ci case TCIFLUSH: 27298c2ecf20Sopenharmony_ci case TCIOFLUSH: 27308c2ecf20Sopenharmony_ci /* flush tty buffer and allow ldisc to process ioctl */ 27318c2ecf20Sopenharmony_ci tty_buffer_flush(tty, NULL); 27328c2ecf20Sopenharmony_ci break; 27338c2ecf20Sopenharmony_ci } 27348c2ecf20Sopenharmony_ci break; 27358c2ecf20Sopenharmony_ci case TIOCSSERIAL: 27368c2ecf20Sopenharmony_ci return tty_tiocsserial(tty, p); 27378c2ecf20Sopenharmony_ci case TIOCGSERIAL: 27388c2ecf20Sopenharmony_ci return tty_tiocgserial(tty, p); 27398c2ecf20Sopenharmony_ci case TIOCGPTPEER: 27408c2ecf20Sopenharmony_ci /* Special because the struct file is needed */ 27418c2ecf20Sopenharmony_ci return ptm_open_peer(file, tty, (int)arg); 27428c2ecf20Sopenharmony_ci default: 27438c2ecf20Sopenharmony_ci retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg); 27448c2ecf20Sopenharmony_ci if (retval != -ENOIOCTLCMD) 27458c2ecf20Sopenharmony_ci return retval; 27468c2ecf20Sopenharmony_ci } 27478c2ecf20Sopenharmony_ci if (tty->ops->ioctl) { 27488c2ecf20Sopenharmony_ci retval = tty->ops->ioctl(tty, cmd, arg); 27498c2ecf20Sopenharmony_ci if (retval != -ENOIOCTLCMD) 27508c2ecf20Sopenharmony_ci return retval; 27518c2ecf20Sopenharmony_ci } 27528c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 27538c2ecf20Sopenharmony_ci if (!ld) 27548c2ecf20Sopenharmony_ci return hung_up_tty_ioctl(file, cmd, arg); 27558c2ecf20Sopenharmony_ci retval = -EINVAL; 27568c2ecf20Sopenharmony_ci if (ld->ops->ioctl) { 27578c2ecf20Sopenharmony_ci retval = ld->ops->ioctl(tty, file, cmd, arg); 27588c2ecf20Sopenharmony_ci if (retval == -ENOIOCTLCMD) 27598c2ecf20Sopenharmony_ci retval = -ENOTTY; 27608c2ecf20Sopenharmony_ci } 27618c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 27628c2ecf20Sopenharmony_ci return retval; 27638c2ecf20Sopenharmony_ci} 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 27668c2ecf20Sopenharmony_ci 27678c2ecf20Sopenharmony_cistruct serial_struct32 { 27688c2ecf20Sopenharmony_ci compat_int_t type; 27698c2ecf20Sopenharmony_ci compat_int_t line; 27708c2ecf20Sopenharmony_ci compat_uint_t port; 27718c2ecf20Sopenharmony_ci compat_int_t irq; 27728c2ecf20Sopenharmony_ci compat_int_t flags; 27738c2ecf20Sopenharmony_ci compat_int_t xmit_fifo_size; 27748c2ecf20Sopenharmony_ci compat_int_t custom_divisor; 27758c2ecf20Sopenharmony_ci compat_int_t baud_base; 27768c2ecf20Sopenharmony_ci unsigned short close_delay; 27778c2ecf20Sopenharmony_ci char io_type; 27788c2ecf20Sopenharmony_ci char reserved_char; 27798c2ecf20Sopenharmony_ci compat_int_t hub6; 27808c2ecf20Sopenharmony_ci unsigned short closing_wait; /* time to wait before closing */ 27818c2ecf20Sopenharmony_ci unsigned short closing_wait2; /* no longer used... */ 27828c2ecf20Sopenharmony_ci compat_uint_t iomem_base; 27838c2ecf20Sopenharmony_ci unsigned short iomem_reg_shift; 27848c2ecf20Sopenharmony_ci unsigned int port_high; 27858c2ecf20Sopenharmony_ci /* compat_ulong_t iomap_base FIXME */ 27868c2ecf20Sopenharmony_ci compat_int_t reserved; 27878c2ecf20Sopenharmony_ci}; 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_cistatic int compat_tty_tiocsserial(struct tty_struct *tty, 27908c2ecf20Sopenharmony_ci struct serial_struct32 __user *ss) 27918c2ecf20Sopenharmony_ci{ 27928c2ecf20Sopenharmony_ci static DEFINE_RATELIMIT_STATE(depr_flags, 27938c2ecf20Sopenharmony_ci DEFAULT_RATELIMIT_INTERVAL, 27948c2ecf20Sopenharmony_ci DEFAULT_RATELIMIT_BURST); 27958c2ecf20Sopenharmony_ci char comm[TASK_COMM_LEN]; 27968c2ecf20Sopenharmony_ci struct serial_struct32 v32; 27978c2ecf20Sopenharmony_ci struct serial_struct v; 27988c2ecf20Sopenharmony_ci int flags; 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci if (copy_from_user(&v32, ss, sizeof(*ss))) 28018c2ecf20Sopenharmony_ci return -EFAULT; 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci memcpy(&v, &v32, offsetof(struct serial_struct32, iomem_base)); 28048c2ecf20Sopenharmony_ci v.iomem_base = compat_ptr(v32.iomem_base); 28058c2ecf20Sopenharmony_ci v.iomem_reg_shift = v32.iomem_reg_shift; 28068c2ecf20Sopenharmony_ci v.port_high = v32.port_high; 28078c2ecf20Sopenharmony_ci v.iomap_base = 0; 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_ci flags = v.flags & ASYNC_DEPRECATED; 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci if (flags && __ratelimit(&depr_flags)) 28128c2ecf20Sopenharmony_ci pr_warn("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n", 28138c2ecf20Sopenharmony_ci __func__, get_task_comm(comm, current), flags); 28148c2ecf20Sopenharmony_ci if (!tty->ops->set_serial) 28158c2ecf20Sopenharmony_ci return -ENOTTY; 28168c2ecf20Sopenharmony_ci return tty->ops->set_serial(tty, &v); 28178c2ecf20Sopenharmony_ci} 28188c2ecf20Sopenharmony_ci 28198c2ecf20Sopenharmony_cistatic int compat_tty_tiocgserial(struct tty_struct *tty, 28208c2ecf20Sopenharmony_ci struct serial_struct32 __user *ss) 28218c2ecf20Sopenharmony_ci{ 28228c2ecf20Sopenharmony_ci struct serial_struct32 v32; 28238c2ecf20Sopenharmony_ci struct serial_struct v; 28248c2ecf20Sopenharmony_ci int err; 28258c2ecf20Sopenharmony_ci 28268c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 28278c2ecf20Sopenharmony_ci memset(&v32, 0, sizeof(v32)); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci if (!tty->ops->get_serial) 28308c2ecf20Sopenharmony_ci return -ENOTTY; 28318c2ecf20Sopenharmony_ci err = tty->ops->get_serial(tty, &v); 28328c2ecf20Sopenharmony_ci if (!err) { 28338c2ecf20Sopenharmony_ci memcpy(&v32, &v, offsetof(struct serial_struct32, iomem_base)); 28348c2ecf20Sopenharmony_ci v32.iomem_base = (unsigned long)v.iomem_base >> 32 ? 28358c2ecf20Sopenharmony_ci 0xfffffff : ptr_to_compat(v.iomem_base); 28368c2ecf20Sopenharmony_ci v32.iomem_reg_shift = v.iomem_reg_shift; 28378c2ecf20Sopenharmony_ci v32.port_high = v.port_high; 28388c2ecf20Sopenharmony_ci if (copy_to_user(ss, &v32, sizeof(v32))) 28398c2ecf20Sopenharmony_ci err = -EFAULT; 28408c2ecf20Sopenharmony_ci } 28418c2ecf20Sopenharmony_ci return err; 28428c2ecf20Sopenharmony_ci} 28438c2ecf20Sopenharmony_cistatic long tty_compat_ioctl(struct file *file, unsigned int cmd, 28448c2ecf20Sopenharmony_ci unsigned long arg) 28458c2ecf20Sopenharmony_ci{ 28468c2ecf20Sopenharmony_ci struct tty_struct *tty = file_tty(file); 28478c2ecf20Sopenharmony_ci struct tty_ldisc *ld; 28488c2ecf20Sopenharmony_ci int retval = -ENOIOCTLCMD; 28498c2ecf20Sopenharmony_ci 28508c2ecf20Sopenharmony_ci switch (cmd) { 28518c2ecf20Sopenharmony_ci case TIOCOUTQ: 28528c2ecf20Sopenharmony_ci case TIOCSTI: 28538c2ecf20Sopenharmony_ci case TIOCGWINSZ: 28548c2ecf20Sopenharmony_ci case TIOCSWINSZ: 28558c2ecf20Sopenharmony_ci case TIOCGEXCL: 28568c2ecf20Sopenharmony_ci case TIOCGETD: 28578c2ecf20Sopenharmony_ci case TIOCSETD: 28588c2ecf20Sopenharmony_ci case TIOCGDEV: 28598c2ecf20Sopenharmony_ci case TIOCMGET: 28608c2ecf20Sopenharmony_ci case TIOCMSET: 28618c2ecf20Sopenharmony_ci case TIOCMBIC: 28628c2ecf20Sopenharmony_ci case TIOCMBIS: 28638c2ecf20Sopenharmony_ci case TIOCGICOUNT: 28648c2ecf20Sopenharmony_ci case TIOCGPGRP: 28658c2ecf20Sopenharmony_ci case TIOCSPGRP: 28668c2ecf20Sopenharmony_ci case TIOCGSID: 28678c2ecf20Sopenharmony_ci case TIOCSERGETLSR: 28688c2ecf20Sopenharmony_ci case TIOCGRS485: 28698c2ecf20Sopenharmony_ci case TIOCSRS485: 28708c2ecf20Sopenharmony_ci#ifdef TIOCGETP 28718c2ecf20Sopenharmony_ci case TIOCGETP: 28728c2ecf20Sopenharmony_ci case TIOCSETP: 28738c2ecf20Sopenharmony_ci case TIOCSETN: 28748c2ecf20Sopenharmony_ci#endif 28758c2ecf20Sopenharmony_ci#ifdef TIOCGETC 28768c2ecf20Sopenharmony_ci case TIOCGETC: 28778c2ecf20Sopenharmony_ci case TIOCSETC: 28788c2ecf20Sopenharmony_ci#endif 28798c2ecf20Sopenharmony_ci#ifdef TIOCGLTC 28808c2ecf20Sopenharmony_ci case TIOCGLTC: 28818c2ecf20Sopenharmony_ci case TIOCSLTC: 28828c2ecf20Sopenharmony_ci#endif 28838c2ecf20Sopenharmony_ci case TCSETSF: 28848c2ecf20Sopenharmony_ci case TCSETSW: 28858c2ecf20Sopenharmony_ci case TCSETS: 28868c2ecf20Sopenharmony_ci case TCGETS: 28878c2ecf20Sopenharmony_ci#ifdef TCGETS2 28888c2ecf20Sopenharmony_ci case TCGETS2: 28898c2ecf20Sopenharmony_ci case TCSETSF2: 28908c2ecf20Sopenharmony_ci case TCSETSW2: 28918c2ecf20Sopenharmony_ci case TCSETS2: 28928c2ecf20Sopenharmony_ci#endif 28938c2ecf20Sopenharmony_ci case TCGETA: 28948c2ecf20Sopenharmony_ci case TCSETAF: 28958c2ecf20Sopenharmony_ci case TCSETAW: 28968c2ecf20Sopenharmony_ci case TCSETA: 28978c2ecf20Sopenharmony_ci case TIOCGLCKTRMIOS: 28988c2ecf20Sopenharmony_ci case TIOCSLCKTRMIOS: 28998c2ecf20Sopenharmony_ci#ifdef TCGETX 29008c2ecf20Sopenharmony_ci case TCGETX: 29018c2ecf20Sopenharmony_ci case TCSETX: 29028c2ecf20Sopenharmony_ci case TCSETXW: 29038c2ecf20Sopenharmony_ci case TCSETXF: 29048c2ecf20Sopenharmony_ci#endif 29058c2ecf20Sopenharmony_ci case TIOCGSOFTCAR: 29068c2ecf20Sopenharmony_ci case TIOCSSOFTCAR: 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci case PPPIOCGCHAN: 29098c2ecf20Sopenharmony_ci case PPPIOCGUNIT: 29108c2ecf20Sopenharmony_ci return tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); 29118c2ecf20Sopenharmony_ci case TIOCCONS: 29128c2ecf20Sopenharmony_ci case TIOCEXCL: 29138c2ecf20Sopenharmony_ci case TIOCNXCL: 29148c2ecf20Sopenharmony_ci case TIOCVHANGUP: 29158c2ecf20Sopenharmony_ci case TIOCSBRK: 29168c2ecf20Sopenharmony_ci case TIOCCBRK: 29178c2ecf20Sopenharmony_ci case TCSBRK: 29188c2ecf20Sopenharmony_ci case TCSBRKP: 29198c2ecf20Sopenharmony_ci case TCFLSH: 29208c2ecf20Sopenharmony_ci case TIOCGPTPEER: 29218c2ecf20Sopenharmony_ci case TIOCNOTTY: 29228c2ecf20Sopenharmony_ci case TIOCSCTTY: 29238c2ecf20Sopenharmony_ci case TCXONC: 29248c2ecf20Sopenharmony_ci case TIOCMIWAIT: 29258c2ecf20Sopenharmony_ci case TIOCSERCONFIG: 29268c2ecf20Sopenharmony_ci return tty_ioctl(file, cmd, arg); 29278c2ecf20Sopenharmony_ci } 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_ci if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl")) 29308c2ecf20Sopenharmony_ci return -EINVAL; 29318c2ecf20Sopenharmony_ci 29328c2ecf20Sopenharmony_ci switch (cmd) { 29338c2ecf20Sopenharmony_ci case TIOCSSERIAL: 29348c2ecf20Sopenharmony_ci return compat_tty_tiocsserial(tty, compat_ptr(arg)); 29358c2ecf20Sopenharmony_ci case TIOCGSERIAL: 29368c2ecf20Sopenharmony_ci return compat_tty_tiocgserial(tty, compat_ptr(arg)); 29378c2ecf20Sopenharmony_ci } 29388c2ecf20Sopenharmony_ci if (tty->ops->compat_ioctl) { 29398c2ecf20Sopenharmony_ci retval = tty->ops->compat_ioctl(tty, cmd, arg); 29408c2ecf20Sopenharmony_ci if (retval != -ENOIOCTLCMD) 29418c2ecf20Sopenharmony_ci return retval; 29428c2ecf20Sopenharmony_ci } 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci ld = tty_ldisc_ref_wait(tty); 29458c2ecf20Sopenharmony_ci if (!ld) 29468c2ecf20Sopenharmony_ci return hung_up_tty_compat_ioctl(file, cmd, arg); 29478c2ecf20Sopenharmony_ci if (ld->ops->compat_ioctl) 29488c2ecf20Sopenharmony_ci retval = ld->ops->compat_ioctl(tty, file, cmd, arg); 29498c2ecf20Sopenharmony_ci if (retval == -ENOIOCTLCMD && ld->ops->ioctl) 29508c2ecf20Sopenharmony_ci retval = ld->ops->ioctl(tty, file, 29518c2ecf20Sopenharmony_ci (unsigned long)compat_ptr(cmd), arg); 29528c2ecf20Sopenharmony_ci tty_ldisc_deref(ld); 29538c2ecf20Sopenharmony_ci 29548c2ecf20Sopenharmony_ci return retval; 29558c2ecf20Sopenharmony_ci} 29568c2ecf20Sopenharmony_ci#endif 29578c2ecf20Sopenharmony_ci 29588c2ecf20Sopenharmony_cistatic int this_tty(const void *t, struct file *file, unsigned fd) 29598c2ecf20Sopenharmony_ci{ 29608c2ecf20Sopenharmony_ci if (likely(file->f_op->read_iter != tty_read)) 29618c2ecf20Sopenharmony_ci return 0; 29628c2ecf20Sopenharmony_ci return file_tty(file) != t ? 0 : fd + 1; 29638c2ecf20Sopenharmony_ci} 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_ci/* 29668c2ecf20Sopenharmony_ci * This implements the "Secure Attention Key" --- the idea is to 29678c2ecf20Sopenharmony_ci * prevent trojan horses by killing all processes associated with this 29688c2ecf20Sopenharmony_ci * tty when the user hits the "Secure Attention Key". Required for 29698c2ecf20Sopenharmony_ci * super-paranoid applications --- see the Orange Book for more details. 29708c2ecf20Sopenharmony_ci * 29718c2ecf20Sopenharmony_ci * This code could be nicer; ideally it should send a HUP, wait a few 29728c2ecf20Sopenharmony_ci * seconds, then send a INT, and then a KILL signal. But you then 29738c2ecf20Sopenharmony_ci * have to coordinate with the init process, since all processes associated 29748c2ecf20Sopenharmony_ci * with the current tty must be dead before the new getty is allowed 29758c2ecf20Sopenharmony_ci * to spawn. 29768c2ecf20Sopenharmony_ci * 29778c2ecf20Sopenharmony_ci * Now, if it would be correct ;-/ The current code has a nasty hole - 29788c2ecf20Sopenharmony_ci * it doesn't catch files in flight. We may send the descriptor to ourselves 29798c2ecf20Sopenharmony_ci * via AF_UNIX socket, close it and later fetch from socket. FIXME. 29808c2ecf20Sopenharmony_ci * 29818c2ecf20Sopenharmony_ci * Nasty bug: do_SAK is being called in interrupt context. This can 29828c2ecf20Sopenharmony_ci * deadlock. We punt it up to process context. AKPM - 16Mar2001 29838c2ecf20Sopenharmony_ci */ 29848c2ecf20Sopenharmony_civoid __do_SAK(struct tty_struct *tty) 29858c2ecf20Sopenharmony_ci{ 29868c2ecf20Sopenharmony_ci#ifdef TTY_SOFT_SAK 29878c2ecf20Sopenharmony_ci tty_hangup(tty); 29888c2ecf20Sopenharmony_ci#else 29898c2ecf20Sopenharmony_ci struct task_struct *g, *p; 29908c2ecf20Sopenharmony_ci struct pid *session; 29918c2ecf20Sopenharmony_ci int i; 29928c2ecf20Sopenharmony_ci unsigned long flags; 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci if (!tty) 29958c2ecf20Sopenharmony_ci return; 29968c2ecf20Sopenharmony_ci 29978c2ecf20Sopenharmony_ci spin_lock_irqsave(&tty->ctrl_lock, flags); 29988c2ecf20Sopenharmony_ci session = get_pid(tty->session); 29998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&tty->ctrl_lock, flags); 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_ci tty_ldisc_flush(tty); 30028c2ecf20Sopenharmony_ci 30038c2ecf20Sopenharmony_ci tty_driver_flush_buffer(tty); 30048c2ecf20Sopenharmony_ci 30058c2ecf20Sopenharmony_ci read_lock(&tasklist_lock); 30068c2ecf20Sopenharmony_ci /* Kill the entire session */ 30078c2ecf20Sopenharmony_ci do_each_pid_task(session, PIDTYPE_SID, p) { 30088c2ecf20Sopenharmony_ci tty_notice(tty, "SAK: killed process %d (%s): by session\n", 30098c2ecf20Sopenharmony_ci task_pid_nr(p), p->comm); 30108c2ecf20Sopenharmony_ci group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID); 30118c2ecf20Sopenharmony_ci } while_each_pid_task(session, PIDTYPE_SID, p); 30128c2ecf20Sopenharmony_ci 30138c2ecf20Sopenharmony_ci /* Now kill any processes that happen to have the tty open */ 30148c2ecf20Sopenharmony_ci do_each_thread(g, p) { 30158c2ecf20Sopenharmony_ci if (p->signal->tty == tty) { 30168c2ecf20Sopenharmony_ci tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n", 30178c2ecf20Sopenharmony_ci task_pid_nr(p), p->comm); 30188c2ecf20Sopenharmony_ci group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID); 30198c2ecf20Sopenharmony_ci continue; 30208c2ecf20Sopenharmony_ci } 30218c2ecf20Sopenharmony_ci task_lock(p); 30228c2ecf20Sopenharmony_ci i = iterate_fd(p->files, 0, this_tty, tty); 30238c2ecf20Sopenharmony_ci if (i != 0) { 30248c2ecf20Sopenharmony_ci tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n", 30258c2ecf20Sopenharmony_ci task_pid_nr(p), p->comm, i - 1); 30268c2ecf20Sopenharmony_ci group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID); 30278c2ecf20Sopenharmony_ci } 30288c2ecf20Sopenharmony_ci task_unlock(p); 30298c2ecf20Sopenharmony_ci } while_each_thread(g, p); 30308c2ecf20Sopenharmony_ci read_unlock(&tasklist_lock); 30318c2ecf20Sopenharmony_ci put_pid(session); 30328c2ecf20Sopenharmony_ci#endif 30338c2ecf20Sopenharmony_ci} 30348c2ecf20Sopenharmony_ci 30358c2ecf20Sopenharmony_cistatic void do_SAK_work(struct work_struct *work) 30368c2ecf20Sopenharmony_ci{ 30378c2ecf20Sopenharmony_ci struct tty_struct *tty = 30388c2ecf20Sopenharmony_ci container_of(work, struct tty_struct, SAK_work); 30398c2ecf20Sopenharmony_ci __do_SAK(tty); 30408c2ecf20Sopenharmony_ci} 30418c2ecf20Sopenharmony_ci 30428c2ecf20Sopenharmony_ci/* 30438c2ecf20Sopenharmony_ci * The tq handling here is a little racy - tty->SAK_work may already be queued. 30448c2ecf20Sopenharmony_ci * Fortunately we don't need to worry, because if ->SAK_work is already queued, 30458c2ecf20Sopenharmony_ci * the values which we write to it will be identical to the values which it 30468c2ecf20Sopenharmony_ci * already has. --akpm 30478c2ecf20Sopenharmony_ci */ 30488c2ecf20Sopenharmony_civoid do_SAK(struct tty_struct *tty) 30498c2ecf20Sopenharmony_ci{ 30508c2ecf20Sopenharmony_ci if (!tty) 30518c2ecf20Sopenharmony_ci return; 30528c2ecf20Sopenharmony_ci schedule_work(&tty->SAK_work); 30538c2ecf20Sopenharmony_ci} 30548c2ecf20Sopenharmony_ci 30558c2ecf20Sopenharmony_ciEXPORT_SYMBOL(do_SAK); 30568c2ecf20Sopenharmony_ci 30578c2ecf20Sopenharmony_ci/* Must put_device() after it's unused! */ 30588c2ecf20Sopenharmony_cistatic struct device *tty_get_device(struct tty_struct *tty) 30598c2ecf20Sopenharmony_ci{ 30608c2ecf20Sopenharmony_ci dev_t devt = tty_devnum(tty); 30618c2ecf20Sopenharmony_ci return class_find_device_by_devt(tty_class, devt); 30628c2ecf20Sopenharmony_ci} 30638c2ecf20Sopenharmony_ci 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci/** 30668c2ecf20Sopenharmony_ci * alloc_tty_struct 30678c2ecf20Sopenharmony_ci * 30688c2ecf20Sopenharmony_ci * This subroutine allocates and initializes a tty structure. 30698c2ecf20Sopenharmony_ci * 30708c2ecf20Sopenharmony_ci * Locking: none - tty in question is not exposed at this point 30718c2ecf20Sopenharmony_ci */ 30728c2ecf20Sopenharmony_ci 30738c2ecf20Sopenharmony_cistruct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx) 30748c2ecf20Sopenharmony_ci{ 30758c2ecf20Sopenharmony_ci struct tty_struct *tty; 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci tty = kzalloc(sizeof(*tty), GFP_KERNEL); 30788c2ecf20Sopenharmony_ci if (!tty) 30798c2ecf20Sopenharmony_ci return NULL; 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci kref_init(&tty->kref); 30828c2ecf20Sopenharmony_ci tty->magic = TTY_MAGIC; 30838c2ecf20Sopenharmony_ci if (tty_ldisc_init(tty)) { 30848c2ecf20Sopenharmony_ci kfree(tty); 30858c2ecf20Sopenharmony_ci return NULL; 30868c2ecf20Sopenharmony_ci } 30878c2ecf20Sopenharmony_ci tty->session = NULL; 30888c2ecf20Sopenharmony_ci tty->pgrp = NULL; 30898c2ecf20Sopenharmony_ci mutex_init(&tty->legacy_mutex); 30908c2ecf20Sopenharmony_ci mutex_init(&tty->throttle_mutex); 30918c2ecf20Sopenharmony_ci init_rwsem(&tty->termios_rwsem); 30928c2ecf20Sopenharmony_ci mutex_init(&tty->winsize_mutex); 30938c2ecf20Sopenharmony_ci init_ldsem(&tty->ldisc_sem); 30948c2ecf20Sopenharmony_ci init_waitqueue_head(&tty->write_wait); 30958c2ecf20Sopenharmony_ci init_waitqueue_head(&tty->read_wait); 30968c2ecf20Sopenharmony_ci INIT_WORK(&tty->hangup_work, do_tty_hangup); 30978c2ecf20Sopenharmony_ci mutex_init(&tty->atomic_write_lock); 30988c2ecf20Sopenharmony_ci spin_lock_init(&tty->ctrl_lock); 30998c2ecf20Sopenharmony_ci spin_lock_init(&tty->flow_lock); 31008c2ecf20Sopenharmony_ci spin_lock_init(&tty->files_lock); 31018c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tty->tty_files); 31028c2ecf20Sopenharmony_ci INIT_WORK(&tty->SAK_work, do_SAK_work); 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_ci tty->driver = driver; 31058c2ecf20Sopenharmony_ci tty->ops = driver->ops; 31068c2ecf20Sopenharmony_ci tty->index = idx; 31078c2ecf20Sopenharmony_ci tty_line_name(driver, idx, tty->name); 31088c2ecf20Sopenharmony_ci tty->dev = tty_get_device(tty); 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci return tty; 31118c2ecf20Sopenharmony_ci} 31128c2ecf20Sopenharmony_ci 31138c2ecf20Sopenharmony_ci/** 31148c2ecf20Sopenharmony_ci * tty_put_char - write one character to a tty 31158c2ecf20Sopenharmony_ci * @tty: tty 31168c2ecf20Sopenharmony_ci * @ch: character 31178c2ecf20Sopenharmony_ci * 31188c2ecf20Sopenharmony_ci * Write one byte to the tty using the provided put_char method 31198c2ecf20Sopenharmony_ci * if present. Returns the number of characters successfully output. 31208c2ecf20Sopenharmony_ci * 31218c2ecf20Sopenharmony_ci * Note: the specific put_char operation in the driver layer may go 31228c2ecf20Sopenharmony_ci * away soon. Don't call it directly, use this method 31238c2ecf20Sopenharmony_ci */ 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ciint tty_put_char(struct tty_struct *tty, unsigned char ch) 31268c2ecf20Sopenharmony_ci{ 31278c2ecf20Sopenharmony_ci if (tty->ops->put_char) 31288c2ecf20Sopenharmony_ci return tty->ops->put_char(tty, ch); 31298c2ecf20Sopenharmony_ci return tty->ops->write(tty, &ch, 1); 31308c2ecf20Sopenharmony_ci} 31318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_put_char); 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_cistruct class *tty_class; 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_cistatic int tty_cdev_add(struct tty_driver *driver, dev_t dev, 31368c2ecf20Sopenharmony_ci unsigned int index, unsigned int count) 31378c2ecf20Sopenharmony_ci{ 31388c2ecf20Sopenharmony_ci int err; 31398c2ecf20Sopenharmony_ci 31408c2ecf20Sopenharmony_ci /* init here, since reused cdevs cause crashes */ 31418c2ecf20Sopenharmony_ci driver->cdevs[index] = cdev_alloc(); 31428c2ecf20Sopenharmony_ci if (!driver->cdevs[index]) 31438c2ecf20Sopenharmony_ci return -ENOMEM; 31448c2ecf20Sopenharmony_ci driver->cdevs[index]->ops = &tty_fops; 31458c2ecf20Sopenharmony_ci driver->cdevs[index]->owner = driver->owner; 31468c2ecf20Sopenharmony_ci err = cdev_add(driver->cdevs[index], dev, count); 31478c2ecf20Sopenharmony_ci if (err) 31488c2ecf20Sopenharmony_ci kobject_put(&driver->cdevs[index]->kobj); 31498c2ecf20Sopenharmony_ci return err; 31508c2ecf20Sopenharmony_ci} 31518c2ecf20Sopenharmony_ci 31528c2ecf20Sopenharmony_ci/** 31538c2ecf20Sopenharmony_ci * tty_register_device - register a tty device 31548c2ecf20Sopenharmony_ci * @driver: the tty driver that describes the tty device 31558c2ecf20Sopenharmony_ci * @index: the index in the tty driver for this tty device 31568c2ecf20Sopenharmony_ci * @device: a struct device that is associated with this tty device. 31578c2ecf20Sopenharmony_ci * This field is optional, if there is no known struct device 31588c2ecf20Sopenharmony_ci * for this tty device it can be set to NULL safely. 31598c2ecf20Sopenharmony_ci * 31608c2ecf20Sopenharmony_ci * Returns a pointer to the struct device for this tty device 31618c2ecf20Sopenharmony_ci * (or ERR_PTR(-EFOO) on error). 31628c2ecf20Sopenharmony_ci * 31638c2ecf20Sopenharmony_ci * This call is required to be made to register an individual tty device 31648c2ecf20Sopenharmony_ci * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If 31658c2ecf20Sopenharmony_ci * that bit is not set, this function should not be called by a tty 31668c2ecf20Sopenharmony_ci * driver. 31678c2ecf20Sopenharmony_ci * 31688c2ecf20Sopenharmony_ci * Locking: ?? 31698c2ecf20Sopenharmony_ci */ 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_cistruct device *tty_register_device(struct tty_driver *driver, unsigned index, 31728c2ecf20Sopenharmony_ci struct device *device) 31738c2ecf20Sopenharmony_ci{ 31748c2ecf20Sopenharmony_ci return tty_register_device_attr(driver, index, device, NULL, NULL); 31758c2ecf20Sopenharmony_ci} 31768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_register_device); 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_cistatic void tty_device_create_release(struct device *dev) 31798c2ecf20Sopenharmony_ci{ 31808c2ecf20Sopenharmony_ci dev_dbg(dev, "releasing...\n"); 31818c2ecf20Sopenharmony_ci kfree(dev); 31828c2ecf20Sopenharmony_ci} 31838c2ecf20Sopenharmony_ci 31848c2ecf20Sopenharmony_ci/** 31858c2ecf20Sopenharmony_ci * tty_register_device_attr - register a tty device 31868c2ecf20Sopenharmony_ci * @driver: the tty driver that describes the tty device 31878c2ecf20Sopenharmony_ci * @index: the index in the tty driver for this tty device 31888c2ecf20Sopenharmony_ci * @device: a struct device that is associated with this tty device. 31898c2ecf20Sopenharmony_ci * This field is optional, if there is no known struct device 31908c2ecf20Sopenharmony_ci * for this tty device it can be set to NULL safely. 31918c2ecf20Sopenharmony_ci * @drvdata: Driver data to be set to device. 31928c2ecf20Sopenharmony_ci * @attr_grp: Attribute group to be set on device. 31938c2ecf20Sopenharmony_ci * 31948c2ecf20Sopenharmony_ci * Returns a pointer to the struct device for this tty device 31958c2ecf20Sopenharmony_ci * (or ERR_PTR(-EFOO) on error). 31968c2ecf20Sopenharmony_ci * 31978c2ecf20Sopenharmony_ci * This call is required to be made to register an individual tty device 31988c2ecf20Sopenharmony_ci * if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set. If 31998c2ecf20Sopenharmony_ci * that bit is not set, this function should not be called by a tty 32008c2ecf20Sopenharmony_ci * driver. 32018c2ecf20Sopenharmony_ci * 32028c2ecf20Sopenharmony_ci * Locking: ?? 32038c2ecf20Sopenharmony_ci */ 32048c2ecf20Sopenharmony_cistruct device *tty_register_device_attr(struct tty_driver *driver, 32058c2ecf20Sopenharmony_ci unsigned index, struct device *device, 32068c2ecf20Sopenharmony_ci void *drvdata, 32078c2ecf20Sopenharmony_ci const struct attribute_group **attr_grp) 32088c2ecf20Sopenharmony_ci{ 32098c2ecf20Sopenharmony_ci char name[64]; 32108c2ecf20Sopenharmony_ci dev_t devt = MKDEV(driver->major, driver->minor_start) + index; 32118c2ecf20Sopenharmony_ci struct ktermios *tp; 32128c2ecf20Sopenharmony_ci struct device *dev; 32138c2ecf20Sopenharmony_ci int retval; 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci if (index >= driver->num) { 32168c2ecf20Sopenharmony_ci pr_err("%s: Attempt to register invalid tty line number (%d)\n", 32178c2ecf20Sopenharmony_ci driver->name, index); 32188c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 32198c2ecf20Sopenharmony_ci } 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci if (driver->type == TTY_DRIVER_TYPE_PTY) 32228c2ecf20Sopenharmony_ci pty_line_name(driver, index, name); 32238c2ecf20Sopenharmony_ci else 32248c2ecf20Sopenharmony_ci tty_line_name(driver, index, name); 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 32278c2ecf20Sopenharmony_ci if (!dev) 32288c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci dev->devt = devt; 32318c2ecf20Sopenharmony_ci dev->class = tty_class; 32328c2ecf20Sopenharmony_ci dev->parent = device; 32338c2ecf20Sopenharmony_ci dev->release = tty_device_create_release; 32348c2ecf20Sopenharmony_ci dev_set_name(dev, "%s", name); 32358c2ecf20Sopenharmony_ci dev->groups = attr_grp; 32368c2ecf20Sopenharmony_ci dev_set_drvdata(dev, drvdata); 32378c2ecf20Sopenharmony_ci 32388c2ecf20Sopenharmony_ci dev_set_uevent_suppress(dev, 1); 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci retval = device_register(dev); 32418c2ecf20Sopenharmony_ci if (retval) 32428c2ecf20Sopenharmony_ci goto err_put; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { 32458c2ecf20Sopenharmony_ci /* 32468c2ecf20Sopenharmony_ci * Free any saved termios data so that the termios state is 32478c2ecf20Sopenharmony_ci * reset when reusing a minor number. 32488c2ecf20Sopenharmony_ci */ 32498c2ecf20Sopenharmony_ci tp = driver->termios[index]; 32508c2ecf20Sopenharmony_ci if (tp) { 32518c2ecf20Sopenharmony_ci driver->termios[index] = NULL; 32528c2ecf20Sopenharmony_ci kfree(tp); 32538c2ecf20Sopenharmony_ci } 32548c2ecf20Sopenharmony_ci 32558c2ecf20Sopenharmony_ci retval = tty_cdev_add(driver, devt, index, 1); 32568c2ecf20Sopenharmony_ci if (retval) 32578c2ecf20Sopenharmony_ci goto err_del; 32588c2ecf20Sopenharmony_ci } 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci dev_set_uevent_suppress(dev, 0); 32618c2ecf20Sopenharmony_ci kobject_uevent(&dev->kobj, KOBJ_ADD); 32628c2ecf20Sopenharmony_ci 32638c2ecf20Sopenharmony_ci return dev; 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_cierr_del: 32668c2ecf20Sopenharmony_ci device_del(dev); 32678c2ecf20Sopenharmony_cierr_put: 32688c2ecf20Sopenharmony_ci put_device(dev); 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci return ERR_PTR(retval); 32718c2ecf20Sopenharmony_ci} 32728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tty_register_device_attr); 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci/** 32758c2ecf20Sopenharmony_ci * tty_unregister_device - unregister a tty device 32768c2ecf20Sopenharmony_ci * @driver: the tty driver that describes the tty device 32778c2ecf20Sopenharmony_ci * @index: the index in the tty driver for this tty device 32788c2ecf20Sopenharmony_ci * 32798c2ecf20Sopenharmony_ci * If a tty device is registered with a call to tty_register_device() then 32808c2ecf20Sopenharmony_ci * this function must be called when the tty device is gone. 32818c2ecf20Sopenharmony_ci * 32828c2ecf20Sopenharmony_ci * Locking: ?? 32838c2ecf20Sopenharmony_ci */ 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_civoid tty_unregister_device(struct tty_driver *driver, unsigned index) 32868c2ecf20Sopenharmony_ci{ 32878c2ecf20Sopenharmony_ci device_destroy(tty_class, 32888c2ecf20Sopenharmony_ci MKDEV(driver->major, driver->minor_start) + index); 32898c2ecf20Sopenharmony_ci if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) { 32908c2ecf20Sopenharmony_ci cdev_del(driver->cdevs[index]); 32918c2ecf20Sopenharmony_ci driver->cdevs[index] = NULL; 32928c2ecf20Sopenharmony_ci } 32938c2ecf20Sopenharmony_ci} 32948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_unregister_device); 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci/** 32978c2ecf20Sopenharmony_ci * __tty_alloc_driver -- allocate tty driver 32988c2ecf20Sopenharmony_ci * @lines: count of lines this driver can handle at most 32998c2ecf20Sopenharmony_ci * @owner: module which is responsible for this driver 33008c2ecf20Sopenharmony_ci * @flags: some of TTY_DRIVER_* flags, will be set in driver->flags 33018c2ecf20Sopenharmony_ci * 33028c2ecf20Sopenharmony_ci * This should not be called directly, some of the provided macros should be 33038c2ecf20Sopenharmony_ci * used instead. Use IS_ERR and friends on @retval. 33048c2ecf20Sopenharmony_ci */ 33058c2ecf20Sopenharmony_cistruct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner, 33068c2ecf20Sopenharmony_ci unsigned long flags) 33078c2ecf20Sopenharmony_ci{ 33088c2ecf20Sopenharmony_ci struct tty_driver *driver; 33098c2ecf20Sopenharmony_ci unsigned int cdevs = 1; 33108c2ecf20Sopenharmony_ci int err; 33118c2ecf20Sopenharmony_ci 33128c2ecf20Sopenharmony_ci if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1)) 33138c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 33148c2ecf20Sopenharmony_ci 33158c2ecf20Sopenharmony_ci driver = kzalloc(sizeof(*driver), GFP_KERNEL); 33168c2ecf20Sopenharmony_ci if (!driver) 33178c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci kref_init(&driver->kref); 33208c2ecf20Sopenharmony_ci driver->magic = TTY_DRIVER_MAGIC; 33218c2ecf20Sopenharmony_ci driver->num = lines; 33228c2ecf20Sopenharmony_ci driver->owner = owner; 33238c2ecf20Sopenharmony_ci driver->flags = flags; 33248c2ecf20Sopenharmony_ci 33258c2ecf20Sopenharmony_ci if (!(flags & TTY_DRIVER_DEVPTS_MEM)) { 33268c2ecf20Sopenharmony_ci driver->ttys = kcalloc(lines, sizeof(*driver->ttys), 33278c2ecf20Sopenharmony_ci GFP_KERNEL); 33288c2ecf20Sopenharmony_ci driver->termios = kcalloc(lines, sizeof(*driver->termios), 33298c2ecf20Sopenharmony_ci GFP_KERNEL); 33308c2ecf20Sopenharmony_ci if (!driver->ttys || !driver->termios) { 33318c2ecf20Sopenharmony_ci err = -ENOMEM; 33328c2ecf20Sopenharmony_ci goto err_free_all; 33338c2ecf20Sopenharmony_ci } 33348c2ecf20Sopenharmony_ci } 33358c2ecf20Sopenharmony_ci 33368c2ecf20Sopenharmony_ci if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) { 33378c2ecf20Sopenharmony_ci driver->ports = kcalloc(lines, sizeof(*driver->ports), 33388c2ecf20Sopenharmony_ci GFP_KERNEL); 33398c2ecf20Sopenharmony_ci if (!driver->ports) { 33408c2ecf20Sopenharmony_ci err = -ENOMEM; 33418c2ecf20Sopenharmony_ci goto err_free_all; 33428c2ecf20Sopenharmony_ci } 33438c2ecf20Sopenharmony_ci cdevs = lines; 33448c2ecf20Sopenharmony_ci } 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL); 33478c2ecf20Sopenharmony_ci if (!driver->cdevs) { 33488c2ecf20Sopenharmony_ci err = -ENOMEM; 33498c2ecf20Sopenharmony_ci goto err_free_all; 33508c2ecf20Sopenharmony_ci } 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci return driver; 33538c2ecf20Sopenharmony_cierr_free_all: 33548c2ecf20Sopenharmony_ci kfree(driver->ports); 33558c2ecf20Sopenharmony_ci kfree(driver->ttys); 33568c2ecf20Sopenharmony_ci kfree(driver->termios); 33578c2ecf20Sopenharmony_ci kfree(driver->cdevs); 33588c2ecf20Sopenharmony_ci kfree(driver); 33598c2ecf20Sopenharmony_ci return ERR_PTR(err); 33608c2ecf20Sopenharmony_ci} 33618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__tty_alloc_driver); 33628c2ecf20Sopenharmony_ci 33638c2ecf20Sopenharmony_cistatic void destruct_tty_driver(struct kref *kref) 33648c2ecf20Sopenharmony_ci{ 33658c2ecf20Sopenharmony_ci struct tty_driver *driver = container_of(kref, struct tty_driver, kref); 33668c2ecf20Sopenharmony_ci int i; 33678c2ecf20Sopenharmony_ci struct ktermios *tp; 33688c2ecf20Sopenharmony_ci 33698c2ecf20Sopenharmony_ci if (driver->flags & TTY_DRIVER_INSTALLED) { 33708c2ecf20Sopenharmony_ci for (i = 0; i < driver->num; i++) { 33718c2ecf20Sopenharmony_ci tp = driver->termios[i]; 33728c2ecf20Sopenharmony_ci if (tp) { 33738c2ecf20Sopenharmony_ci driver->termios[i] = NULL; 33748c2ecf20Sopenharmony_ci kfree(tp); 33758c2ecf20Sopenharmony_ci } 33768c2ecf20Sopenharmony_ci if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) 33778c2ecf20Sopenharmony_ci tty_unregister_device(driver, i); 33788c2ecf20Sopenharmony_ci } 33798c2ecf20Sopenharmony_ci proc_tty_unregister_driver(driver); 33808c2ecf20Sopenharmony_ci if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) 33818c2ecf20Sopenharmony_ci cdev_del(driver->cdevs[0]); 33828c2ecf20Sopenharmony_ci } 33838c2ecf20Sopenharmony_ci kfree(driver->cdevs); 33848c2ecf20Sopenharmony_ci kfree(driver->ports); 33858c2ecf20Sopenharmony_ci kfree(driver->termios); 33868c2ecf20Sopenharmony_ci kfree(driver->ttys); 33878c2ecf20Sopenharmony_ci kfree(driver); 33888c2ecf20Sopenharmony_ci} 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_civoid tty_driver_kref_put(struct tty_driver *driver) 33918c2ecf20Sopenharmony_ci{ 33928c2ecf20Sopenharmony_ci kref_put(&driver->kref, destruct_tty_driver); 33938c2ecf20Sopenharmony_ci} 33948c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_driver_kref_put); 33958c2ecf20Sopenharmony_ci 33968c2ecf20Sopenharmony_civoid tty_set_operations(struct tty_driver *driver, 33978c2ecf20Sopenharmony_ci const struct tty_operations *op) 33988c2ecf20Sopenharmony_ci{ 33998c2ecf20Sopenharmony_ci driver->ops = op; 34008c2ecf20Sopenharmony_ci}; 34018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_set_operations); 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_civoid put_tty_driver(struct tty_driver *d) 34048c2ecf20Sopenharmony_ci{ 34058c2ecf20Sopenharmony_ci tty_driver_kref_put(d); 34068c2ecf20Sopenharmony_ci} 34078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(put_tty_driver); 34088c2ecf20Sopenharmony_ci 34098c2ecf20Sopenharmony_ci/* 34108c2ecf20Sopenharmony_ci * Called by a tty driver to register itself. 34118c2ecf20Sopenharmony_ci */ 34128c2ecf20Sopenharmony_ciint tty_register_driver(struct tty_driver *driver) 34138c2ecf20Sopenharmony_ci{ 34148c2ecf20Sopenharmony_ci int error; 34158c2ecf20Sopenharmony_ci int i; 34168c2ecf20Sopenharmony_ci dev_t dev; 34178c2ecf20Sopenharmony_ci struct device *d; 34188c2ecf20Sopenharmony_ci 34198c2ecf20Sopenharmony_ci if (!driver->major) { 34208c2ecf20Sopenharmony_ci error = alloc_chrdev_region(&dev, driver->minor_start, 34218c2ecf20Sopenharmony_ci driver->num, driver->name); 34228c2ecf20Sopenharmony_ci if (!error) { 34238c2ecf20Sopenharmony_ci driver->major = MAJOR(dev); 34248c2ecf20Sopenharmony_ci driver->minor_start = MINOR(dev); 34258c2ecf20Sopenharmony_ci } 34268c2ecf20Sopenharmony_ci } else { 34278c2ecf20Sopenharmony_ci dev = MKDEV(driver->major, driver->minor_start); 34288c2ecf20Sopenharmony_ci error = register_chrdev_region(dev, driver->num, driver->name); 34298c2ecf20Sopenharmony_ci } 34308c2ecf20Sopenharmony_ci if (error < 0) 34318c2ecf20Sopenharmony_ci goto err; 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { 34348c2ecf20Sopenharmony_ci error = tty_cdev_add(driver, dev, 0, driver->num); 34358c2ecf20Sopenharmony_ci if (error) 34368c2ecf20Sopenharmony_ci goto err_unreg_char; 34378c2ecf20Sopenharmony_ci } 34388c2ecf20Sopenharmony_ci 34398c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 34408c2ecf20Sopenharmony_ci list_add(&driver->tty_drivers, &tty_drivers); 34418c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 34428c2ecf20Sopenharmony_ci 34438c2ecf20Sopenharmony_ci if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) { 34448c2ecf20Sopenharmony_ci for (i = 0; i < driver->num; i++) { 34458c2ecf20Sopenharmony_ci d = tty_register_device(driver, i, NULL); 34468c2ecf20Sopenharmony_ci if (IS_ERR(d)) { 34478c2ecf20Sopenharmony_ci error = PTR_ERR(d); 34488c2ecf20Sopenharmony_ci goto err_unreg_devs; 34498c2ecf20Sopenharmony_ci } 34508c2ecf20Sopenharmony_ci } 34518c2ecf20Sopenharmony_ci } 34528c2ecf20Sopenharmony_ci proc_tty_register_driver(driver); 34538c2ecf20Sopenharmony_ci driver->flags |= TTY_DRIVER_INSTALLED; 34548c2ecf20Sopenharmony_ci return 0; 34558c2ecf20Sopenharmony_ci 34568c2ecf20Sopenharmony_cierr_unreg_devs: 34578c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 34588c2ecf20Sopenharmony_ci tty_unregister_device(driver, i); 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 34618c2ecf20Sopenharmony_ci list_del(&driver->tty_drivers); 34628c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_cierr_unreg_char: 34658c2ecf20Sopenharmony_ci unregister_chrdev_region(dev, driver->num); 34668c2ecf20Sopenharmony_cierr: 34678c2ecf20Sopenharmony_ci return error; 34688c2ecf20Sopenharmony_ci} 34698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_register_driver); 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci/* 34728c2ecf20Sopenharmony_ci * Called by a tty driver to unregister itself. 34738c2ecf20Sopenharmony_ci */ 34748c2ecf20Sopenharmony_ciint tty_unregister_driver(struct tty_driver *driver) 34758c2ecf20Sopenharmony_ci{ 34768c2ecf20Sopenharmony_ci#if 0 34778c2ecf20Sopenharmony_ci /* FIXME */ 34788c2ecf20Sopenharmony_ci if (driver->refcount) 34798c2ecf20Sopenharmony_ci return -EBUSY; 34808c2ecf20Sopenharmony_ci#endif 34818c2ecf20Sopenharmony_ci unregister_chrdev_region(MKDEV(driver->major, driver->minor_start), 34828c2ecf20Sopenharmony_ci driver->num); 34838c2ecf20Sopenharmony_ci mutex_lock(&tty_mutex); 34848c2ecf20Sopenharmony_ci list_del(&driver->tty_drivers); 34858c2ecf20Sopenharmony_ci mutex_unlock(&tty_mutex); 34868c2ecf20Sopenharmony_ci return 0; 34878c2ecf20Sopenharmony_ci} 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_unregister_driver); 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_cidev_t tty_devnum(struct tty_struct *tty) 34928c2ecf20Sopenharmony_ci{ 34938c2ecf20Sopenharmony_ci return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index; 34948c2ecf20Sopenharmony_ci} 34958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(tty_devnum); 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_civoid tty_default_fops(struct file_operations *fops) 34988c2ecf20Sopenharmony_ci{ 34998c2ecf20Sopenharmony_ci *fops = tty_fops; 35008c2ecf20Sopenharmony_ci} 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_cistatic char *tty_devnode(struct device *dev, umode_t *mode) 35038c2ecf20Sopenharmony_ci{ 35048c2ecf20Sopenharmony_ci if (!mode) 35058c2ecf20Sopenharmony_ci return NULL; 35068c2ecf20Sopenharmony_ci if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) || 35078c2ecf20Sopenharmony_ci dev->devt == MKDEV(TTYAUX_MAJOR, 2)) 35088c2ecf20Sopenharmony_ci *mode = 0666; 35098c2ecf20Sopenharmony_ci return NULL; 35108c2ecf20Sopenharmony_ci} 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_cistatic int __init tty_class_init(void) 35138c2ecf20Sopenharmony_ci{ 35148c2ecf20Sopenharmony_ci tty_class = class_create(THIS_MODULE, "tty"); 35158c2ecf20Sopenharmony_ci if (IS_ERR(tty_class)) 35168c2ecf20Sopenharmony_ci return PTR_ERR(tty_class); 35178c2ecf20Sopenharmony_ci tty_class->devnode = tty_devnode; 35188c2ecf20Sopenharmony_ci return 0; 35198c2ecf20Sopenharmony_ci} 35208c2ecf20Sopenharmony_ci 35218c2ecf20Sopenharmony_cipostcore_initcall(tty_class_init); 35228c2ecf20Sopenharmony_ci 35238c2ecf20Sopenharmony_ci/* 3/2004 jmc: why do these devices exist? */ 35248c2ecf20Sopenharmony_cistatic struct cdev tty_cdev, console_cdev; 35258c2ecf20Sopenharmony_ci 35268c2ecf20Sopenharmony_cistatic ssize_t show_cons_active(struct device *dev, 35278c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 35288c2ecf20Sopenharmony_ci{ 35298c2ecf20Sopenharmony_ci struct console *cs[16]; 35308c2ecf20Sopenharmony_ci int i = 0; 35318c2ecf20Sopenharmony_ci struct console *c; 35328c2ecf20Sopenharmony_ci ssize_t count = 0; 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_ci console_lock(); 35358c2ecf20Sopenharmony_ci for_each_console(c) { 35368c2ecf20Sopenharmony_ci if (!c->device) 35378c2ecf20Sopenharmony_ci continue; 35388c2ecf20Sopenharmony_ci if (!c->write) 35398c2ecf20Sopenharmony_ci continue; 35408c2ecf20Sopenharmony_ci if ((c->flags & CON_ENABLED) == 0) 35418c2ecf20Sopenharmony_ci continue; 35428c2ecf20Sopenharmony_ci cs[i++] = c; 35438c2ecf20Sopenharmony_ci if (i >= ARRAY_SIZE(cs)) 35448c2ecf20Sopenharmony_ci break; 35458c2ecf20Sopenharmony_ci } 35468c2ecf20Sopenharmony_ci while (i--) { 35478c2ecf20Sopenharmony_ci int index = cs[i]->index; 35488c2ecf20Sopenharmony_ci struct tty_driver *drv = cs[i]->device(cs[i], &index); 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci /* don't resolve tty0 as some programs depend on it */ 35518c2ecf20Sopenharmony_ci if (drv && (cs[i]->index > 0 || drv->major != TTY_MAJOR)) 35528c2ecf20Sopenharmony_ci count += tty_line_name(drv, index, buf + count); 35538c2ecf20Sopenharmony_ci else 35548c2ecf20Sopenharmony_ci count += sprintf(buf + count, "%s%d", 35558c2ecf20Sopenharmony_ci cs[i]->name, cs[i]->index); 35568c2ecf20Sopenharmony_ci 35578c2ecf20Sopenharmony_ci count += sprintf(buf + count, "%c", i ? ' ':'\n'); 35588c2ecf20Sopenharmony_ci } 35598c2ecf20Sopenharmony_ci console_unlock(); 35608c2ecf20Sopenharmony_ci 35618c2ecf20Sopenharmony_ci return count; 35628c2ecf20Sopenharmony_ci} 35638c2ecf20Sopenharmony_cistatic DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL); 35648c2ecf20Sopenharmony_ci 35658c2ecf20Sopenharmony_cistatic struct attribute *cons_dev_attrs[] = { 35668c2ecf20Sopenharmony_ci &dev_attr_active.attr, 35678c2ecf20Sopenharmony_ci NULL 35688c2ecf20Sopenharmony_ci}; 35698c2ecf20Sopenharmony_ci 35708c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(cons_dev); 35718c2ecf20Sopenharmony_ci 35728c2ecf20Sopenharmony_cistatic struct device *consdev; 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_civoid console_sysfs_notify(void) 35758c2ecf20Sopenharmony_ci{ 35768c2ecf20Sopenharmony_ci if (consdev) 35778c2ecf20Sopenharmony_ci sysfs_notify(&consdev->kobj, NULL, "active"); 35788c2ecf20Sopenharmony_ci} 35798c2ecf20Sopenharmony_ci 35808c2ecf20Sopenharmony_ci/* 35818c2ecf20Sopenharmony_ci * Ok, now we can initialize the rest of the tty devices and can count 35828c2ecf20Sopenharmony_ci * on memory allocations, interrupts etc.. 35838c2ecf20Sopenharmony_ci */ 35848c2ecf20Sopenharmony_ciint __init tty_init(void) 35858c2ecf20Sopenharmony_ci{ 35868c2ecf20Sopenharmony_ci tty_sysctl_init(); 35878c2ecf20Sopenharmony_ci cdev_init(&tty_cdev, &tty_fops); 35888c2ecf20Sopenharmony_ci if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) || 35898c2ecf20Sopenharmony_ci register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) 35908c2ecf20Sopenharmony_ci panic("Couldn't register /dev/tty driver\n"); 35918c2ecf20Sopenharmony_ci device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty"); 35928c2ecf20Sopenharmony_ci 35938c2ecf20Sopenharmony_ci cdev_init(&console_cdev, &console_fops); 35948c2ecf20Sopenharmony_ci if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) || 35958c2ecf20Sopenharmony_ci register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0) 35968c2ecf20Sopenharmony_ci panic("Couldn't register /dev/console driver\n"); 35978c2ecf20Sopenharmony_ci consdev = device_create_with_groups(tty_class, NULL, 35988c2ecf20Sopenharmony_ci MKDEV(TTYAUX_MAJOR, 1), NULL, 35998c2ecf20Sopenharmony_ci cons_dev_groups, "console"); 36008c2ecf20Sopenharmony_ci if (IS_ERR(consdev)) 36018c2ecf20Sopenharmony_ci consdev = NULL; 36028c2ecf20Sopenharmony_ci 36038c2ecf20Sopenharmony_ci#ifdef CONFIG_VT 36048c2ecf20Sopenharmony_ci vty_init(&console_fops); 36058c2ecf20Sopenharmony_ci#endif 36068c2ecf20Sopenharmony_ci return 0; 36078c2ecf20Sopenharmony_ci} 36088c2ecf20Sopenharmony_ci 3609