xref: /openbmc/linux/drivers/tty/tty_io.c (revision 19e321c3)
1e3b3d0f5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
296fd7ce5SGreg Kroah-Hartman /*
396fd7ce5SGreg Kroah-Hartman  *  Copyright (C) 1991, 1992  Linus Torvalds
496fd7ce5SGreg Kroah-Hartman  */
596fd7ce5SGreg Kroah-Hartman 
696fd7ce5SGreg Kroah-Hartman /*
796fd7ce5SGreg Kroah-Hartman  * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
896fd7ce5SGreg Kroah-Hartman  * or rs-channels. It also implements echoing, cooked mode etc.
996fd7ce5SGreg Kroah-Hartman  *
1096fd7ce5SGreg Kroah-Hartman  * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
1196fd7ce5SGreg Kroah-Hartman  *
1296fd7ce5SGreg Kroah-Hartman  * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
1396fd7ce5SGreg Kroah-Hartman  * tty_struct and tty_queue structures.  Previously there was an array
1496fd7ce5SGreg Kroah-Hartman  * of 256 tty_struct's which was statically allocated, and the
1596fd7ce5SGreg Kroah-Hartman  * tty_queue structures were allocated at boot time.  Both are now
1696fd7ce5SGreg Kroah-Hartman  * dynamically allocated only when the tty is open.
1796fd7ce5SGreg Kroah-Hartman  *
1896fd7ce5SGreg Kroah-Hartman  * Also restructured routines so that there is more of a separation
1996fd7ce5SGreg Kroah-Hartman  * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
2096fd7ce5SGreg Kroah-Hartman  * the low-level tty routines (serial.c, pty.c, console.c).  This
2196fd7ce5SGreg Kroah-Hartman  * makes for cleaner and more compact code.  -TYT, 9/17/92
2296fd7ce5SGreg Kroah-Hartman  *
2396fd7ce5SGreg Kroah-Hartman  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
2496fd7ce5SGreg Kroah-Hartman  * which can be dynamically activated and de-activated by the line
2596fd7ce5SGreg Kroah-Hartman  * discipline handling modules (like SLIP).
2696fd7ce5SGreg Kroah-Hartman  *
2796fd7ce5SGreg Kroah-Hartman  * NOTE: pay no attention to the line discipline code (yet); its
2896fd7ce5SGreg Kroah-Hartman  * interface is still subject to change in this version...
2996fd7ce5SGreg Kroah-Hartman  * -- TYT, 1/31/92
3096fd7ce5SGreg Kroah-Hartman  *
3196fd7ce5SGreg Kroah-Hartman  * Added functionality to the OPOST tty handling.  No delays, but all
3296fd7ce5SGreg Kroah-Hartman  * other bits should be there.
3396fd7ce5SGreg Kroah-Hartman  *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
3496fd7ce5SGreg Kroah-Hartman  *
3596fd7ce5SGreg Kroah-Hartman  * Rewrote canonical mode and added more termios flags.
3696fd7ce5SGreg Kroah-Hartman  *	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
3796fd7ce5SGreg Kroah-Hartman  *
3896fd7ce5SGreg Kroah-Hartman  * Reorganized FASYNC support so mouse code can share it.
3996fd7ce5SGreg Kroah-Hartman  *	-- ctm@ardi.com, 9Sep95
4096fd7ce5SGreg Kroah-Hartman  *
4196fd7ce5SGreg Kroah-Hartman  * New TIOCLINUX variants added.
4296fd7ce5SGreg Kroah-Hartman  *	-- mj@k332.feld.cvut.cz, 19-Nov-95
4396fd7ce5SGreg Kroah-Hartman  *
4496fd7ce5SGreg Kroah-Hartman  * Restrict vt switching via ioctl()
4596fd7ce5SGreg Kroah-Hartman  *      -- grif@cs.ucr.edu, 5-Dec-95
4696fd7ce5SGreg Kroah-Hartman  *
4796fd7ce5SGreg Kroah-Hartman  * Move console and virtual terminal code to more appropriate files,
4896fd7ce5SGreg Kroah-Hartman  * implement CONFIG_VT and generalize console device interface.
4996fd7ce5SGreg Kroah-Hartman  *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
5096fd7ce5SGreg Kroah-Hartman  *
5196fd7ce5SGreg Kroah-Hartman  * Rewrote tty_init_dev and tty_release_dev to eliminate races.
5296fd7ce5SGreg Kroah-Hartman  *	-- Bill Hawes <whawes@star.net>, June 97
5396fd7ce5SGreg Kroah-Hartman  *
5496fd7ce5SGreg Kroah-Hartman  * Added devfs support.
5596fd7ce5SGreg Kroah-Hartman  *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
5696fd7ce5SGreg Kroah-Hartman  *
5796fd7ce5SGreg Kroah-Hartman  * Added support for a Unix98-style ptmx device.
5896fd7ce5SGreg Kroah-Hartman  *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
5996fd7ce5SGreg Kroah-Hartman  *
6096fd7ce5SGreg Kroah-Hartman  * Reduced memory usage for older ARM systems
6196fd7ce5SGreg Kroah-Hartman  *      -- Russell King <rmk@arm.linux.org.uk>
6296fd7ce5SGreg Kroah-Hartman  *
6396fd7ce5SGreg Kroah-Hartman  * Move do_SAK() into process context.  Less stack use in devfs functions.
6496fd7ce5SGreg Kroah-Hartman  * alloc_tty_struct() always uses kmalloc()
6596fd7ce5SGreg Kroah-Hartman  *			 -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
6696fd7ce5SGreg Kroah-Hartman  */
6796fd7ce5SGreg Kroah-Hartman 
6896fd7ce5SGreg Kroah-Hartman #include <linux/types.h>
6996fd7ce5SGreg Kroah-Hartman #include <linux/major.h>
7096fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
7196fd7ce5SGreg Kroah-Hartman #include <linux/signal.h>
7296fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
733f07c014SIngo Molnar #include <linux/sched/signal.h>
7429930025SIngo Molnar #include <linux/sched/task.h>
7596fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h>
7696fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
7796fd7ce5SGreg Kroah-Hartman #include <linux/tty_driver.h>
7896fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h>
7996fd7ce5SGreg Kroah-Hartman #include <linux/devpts_fs.h>
8096fd7ce5SGreg Kroah-Hartman #include <linux/file.h>
8196fd7ce5SGreg Kroah-Hartman #include <linux/fdtable.h>
8296fd7ce5SGreg Kroah-Hartman #include <linux/console.h>
8396fd7ce5SGreg Kroah-Hartman #include <linux/timer.h>
8496fd7ce5SGreg Kroah-Hartman #include <linux/ctype.h>
8596fd7ce5SGreg Kroah-Hartman #include <linux/kd.h>
8696fd7ce5SGreg Kroah-Hartman #include <linux/mm.h>
8796fd7ce5SGreg Kroah-Hartman #include <linux/string.h>
8896fd7ce5SGreg Kroah-Hartman #include <linux/slab.h>
8996fd7ce5SGreg Kroah-Hartman #include <linux/poll.h>
90b7aff093SArnd Bergmann #include <linux/ppp-ioctl.h>
9196fd7ce5SGreg Kroah-Hartman #include <linux/proc_fs.h>
9296fd7ce5SGreg Kroah-Hartman #include <linux/init.h>
9396fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
9496fd7ce5SGreg Kroah-Hartman #include <linux/device.h>
9596fd7ce5SGreg Kroah-Hartman #include <linux/wait.h>
9696fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
9796fd7ce5SGreg Kroah-Hartman #include <linux/delay.h>
9896fd7ce5SGreg Kroah-Hartman #include <linux/seq_file.h>
9996fd7ce5SGreg Kroah-Hartman #include <linux/serial.h>
1005a3c6b25SManuel Zerpies #include <linux/ratelimit.h>
101e2112038SAl Viro #include <linux/compat.h>
10296fd7ce5SGreg Kroah-Hartman #include <linux/uaccess.h>
103c9874d3fSAl Viro #include <linux/termios_internal.h>
104360c11e2SMichal Sekletar #include <linux/fs.h>
10596fd7ce5SGreg Kroah-Hartman 
10696fd7ce5SGreg Kroah-Hartman #include <linux/kbd_kern.h>
10796fd7ce5SGreg Kroah-Hartman #include <linux/vt_kern.h>
10896fd7ce5SGreg Kroah-Hartman #include <linux/selection.h>
10996fd7ce5SGreg Kroah-Hartman 
11096fd7ce5SGreg Kroah-Hartman #include <linux/kmod.h>
11196fd7ce5SGreg Kroah-Hartman #include <linux/nsproxy.h>
11298602c01SGreg Kroah-Hartman #include "tty.h"
11396fd7ce5SGreg Kroah-Hartman 
11496fd7ce5SGreg Kroah-Hartman #undef TTY_DEBUG_HANGUP
115accff793SPeter Hurley #ifdef TTY_DEBUG_HANGUP
116accff793SPeter Hurley # define tty_debug_hangup(tty, f, args...)	tty_debug(tty, f, ##args)
117accff793SPeter Hurley #else
118accff793SPeter Hurley # define tty_debug_hangup(tty, f, args...)	do { } while (0)
119accff793SPeter Hurley #endif
12096fd7ce5SGreg Kroah-Hartman 
12196fd7ce5SGreg Kroah-Hartman #define TTY_PARANOIA_CHECK 1
12296fd7ce5SGreg Kroah-Hartman #define CHECK_TTY_COUNT 1
12396fd7ce5SGreg Kroah-Hartman 
12496fd7ce5SGreg Kroah-Hartman struct ktermios tty_std_termios = {	/* for the benefit of tty drivers  */
12596fd7ce5SGreg Kroah-Hartman 	.c_iflag = ICRNL | IXON,
12696fd7ce5SGreg Kroah-Hartman 	.c_oflag = OPOST | ONLCR,
12796fd7ce5SGreg Kroah-Hartman 	.c_cflag = B38400 | CS8 | CREAD | HUPCL,
12896fd7ce5SGreg Kroah-Hartman 	.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
12996fd7ce5SGreg Kroah-Hartman 		   ECHOCTL | ECHOKE | IEXTEN,
13096fd7ce5SGreg Kroah-Hartman 	.c_cc = INIT_C_CC,
13196fd7ce5SGreg Kroah-Hartman 	.c_ispeed = 38400,
132133b1306SPeter Hurley 	.c_ospeed = 38400,
133133b1306SPeter Hurley 	/* .c_line = N_TTY, */
13496fd7ce5SGreg Kroah-Hartman };
13596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_std_termios);
13696fd7ce5SGreg Kroah-Hartman 
13796fd7ce5SGreg Kroah-Hartman /* This list gets poked at by procfs and various bits of boot up code. This
138b426a5b8SXiaofei Tan  * could do with some rationalisation such as pulling the tty proc function
139b426a5b8SXiaofei Tan  * into this file.
140b426a5b8SXiaofei Tan  */
14196fd7ce5SGreg Kroah-Hartman 
14296fd7ce5SGreg Kroah-Hartman LIST_HEAD(tty_drivers);			/* linked list of tty drivers */
14396fd7ce5SGreg Kroah-Hartman 
144d1d027efSPeter Hurley /* Mutex to protect creating and releasing a tty */
14596fd7ce5SGreg Kroah-Hartman DEFINE_MUTEX(tty_mutex);
14696fd7ce5SGreg Kroah-Hartman 
147dd78b0c4SLinus Torvalds static ssize_t tty_read(struct kiocb *, struct iov_iter *);
1489bb48c82SLinus Torvalds static ssize_t tty_write(struct kiocb *, struct iov_iter *);
149afc9a42bSAl Viro static __poll_t tty_poll(struct file *, poll_table *);
15096fd7ce5SGreg Kroah-Hartman static int tty_open(struct inode *, struct file *);
15196fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_COMPAT
15296fd7ce5SGreg Kroah-Hartman static long tty_compat_ioctl(struct file *file, unsigned int cmd,
15396fd7ce5SGreg Kroah-Hartman 				unsigned long arg);
15496fd7ce5SGreg Kroah-Hartman #else
15596fd7ce5SGreg Kroah-Hartman #define tty_compat_ioctl NULL
15696fd7ce5SGreg Kroah-Hartman #endif
15796fd7ce5SGreg Kroah-Hartman static int __tty_fasync(int fd, struct file *filp, int on);
15896fd7ce5SGreg Kroah-Hartman static int tty_fasync(int fd, struct file *filp, int on);
15996fd7ce5SGreg Kroah-Hartman static void release_tty(struct tty_struct *tty, int idx);
16096fd7ce5SGreg Kroah-Hartman 
16196fd7ce5SGreg Kroah-Hartman /**
16296fd7ce5SGreg Kroah-Hartman  * free_tty_struct	-	free a disused tty
16396fd7ce5SGreg Kroah-Hartman  * @tty: tty struct to free
16496fd7ce5SGreg Kroah-Hartman  *
16596fd7ce5SGreg Kroah-Hartman  * Free the write buffers, tty queue and tty memory itself.
16696fd7ce5SGreg Kroah-Hartman  *
16796fd7ce5SGreg Kroah-Hartman  * Locking: none. Must be called after tty is definitely unused
16896fd7ce5SGreg Kroah-Hartman  */
free_tty_struct(struct tty_struct * tty)169a3123fd0SPeter Hurley static void free_tty_struct(struct tty_struct *tty)
17096fd7ce5SGreg Kroah-Hartman {
171c8b710b3SPeter Hurley 	tty_ldisc_deinit(tty);
17296fd7ce5SGreg Kroah-Hartman 	put_device(tty->dev);
173d6d9d17aSAndy Shevchenko 	kvfree(tty->write_buf);
17496fd7ce5SGreg Kroah-Hartman 	kfree(tty);
17596fd7ce5SGreg Kroah-Hartman }
17696fd7ce5SGreg Kroah-Hartman 
file_tty(struct file * file)17796fd7ce5SGreg Kroah-Hartman static inline struct tty_struct *file_tty(struct file *file)
17896fd7ce5SGreg Kroah-Hartman {
17996fd7ce5SGreg Kroah-Hartman 	return ((struct tty_file_private *)file->private_data)->tty;
18096fd7ce5SGreg Kroah-Hartman }
18196fd7ce5SGreg Kroah-Hartman 
tty_alloc_file(struct file * file)182fa90e1c9SJiri Slaby int tty_alloc_file(struct file *file)
18396fd7ce5SGreg Kroah-Hartman {
18496fd7ce5SGreg Kroah-Hartman 	struct tty_file_private *priv;
18596fd7ce5SGreg Kroah-Hartman 
18696fd7ce5SGreg Kroah-Hartman 	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
18796fd7ce5SGreg Kroah-Hartman 	if (!priv)
18896fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
18996fd7ce5SGreg Kroah-Hartman 
190fa90e1c9SJiri Slaby 	file->private_data = priv;
191fa90e1c9SJiri Slaby 
192fa90e1c9SJiri Slaby 	return 0;
193fa90e1c9SJiri Slaby }
194fa90e1c9SJiri Slaby 
195fa90e1c9SJiri Slaby /* Associate a new file with the tty structure */
tty_add_file(struct tty_struct * tty,struct file * file)196fa90e1c9SJiri Slaby void tty_add_file(struct tty_struct *tty, struct file *file)
197fa90e1c9SJiri Slaby {
198fa90e1c9SJiri Slaby 	struct tty_file_private *priv = file->private_data;
199fa90e1c9SJiri Slaby 
20096fd7ce5SGreg Kroah-Hartman 	priv->tty = tty;
20196fd7ce5SGreg Kroah-Hartman 	priv->file = file;
20296fd7ce5SGreg Kroah-Hartman 
2034a510969SPeter Hurley 	spin_lock(&tty->files_lock);
20496fd7ce5SGreg Kroah-Hartman 	list_add(&priv->list, &tty->tty_files);
2054a510969SPeter Hurley 	spin_unlock(&tty->files_lock);
206fa90e1c9SJiri Slaby }
20796fd7ce5SGreg Kroah-Hartman 
208796a75a9SJiri Slaby /**
209fa90e1c9SJiri Slaby  * tty_free_file - free file->private_data
210796a75a9SJiri Slaby  * @file: to free private_data of
211fa90e1c9SJiri Slaby  *
212fa90e1c9SJiri Slaby  * This shall be used only for fail path handling when tty_add_file was not
213fa90e1c9SJiri Slaby  * called yet.
214fa90e1c9SJiri Slaby  */
tty_free_file(struct file * file)215fa90e1c9SJiri Slaby void tty_free_file(struct file *file)
216fa90e1c9SJiri Slaby {
217fa90e1c9SJiri Slaby 	struct tty_file_private *priv = file->private_data;
218fa90e1c9SJiri Slaby 
219fa90e1c9SJiri Slaby 	file->private_data = NULL;
220fa90e1c9SJiri Slaby 	kfree(priv);
22196fd7ce5SGreg Kroah-Hartman }
22296fd7ce5SGreg Kroah-Hartman 
22396fd7ce5SGreg Kroah-Hartman /* Delete file from its tty */
tty_del_file(struct file * file)2242520e274SJosh Triplett static void tty_del_file(struct file *file)
22596fd7ce5SGreg Kroah-Hartman {
22696fd7ce5SGreg Kroah-Hartman 	struct tty_file_private *priv = file->private_data;
2274a510969SPeter Hurley 	struct tty_struct *tty = priv->tty;
22896fd7ce5SGreg Kroah-Hartman 
2294a510969SPeter Hurley 	spin_lock(&tty->files_lock);
23096fd7ce5SGreg Kroah-Hartman 	list_del(&priv->list);
2314a510969SPeter Hurley 	spin_unlock(&tty->files_lock);
232fa90e1c9SJiri Slaby 	tty_free_file(file);
23396fd7ce5SGreg Kroah-Hartman }
23496fd7ce5SGreg Kroah-Hartman 
23596fd7ce5SGreg Kroah-Hartman /**
23696fd7ce5SGreg Kroah-Hartman  * tty_name	-	return tty naming
23796fd7ce5SGreg Kroah-Hartman  * @tty: tty structure
23896fd7ce5SGreg Kroah-Hartman  *
239796a75a9SJiri Slaby  * Convert a tty structure into a name. The name reflects the kernel naming
240796a75a9SJiri Slaby  * policy and if udev is in use may not reflect user space
24196fd7ce5SGreg Kroah-Hartman  *
24296fd7ce5SGreg Kroah-Hartman  * Locking: none
24396fd7ce5SGreg Kroah-Hartman  */
tty_name(const struct tty_struct * tty)244429b4749SRasmus Villemoes const char *tty_name(const struct tty_struct *tty)
24596fd7ce5SGreg Kroah-Hartman {
24696fd7ce5SGreg Kroah-Hartman 	if (!tty) /* Hmm.  NULL pointer.  That's fun. */
247917162c9SRasmus Villemoes 		return "NULL tty";
248917162c9SRasmus Villemoes 	return tty->name;
24996fd7ce5SGreg Kroah-Hartman }
25096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_name);
25196fd7ce5SGreg Kroah-Hartman 
tty_driver_name(const struct tty_struct * tty)2520a083eddSPeter Hurley const char *tty_driver_name(const struct tty_struct *tty)
2530a083eddSPeter Hurley {
2540a083eddSPeter Hurley 	if (!tty || !tty->driver)
2550a083eddSPeter Hurley 		return "";
2560a083eddSPeter Hurley 	return tty->driver->name;
2570a083eddSPeter Hurley }
2580a083eddSPeter Hurley 
tty_paranoia_check(struct tty_struct * tty,struct inode * inode,const char * routine)25982b8f888SPeter Hurley static int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
26096fd7ce5SGreg Kroah-Hartman 			      const char *routine)
26196fd7ce5SGreg Kroah-Hartman {
26296fd7ce5SGreg Kroah-Hartman #ifdef TTY_PARANOIA_CHECK
26396fd7ce5SGreg Kroah-Hartman 	if (!tty) {
26489222e62SPeter Hurley 		pr_warn("(%d:%d): %s: NULL tty\n",
26596fd7ce5SGreg Kroah-Hartman 			imajor(inode), iminor(inode), routine);
26696fd7ce5SGreg Kroah-Hartman 		return 1;
26796fd7ce5SGreg Kroah-Hartman 	}
26896fd7ce5SGreg Kroah-Hartman #endif
26996fd7ce5SGreg Kroah-Hartman 	return 0;
27096fd7ce5SGreg Kroah-Hartman }
27196fd7ce5SGreg Kroah-Hartman 
272deb287e7SPeter Hurley /* Caller must hold tty_lock */
check_tty_count(struct tty_struct * tty,const char * routine)273b4940f6cSJiri Slaby static void check_tty_count(struct tty_struct *tty, const char *routine)
27496fd7ce5SGreg Kroah-Hartman {
27596fd7ce5SGreg Kroah-Hartman #ifdef CHECK_TTY_COUNT
27696fd7ce5SGreg Kroah-Hartman 	struct list_head *p;
277a09ac397SOkash Khawaja 	int count = 0, kopen_count = 0;
27896fd7ce5SGreg Kroah-Hartman 
2794a510969SPeter Hurley 	spin_lock(&tty->files_lock);
28096fd7ce5SGreg Kroah-Hartman 	list_for_each(p, &tty->tty_files) {
28196fd7ce5SGreg Kroah-Hartman 		count++;
28296fd7ce5SGreg Kroah-Hartman 	}
2834a510969SPeter Hurley 	spin_unlock(&tty->files_lock);
28496fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
28596fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_SLAVE &&
28696fd7ce5SGreg Kroah-Hartman 	    tty->link && tty->link->count)
28796fd7ce5SGreg Kroah-Hartman 		count++;
288a09ac397SOkash Khawaja 	if (tty_port_kopened(tty->port))
289a09ac397SOkash Khawaja 		kopen_count++;
290a09ac397SOkash Khawaja 	if (tty->count != (count + kopen_count)) {
291a09ac397SOkash Khawaja 		tty_warn(tty, "%s: tty->count(%d) != (#fd's(%d) + #kopen's(%d))\n",
292a09ac397SOkash Khawaja 			 routine, tty->count, count, kopen_count);
29396fd7ce5SGreg Kroah-Hartman 	}
29496fd7ce5SGreg Kroah-Hartman #endif
29596fd7ce5SGreg Kroah-Hartman }
29696fd7ce5SGreg Kroah-Hartman 
29796fd7ce5SGreg Kroah-Hartman /**
29896fd7ce5SGreg Kroah-Hartman  * get_tty_driver		-	find device of a tty
299fa441954SJiri Slaby  * @device: device identifier
30096fd7ce5SGreg Kroah-Hartman  * @index: returns the index of the tty
30196fd7ce5SGreg Kroah-Hartman  *
302796a75a9SJiri Slaby  * This routine returns a tty driver structure, given a device number and also
303796a75a9SJiri Slaby  * passes back the index number.
30496fd7ce5SGreg Kroah-Hartman  *
30596fd7ce5SGreg Kroah-Hartman  * Locking: caller must hold tty_mutex
30696fd7ce5SGreg Kroah-Hartman  */
get_tty_driver(dev_t device,int * index)30796fd7ce5SGreg Kroah-Hartman static struct tty_driver *get_tty_driver(dev_t device, int *index)
30896fd7ce5SGreg Kroah-Hartman {
30996fd7ce5SGreg Kroah-Hartman 	struct tty_driver *p;
31096fd7ce5SGreg Kroah-Hartman 
31196fd7ce5SGreg Kroah-Hartman 	list_for_each_entry(p, &tty_drivers, tty_drivers) {
31296fd7ce5SGreg Kroah-Hartman 		dev_t base = MKDEV(p->major, p->minor_start);
313e73b2407SXiaofei Tan 
31496fd7ce5SGreg Kroah-Hartman 		if (device < base || device >= base + p->num)
31596fd7ce5SGreg Kroah-Hartman 			continue;
31696fd7ce5SGreg Kroah-Hartman 		*index = device - base;
31796fd7ce5SGreg Kroah-Hartman 		return tty_driver_kref_get(p);
31896fd7ce5SGreg Kroah-Hartman 	}
31996fd7ce5SGreg Kroah-Hartman 	return NULL;
32096fd7ce5SGreg Kroah-Hartman }
32196fd7ce5SGreg Kroah-Hartman 
322fc61ed51SOkash Khawaja /**
323fc61ed51SOkash Khawaja  * tty_dev_name_to_number	-	return dev_t for device name
324fc61ed51SOkash Khawaja  * @name: user space name of device under /dev
325fc61ed51SOkash Khawaja  * @number: pointer to dev_t that this function will populate
326fc61ed51SOkash Khawaja  *
327796a75a9SJiri Slaby  * This function converts device names like ttyS0 or ttyUSB1 into dev_t like
328796a75a9SJiri Slaby  * (4, 64) or (188, 1). If no corresponding driver is registered then the
329796a75a9SJiri Slaby  * function returns -%ENODEV.
330fc61ed51SOkash Khawaja  *
331fc61ed51SOkash Khawaja  * Locking: this acquires tty_mutex to protect the tty_drivers list from
332fc61ed51SOkash Khawaja  *	being modified while we are traversing it, and makes sure to
333fc61ed51SOkash Khawaja  *	release it before exiting.
334fc61ed51SOkash Khawaja  */
tty_dev_name_to_number(const char * name,dev_t * number)335fc61ed51SOkash Khawaja int tty_dev_name_to_number(const char *name, dev_t *number)
336fc61ed51SOkash Khawaja {
337fc61ed51SOkash Khawaja 	struct tty_driver *p;
338fc61ed51SOkash Khawaja 	int ret;
339fc61ed51SOkash Khawaja 	int index, prefix_length = 0;
340fc61ed51SOkash Khawaja 	const char *str;
341fc61ed51SOkash Khawaja 
342fc61ed51SOkash Khawaja 	for (str = name; *str && !isdigit(*str); str++)
343fc61ed51SOkash Khawaja 		;
344fc61ed51SOkash Khawaja 
345fc61ed51SOkash Khawaja 	if (!*str)
346fc61ed51SOkash Khawaja 		return -EINVAL;
347fc61ed51SOkash Khawaja 
348fc61ed51SOkash Khawaja 	ret = kstrtoint(str, 10, &index);
349fc61ed51SOkash Khawaja 	if (ret)
350fc61ed51SOkash Khawaja 		return ret;
351fc61ed51SOkash Khawaja 
352fc61ed51SOkash Khawaja 	prefix_length = str - name;
353fc61ed51SOkash Khawaja 	mutex_lock(&tty_mutex);
354fc61ed51SOkash Khawaja 
355fc61ed51SOkash Khawaja 	list_for_each_entry(p, &tty_drivers, tty_drivers)
356fc61ed51SOkash Khawaja 		if (prefix_length == strlen(p->name) && strncmp(name,
357fc61ed51SOkash Khawaja 					p->name, prefix_length) == 0) {
358fc61ed51SOkash Khawaja 			if (index < p->num) {
359fc61ed51SOkash Khawaja 				*number = MKDEV(p->major, p->minor_start + index);
360fc61ed51SOkash Khawaja 				goto out;
361fc61ed51SOkash Khawaja 			}
362fc61ed51SOkash Khawaja 		}
363fc61ed51SOkash Khawaja 
364fc61ed51SOkash Khawaja 	/* if here then driver wasn't found */
365fc61ed51SOkash Khawaja 	ret = -ENODEV;
366fc61ed51SOkash Khawaja out:
367fc61ed51SOkash Khawaja 	mutex_unlock(&tty_mutex);
368fc61ed51SOkash Khawaja 	return ret;
369fc61ed51SOkash Khawaja }
370fc61ed51SOkash Khawaja EXPORT_SYMBOL_GPL(tty_dev_name_to_number);
371fc61ed51SOkash Khawaja 
37296fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_CONSOLE_POLL
37396fd7ce5SGreg Kroah-Hartman 
37496fd7ce5SGreg Kroah-Hartman /**
37596fd7ce5SGreg Kroah-Hartman  * tty_find_polling_driver	-	find device of a polled tty
37696fd7ce5SGreg Kroah-Hartman  * @name: name string to match
37796fd7ce5SGreg Kroah-Hartman  * @line: pointer to resulting tty line nr
37896fd7ce5SGreg Kroah-Hartman  *
379796a75a9SJiri Slaby  * This routine returns a tty driver structure, given a name and the condition
380796a75a9SJiri Slaby  * that the tty driver is capable of polled operation.
38196fd7ce5SGreg Kroah-Hartman  */
tty_find_polling_driver(char * name,int * line)38296fd7ce5SGreg Kroah-Hartman struct tty_driver *tty_find_polling_driver(char *name, int *line)
38396fd7ce5SGreg Kroah-Hartman {
38496fd7ce5SGreg Kroah-Hartman 	struct tty_driver *p, *res = NULL;
38596fd7ce5SGreg Kroah-Hartman 	int tty_line = 0;
38696fd7ce5SGreg Kroah-Hartman 	int len;
38796fd7ce5SGreg Kroah-Hartman 	char *str, *stp;
38896fd7ce5SGreg Kroah-Hartman 
38996fd7ce5SGreg Kroah-Hartman 	for (str = name; *str; str++)
39096fd7ce5SGreg Kroah-Hartman 		if ((*str >= '0' && *str <= '9') || *str == ',')
39196fd7ce5SGreg Kroah-Hartman 			break;
39296fd7ce5SGreg Kroah-Hartman 	if (!*str)
39396fd7ce5SGreg Kroah-Hartman 		return NULL;
39496fd7ce5SGreg Kroah-Hartman 
39596fd7ce5SGreg Kroah-Hartman 	len = str - name;
39696fd7ce5SGreg Kroah-Hartman 	tty_line = simple_strtoul(str, &str, 10);
39796fd7ce5SGreg Kroah-Hartman 
39896fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
39996fd7ce5SGreg Kroah-Hartman 	/* Search through the tty devices to look for a match */
40096fd7ce5SGreg Kroah-Hartman 	list_for_each_entry(p, &tty_drivers, tty_drivers) {
40133a1a7beSMiles Chen 		if (!len || strncmp(name, p->name, len) != 0)
40296fd7ce5SGreg Kroah-Hartman 			continue;
40396fd7ce5SGreg Kroah-Hartman 		stp = str;
40496fd7ce5SGreg Kroah-Hartman 		if (*stp == ',')
40596fd7ce5SGreg Kroah-Hartman 			stp++;
40696fd7ce5SGreg Kroah-Hartman 		if (*stp == '\0')
40796fd7ce5SGreg Kroah-Hartman 			stp = NULL;
40896fd7ce5SGreg Kroah-Hartman 
40996fd7ce5SGreg Kroah-Hartman 		if (tty_line >= 0 && tty_line < p->num && p->ops &&
41096fd7ce5SGreg Kroah-Hartman 		    p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
41196fd7ce5SGreg Kroah-Hartman 			res = tty_driver_kref_get(p);
41296fd7ce5SGreg Kroah-Hartman 			*line = tty_line;
41396fd7ce5SGreg Kroah-Hartman 			break;
41496fd7ce5SGreg Kroah-Hartman 		}
41596fd7ce5SGreg Kroah-Hartman 	}
41696fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
41796fd7ce5SGreg Kroah-Hartman 
41896fd7ce5SGreg Kroah-Hartman 	return res;
41996fd7ce5SGreg Kroah-Hartman }
42096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_find_polling_driver);
42196fd7ce5SGreg Kroah-Hartman #endif
42296fd7ce5SGreg Kroah-Hartman 
hung_up_tty_read(struct kiocb * iocb,struct iov_iter * to)423ddc5fda7SLinus Torvalds static ssize_t hung_up_tty_read(struct kiocb *iocb, struct iov_iter *to)
42496fd7ce5SGreg Kroah-Hartman {
42596fd7ce5SGreg Kroah-Hartman 	return 0;
42696fd7ce5SGreg Kroah-Hartman }
42796fd7ce5SGreg Kroah-Hartman 
hung_up_tty_write(struct kiocb * iocb,struct iov_iter * from)42817749851SLinus Torvalds static ssize_t hung_up_tty_write(struct kiocb *iocb, struct iov_iter *from)
42996fd7ce5SGreg Kroah-Hartman {
43096fd7ce5SGreg Kroah-Hartman 	return -EIO;
43196fd7ce5SGreg Kroah-Hartman }
43296fd7ce5SGreg Kroah-Hartman 
43396fd7ce5SGreg Kroah-Hartman /* No kernel lock held - none needed ;) */
hung_up_tty_poll(struct file * filp,poll_table * wait)434afc9a42bSAl Viro static __poll_t hung_up_tty_poll(struct file *filp, poll_table *wait)
43596fd7ce5SGreg Kroah-Hartman {
436a9a08845SLinus Torvalds 	return EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP | EPOLLRDNORM | EPOLLWRNORM;
43796fd7ce5SGreg Kroah-Hartman }
43896fd7ce5SGreg Kroah-Hartman 
hung_up_tty_ioctl(struct file * file,unsigned int cmd,unsigned long arg)43996fd7ce5SGreg Kroah-Hartman static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
44096fd7ce5SGreg Kroah-Hartman 		unsigned long arg)
44196fd7ce5SGreg Kroah-Hartman {
44296fd7ce5SGreg Kroah-Hartman 	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
44396fd7ce5SGreg Kroah-Hartman }
44496fd7ce5SGreg Kroah-Hartman 
hung_up_tty_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)44596fd7ce5SGreg Kroah-Hartman static long hung_up_tty_compat_ioctl(struct file *file,
44696fd7ce5SGreg Kroah-Hartman 				     unsigned int cmd, unsigned long arg)
44796fd7ce5SGreg Kroah-Hartman {
44896fd7ce5SGreg Kroah-Hartman 	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
44996fd7ce5SGreg Kroah-Hartman }
45096fd7ce5SGreg Kroah-Hartman 
hung_up_tty_fasync(int fd,struct file * file,int on)451f557474cSPeter Hurley static int hung_up_tty_fasync(int fd, struct file *file, int on)
452f557474cSPeter Hurley {
453f557474cSPeter Hurley 	return -ENOTTY;
454f557474cSPeter Hurley }
455f557474cSPeter Hurley 
tty_show_fdinfo(struct seq_file * m,struct file * file)456d01c3289SMasatake YAMATO static void tty_show_fdinfo(struct seq_file *m, struct file *file)
457d01c3289SMasatake YAMATO {
458d01c3289SMasatake YAMATO 	struct tty_struct *tty = file_tty(file);
459d01c3289SMasatake YAMATO 
460d01c3289SMasatake YAMATO 	if (tty && tty->ops && tty->ops->show_fdinfo)
461d01c3289SMasatake YAMATO 		tty->ops->show_fdinfo(tty, m);
462d01c3289SMasatake YAMATO }
463d01c3289SMasatake YAMATO 
46496fd7ce5SGreg Kroah-Hartman static const struct file_operations tty_fops = {
46596fd7ce5SGreg Kroah-Hartman 	.llseek		= no_llseek,
466dd78b0c4SLinus Torvalds 	.read_iter	= tty_read,
4679bb48c82SLinus Torvalds 	.write_iter	= tty_write,
468b0072734SDavid Howells 	.splice_read	= copy_splice_read,
4699bb48c82SLinus Torvalds 	.splice_write	= iter_file_splice_write,
47096fd7ce5SGreg Kroah-Hartman 	.poll		= tty_poll,
47196fd7ce5SGreg Kroah-Hartman 	.unlocked_ioctl	= tty_ioctl,
47296fd7ce5SGreg Kroah-Hartman 	.compat_ioctl	= tty_compat_ioctl,
47396fd7ce5SGreg Kroah-Hartman 	.open		= tty_open,
47496fd7ce5SGreg Kroah-Hartman 	.release	= tty_release,
47596fd7ce5SGreg Kroah-Hartman 	.fasync		= tty_fasync,
476d01c3289SMasatake YAMATO 	.show_fdinfo	= tty_show_fdinfo,
47796fd7ce5SGreg Kroah-Hartman };
47896fd7ce5SGreg Kroah-Hartman 
47996fd7ce5SGreg Kroah-Hartman static const struct file_operations console_fops = {
48096fd7ce5SGreg Kroah-Hartman 	.llseek		= no_llseek,
481dd78b0c4SLinus Torvalds 	.read_iter	= tty_read,
4829bb48c82SLinus Torvalds 	.write_iter	= redirected_tty_write,
483b0072734SDavid Howells 	.splice_read	= copy_splice_read,
4849bb48c82SLinus Torvalds 	.splice_write	= iter_file_splice_write,
48596fd7ce5SGreg Kroah-Hartman 	.poll		= tty_poll,
48696fd7ce5SGreg Kroah-Hartman 	.unlocked_ioctl	= tty_ioctl,
48796fd7ce5SGreg Kroah-Hartman 	.compat_ioctl	= tty_compat_ioctl,
48896fd7ce5SGreg Kroah-Hartman 	.open		= tty_open,
48996fd7ce5SGreg Kroah-Hartman 	.release	= tty_release,
49096fd7ce5SGreg Kroah-Hartman 	.fasync		= tty_fasync,
49196fd7ce5SGreg Kroah-Hartman };
49296fd7ce5SGreg Kroah-Hartman 
49396fd7ce5SGreg Kroah-Hartman static const struct file_operations hung_up_tty_fops = {
49496fd7ce5SGreg Kroah-Hartman 	.llseek		= no_llseek,
495ddc5fda7SLinus Torvalds 	.read_iter	= hung_up_tty_read,
49617749851SLinus Torvalds 	.write_iter	= hung_up_tty_write,
49796fd7ce5SGreg Kroah-Hartman 	.poll		= hung_up_tty_poll,
49896fd7ce5SGreg Kroah-Hartman 	.unlocked_ioctl	= hung_up_tty_ioctl,
49996fd7ce5SGreg Kroah-Hartman 	.compat_ioctl	= hung_up_tty_compat_ioctl,
50096fd7ce5SGreg Kroah-Hartman 	.release	= tty_release,
501f557474cSPeter Hurley 	.fasync		= hung_up_tty_fasync,
50296fd7ce5SGreg Kroah-Hartman };
50396fd7ce5SGreg Kroah-Hartman 
50496fd7ce5SGreg Kroah-Hartman static DEFINE_SPINLOCK(redirect_lock);
50596fd7ce5SGreg Kroah-Hartman static struct file *redirect;
50696fd7ce5SGreg Kroah-Hartman 
50796fd7ce5SGreg Kroah-Hartman /**
50896fd7ce5SGreg Kroah-Hartman  * tty_wakeup	-	request more data
50996fd7ce5SGreg Kroah-Hartman  * @tty: terminal
51096fd7ce5SGreg Kroah-Hartman  *
511796a75a9SJiri Slaby  * Internal and external helper for wakeups of tty. This function informs the
512796a75a9SJiri Slaby  * line discipline if present that the driver is ready to receive more output
513796a75a9SJiri Slaby  * data.
51496fd7ce5SGreg Kroah-Hartman  */
tty_wakeup(struct tty_struct * tty)51596fd7ce5SGreg Kroah-Hartman void tty_wakeup(struct tty_struct *tty)
51696fd7ce5SGreg Kroah-Hartman {
51796fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
51896fd7ce5SGreg Kroah-Hartman 
51996fd7ce5SGreg Kroah-Hartman 	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
52096fd7ce5SGreg Kroah-Hartman 		ld = tty_ldisc_ref(tty);
52196fd7ce5SGreg Kroah-Hartman 		if (ld) {
52296fd7ce5SGreg Kroah-Hartman 			if (ld->ops->write_wakeup)
52396fd7ce5SGreg Kroah-Hartman 				ld->ops->write_wakeup(tty);
52496fd7ce5SGreg Kroah-Hartman 			tty_ldisc_deref(ld);
52596fd7ce5SGreg Kroah-Hartman 		}
52696fd7ce5SGreg Kroah-Hartman 	}
527a9a08845SLinus Torvalds 	wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT);
52896fd7ce5SGreg Kroah-Hartman }
52996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_wakeup);
53096fd7ce5SGreg Kroah-Hartman 
53196fd7ce5SGreg Kroah-Hartman /**
532f446776eSCorey Minyard  * tty_release_redirect	-	Release a redirect on a pty if present
533f446776eSCorey Minyard  * @tty: tty device
534f446776eSCorey Minyard  *
535796a75a9SJiri Slaby  * This is available to the pty code so if the master closes, if the slave is a
536796a75a9SJiri Slaby  * redirect it can release the redirect.
537f446776eSCorey Minyard  */
tty_release_redirect(struct tty_struct * tty)538dd9f6114SGreg Kroah-Hartman static struct file *tty_release_redirect(struct tty_struct *tty)
539f446776eSCorey Minyard {
540f446776eSCorey Minyard 	struct file *f = NULL;
541f446776eSCorey Minyard 
542f446776eSCorey Minyard 	spin_lock(&redirect_lock);
543f446776eSCorey Minyard 	if (redirect && file_tty(redirect) == tty) {
544f446776eSCorey Minyard 		f = redirect;
545f446776eSCorey Minyard 		redirect = NULL;
546f446776eSCorey Minyard 	}
547f446776eSCorey Minyard 	spin_unlock(&redirect_lock);
548f446776eSCorey Minyard 
549f446776eSCorey Minyard 	return f;
550f446776eSCorey Minyard }
551f446776eSCorey Minyard 
552f446776eSCorey Minyard /**
55396fd7ce5SGreg Kroah-Hartman  * __tty_hangup		-	actual handler for hangup events
554fa441954SJiri Slaby  * @tty: tty device
55508aa5042SLee Jones  * @exit_session: if non-zero, signal all foreground group processes
55696fd7ce5SGreg Kroah-Hartman  *
557796a75a9SJiri Slaby  * This can be called by a "kworker" kernel thread. That is process synchronous
558796a75a9SJiri Slaby  * but doesn't hold any locks, so we need to make sure we have the appropriate
559796a75a9SJiri Slaby  * locks for what we're doing.
56096fd7ce5SGreg Kroah-Hartman  *
561796a75a9SJiri Slaby  * The hangup event clears any pending redirections onto the hung up device. It
562796a75a9SJiri Slaby  * ensures future writes will error and it does the needed line discipline
563796a75a9SJiri Slaby  * hangup and signal delivery. The tty object itself remains intact.
56496fd7ce5SGreg Kroah-Hartman  *
56596fd7ce5SGreg Kroah-Hartman  * Locking:
566796a75a9SJiri Slaby  *  * BTM
567796a75a9SJiri Slaby  *
568796a75a9SJiri Slaby  *   * redirect lock for undoing redirection
569796a75a9SJiri Slaby  *   * file list lock for manipulating list of ttys
570796a75a9SJiri Slaby  *   * tty_ldiscs_lock from called functions
571796a75a9SJiri Slaby  *   * termios_rwsem resetting termios data
572796a75a9SJiri Slaby  *   * tasklist_lock to walk task list for hangup event
573796a75a9SJiri Slaby  *
574796a75a9SJiri Slaby  *    * ->siglock to protect ->signal/->sighand
575796a75a9SJiri Slaby  *
57696fd7ce5SGreg Kroah-Hartman  */
__tty_hangup(struct tty_struct * tty,int exit_session)577f91e2590SPeter Hurley static void __tty_hangup(struct tty_struct *tty, int exit_session)
57896fd7ce5SGreg Kroah-Hartman {
57996fd7ce5SGreg Kroah-Hartman 	struct file *cons_filp = NULL;
580f446776eSCorey Minyard 	struct file *filp, *f;
58196fd7ce5SGreg Kroah-Hartman 	struct tty_file_private *priv;
58296fd7ce5SGreg Kroah-Hartman 	int    closecount = 0, n;
583ea648a47SPeter Hurley 	int refs;
58496fd7ce5SGreg Kroah-Hartman 
58596fd7ce5SGreg Kroah-Hartman 	if (!tty)
58696fd7ce5SGreg Kroah-Hartman 		return;
58796fd7ce5SGreg Kroah-Hartman 
588f446776eSCorey Minyard 	f = tty_release_redirect(tty);
58996fd7ce5SGreg Kroah-Hartman 
59089c8d91eSAlan Cox 	tty_lock(tty);
59196fd7ce5SGreg Kroah-Hartman 
592cb50e523SPeter Hurley 	if (test_bit(TTY_HUPPED, &tty->flags)) {
593cb50e523SPeter Hurley 		tty_unlock(tty);
594cb50e523SPeter Hurley 		return;
595cb50e523SPeter Hurley 	}
596cb50e523SPeter Hurley 
59728b0f8a6STejun Heo 	/*
59828b0f8a6STejun Heo 	 * Some console devices aren't actually hung up for technical and
59928b0f8a6STejun Heo 	 * historical reasons, which can lead to indefinite interruptible
60028b0f8a6STejun Heo 	 * sleep in n_tty_read().  The following explicitly tells
60128b0f8a6STejun Heo 	 * n_tty_read() to abort readers.
60228b0f8a6STejun Heo 	 */
60328b0f8a6STejun Heo 	set_bit(TTY_HUPPING, &tty->flags);
60428b0f8a6STejun Heo 
60596fd7ce5SGreg Kroah-Hartman 	/* inuse_filps is protected by the single tty lock,
606b426a5b8SXiaofei Tan 	 * this really needs to change if we want to flush the
607b426a5b8SXiaofei Tan 	 * workqueue with the lock held.
608b426a5b8SXiaofei Tan 	 */
60996fd7ce5SGreg Kroah-Hartman 	check_tty_count(tty, "tty_hangup");
61096fd7ce5SGreg Kroah-Hartman 
6114a510969SPeter Hurley 	spin_lock(&tty->files_lock);
61296fd7ce5SGreg Kroah-Hartman 	/* This breaks for file handles being sent over AF_UNIX sockets ? */
61396fd7ce5SGreg Kroah-Hartman 	list_for_each_entry(priv, &tty->tty_files, list) {
61496fd7ce5SGreg Kroah-Hartman 		filp = priv->file;
6159bb48c82SLinus Torvalds 		if (filp->f_op->write_iter == redirected_tty_write)
61696fd7ce5SGreg Kroah-Hartman 			cons_filp = filp;
6179bb48c82SLinus Torvalds 		if (filp->f_op->write_iter != tty_write)
61896fd7ce5SGreg Kroah-Hartman 			continue;
61996fd7ce5SGreg Kroah-Hartman 		closecount++;
62096fd7ce5SGreg Kroah-Hartman 		__tty_fasync(-1, filp, 0);	/* can't block */
62196fd7ce5SGreg Kroah-Hartman 		filp->f_op = &hung_up_tty_fops;
62296fd7ce5SGreg Kroah-Hartman 	}
6234a510969SPeter Hurley 	spin_unlock(&tty->files_lock);
62496fd7ce5SGreg Kroah-Hartman 
62525fdf243SPeter Hurley 	refs = tty_signal_session_leader(tty, exit_session);
62625fdf243SPeter Hurley 	/* Account for the p->signal references we killed */
62725fdf243SPeter Hurley 	while (refs--)
62825fdf243SPeter Hurley 		tty_kref_put(tty);
62925fdf243SPeter Hurley 
630892d1fa7SPeter Hurley 	tty_ldisc_hangup(tty, cons_filp != NULL);
63196fd7ce5SGreg Kroah-Hartman 
63264d608dbSJiri Slaby 	spin_lock_irq(&tty->ctrl.lock);
63396fd7ce5SGreg Kroah-Hartman 	clear_bit(TTY_THROTTLED, &tty->flags);
63496fd7ce5SGreg Kroah-Hartman 	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
63564d608dbSJiri Slaby 	put_pid(tty->ctrl.session);
63664d608dbSJiri Slaby 	put_pid(tty->ctrl.pgrp);
63764d608dbSJiri Slaby 	tty->ctrl.session = NULL;
63864d608dbSJiri Slaby 	tty->ctrl.pgrp = NULL;
63964d608dbSJiri Slaby 	tty->ctrl.pktstatus = 0;
64064d608dbSJiri Slaby 	spin_unlock_irq(&tty->ctrl.lock);
64196fd7ce5SGreg Kroah-Hartman 
64296fd7ce5SGreg Kroah-Hartman 	/*
64396fd7ce5SGreg Kroah-Hartman 	 * If one of the devices matches a console pointer, we
64496fd7ce5SGreg Kroah-Hartman 	 * cannot just call hangup() because that will cause
64596fd7ce5SGreg Kroah-Hartman 	 * tty->count and state->count to go out of sync.
64696fd7ce5SGreg Kroah-Hartman 	 * So we just call close() the right number of times.
64796fd7ce5SGreg Kroah-Hartman 	 */
64896fd7ce5SGreg Kroah-Hartman 	if (cons_filp) {
64996fd7ce5SGreg Kroah-Hartman 		if (tty->ops->close)
65096fd7ce5SGreg Kroah-Hartman 			for (n = 0; n < closecount; n++)
65196fd7ce5SGreg Kroah-Hartman 				tty->ops->close(tty, cons_filp);
65296fd7ce5SGreg Kroah-Hartman 	} else if (tty->ops->hangup)
6537c6d340fSPeter Hurley 		tty->ops->hangup(tty);
65496fd7ce5SGreg Kroah-Hartman 	/*
655892d1fa7SPeter Hurley 	 * We don't want to have driver/ldisc interactions beyond the ones
656892d1fa7SPeter Hurley 	 * we did here. The driver layer expects no calls after ->hangup()
657892d1fa7SPeter Hurley 	 * from the ldisc side, which is now guaranteed.
65896fd7ce5SGreg Kroah-Hartman 	 */
65996fd7ce5SGreg Kroah-Hartman 	set_bit(TTY_HUPPED, &tty->flags);
66028b0f8a6STejun Heo 	clear_bit(TTY_HUPPING, &tty->flags);
66189c8d91eSAlan Cox 	tty_unlock(tty);
66296fd7ce5SGreg Kroah-Hartman 
66396fd7ce5SGreg Kroah-Hartman 	if (f)
66496fd7ce5SGreg Kroah-Hartman 		fput(f);
66596fd7ce5SGreg Kroah-Hartman }
66696fd7ce5SGreg Kroah-Hartman 
do_tty_hangup(struct work_struct * work)66796fd7ce5SGreg Kroah-Hartman static void do_tty_hangup(struct work_struct *work)
66896fd7ce5SGreg Kroah-Hartman {
66996fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty =
67096fd7ce5SGreg Kroah-Hartman 		container_of(work, struct tty_struct, hangup_work);
67196fd7ce5SGreg Kroah-Hartman 
672f91e2590SPeter Hurley 	__tty_hangup(tty, 0);
67396fd7ce5SGreg Kroah-Hartman }
67496fd7ce5SGreg Kroah-Hartman 
67596fd7ce5SGreg Kroah-Hartman /**
67696fd7ce5SGreg Kroah-Hartman  * tty_hangup		-	trigger a hangup event
67796fd7ce5SGreg Kroah-Hartman  * @tty: tty to hangup
67896fd7ce5SGreg Kroah-Hartman  *
679796a75a9SJiri Slaby  * A carrier loss (virtual or otherwise) has occurred on @tty. Schedule a
680796a75a9SJiri Slaby  * hangup sequence to run after this event.
68196fd7ce5SGreg Kroah-Hartman  */
tty_hangup(struct tty_struct * tty)68296fd7ce5SGreg Kroah-Hartman void tty_hangup(struct tty_struct *tty)
68396fd7ce5SGreg Kroah-Hartman {
684d435cefeSPeter Hurley 	tty_debug_hangup(tty, "hangup\n");
68596fd7ce5SGreg Kroah-Hartman 	schedule_work(&tty->hangup_work);
68696fd7ce5SGreg Kroah-Hartman }
68796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_hangup);
68896fd7ce5SGreg Kroah-Hartman 
68996fd7ce5SGreg Kroah-Hartman /**
69096fd7ce5SGreg Kroah-Hartman  * tty_vhangup		-	process vhangup
69196fd7ce5SGreg Kroah-Hartman  * @tty: tty to hangup
69296fd7ce5SGreg Kroah-Hartman  *
693796a75a9SJiri Slaby  * The user has asked via system call for the terminal to be hung up. We do
694796a75a9SJiri Slaby  * this synchronously so that when the syscall returns the process is complete.
695796a75a9SJiri Slaby  * That guarantee is necessary for security reasons.
69696fd7ce5SGreg Kroah-Hartman  */
tty_vhangup(struct tty_struct * tty)69796fd7ce5SGreg Kroah-Hartman void tty_vhangup(struct tty_struct *tty)
69896fd7ce5SGreg Kroah-Hartman {
699d435cefeSPeter Hurley 	tty_debug_hangup(tty, "vhangup\n");
700f91e2590SPeter Hurley 	__tty_hangup(tty, 0);
70196fd7ce5SGreg Kroah-Hartman }
70296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_vhangup);
70396fd7ce5SGreg Kroah-Hartman 
70496fd7ce5SGreg Kroah-Hartman 
70596fd7ce5SGreg Kroah-Hartman /**
70696fd7ce5SGreg Kroah-Hartman  * tty_vhangup_self	-	process vhangup for own ctty
70796fd7ce5SGreg Kroah-Hartman  *
70896fd7ce5SGreg Kroah-Hartman  * Perform a vhangup on the current controlling tty
70996fd7ce5SGreg Kroah-Hartman  */
tty_vhangup_self(void)71096fd7ce5SGreg Kroah-Hartman void tty_vhangup_self(void)
71196fd7ce5SGreg Kroah-Hartman {
71296fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
71396fd7ce5SGreg Kroah-Hartman 
71496fd7ce5SGreg Kroah-Hartman 	tty = get_current_tty();
71596fd7ce5SGreg Kroah-Hartman 	if (tty) {
71696fd7ce5SGreg Kroah-Hartman 		tty_vhangup(tty);
71796fd7ce5SGreg Kroah-Hartman 		tty_kref_put(tty);
71896fd7ce5SGreg Kroah-Hartman 	}
71996fd7ce5SGreg Kroah-Hartman }
72096fd7ce5SGreg Kroah-Hartman 
72196fd7ce5SGreg Kroah-Hartman /**
722f91e2590SPeter Hurley  * tty_vhangup_session	-	hangup session leader exit
723f91e2590SPeter Hurley  * @tty: tty to hangup
724f91e2590SPeter Hurley  *
725f91e2590SPeter Hurley  * The session leader is exiting and hanging up its controlling terminal.
726796a75a9SJiri Slaby  * Every process in the foreground process group is signalled %SIGHUP.
727f91e2590SPeter Hurley  *
728796a75a9SJiri Slaby  * We do this synchronously so that when the syscall returns the process is
729796a75a9SJiri Slaby  * complete. That guarantee is necessary for security reasons.
730f91e2590SPeter Hurley  */
tty_vhangup_session(struct tty_struct * tty)731a1235b3eSNicolas Pitre void tty_vhangup_session(struct tty_struct *tty)
732f91e2590SPeter Hurley {
733d435cefeSPeter Hurley 	tty_debug_hangup(tty, "session hangup\n");
734f91e2590SPeter Hurley 	__tty_hangup(tty, 1);
735f91e2590SPeter Hurley }
736f91e2590SPeter Hurley 
737f91e2590SPeter Hurley /**
73896fd7ce5SGreg Kroah-Hartman  * tty_hung_up_p	-	was tty hung up
73996fd7ce5SGreg Kroah-Hartman  * @filp: file pointer of tty
74096fd7ce5SGreg Kroah-Hartman  *
741796a75a9SJiri Slaby  * Return: true if the tty has been subject to a vhangup or a carrier loss
74296fd7ce5SGreg Kroah-Hartman  */
tty_hung_up_p(struct file * filp)74396fd7ce5SGreg Kroah-Hartman int tty_hung_up_p(struct file *filp)
74496fd7ce5SGreg Kroah-Hartman {
745ed3f0af8SAlan Cox 	return (filp && filp->f_op == &hung_up_tty_fops);
74696fd7ce5SGreg Kroah-Hartman }
74796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_hung_up_p);
74896fd7ce5SGreg Kroah-Hartman 
__stop_tty(struct tty_struct * tty)749d06e256fSJiri Slaby void __stop_tty(struct tty_struct *tty)
750d06e256fSJiri Slaby {
751d06e256fSJiri Slaby 	if (tty->flow.stopped)
752d06e256fSJiri Slaby 		return;
753d06e256fSJiri Slaby 	tty->flow.stopped = true;
754d06e256fSJiri Slaby 	if (tty->ops->stop)
755d06e256fSJiri Slaby 		tty->ops->stop(tty);
756d06e256fSJiri Slaby }
757d06e256fSJiri Slaby 
75896fd7ce5SGreg Kroah-Hartman /**
75996fd7ce5SGreg Kroah-Hartman  * stop_tty	-	propagate flow control
76096fd7ce5SGreg Kroah-Hartman  * @tty: tty to stop
76196fd7ce5SGreg Kroah-Hartman  *
762796a75a9SJiri Slaby  * Perform flow control to the driver. May be called on an already stopped
763796a75a9SJiri Slaby  * device and will not re-call the &tty_driver->stop() method.
76496fd7ce5SGreg Kroah-Hartman  *
765796a75a9SJiri Slaby  * This functionality is used by both the line disciplines for halting incoming
766796a75a9SJiri Slaby  * flow and by the driver. It may therefore be called from any context, may be
767796a75a9SJiri Slaby  * under the tty %atomic_write_lock but not always.
76896fd7ce5SGreg Kroah-Hartman  *
76996fd7ce5SGreg Kroah-Hartman  * Locking:
7706e94dbc7SJiri Slaby  *	flow.lock
77196fd7ce5SGreg Kroah-Hartman  */
stop_tty(struct tty_struct * tty)772f9e053dcSPeter Hurley void stop_tty(struct tty_struct *tty)
773f9e053dcSPeter Hurley {
774f9e053dcSPeter Hurley 	unsigned long flags;
775f9e053dcSPeter Hurley 
7766e94dbc7SJiri Slaby 	spin_lock_irqsave(&tty->flow.lock, flags);
777f9e053dcSPeter Hurley 	__stop_tty(tty);
7786e94dbc7SJiri Slaby 	spin_unlock_irqrestore(&tty->flow.lock, flags);
779f9e053dcSPeter Hurley }
78096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(stop_tty);
78196fd7ce5SGreg Kroah-Hartman 
__start_tty(struct tty_struct * tty)782d06e256fSJiri Slaby void __start_tty(struct tty_struct *tty)
783d06e256fSJiri Slaby {
784d06e256fSJiri Slaby 	if (!tty->flow.stopped || tty->flow.tco_stopped)
785d06e256fSJiri Slaby 		return;
786d06e256fSJiri Slaby 	tty->flow.stopped = false;
787d06e256fSJiri Slaby 	if (tty->ops->start)
788d06e256fSJiri Slaby 		tty->ops->start(tty);
789d06e256fSJiri Slaby 	tty_wakeup(tty);
790d06e256fSJiri Slaby }
791d06e256fSJiri Slaby 
79296fd7ce5SGreg Kroah-Hartman /**
79396fd7ce5SGreg Kroah-Hartman  * start_tty	-	propagate flow control
79496fd7ce5SGreg Kroah-Hartman  * @tty: tty to start
79596fd7ce5SGreg Kroah-Hartman  *
796796a75a9SJiri Slaby  * Start a tty that has been stopped if at all possible. If @tty was previously
797796a75a9SJiri Slaby  * stopped and is now being started, the &tty_driver->start() method is invoked
798796a75a9SJiri Slaby  * and the line discipline woken.
79996fd7ce5SGreg Kroah-Hartman  *
80096fd7ce5SGreg Kroah-Hartman  * Locking:
8016e94dbc7SJiri Slaby  *	flow.lock
80296fd7ce5SGreg Kroah-Hartman  */
start_tty(struct tty_struct * tty)803f9e053dcSPeter Hurley void start_tty(struct tty_struct *tty)
804f9e053dcSPeter Hurley {
805f9e053dcSPeter Hurley 	unsigned long flags;
806f9e053dcSPeter Hurley 
8076e94dbc7SJiri Slaby 	spin_lock_irqsave(&tty->flow.lock, flags);
808f9e053dcSPeter Hurley 	__start_tty(tty);
8096e94dbc7SJiri Slaby 	spin_unlock_irqrestore(&tty->flow.lock, flags);
810f9e053dcSPeter Hurley }
81196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(start_tty);
81296fd7ce5SGreg Kroah-Hartman 
tty_update_time(struct tty_struct * tty,bool mtime)813360c11e2SMichal Sekletar static void tty_update_time(struct tty_struct *tty, bool mtime)
81437b7f3c7SJiri Slaby {
815c884f871SArnd Bergmann 	time64_t sec = ktime_get_real_seconds();
816360c11e2SMichal Sekletar 	struct tty_file_private *priv;
817360c11e2SMichal Sekletar 
818360c11e2SMichal Sekletar 	spin_lock(&tty->files_lock);
819360c11e2SMichal Sekletar 	list_for_each_entry(priv, &tty->tty_files, list) {
820360c11e2SMichal Sekletar 		struct inode *inode = file_inode(priv->file);
821360c11e2SMichal Sekletar 		struct timespec64 *time = mtime ? &inode->i_mtime : &inode->i_atime;
822fbf47635SGreg Kroah-Hartman 
823fbf47635SGreg Kroah-Hartman 		/*
824fbf47635SGreg Kroah-Hartman 		 * We only care if the two values differ in anything other than the
825fbf47635SGreg Kroah-Hartman 		 * lower three bits (i.e every 8 seconds).  If so, then we can update
826fbf47635SGreg Kroah-Hartman 		 * the time of the tty device, otherwise it could be construded as a
827fbf47635SGreg Kroah-Hartman 		 * security leak to let userspace know the exact timing of the tty.
828fbf47635SGreg Kroah-Hartman 		 */
829fbf47635SGreg Kroah-Hartman 		if ((sec ^ time->tv_sec) & ~7)
83037b7f3c7SJiri Slaby 			time->tv_sec = sec;
83137b7f3c7SJiri Slaby 	}
832360c11e2SMichal Sekletar 	spin_unlock(&tty->files_lock);
833360c11e2SMichal Sekletar }
83437b7f3c7SJiri Slaby 
8353b830a9cSLinus Torvalds /*
8363b830a9cSLinus Torvalds  * Iterate on the ldisc ->read() function until we've gotten all
8373b830a9cSLinus Torvalds  * the data the ldisc has for us.
8383b830a9cSLinus Torvalds  *
8393b830a9cSLinus Torvalds  * The "cookie" is something that the ldisc read function can fill
8403b830a9cSLinus Torvalds  * in to let us know that there is more data to be had.
8413b830a9cSLinus Torvalds  *
8423b830a9cSLinus Torvalds  * We promise to continue to call the ldisc until it stops returning
8433b830a9cSLinus Torvalds  * data or clears the cookie. The cookie may be something that the
8443b830a9cSLinus Torvalds  * ldisc maintains state for and needs to free.
8453b830a9cSLinus Torvalds  */
iterate_tty_read(struct tty_ldisc * ld,struct tty_struct * tty,struct file * file,struct iov_iter * to)84624b01c5dSJiri Slaby (SUSE) static ssize_t iterate_tty_read(struct tty_ldisc *ld, struct tty_struct *tty,
847dd78b0c4SLinus Torvalds 				struct file *file, struct iov_iter *to)
8483b830a9cSLinus Torvalds {
8493b830a9cSLinus Torvalds 	void *cookie = NULL;
8503b830a9cSLinus Torvalds 	unsigned long offset = 0;
8513b830a9cSLinus Torvalds 	char kernel_buf[64];
85224b01c5dSJiri Slaby (SUSE) 	ssize_t retval = 0;
853e3afc5b0SJiri Slaby (SUSE) 	size_t copied, count = iov_iter_count(to);
8543b830a9cSLinus Torvalds 
8553b830a9cSLinus Torvalds 	do {
85648a6ab88SJiri Slaby (SUSE) 		ssize_t size = min(count, sizeof(kernel_buf));
8573b830a9cSLinus Torvalds 
8583b830a9cSLinus Torvalds 		size = ld->ops->read(tty, file, kernel_buf, size, &cookie, offset);
8593b830a9cSLinus Torvalds 		if (!size)
8603b830a9cSLinus Torvalds 			break;
8613b830a9cSLinus Torvalds 
8623b830a9cSLinus Torvalds 		if (size < 0) {
863e71a8d5cSLinus Torvalds 			/* Did we have an earlier error (ie -EFAULT)? */
864e71a8d5cSLinus Torvalds 			if (retval)
865e71a8d5cSLinus Torvalds 				break;
866e71a8d5cSLinus Torvalds 			retval = size;
867e71a8d5cSLinus Torvalds 
868e71a8d5cSLinus Torvalds 			/*
869e71a8d5cSLinus Torvalds 			 * -EOVERFLOW means we didn't have enough space
870e71a8d5cSLinus Torvalds 			 * for a whole packet, and we shouldn't return
871e71a8d5cSLinus Torvalds 			 * a partial result.
872e71a8d5cSLinus Torvalds 			 */
873e71a8d5cSLinus Torvalds 			if (retval == -EOVERFLOW)
874e71a8d5cSLinus Torvalds 				offset = 0;
875e71a8d5cSLinus Torvalds 			break;
8763b830a9cSLinus Torvalds 		}
8773b830a9cSLinus Torvalds 
878dd78b0c4SLinus Torvalds 		copied = copy_to_iter(kernel_buf, size, to);
879dd78b0c4SLinus Torvalds 		offset += copied;
880dd78b0c4SLinus Torvalds 		count -= copied;
8813b830a9cSLinus Torvalds 
8823b830a9cSLinus Torvalds 		/*
8833b830a9cSLinus Torvalds 		 * If the user copy failed, we still need to do another ->read()
8843b830a9cSLinus Torvalds 		 * call if we had a cookie to let the ldisc clear up.
8853b830a9cSLinus Torvalds 		 *
8863b830a9cSLinus Torvalds 		 * But make sure size is zeroed.
8873b830a9cSLinus Torvalds 		 */
888dd78b0c4SLinus Torvalds 		if (unlikely(copied != size)) {
8893b830a9cSLinus Torvalds 			count = 0;
8903b830a9cSLinus Torvalds 			retval = -EFAULT;
8913b830a9cSLinus Torvalds 		}
8923b830a9cSLinus Torvalds 	} while (cookie);
8933b830a9cSLinus Torvalds 
8943b830a9cSLinus Torvalds 	/* We always clear tty buffer in case they contained passwords */
8953b830a9cSLinus Torvalds 	memzero_explicit(kernel_buf, sizeof(kernel_buf));
8963b830a9cSLinus Torvalds 	return offset ? offset : retval;
8973b830a9cSLinus Torvalds }
8983b830a9cSLinus Torvalds 
8993b830a9cSLinus Torvalds 
90096fd7ce5SGreg Kroah-Hartman /**
90196fd7ce5SGreg Kroah-Hartman  * tty_read	-	read method for tty device files
902756a4e4aSJiri Slaby  * @iocb: kernel I/O control block
903756a4e4aSJiri Slaby  * @to: destination for the data read
90496fd7ce5SGreg Kroah-Hartman  *
90596fd7ce5SGreg Kroah-Hartman  * Perform the read system call function on this terminal device. Checks
90696fd7ce5SGreg Kroah-Hartman  * for hung up devices before calling the line discipline method.
90796fd7ce5SGreg Kroah-Hartman  *
90896fd7ce5SGreg Kroah-Hartman  * Locking:
909796a75a9SJiri Slaby  *	Locks the line discipline internally while needed. Multiple read calls
910796a75a9SJiri Slaby  *	may be outstanding in parallel.
91196fd7ce5SGreg Kroah-Hartman  */
tty_read(struct kiocb * iocb,struct iov_iter * to)912dd78b0c4SLinus Torvalds static ssize_t tty_read(struct kiocb *iocb, struct iov_iter *to)
91396fd7ce5SGreg Kroah-Hartman {
914dd78b0c4SLinus Torvalds 	struct file *file = iocb->ki_filp;
91537b7f3c7SJiri Slaby 	struct inode *inode = file_inode(file);
91696fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
91796fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
91824b01c5dSJiri Slaby (SUSE) 	ssize_t ret;
91996fd7ce5SGreg Kroah-Hartman 
92037b7f3c7SJiri Slaby 	if (tty_paranoia_check(tty, inode, "tty_read"))
92196fd7ce5SGreg Kroah-Hartman 		return -EIO;
92218900ca6SPeter Hurley 	if (!tty || tty_io_error(tty))
92396fd7ce5SGreg Kroah-Hartman 		return -EIO;
92496fd7ce5SGreg Kroah-Hartman 
92596fd7ce5SGreg Kroah-Hartman 	/* We want to wait for the line discipline to sort out in this
926b426a5b8SXiaofei Tan 	 * situation.
927b426a5b8SXiaofei Tan 	 */
92896fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
929e55afd11SPeter Hurley 	if (!ld)
930ddc5fda7SLinus Torvalds 		return hung_up_tty_read(iocb, to);
93124b01c5dSJiri Slaby (SUSE) 	ret = -EIO;
932ddc5fda7SLinus Torvalds 	if (ld->ops->read)
93324b01c5dSJiri Slaby (SUSE) 		ret = iterate_tty_read(ld, tty, file, to);
93496fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
935b0de59b5SJiri Slaby 
93624b01c5dSJiri Slaby (SUSE) 	if (ret > 0)
937360c11e2SMichal Sekletar 		tty_update_time(tty, false);
93837b7f3c7SJiri Slaby 
93924b01c5dSJiri Slaby (SUSE) 	return ret;
94096fd7ce5SGreg Kroah-Hartman }
94196fd7ce5SGreg Kroah-Hartman 
tty_write_unlock(struct tty_struct * tty)942094fb49aSIlpo Järvinen void tty_write_unlock(struct tty_struct *tty)
94396fd7ce5SGreg Kroah-Hartman {
94496fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty->atomic_write_lock);
945a9a08845SLinus Torvalds 	wake_up_interruptible_poll(&tty->write_wait, EPOLLOUT);
94696fd7ce5SGreg Kroah-Hartman }
94796fd7ce5SGreg Kroah-Hartman 
tty_write_lock(struct tty_struct * tty,bool ndelay)948af815336SJiri Slaby (SUSE) int tty_write_lock(struct tty_struct *tty, bool ndelay)
94996fd7ce5SGreg Kroah-Hartman {
95096fd7ce5SGreg Kroah-Hartman 	if (!mutex_trylock(&tty->atomic_write_lock)) {
95196fd7ce5SGreg Kroah-Hartman 		if (ndelay)
95296fd7ce5SGreg Kroah-Hartman 			return -EAGAIN;
95396fd7ce5SGreg Kroah-Hartman 		if (mutex_lock_interruptible(&tty->atomic_write_lock))
95496fd7ce5SGreg Kroah-Hartman 			return -ERESTARTSYS;
95596fd7ce5SGreg Kroah-Hartman 	}
95696fd7ce5SGreg Kroah-Hartman 	return 0;
95796fd7ce5SGreg Kroah-Hartman }
95896fd7ce5SGreg Kroah-Hartman 
95996fd7ce5SGreg Kroah-Hartman /*
96096fd7ce5SGreg Kroah-Hartman  * Split writes up in sane blocksizes to avoid
96196fd7ce5SGreg Kroah-Hartman  * denial-of-service type attacks
96296fd7ce5SGreg Kroah-Hartman  */
iterate_tty_write(struct tty_ldisc * ld,struct tty_struct * tty,struct file * file,struct iov_iter * from)963a32a672dSJiri Slaby (SUSE) static ssize_t iterate_tty_write(struct tty_ldisc *ld, struct tty_struct *tty,
964f47a4fd6SJiri Slaby (SUSE) 				 struct file *file, struct iov_iter *from)
96596fd7ce5SGreg Kroah-Hartman {
966ccc8dc00SJiri Slaby (SUSE) 	size_t chunk, count = iov_iter_count(from);
96796fd7ce5SGreg Kroah-Hartman 	ssize_t ret, written = 0;
96896fd7ce5SGreg Kroah-Hartman 
96996fd7ce5SGreg Kroah-Hartman 	ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
97096fd7ce5SGreg Kroah-Hartman 	if (ret < 0)
97196fd7ce5SGreg Kroah-Hartman 		return ret;
97296fd7ce5SGreg Kroah-Hartman 
97396fd7ce5SGreg Kroah-Hartman 	/*
97496fd7ce5SGreg Kroah-Hartman 	 * We chunk up writes into a temporary buffer. This
97596fd7ce5SGreg Kroah-Hartman 	 * simplifies low-level drivers immensely, since they
97696fd7ce5SGreg Kroah-Hartman 	 * don't have locking issues and user mode accesses.
97796fd7ce5SGreg Kroah-Hartman 	 *
97896fd7ce5SGreg Kroah-Hartman 	 * But if TTY_NO_WRITE_SPLIT is set, we should use a
97996fd7ce5SGreg Kroah-Hartman 	 * big chunk-size..
98096fd7ce5SGreg Kroah-Hartman 	 *
98196fd7ce5SGreg Kroah-Hartman 	 * The default chunk-size is 2kB, because the NTTY
98296fd7ce5SGreg Kroah-Hartman 	 * layer has problems with bigger chunks. It will
98396fd7ce5SGreg Kroah-Hartman 	 * claim to be able to handle more characters than
98496fd7ce5SGreg Kroah-Hartman 	 * it actually does.
98596fd7ce5SGreg Kroah-Hartman 	 */
98696fd7ce5SGreg Kroah-Hartman 	chunk = 2048;
98796fd7ce5SGreg Kroah-Hartman 	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
98896fd7ce5SGreg Kroah-Hartman 		chunk = 65536;
98996fd7ce5SGreg Kroah-Hartman 	if (count < chunk)
99096fd7ce5SGreg Kroah-Hartman 		chunk = count;
99196fd7ce5SGreg Kroah-Hartman 
99296fd7ce5SGreg Kroah-Hartman 	/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
99396fd7ce5SGreg Kroah-Hartman 	if (tty->write_cnt < chunk) {
99496fd7ce5SGreg Kroah-Hartman 		unsigned char *buf_chunk;
99596fd7ce5SGreg Kroah-Hartman 
99696fd7ce5SGreg Kroah-Hartman 		if (chunk < 1024)
99796fd7ce5SGreg Kroah-Hartman 			chunk = 1024;
99896fd7ce5SGreg Kroah-Hartman 
999d6d9d17aSAndy Shevchenko 		buf_chunk = kvmalloc(chunk, GFP_KERNEL | __GFP_RETRY_MAYFAIL);
100096fd7ce5SGreg Kroah-Hartman 		if (!buf_chunk) {
100196fd7ce5SGreg Kroah-Hartman 			ret = -ENOMEM;
100296fd7ce5SGreg Kroah-Hartman 			goto out;
100396fd7ce5SGreg Kroah-Hartman 		}
1004d6d9d17aSAndy Shevchenko 		kvfree(tty->write_buf);
100596fd7ce5SGreg Kroah-Hartman 		tty->write_cnt = chunk;
100696fd7ce5SGreg Kroah-Hartman 		tty->write_buf = buf_chunk;
100796fd7ce5SGreg Kroah-Hartman 	}
100896fd7ce5SGreg Kroah-Hartman 
100996fd7ce5SGreg Kroah-Hartman 	/* Do the write .. */
101096fd7ce5SGreg Kroah-Hartman 	for (;;) {
1011ccc8dc00SJiri Slaby (SUSE) 		size_t size = min(chunk, count);
10129bb48c82SLinus Torvalds 
101396fd7ce5SGreg Kroah-Hartman 		ret = -EFAULT;
10149bb48c82SLinus Torvalds 		if (copy_from_iter(tty->write_buf, size, from) != size)
101596fd7ce5SGreg Kroah-Hartman 			break;
10169bb48c82SLinus Torvalds 
1017f47a4fd6SJiri Slaby (SUSE) 		ret = ld->ops->write(tty, file, tty->write_buf, size);
101896fd7ce5SGreg Kroah-Hartman 		if (ret <= 0)
101996fd7ce5SGreg Kroah-Hartman 			break;
10209bb48c82SLinus Torvalds 
10213342ff26SLinus Torvalds 		written += ret;
10223342ff26SLinus Torvalds 		if (ret > size)
10233342ff26SLinus Torvalds 			break;
10243342ff26SLinus Torvalds 
10259bb48c82SLinus Torvalds 		/* FIXME! Have Al check this! */
10269bb48c82SLinus Torvalds 		if (ret != size)
10279bb48c82SLinus Torvalds 			iov_iter_revert(from, size-ret);
10289bb48c82SLinus Torvalds 
102996fd7ce5SGreg Kroah-Hartman 		count -= ret;
103096fd7ce5SGreg Kroah-Hartman 		if (!count)
103196fd7ce5SGreg Kroah-Hartman 			break;
103296fd7ce5SGreg Kroah-Hartman 		ret = -ERESTARTSYS;
103396fd7ce5SGreg Kroah-Hartman 		if (signal_pending(current))
103496fd7ce5SGreg Kroah-Hartman 			break;
103596fd7ce5SGreg Kroah-Hartman 		cond_resched();
103696fd7ce5SGreg Kroah-Hartman 	}
103737b7f3c7SJiri Slaby 	if (written) {
1038360c11e2SMichal Sekletar 		tty_update_time(tty, true);
103996fd7ce5SGreg Kroah-Hartman 		ret = written;
104037b7f3c7SJiri Slaby 	}
104196fd7ce5SGreg Kroah-Hartman out:
104296fd7ce5SGreg Kroah-Hartman 	tty_write_unlock(tty);
104396fd7ce5SGreg Kroah-Hartman 	return ret;
104496fd7ce5SGreg Kroah-Hartman }
104596fd7ce5SGreg Kroah-Hartman 
104696fd7ce5SGreg Kroah-Hartman /**
104796fd7ce5SGreg Kroah-Hartman  * tty_write_message - write a message to a certain tty, not just the console.
104896fd7ce5SGreg Kroah-Hartman  * @tty: the destination tty_struct
104996fd7ce5SGreg Kroah-Hartman  * @msg: the message to write
105096fd7ce5SGreg Kroah-Hartman  *
1051796a75a9SJiri Slaby  * This is used for messages that need to be redirected to a specific tty. We
1052796a75a9SJiri Slaby  * don't put it into the syslog queue right now maybe in the future if really
1053796a75a9SJiri Slaby  * needed.
105496fd7ce5SGreg Kroah-Hartman  *
105596fd7ce5SGreg Kroah-Hartman  * We must still hold the BTM and test the CLOSING flag for the moment.
105696fd7ce5SGreg Kroah-Hartman  */
tty_write_message(struct tty_struct * tty,char * msg)105796fd7ce5SGreg Kroah-Hartman void tty_write_message(struct tty_struct *tty, char *msg)
105896fd7ce5SGreg Kroah-Hartman {
105996fd7ce5SGreg Kroah-Hartman 	if (tty) {
106096fd7ce5SGreg Kroah-Hartman 		mutex_lock(&tty->atomic_write_lock);
106189c8d91eSAlan Cox 		tty_lock(tty);
10624b41b953SPeter Hurley 		if (tty->ops->write && tty->count > 0)
106396fd7ce5SGreg Kroah-Hartman 			tty->ops->write(tty, msg, strlen(msg));
106489c8d91eSAlan Cox 		tty_unlock(tty);
106596fd7ce5SGreg Kroah-Hartman 		tty_write_unlock(tty);
106696fd7ce5SGreg Kroah-Hartman 	}
106796fd7ce5SGreg Kroah-Hartman }
106896fd7ce5SGreg Kroah-Hartman 
file_tty_write(struct file * file,struct kiocb * iocb,struct iov_iter * from)1069a9cbbb80SLinus Torvalds static ssize_t file_tty_write(struct file *file, struct kiocb *iocb, struct iov_iter *from)
107096fd7ce5SGreg Kroah-Hartman {
107196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
107296fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
107396fd7ce5SGreg Kroah-Hartman 	ssize_t ret;
107496fd7ce5SGreg Kroah-Hartman 
10756131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_write"))
107696fd7ce5SGreg Kroah-Hartman 		return -EIO;
107718900ca6SPeter Hurley 	if (!tty || !tty->ops->write ||	tty_io_error(tty))
107896fd7ce5SGreg Kroah-Hartman 		return -EIO;
107996fd7ce5SGreg Kroah-Hartman 	/* Short term debug to catch buggy drivers */
108096fd7ce5SGreg Kroah-Hartman 	if (tty->ops->write_room == NULL)
1081339f36baSPeter Hurley 		tty_err(tty, "missing write_room method\n");
108296fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
108317749851SLinus Torvalds 	if (!ld)
108417749851SLinus Torvalds 		return hung_up_tty_write(iocb, from);
108517749851SLinus Torvalds 	if (!ld->ops->write)
108696fd7ce5SGreg Kroah-Hartman 		ret = -EIO;
108796fd7ce5SGreg Kroah-Hartman 	else
1088a32a672dSJiri Slaby (SUSE) 		ret = iterate_tty_write(ld, tty, file, from);
108996fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
109096fd7ce5SGreg Kroah-Hartman 	return ret;
109196fd7ce5SGreg Kroah-Hartman }
109296fd7ce5SGreg Kroah-Hartman 
1093756a4e4aSJiri Slaby /**
1094756a4e4aSJiri Slaby  * tty_write		-	write method for tty device file
1095756a4e4aSJiri Slaby  * @iocb: kernel I/O control block
1096756a4e4aSJiri Slaby  * @from: iov_iter with data to write
1097756a4e4aSJiri Slaby  *
1098756a4e4aSJiri Slaby  * Write data to a tty device via the line discipline.
1099756a4e4aSJiri Slaby  *
1100756a4e4aSJiri Slaby  * Locking:
1101756a4e4aSJiri Slaby  *	Locks the line discipline as required
1102756a4e4aSJiri Slaby  *	Writes to the tty driver are serialized by the atomic_write_lock
1103756a4e4aSJiri Slaby  *	and are then processed in chunks to the device. The line
1104756a4e4aSJiri Slaby  *	discipline write method will not be invoked in parallel for
1105756a4e4aSJiri Slaby  *	each device.
1106756a4e4aSJiri Slaby  */
tty_write(struct kiocb * iocb,struct iov_iter * from)1107a9cbbb80SLinus Torvalds static ssize_t tty_write(struct kiocb *iocb, struct iov_iter *from)
1108a9cbbb80SLinus Torvalds {
1109a9cbbb80SLinus Torvalds 	return file_tty_write(iocb->ki_filp, iocb, from);
1110a9cbbb80SLinus Torvalds }
1111a9cbbb80SLinus Torvalds 
redirected_tty_write(struct kiocb * iocb,struct iov_iter * iter)11129bb48c82SLinus Torvalds ssize_t redirected_tty_write(struct kiocb *iocb, struct iov_iter *iter)
111396fd7ce5SGreg Kroah-Hartman {
111496fd7ce5SGreg Kroah-Hartman 	struct file *p = NULL;
111596fd7ce5SGreg Kroah-Hartman 
111696fd7ce5SGreg Kroah-Hartman 	spin_lock(&redirect_lock);
1117cb0942b8SAl Viro 	if (redirect)
1118cb0942b8SAl Viro 		p = get_file(redirect);
111996fd7ce5SGreg Kroah-Hartman 	spin_unlock(&redirect_lock);
112096fd7ce5SGreg Kroah-Hartman 
1121a9cbbb80SLinus Torvalds 	/*
112244969f85SXiaofei Tan 	 * We know the redirected tty is just another tty, we can
1123a9cbbb80SLinus Torvalds 	 * call file_tty_write() directly with that file pointer.
1124a9cbbb80SLinus Torvalds 	 */
112596fd7ce5SGreg Kroah-Hartman 	if (p) {
112696fd7ce5SGreg Kroah-Hartman 		ssize_t res;
1127e73b2407SXiaofei Tan 
1128a9cbbb80SLinus Torvalds 		res = file_tty_write(p, iocb, iter);
112996fd7ce5SGreg Kroah-Hartman 		fput(p);
113096fd7ce5SGreg Kroah-Hartman 		return res;
113196fd7ce5SGreg Kroah-Hartman 	}
11329bb48c82SLinus Torvalds 	return tty_write(iocb, iter);
113396fd7ce5SGreg Kroah-Hartman }
113496fd7ce5SGreg Kroah-Hartman 
1135796a75a9SJiri Slaby /**
1136136d5258SPeter Hurley  * tty_send_xchar	-	send priority character
1137796a75a9SJiri Slaby  * @tty: the tty to send to
1138796a75a9SJiri Slaby  * @ch: xchar to send
1139136d5258SPeter Hurley  *
1140796a75a9SJiri Slaby  * Send a high priority character to the tty even if stopped.
1141136d5258SPeter Hurley  *
1142136d5258SPeter Hurley  * Locking: none for xchar method, write ordering for write method.
1143136d5258SPeter Hurley  */
tty_send_xchar(struct tty_struct * tty,char ch)1144136d5258SPeter Hurley int tty_send_xchar(struct tty_struct *tty, char ch)
1145136d5258SPeter Hurley {
11466e94dbc7SJiri Slaby 	bool was_stopped = tty->flow.stopped;
1147136d5258SPeter Hurley 
1148136d5258SPeter Hurley 	if (tty->ops->send_xchar) {
1149ee0c1a65SPeter Hurley 		down_read(&tty->termios_rwsem);
1150136d5258SPeter Hurley 		tty->ops->send_xchar(tty, ch);
1151ee0c1a65SPeter Hurley 		up_read(&tty->termios_rwsem);
1152136d5258SPeter Hurley 		return 0;
1153136d5258SPeter Hurley 	}
1154136d5258SPeter Hurley 
1155af815336SJiri Slaby (SUSE) 	if (tty_write_lock(tty, false) < 0)
1156136d5258SPeter Hurley 		return -ERESTARTSYS;
1157136d5258SPeter Hurley 
1158ee0c1a65SPeter Hurley 	down_read(&tty->termios_rwsem);
1159136d5258SPeter Hurley 	if (was_stopped)
1160136d5258SPeter Hurley 		start_tty(tty);
1161136d5258SPeter Hurley 	tty->ops->write(tty, &ch, 1);
1162136d5258SPeter Hurley 	if (was_stopped)
1163136d5258SPeter Hurley 		stop_tty(tty);
1164ee0c1a65SPeter Hurley 	up_read(&tty->termios_rwsem);
1165136d5258SPeter Hurley 	tty_write_unlock(tty);
1166136d5258SPeter Hurley 	return 0;
1167136d5258SPeter Hurley }
1168136d5258SPeter Hurley 
116996fd7ce5SGreg Kroah-Hartman /**
117096fd7ce5SGreg Kroah-Hartman  * pty_line_name	-	generate name for a pty
117196fd7ce5SGreg Kroah-Hartman  * @driver: the tty driver in use
117296fd7ce5SGreg Kroah-Hartman  * @index: the minor number
117396fd7ce5SGreg Kroah-Hartman  * @p: output buffer of at least 6 bytes
117496fd7ce5SGreg Kroah-Hartman  *
1175796a75a9SJiri Slaby  * Generate a name from a @driver reference and write it to the output buffer
1176796a75a9SJiri Slaby  * @p.
117796fd7ce5SGreg Kroah-Hartman  *
117896fd7ce5SGreg Kroah-Hartman  * Locking: None
117996fd7ce5SGreg Kroah-Hartman  */
pty_line_name(struct tty_driver * driver,int index,char * p)118096fd7ce5SGreg Kroah-Hartman static void pty_line_name(struct tty_driver *driver, int index, char *p)
118196fd7ce5SGreg Kroah-Hartman {
1182a846dcf9SJiri Slaby 	static const char ptychar[] = "pqrstuvwxyzabcde";
118396fd7ce5SGreg Kroah-Hartman 	int i = index + driver->name_base;
118496fd7ce5SGreg Kroah-Hartman 	/* ->name is initialized to "ttyp", but "tty" is expected */
118596fd7ce5SGreg Kroah-Hartman 	sprintf(p, "%s%c%x",
118696fd7ce5SGreg Kroah-Hartman 		driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
118796fd7ce5SGreg Kroah-Hartman 		ptychar[i >> 4 & 0xf], i & 0xf);
118896fd7ce5SGreg Kroah-Hartman }
118996fd7ce5SGreg Kroah-Hartman 
119096fd7ce5SGreg Kroah-Hartman /**
119196fd7ce5SGreg Kroah-Hartman  * tty_line_name	-	generate name for a tty
119296fd7ce5SGreg Kroah-Hartman  * @driver: the tty driver in use
119396fd7ce5SGreg Kroah-Hartman  * @index: the minor number
119496fd7ce5SGreg Kroah-Hartman  * @p: output buffer of at least 7 bytes
119596fd7ce5SGreg Kroah-Hartman  *
1196796a75a9SJiri Slaby  * Generate a name from a @driver reference and write it to the output buffer
1197796a75a9SJiri Slaby  * @p.
119896fd7ce5SGreg Kroah-Hartman  *
119996fd7ce5SGreg Kroah-Hartman  * Locking: None
120096fd7ce5SGreg Kroah-Hartman  */
tty_line_name(struct tty_driver * driver,int index,char * p)1201723abd87SHannes Reinecke static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
120296fd7ce5SGreg Kroah-Hartman {
12030019b408SJiri Slaby 	if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE)
1204723abd87SHannes Reinecke 		return sprintf(p, "%s", driver->name);
12050019b408SJiri Slaby 	else
1206723abd87SHannes Reinecke 		return sprintf(p, "%s%d", driver->name,
1207723abd87SHannes Reinecke 			       index + driver->name_base);
120896fd7ce5SGreg Kroah-Hartman }
120996fd7ce5SGreg Kroah-Hartman 
121096fd7ce5SGreg Kroah-Hartman /**
121196fd7ce5SGreg Kroah-Hartman  * tty_driver_lookup_tty() - find an existing tty, if any
121296fd7ce5SGreg Kroah-Hartman  * @driver: the driver for the tty
121308aa5042SLee Jones  * @file: file object
121496fd7ce5SGreg Kroah-Hartman  * @idx: the minor number
121596fd7ce5SGreg Kroah-Hartman  *
1216796a75a9SJiri Slaby  * Return: the tty, if found. If not found, return %NULL or ERR_PTR() if the
1217aa3cb814SPeter Hurley  * driver lookup() method returns an error.
121896fd7ce5SGreg Kroah-Hartman  *
1219aa3cb814SPeter Hurley  * Locking: tty_mutex must be held. If the tty is found, bump the tty kref.
122096fd7ce5SGreg Kroah-Hartman  */
tty_driver_lookup_tty(struct tty_driver * driver,struct file * file,int idx)122196fd7ce5SGreg Kroah-Hartman static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
12228ead9dd5SLinus Torvalds 		struct file *file, int idx)
122396fd7ce5SGreg Kroah-Hartman {
1224aa3cb814SPeter Hurley 	struct tty_struct *tty;
122596fd7ce5SGreg Kroah-Hartman 
1226db4df8e9SSven Schnelle 	if (driver->ops->lookup) {
122712e84c71SOkash Khawaja 		if (!file)
122812e84c71SOkash Khawaja 			tty = ERR_PTR(-EIO);
122912e84c71SOkash Khawaja 		else
12308ead9dd5SLinus Torvalds 			tty = driver->ops->lookup(driver, file, idx);
1231db4df8e9SSven Schnelle 	} else {
1232db4df8e9SSven Schnelle 		if (idx >= driver->num)
1233db4df8e9SSven Schnelle 			return ERR_PTR(-EINVAL);
1234aa3cb814SPeter Hurley 		tty = driver->ttys[idx];
1235db4df8e9SSven Schnelle 	}
1236aa3cb814SPeter Hurley 	if (!IS_ERR(tty))
1237aa3cb814SPeter Hurley 		tty_kref_get(tty);
1238aa3cb814SPeter Hurley 	return tty;
123996fd7ce5SGreg Kroah-Hartman }
124096fd7ce5SGreg Kroah-Hartman 
124196fd7ce5SGreg Kroah-Hartman /**
124296fd7ce5SGreg Kroah-Hartman  * tty_init_termios	-  helper for termios setup
124396fd7ce5SGreg Kroah-Hartman  * @tty: the tty to set up
124496fd7ce5SGreg Kroah-Hartman  *
1245796a75a9SJiri Slaby  * Initialise the termios structure for this tty. This runs under the
1246796a75a9SJiri Slaby  * %tty_mutex currently so we can be relaxed about ordering.
124796fd7ce5SGreg Kroah-Hartman  */
tty_init_termios(struct tty_struct * tty)1248a3123fd0SPeter Hurley void tty_init_termios(struct tty_struct *tty)
124996fd7ce5SGreg Kroah-Hartman {
125096fd7ce5SGreg Kroah-Hartman 	struct ktermios *tp;
125196fd7ce5SGreg Kroah-Hartman 	int idx = tty->index;
125296fd7ce5SGreg Kroah-Hartman 
125336b3c070SAlan Cox 	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
125436b3c070SAlan Cox 		tty->termios = tty->driver->init_termios;
125536b3c070SAlan Cox 	else {
125636b3c070SAlan Cox 		/* Check for lazy saved data */
125796fd7ce5SGreg Kroah-Hartman 		tp = tty->driver->termios[idx];
1258ece53405SPeter Hurley 		if (tp != NULL) {
1259adc8d746SAlan Cox 			tty->termios = *tp;
1260ece53405SPeter Hurley 			tty->termios.c_line  = tty->driver->init_termios.c_line;
1261ece53405SPeter Hurley 		} else
126236b3c070SAlan Cox 			tty->termios = tty->driver->init_termios;
126336b3c070SAlan Cox 	}
126496fd7ce5SGreg Kroah-Hartman 	/* Compatibility until drivers always set this */
1265adc8d746SAlan Cox 	tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
1266adc8d746SAlan Cox 	tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
126796fd7ce5SGreg Kroah-Hartman }
126896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_init_termios);
126996fd7ce5SGreg Kroah-Hartman 
127031bc35d3SJiri Slaby /**
127131bc35d3SJiri Slaby  * tty_standard_install - usual tty->ops->install
127231bc35d3SJiri Slaby  * @driver: the driver for the tty
127331bc35d3SJiri Slaby  * @tty: the tty
127431bc35d3SJiri Slaby  *
127531bc35d3SJiri Slaby  * If the @driver overrides @tty->ops->install, it still can call this function
127631bc35d3SJiri Slaby  * to perform the standard install operations.
127731bc35d3SJiri Slaby  */
tty_standard_install(struct tty_driver * driver,struct tty_struct * tty)127866d450e8SJiri Slaby int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
127966d450e8SJiri Slaby {
1280a3123fd0SPeter Hurley 	tty_init_termios(tty);
128166d450e8SJiri Slaby 	tty_driver_kref_get(driver);
128266d450e8SJiri Slaby 	tty->count++;
128366d450e8SJiri Slaby 	driver->ttys[tty->index] = tty;
128466d450e8SJiri Slaby 	return 0;
128566d450e8SJiri Slaby }
128666d450e8SJiri Slaby EXPORT_SYMBOL_GPL(tty_standard_install);
128766d450e8SJiri Slaby 
128896fd7ce5SGreg Kroah-Hartman /**
128996fd7ce5SGreg Kroah-Hartman  * tty_driver_install_tty() - install a tty entry in the driver
129096fd7ce5SGreg Kroah-Hartman  * @driver: the driver for the tty
129196fd7ce5SGreg Kroah-Hartman  * @tty: the tty
129296fd7ce5SGreg Kroah-Hartman  *
1293796a75a9SJiri Slaby  * Install a tty object into the driver tables. The @tty->index field will be
1294796a75a9SJiri Slaby  * set by the time this is called. This method is responsible for ensuring any
1295796a75a9SJiri Slaby  * need additional structures are allocated and configured.
129696fd7ce5SGreg Kroah-Hartman  *
129796fd7ce5SGreg Kroah-Hartman  * Locking: tty_mutex for now
129896fd7ce5SGreg Kroah-Hartman  */
tty_driver_install_tty(struct tty_driver * driver,struct tty_struct * tty)129996fd7ce5SGreg Kroah-Hartman static int tty_driver_install_tty(struct tty_driver *driver,
130096fd7ce5SGreg Kroah-Hartman 						struct tty_struct *tty)
130196fd7ce5SGreg Kroah-Hartman {
130266d450e8SJiri Slaby 	return driver->ops->install ? driver->ops->install(driver, tty) :
130366d450e8SJiri Slaby 		tty_standard_install(driver, tty);
130496fd7ce5SGreg Kroah-Hartman }
130596fd7ce5SGreg Kroah-Hartman 
130696fd7ce5SGreg Kroah-Hartman /**
130796fd7ce5SGreg Kroah-Hartman  * tty_driver_remove_tty() - remove a tty from the driver tables
130896fd7ce5SGreg Kroah-Hartman  * @driver: the driver for the tty
1309fa441954SJiri Slaby  * @tty: tty to remove
131096fd7ce5SGreg Kroah-Hartman  *
1311796a75a9SJiri Slaby  * Remove a tty object from the driver tables. The tty->index field will be set
1312796a75a9SJiri Slaby  * by the time this is called.
131396fd7ce5SGreg Kroah-Hartman  *
131496fd7ce5SGreg Kroah-Hartman  * Locking: tty_mutex for now
131596fd7ce5SGreg Kroah-Hartman  */
tty_driver_remove_tty(struct tty_driver * driver,struct tty_struct * tty)131605de87edSPeter Hurley static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
131796fd7ce5SGreg Kroah-Hartman {
131896fd7ce5SGreg Kroah-Hartman 	if (driver->ops->remove)
131996fd7ce5SGreg Kroah-Hartman 		driver->ops->remove(driver, tty);
132096fd7ce5SGreg Kroah-Hartman 	else
132196fd7ce5SGreg Kroah-Hartman 		driver->ttys[tty->index] = NULL;
132296fd7ce5SGreg Kroah-Hartman }
132396fd7ce5SGreg Kroah-Hartman 
1324fa441954SJiri Slaby /**
132596fd7ce5SGreg Kroah-Hartman  * tty_reopen()	- fast re-open of an open tty
1326fa441954SJiri Slaby  * @tty: the tty to open
132796fd7ce5SGreg Kroah-Hartman  *
1328796a75a9SJiri Slaby  * Re-opens on master ptys are not allowed and return -%EIO.
132996fd7ce5SGreg Kroah-Hartman  *
13305d93e748SPeter Hurley  * Locking: Caller must hold tty_lock
1331796a75a9SJiri Slaby  * Return: 0 on success, -errno on error.
133296fd7ce5SGreg Kroah-Hartman  */
tty_reopen(struct tty_struct * tty)133396fd7ce5SGreg Kroah-Hartman static int tty_reopen(struct tty_struct *tty)
133496fd7ce5SGreg Kroah-Hartman {
133596fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver = tty->driver;
1336d3736d82SDmitry Safonov 	struct tty_ldisc *ld;
1337d3736d82SDmitry Safonov 	int retval = 0;
133896fd7ce5SGreg Kroah-Hartman 
133996fd7ce5SGreg Kroah-Hartman 	if (driver->type == TTY_DRIVER_TYPE_PTY &&
13405d93e748SPeter Hurley 	    driver->subtype == PTY_TYPE_MASTER)
134196fd7ce5SGreg Kroah-Hartman 		return -EIO;
134296fd7ce5SGreg Kroah-Hartman 
13437f22f6c9SPeter Hurley 	if (!tty->count)
13447f22f6c9SPeter Hurley 		return -EAGAIN;
13457f22f6c9SPeter Hurley 
134686f2c00fSPeter Hurley 	if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
134786f2c00fSPeter Hurley 		return -EBUSY;
134886f2c00fSPeter Hurley 
1349d3736d82SDmitry Safonov 	ld = tty_ldisc_ref_wait(tty);
1350d3736d82SDmitry Safonov 	if (ld) {
1351d3736d82SDmitry Safonov 		tty_ldisc_deref(ld);
1352d3736d82SDmitry Safonov 	} else {
135383d817f4SDmitry Safonov 		retval = tty_ldisc_lock(tty, 5 * HZ);
135483d817f4SDmitry Safonov 		if (retval)
135583d817f4SDmitry Safonov 			return retval;
135696fd7ce5SGreg Kroah-Hartman 
1357cf62a1a1SDmitry Safonov 		if (!tty->ldisc)
1358fe324167SDmitry Safonov 			retval = tty_ldisc_reinit(tty, tty->termios.c_line);
135983d817f4SDmitry Safonov 		tty_ldisc_unlock(tty);
1360d3736d82SDmitry Safonov 	}
1361cf62a1a1SDmitry Safonov 
1362cf62a1a1SDmitry Safonov 	if (retval == 0)
1363cf62a1a1SDmitry Safonov 		tty->count++;
1364cf62a1a1SDmitry Safonov 
1365fe324167SDmitry Safonov 	return retval;
136696fd7ce5SGreg Kroah-Hartman }
136796fd7ce5SGreg Kroah-Hartman 
136896fd7ce5SGreg Kroah-Hartman /**
136996fd7ce5SGreg Kroah-Hartman  * tty_init_dev		-	initialise a tty device
137096fd7ce5SGreg Kroah-Hartman  * @driver: tty driver we are opening a device on
137196fd7ce5SGreg Kroah-Hartman  * @idx: device index
137296fd7ce5SGreg Kroah-Hartman  *
1373796a75a9SJiri Slaby  * Prepare a tty device. This may not be a "new" clean device but could also be
1374796a75a9SJiri Slaby  * an active device. The pty drivers require special handling because of this.
137596fd7ce5SGreg Kroah-Hartman  *
137696fd7ce5SGreg Kroah-Hartman  * Locking:
1377796a75a9SJiri Slaby  *	The function is called under the tty_mutex, which protects us from the
1378796a75a9SJiri Slaby  *	tty struct or driver itself going away.
137996fd7ce5SGreg Kroah-Hartman  *
1380796a75a9SJiri Slaby  * On exit the tty device has the line discipline attached and a reference
1381796a75a9SJiri Slaby  * count of 1. If a pair was created for pty/tty use and the other was a pty
1382796a75a9SJiri Slaby  * master then it too has a reference count of 1.
138396fd7ce5SGreg Kroah-Hartman  *
1384796a75a9SJiri Slaby  * WSH 06/09/97: Rewritten to remove races and properly clean up after a failed
1385796a75a9SJiri Slaby  * open. The new code protects the open with a mutex, so it's really quite
1386796a75a9SJiri Slaby  * straightforward. The mutex locking can probably be relaxed for the (most
1387796a75a9SJiri Slaby  * common) case of reopening a tty.
1388fa441954SJiri Slaby  *
1389796a75a9SJiri Slaby  * Return: new tty structure
139096fd7ce5SGreg Kroah-Hartman  */
tty_init_dev(struct tty_driver * driver,int idx)1391593a27c4SKonstantin Khlebnikov struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
139296fd7ce5SGreg Kroah-Hartman {
139396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
139496fd7ce5SGreg Kroah-Hartman 	int retval;
139596fd7ce5SGreg Kroah-Hartman 
139696fd7ce5SGreg Kroah-Hartman 	/*
139796fd7ce5SGreg Kroah-Hartman 	 * First time open is complex, especially for PTY devices.
139896fd7ce5SGreg Kroah-Hartman 	 * This code guarantees that either everything succeeds and the
139996fd7ce5SGreg Kroah-Hartman 	 * TTY is ready for operation, or else the table slots are vacated
140096fd7ce5SGreg Kroah-Hartman 	 * and the allocated memory released.  (Except that the termios
140116b00ae8SJohan Hovold 	 * may be retained.)
140296fd7ce5SGreg Kroah-Hartman 	 */
140396fd7ce5SGreg Kroah-Hartman 
140496fd7ce5SGreg Kroah-Hartman 	if (!try_module_get(driver->owner))
140596fd7ce5SGreg Kroah-Hartman 		return ERR_PTR(-ENODEV);
140696fd7ce5SGreg Kroah-Hartman 
14072c964a2fSRasmus Villemoes 	tty = alloc_tty_struct(driver, idx);
1408d5543503SJiri Slaby 	if (!tty) {
1409d5543503SJiri Slaby 		retval = -ENOMEM;
1410d5543503SJiri Slaby 		goto err_module_put;
1411d5543503SJiri Slaby 	}
141296fd7ce5SGreg Kroah-Hartman 
141389c8d91eSAlan Cox 	tty_lock(tty);
141496fd7ce5SGreg Kroah-Hartman 	retval = tty_driver_install_tty(driver, tty);
1415d5543503SJiri Slaby 	if (retval < 0)
1416c8b710b3SPeter Hurley 		goto err_free_tty;
141796fd7ce5SGreg Kroah-Hartman 
141804831dc1SJiri Slaby 	if (!tty->port)
141904831dc1SJiri Slaby 		tty->port = driver->ports[idx];
142004831dc1SJiri Slaby 
14212ae0b31eSJiri Slaby 	if (WARN_RATELIMIT(!tty->port,
14222ae0b31eSJiri Slaby 			"%s: %s driver does not set tty->port. This would crash the kernel. Fix the driver!\n",
14232ae0b31eSJiri Slaby 			__func__, tty->driver->name)) {
14242ae0b31eSJiri Slaby 		retval = -EINVAL;
14252ae0b31eSJiri Slaby 		goto err_release_lock;
14262ae0b31eSJiri Slaby 	}
14275d4121c0SJiri Slaby 
1428b027e229SGaurav Kohli 	retval = tty_ldisc_lock(tty, 5 * HZ);
1429b027e229SGaurav Kohli 	if (retval)
1430b027e229SGaurav Kohli 		goto err_release_lock;
1431967fab69SJiri Slaby 	tty->port->itty = tty;
1432967fab69SJiri Slaby 
143396fd7ce5SGreg Kroah-Hartman 	/*
143496fd7ce5SGreg Kroah-Hartman 	 * Structures all installed ... call the ldisc open routines.
143596fd7ce5SGreg Kroah-Hartman 	 * If we fail here just call release_tty to clean up.  No need
143696fd7ce5SGreg Kroah-Hartman 	 * to decrement the use counts, as release_tty doesn't care.
143796fd7ce5SGreg Kroah-Hartman 	 */
143896fd7ce5SGreg Kroah-Hartman 	retval = tty_ldisc_setup(tty, tty->link);
143996fd7ce5SGreg Kroah-Hartman 	if (retval)
1440d5543503SJiri Slaby 		goto err_release_tty;
1441b027e229SGaurav Kohli 	tty_ldisc_unlock(tty);
144289c8d91eSAlan Cox 	/* Return the tty locked so that it cannot vanish under the caller */
144396fd7ce5SGreg Kroah-Hartman 	return tty;
144496fd7ce5SGreg Kroah-Hartman 
1445c8b710b3SPeter Hurley err_free_tty:
144689c8d91eSAlan Cox 	tty_unlock(tty);
1447d5543503SJiri Slaby 	free_tty_struct(tty);
1448d5543503SJiri Slaby err_module_put:
144996fd7ce5SGreg Kroah-Hartman 	module_put(driver->owner);
1450d5543503SJiri Slaby 	return ERR_PTR(retval);
145196fd7ce5SGreg Kroah-Hartman 
145296fd7ce5SGreg Kroah-Hartman 	/* call the tty release_tty routine to clean out this slot */
1453d5543503SJiri Slaby err_release_tty:
1454b027e229SGaurav Kohli 	tty_ldisc_unlock(tty);
1455339f36baSPeter Hurley 	tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
1456339f36baSPeter Hurley 			     retval, idx);
1457b027e229SGaurav Kohli err_release_lock:
1458b027e229SGaurav Kohli 	tty_unlock(tty);
145996fd7ce5SGreg Kroah-Hartman 	release_tty(tty, idx);
146096fd7ce5SGreg Kroah-Hartman 	return ERR_PTR(retval);
146196fd7ce5SGreg Kroah-Hartman }
146296fd7ce5SGreg Kroah-Hartman 
1463f51ccf46SJohan Hovold /**
1464f51ccf46SJohan Hovold  * tty_save_termios() - save tty termios data in driver table
1465f51ccf46SJohan Hovold  * @tty: tty whose termios data to save
1466f51ccf46SJohan Hovold  *
1467f51ccf46SJohan Hovold  * Locking: Caller guarantees serialisation with tty_init_termios().
1468f51ccf46SJohan Hovold  */
tty_save_termios(struct tty_struct * tty)1469f51ccf46SJohan Hovold void tty_save_termios(struct tty_struct *tty)
147096fd7ce5SGreg Kroah-Hartman {
147196fd7ce5SGreg Kroah-Hartman 	struct ktermios *tp;
147296fd7ce5SGreg Kroah-Hartman 	int idx = tty->index;
147336b3c070SAlan Cox 
147436b3c070SAlan Cox 	/* If the port is going to reset then it has no termios to save */
147536b3c070SAlan Cox 	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
147636b3c070SAlan Cox 		return;
147736b3c070SAlan Cox 
147836b3c070SAlan Cox 	/* Stash the termios data */
1479adc8d746SAlan Cox 	tp = tty->driver->termios[idx];
148036b3c070SAlan Cox 	if (tp == NULL) {
1481a3241891SGustavo A. R. Silva 		tp = kmalloc(sizeof(*tp), GFP_KERNEL);
148225080652SPeter Hurley 		if (tp == NULL)
148336b3c070SAlan Cox 			return;
14844ac5d705SDan Carpenter 		tty->driver->termios[idx] = tp;
148536b3c070SAlan Cox 	}
148636b3c070SAlan Cox 	*tp = tty->termios;
148796fd7ce5SGreg Kroah-Hartman }
1488f51ccf46SJohan Hovold EXPORT_SYMBOL_GPL(tty_save_termios);
148996fd7ce5SGreg Kroah-Hartman 
1490a2965b7bSPeter Hurley /**
1491949aa64fSPeter Hurley  * tty_flush_works	-	flush all works of a tty/pty pair
1492949aa64fSPeter Hurley  * @tty: tty device to flush works for (or either end of a pty pair)
1493a2965b7bSPeter Hurley  *
1494949aa64fSPeter Hurley  * Sync flush all works belonging to @tty (and the 'other' tty).
1495a2965b7bSPeter Hurley  */
tty_flush_works(struct tty_struct * tty)1496a2965b7bSPeter Hurley static void tty_flush_works(struct tty_struct *tty)
1497a2965b7bSPeter Hurley {
1498a2965b7bSPeter Hurley 	flush_work(&tty->SAK_work);
1499a2965b7bSPeter Hurley 	flush_work(&tty->hangup_work);
1500949aa64fSPeter Hurley 	if (tty->link) {
1501949aa64fSPeter Hurley 		flush_work(&tty->link->SAK_work);
1502949aa64fSPeter Hurley 		flush_work(&tty->link->hangup_work);
1503949aa64fSPeter Hurley 	}
1504a2965b7bSPeter Hurley }
150596fd7ce5SGreg Kroah-Hartman 
150696fd7ce5SGreg Kroah-Hartman /**
150796fd7ce5SGreg Kroah-Hartman  * release_one_tty	-	release tty structure memory
1508fa441954SJiri Slaby  * @work: work of tty we are obliterating
150996fd7ce5SGreg Kroah-Hartman  *
151096fd7ce5SGreg Kroah-Hartman  * Releases memory associated with a tty structure, and clears out the
151196fd7ce5SGreg Kroah-Hartman  * driver table slots. This function is called when a device is no longer
151296fd7ce5SGreg Kroah-Hartman  * in use. It also gets called when setup of a device fails.
151396fd7ce5SGreg Kroah-Hartman  *
151496fd7ce5SGreg Kroah-Hartman  * Locking:
1515796a75a9SJiri Slaby  *	takes the file list lock internally when working on the list of ttys
1516796a75a9SJiri Slaby  *	that the driver keeps.
151796fd7ce5SGreg Kroah-Hartman  *
151896fd7ce5SGreg Kroah-Hartman  * This method gets called from a work queue so that the driver private
151996fd7ce5SGreg Kroah-Hartman  * cleanup ops can sleep (needed for USB at least)
152096fd7ce5SGreg Kroah-Hartman  */
release_one_tty(struct work_struct * work)152196fd7ce5SGreg Kroah-Hartman static void release_one_tty(struct work_struct *work)
152296fd7ce5SGreg Kroah-Hartman {
152396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty =
152496fd7ce5SGreg Kroah-Hartman 		container_of(work, struct tty_struct, hangup_work);
152596fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver = tty->driver;
1526b216df53SCyrill Gorcunov 	struct module *owner = driver->owner;
152796fd7ce5SGreg Kroah-Hartman 
152896fd7ce5SGreg Kroah-Hartman 	if (tty->ops->cleanup)
152996fd7ce5SGreg Kroah-Hartman 		tty->ops->cleanup(tty);
153096fd7ce5SGreg Kroah-Hartman 
153196fd7ce5SGreg Kroah-Hartman 	tty_driver_kref_put(driver);
1532b216df53SCyrill Gorcunov 	module_put(owner);
153396fd7ce5SGreg Kroah-Hartman 
15344a510969SPeter Hurley 	spin_lock(&tty->files_lock);
153596fd7ce5SGreg Kroah-Hartman 	list_del_init(&tty->tty_files);
15364a510969SPeter Hurley 	spin_unlock(&tty->files_lock);
153796fd7ce5SGreg Kroah-Hartman 
153864d608dbSJiri Slaby 	put_pid(tty->ctrl.pgrp);
153964d608dbSJiri Slaby 	put_pid(tty->ctrl.session);
154096fd7ce5SGreg Kroah-Hartman 	free_tty_struct(tty);
154196fd7ce5SGreg Kroah-Hartman }
154296fd7ce5SGreg Kroah-Hartman 
queue_release_one_tty(struct kref * kref)154396fd7ce5SGreg Kroah-Hartman static void queue_release_one_tty(struct kref *kref)
154496fd7ce5SGreg Kroah-Hartman {
154596fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
154696fd7ce5SGreg Kroah-Hartman 
154796fd7ce5SGreg Kroah-Hartman 	/* The hangup queue is now free so we can reuse it rather than
1548b426a5b8SXiaofei Tan 	 *  waste a chunk of memory for each port.
1549b426a5b8SXiaofei Tan 	 */
155096fd7ce5SGreg Kroah-Hartman 	INIT_WORK(&tty->hangup_work, release_one_tty);
155196fd7ce5SGreg Kroah-Hartman 	schedule_work(&tty->hangup_work);
155296fd7ce5SGreg Kroah-Hartman }
155396fd7ce5SGreg Kroah-Hartman 
155496fd7ce5SGreg Kroah-Hartman /**
155596fd7ce5SGreg Kroah-Hartman  * tty_kref_put		-	release a tty kref
155696fd7ce5SGreg Kroah-Hartman  * @tty: tty device
155796fd7ce5SGreg Kroah-Hartman  *
1558796a75a9SJiri Slaby  * Release a reference to the @tty device and if need be let the kref layer
1559796a75a9SJiri Slaby  * destruct the object for us.
156096fd7ce5SGreg Kroah-Hartman  */
tty_kref_put(struct tty_struct * tty)156196fd7ce5SGreg Kroah-Hartman void tty_kref_put(struct tty_struct *tty)
156296fd7ce5SGreg Kroah-Hartman {
156396fd7ce5SGreg Kroah-Hartman 	if (tty)
156496fd7ce5SGreg Kroah-Hartman 		kref_put(&tty->kref, queue_release_one_tty);
156596fd7ce5SGreg Kroah-Hartman }
156696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_kref_put);
156796fd7ce5SGreg Kroah-Hartman 
156896fd7ce5SGreg Kroah-Hartman /**
156996fd7ce5SGreg Kroah-Hartman  * release_tty		-	release tty structure memory
157008aa5042SLee Jones  * @tty: tty device release
157108aa5042SLee Jones  * @idx: index of the tty device release
157296fd7ce5SGreg Kroah-Hartman  *
157396fd7ce5SGreg Kroah-Hartman  * Release both @tty and a possible linked partner (think pty pair),
157496fd7ce5SGreg Kroah-Hartman  * and decrement the refcount of the backing module.
157596fd7ce5SGreg Kroah-Hartman  *
157696fd7ce5SGreg Kroah-Hartman  * Locking:
1577d155255aSAlan Cox  *	tty_mutex
1578796a75a9SJiri Slaby  *	takes the file list lock internally when working on the list of ttys
1579796a75a9SJiri Slaby  *	that the driver keeps.
158096fd7ce5SGreg Kroah-Hartman  */
release_tty(struct tty_struct * tty,int idx)158196fd7ce5SGreg Kroah-Hartman static void release_tty(struct tty_struct *tty, int idx)
158296fd7ce5SGreg Kroah-Hartman {
158396fd7ce5SGreg Kroah-Hartman 	/* This should always be true but check for the moment */
158496fd7ce5SGreg Kroah-Hartman 	WARN_ON(tty->index != idx);
1585d155255aSAlan Cox 	WARN_ON(!mutex_is_locked(&tty_mutex));
158636b3c070SAlan Cox 	if (tty->ops->shutdown)
158736b3c070SAlan Cox 		tty->ops->shutdown(tty);
1588f51ccf46SJohan Hovold 	tty_save_termios(tty);
158936b3c070SAlan Cox 	tty_driver_remove_tty(tty->driver, tty);
15904466d6d2SMatthias Reichl 	if (tty->port)
1591967fab69SJiri Slaby 		tty->port->itty = NULL;
159264e377dcSPeter Hurley 	if (tty->link)
159364e377dcSPeter Hurley 		tty->link->port->itty = NULL;
15944466d6d2SMatthias Reichl 	if (tty->port)
1595e176058fSPeter Hurley 		tty_buffer_cancel_work(tty->port);
15962b022ab7SSahara 	if (tty->link)
15972b022ab7SSahara 		tty_buffer_cancel_work(tty->link->port);
159836b3c070SAlan Cox 
159996fd7ce5SGreg Kroah-Hartman 	tty_kref_put(tty->link);
160096fd7ce5SGreg Kroah-Hartman 	tty_kref_put(tty);
160196fd7ce5SGreg Kroah-Hartman }
160296fd7ce5SGreg Kroah-Hartman 
160396fd7ce5SGreg Kroah-Hartman /**
1604955787caSJiri Slaby  * tty_release_checks - check a tty before real release
1605955787caSJiri Slaby  * @tty: tty to check
1606955787caSJiri Slaby  * @idx: index of the tty
1607955787caSJiri Slaby  *
1608796a75a9SJiri Slaby  * Performs some paranoid checking before true release of the @tty. This is a
1609796a75a9SJiri Slaby  * no-op unless %TTY_PARANOIA_CHECK is defined.
1610955787caSJiri Slaby  */
tty_release_checks(struct tty_struct * tty,int idx)1611359b9fb5SPeter Hurley static int tty_release_checks(struct tty_struct *tty, int idx)
1612955787caSJiri Slaby {
1613955787caSJiri Slaby #ifdef TTY_PARANOIA_CHECK
1614955787caSJiri Slaby 	if (idx < 0 || idx >= tty->driver->num) {
1615e2dfa3d3SPeter Hurley 		tty_debug(tty, "bad idx %d\n", idx);
1616955787caSJiri Slaby 		return -1;
1617955787caSJiri Slaby 	}
1618955787caSJiri Slaby 
1619955787caSJiri Slaby 	/* not much to check for devpts */
1620955787caSJiri Slaby 	if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)
1621955787caSJiri Slaby 		return 0;
1622955787caSJiri Slaby 
1623955787caSJiri Slaby 	if (tty != tty->driver->ttys[idx]) {
1624e2dfa3d3SPeter Hurley 		tty_debug(tty, "bad driver table[%d] = %p\n",
1625e2dfa3d3SPeter Hurley 			  idx, tty->driver->ttys[idx]);
1626955787caSJiri Slaby 		return -1;
1627955787caSJiri Slaby 	}
1628955787caSJiri Slaby 	if (tty->driver->other) {
1629359b9fb5SPeter Hurley 		struct tty_struct *o_tty = tty->link;
1630359b9fb5SPeter Hurley 
1631955787caSJiri Slaby 		if (o_tty != tty->driver->other->ttys[idx]) {
1632e2dfa3d3SPeter Hurley 			tty_debug(tty, "bad other table[%d] = %p\n",
1633e2dfa3d3SPeter Hurley 				  idx, tty->driver->other->ttys[idx]);
1634955787caSJiri Slaby 			return -1;
1635955787caSJiri Slaby 		}
1636955787caSJiri Slaby 		if (o_tty->link != tty) {
1637e2dfa3d3SPeter Hurley 			tty_debug(tty, "bad link = %p\n", o_tty->link);
1638955787caSJiri Slaby 			return -1;
1639955787caSJiri Slaby 		}
1640955787caSJiri Slaby 	}
1641955787caSJiri Slaby #endif
1642955787caSJiri Slaby 	return 0;
1643955787caSJiri Slaby }
1644955787caSJiri Slaby 
1645955787caSJiri Slaby /**
1646a09ac397SOkash Khawaja  * tty_kclose      -       closes tty opened by tty_kopen
1647a09ac397SOkash Khawaja  * @tty: tty device
1648a09ac397SOkash Khawaja  *
1649796a75a9SJiri Slaby  * Performs the final steps to release and free a tty device. It is the same as
1650796a75a9SJiri Slaby  * tty_release_struct() except that it also resets %TTY_PORT_KOPENED flag on
1651796a75a9SJiri Slaby  * @tty->port.
1652a09ac397SOkash Khawaja  */
tty_kclose(struct tty_struct * tty)1653a09ac397SOkash Khawaja void tty_kclose(struct tty_struct *tty)
1654a09ac397SOkash Khawaja {
1655a09ac397SOkash Khawaja 	/*
1656a09ac397SOkash Khawaja 	 * Ask the line discipline code to release its structures
1657a09ac397SOkash Khawaja 	 */
1658a09ac397SOkash Khawaja 	tty_ldisc_release(tty);
1659a09ac397SOkash Khawaja 
1660da1bb4edSJulia Lawall 	/* Wait for pending work before tty destruction commences */
1661a09ac397SOkash Khawaja 	tty_flush_works(tty);
1662a09ac397SOkash Khawaja 
1663a09ac397SOkash Khawaja 	tty_debug_hangup(tty, "freeing structure\n");
1664a09ac397SOkash Khawaja 	/*
1665a09ac397SOkash Khawaja 	 * The release_tty function takes care of the details of clearing
1666ed069827SEric Biggers 	 * the slots and preserving the termios structure.
1667a09ac397SOkash Khawaja 	 */
1668a09ac397SOkash Khawaja 	mutex_lock(&tty_mutex);
1669a09ac397SOkash Khawaja 	tty_port_set_kopened(tty->port, 0);
1670a09ac397SOkash Khawaja 	release_tty(tty, tty->index);
1671a09ac397SOkash Khawaja 	mutex_unlock(&tty_mutex);
1672a09ac397SOkash Khawaja }
1673a09ac397SOkash Khawaja EXPORT_SYMBOL_GPL(tty_kclose);
1674a09ac397SOkash Khawaja 
1675a09ac397SOkash Khawaja /**
16769ed90d20SRob Herring  * tty_release_struct	-	release a tty struct
16779ed90d20SRob Herring  * @tty: tty device
16789ed90d20SRob Herring  * @idx: index of the tty
16799ed90d20SRob Herring  *
1680796a75a9SJiri Slaby  * Performs the final steps to release and free a tty device. It is roughly the
1681796a75a9SJiri Slaby  * reverse of tty_init_dev().
16829ed90d20SRob Herring  */
tty_release_struct(struct tty_struct * tty,int idx)16839ed90d20SRob Herring void tty_release_struct(struct tty_struct *tty, int idx)
16849ed90d20SRob Herring {
16859ed90d20SRob Herring 	/*
16869ed90d20SRob Herring 	 * Ask the line discipline code to release its structures
16879ed90d20SRob Herring 	 */
16889ed90d20SRob Herring 	tty_ldisc_release(tty);
16899ed90d20SRob Herring 
16909ed90d20SRob Herring 	/* Wait for pending work before tty destruction commmences */
16919ed90d20SRob Herring 	tty_flush_works(tty);
16929ed90d20SRob Herring 
16939ed90d20SRob Herring 	tty_debug_hangup(tty, "freeing structure\n");
16949ed90d20SRob Herring 	/*
16959ed90d20SRob Herring 	 * The release_tty function takes care of the details of clearing
1696ed069827SEric Biggers 	 * the slots and preserving the termios structure.
16979ed90d20SRob Herring 	 */
16989ed90d20SRob Herring 	mutex_lock(&tty_mutex);
16999ed90d20SRob Herring 	release_tty(tty, idx);
17009ed90d20SRob Herring 	mutex_unlock(&tty_mutex);
17019ed90d20SRob Herring }
17029ed90d20SRob Herring EXPORT_SYMBOL_GPL(tty_release_struct);
17039ed90d20SRob Herring 
17049ed90d20SRob Herring /**
170596fd7ce5SGreg Kroah-Hartman  * tty_release		-	vfs callback for close
170696fd7ce5SGreg Kroah-Hartman  * @inode: inode of tty
170796fd7ce5SGreg Kroah-Hartman  * @filp: file pointer for handle to tty
170896fd7ce5SGreg Kroah-Hartman  *
1709796a75a9SJiri Slaby  * Called the last time each file handle is closed that references this tty.
1710796a75a9SJiri Slaby  * There may however be several such references.
171196fd7ce5SGreg Kroah-Hartman  *
171296fd7ce5SGreg Kroah-Hartman  * Locking:
1713796a75a9SJiri Slaby  *	Takes BKL. See tty_release_dev().
171496fd7ce5SGreg Kroah-Hartman  *
1715796a75a9SJiri Slaby  * Even releasing the tty structures is a tricky business. We have to be very
1716796a75a9SJiri Slaby  * careful that the structures are all released at the same time, as interrupts
1717796a75a9SJiri Slaby  * might otherwise get the wrong pointers.
171896fd7ce5SGreg Kroah-Hartman  *
171996fd7ce5SGreg Kroah-Hartman  * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
172096fd7ce5SGreg Kroah-Hartman  * lead to double frees or releasing memory still in use.
172196fd7ce5SGreg Kroah-Hartman  */
tty_release(struct inode * inode,struct file * filp)172296fd7ce5SGreg Kroah-Hartman int tty_release(struct inode *inode, struct file *filp)
172396fd7ce5SGreg Kroah-Hartman {
172496fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(filp);
17257ffb6da9SPeter Hurley 	struct tty_struct *o_tty = NULL;
17267ffb6da9SPeter Hurley 	int	do_sleep, final;
172796fd7ce5SGreg Kroah-Hartman 	int	idx;
172837b16457SPeter Hurley 	long	timeout = 0;
1729494c1eacSPeter Hurley 	int	once = 1;
173096fd7ce5SGreg Kroah-Hartman 
17319de44bd6SJiri Slaby 	if (tty_paranoia_check(tty, inode, __func__))
173296fd7ce5SGreg Kroah-Hartman 		return 0;
173396fd7ce5SGreg Kroah-Hartman 
173489c8d91eSAlan Cox 	tty_lock(tty);
17359de44bd6SJiri Slaby 	check_tty_count(tty, __func__);
173696fd7ce5SGreg Kroah-Hartman 
173796fd7ce5SGreg Kroah-Hartman 	__tty_fasync(-1, filp, 0);
173896fd7ce5SGreg Kroah-Hartman 
173996fd7ce5SGreg Kroah-Hartman 	idx = tty->index;
17407ffb6da9SPeter Hurley 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
17417ffb6da9SPeter Hurley 	    tty->driver->subtype == PTY_TYPE_MASTER)
174296fd7ce5SGreg Kroah-Hartman 		o_tty = tty->link;
174396fd7ce5SGreg Kroah-Hartman 
1744359b9fb5SPeter Hurley 	if (tty_release_checks(tty, idx)) {
174589c8d91eSAlan Cox 		tty_unlock(tty);
174696fd7ce5SGreg Kroah-Hartman 		return 0;
174796fd7ce5SGreg Kroah-Hartman 	}
174896fd7ce5SGreg Kroah-Hartman 
1749d435cefeSPeter Hurley 	tty_debug_hangup(tty, "releasing (count=%d)\n", tty->count);
175096fd7ce5SGreg Kroah-Hartman 
175196fd7ce5SGreg Kroah-Hartman 	if (tty->ops->close)
175296fd7ce5SGreg Kroah-Hartman 		tty->ops->close(tty, filp);
175396fd7ce5SGreg Kroah-Hartman 
17542aff5e2bSPeter Hurley 	/* If tty is pty master, lock the slave pty (stable lock order) */
17552aff5e2bSPeter Hurley 	tty_lock_slave(o_tty);
17562aff5e2bSPeter Hurley 
175796fd7ce5SGreg Kroah-Hartman 	/*
175896fd7ce5SGreg Kroah-Hartman 	 * Sanity check: if tty->count is going to zero, there shouldn't be
175996fd7ce5SGreg Kroah-Hartman 	 * any waiters on tty->read_wait or tty->write_wait.  We test the
176096fd7ce5SGreg Kroah-Hartman 	 * wait queues and kick everyone out _before_ actually starting to
176196fd7ce5SGreg Kroah-Hartman 	 * close.  This ensures that we won't block while releasing the tty
176296fd7ce5SGreg Kroah-Hartman 	 * structure.
176396fd7ce5SGreg Kroah-Hartman 	 *
176496fd7ce5SGreg Kroah-Hartman 	 * The test for the o_tty closing is necessary, since the master and
176596fd7ce5SGreg Kroah-Hartman 	 * slave sides may close in any order.  If the slave side closes out
176696fd7ce5SGreg Kroah-Hartman 	 * first, its count will be one, since the master side holds an open.
1767324c1650SPeter Hurley 	 * Thus this test wouldn't be triggered at the time the slave closed,
176896fd7ce5SGreg Kroah-Hartman 	 * so we do it now.
176996fd7ce5SGreg Kroah-Hartman 	 */
1770d5e370a4SPeter Hurley 	while (1) {
177196fd7ce5SGreg Kroah-Hartman 		do_sleep = 0;
177296fd7ce5SGreg Kroah-Hartman 
1773324c1650SPeter Hurley 		if (tty->count <= 1) {
177496fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&tty->read_wait)) {
1775a9a08845SLinus Torvalds 				wake_up_poll(&tty->read_wait, EPOLLIN);
177696fd7ce5SGreg Kroah-Hartman 				do_sleep++;
177796fd7ce5SGreg Kroah-Hartman 			}
177896fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&tty->write_wait)) {
1779a9a08845SLinus Torvalds 				wake_up_poll(&tty->write_wait, EPOLLOUT);
178096fd7ce5SGreg Kroah-Hartman 				do_sleep++;
178196fd7ce5SGreg Kroah-Hartman 			}
178296fd7ce5SGreg Kroah-Hartman 		}
17837ffb6da9SPeter Hurley 		if (o_tty && o_tty->count <= 1) {
178496fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&o_tty->read_wait)) {
1785a9a08845SLinus Torvalds 				wake_up_poll(&o_tty->read_wait, EPOLLIN);
178696fd7ce5SGreg Kroah-Hartman 				do_sleep++;
178796fd7ce5SGreg Kroah-Hartman 			}
178896fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&o_tty->write_wait)) {
1789a9a08845SLinus Torvalds 				wake_up_poll(&o_tty->write_wait, EPOLLOUT);
179096fd7ce5SGreg Kroah-Hartman 				do_sleep++;
179196fd7ce5SGreg Kroah-Hartman 			}
179296fd7ce5SGreg Kroah-Hartman 		}
179396fd7ce5SGreg Kroah-Hartman 		if (!do_sleep)
179496fd7ce5SGreg Kroah-Hartman 			break;
179596fd7ce5SGreg Kroah-Hartman 
1796494c1eacSPeter Hurley 		if (once) {
1797494c1eacSPeter Hurley 			once = 0;
1798339f36baSPeter Hurley 			tty_warn(tty, "read/write wait queue active!\n");
1799494c1eacSPeter Hurley 		}
180037b16457SPeter Hurley 		schedule_timeout_killable(timeout);
180137b16457SPeter Hurley 		if (timeout < 120 * HZ)
180237b16457SPeter Hurley 			timeout = 2 * timeout + 1;
180337b16457SPeter Hurley 		else
180437b16457SPeter Hurley 			timeout = MAX_SCHEDULE_TIMEOUT;
180596fd7ce5SGreg Kroah-Hartman 	}
180696fd7ce5SGreg Kroah-Hartman 
18077ffb6da9SPeter Hurley 	if (o_tty) {
180896fd7ce5SGreg Kroah-Hartman 		if (--o_tty->count < 0) {
1809339f36baSPeter Hurley 			tty_warn(tty, "bad slave count (%d)\n", o_tty->count);
181096fd7ce5SGreg Kroah-Hartman 			o_tty->count = 0;
181196fd7ce5SGreg Kroah-Hartman 		}
181296fd7ce5SGreg Kroah-Hartman 	}
181396fd7ce5SGreg Kroah-Hartman 	if (--tty->count < 0) {
1814339f36baSPeter Hurley 		tty_warn(tty, "bad tty->count (%d)\n", tty->count);
181596fd7ce5SGreg Kroah-Hartman 		tty->count = 0;
181696fd7ce5SGreg Kroah-Hartman 	}
181796fd7ce5SGreg Kroah-Hartman 
181896fd7ce5SGreg Kroah-Hartman 	/*
181996fd7ce5SGreg Kroah-Hartman 	 * We've decremented tty->count, so we need to remove this file
182096fd7ce5SGreg Kroah-Hartman 	 * descriptor off the tty->tty_files list; this serves two
182196fd7ce5SGreg Kroah-Hartman 	 * purposes:
182296fd7ce5SGreg Kroah-Hartman 	 *  - check_tty_count sees the correct number of file descriptors
182396fd7ce5SGreg Kroah-Hartman 	 *    associated with this tty.
182496fd7ce5SGreg Kroah-Hartman 	 *  - do_tty_hangup no longer sees this file descriptor as
182596fd7ce5SGreg Kroah-Hartman 	 *    something that needs to be handled for hangups.
182696fd7ce5SGreg Kroah-Hartman 	 */
182796fd7ce5SGreg Kroah-Hartman 	tty_del_file(filp);
182896fd7ce5SGreg Kroah-Hartman 
182996fd7ce5SGreg Kroah-Hartman 	/*
183096fd7ce5SGreg Kroah-Hartman 	 * Perform some housekeeping before deciding whether to return.
183196fd7ce5SGreg Kroah-Hartman 	 *
183296fd7ce5SGreg Kroah-Hartman 	 * If _either_ side is closing, make sure there aren't any
183396fd7ce5SGreg Kroah-Hartman 	 * processes that still think tty or o_tty is their controlling
183496fd7ce5SGreg Kroah-Hartman 	 * tty.
183596fd7ce5SGreg Kroah-Hartman 	 */
1836324c1650SPeter Hurley 	if (!tty->count) {
183796fd7ce5SGreg Kroah-Hartman 		read_lock(&tasklist_lock);
183864d608dbSJiri Slaby 		session_clear_tty(tty->ctrl.session);
18397ffb6da9SPeter Hurley 		if (o_tty)
184064d608dbSJiri Slaby 			session_clear_tty(o_tty->ctrl.session);
184196fd7ce5SGreg Kroah-Hartman 		read_unlock(&tasklist_lock);
184296fd7ce5SGreg Kroah-Hartman 	}
184396fd7ce5SGreg Kroah-Hartman 
1844324c1650SPeter Hurley 	/* check whether both sides are closing ... */
18457ffb6da9SPeter Hurley 	final = !tty->count && !(o_tty && o_tty->count);
1846324c1650SPeter Hurley 
18472aff5e2bSPeter Hurley 	tty_unlock_slave(o_tty);
18482aff5e2bSPeter Hurley 	tty_unlock(tty);
18492aff5e2bSPeter Hurley 
185004980706SPeter Hurley 	/* At this point, the tty->count == 0 should ensure a dead tty
1851b426a5b8SXiaofei Tan 	 * cannot be re-opened by a racing opener.
1852b426a5b8SXiaofei Tan 	 */
185396fd7ce5SGreg Kroah-Hartman 
1854324c1650SPeter Hurley 	if (!final)
185596fd7ce5SGreg Kroah-Hartman 		return 0;
185696fd7ce5SGreg Kroah-Hartman 
1857accff793SPeter Hurley 	tty_debug_hangup(tty, "final close\n");
1858a2965b7bSPeter Hurley 
18599ed90d20SRob Herring 	tty_release_struct(tty, idx);
186096fd7ce5SGreg Kroah-Hartman 	return 0;
186196fd7ce5SGreg Kroah-Hartman }
186296fd7ce5SGreg Kroah-Hartman 
186396fd7ce5SGreg Kroah-Hartman /**
186452494eebSPeter Hurley  * tty_open_current_tty - get locked tty of current task
1865b82154acSJiri Slaby  * @device: device number
1866b82154acSJiri Slaby  * @filp: file pointer to tty
186752494eebSPeter Hurley  * @return: locked tty of the current task iff @device is /dev/tty
186852494eebSPeter Hurley  *
186952494eebSPeter Hurley  * Performs a re-open of the current task's controlling tty.
1870b82154acSJiri Slaby  *
1871796a75a9SJiri Slaby  * We cannot return driver and index like for the other nodes because devpts
1872796a75a9SJiri Slaby  * will not work then. It expects inodes to be from devpts FS.
1873b82154acSJiri Slaby  */
tty_open_current_tty(dev_t device,struct file * filp)1874b82154acSJiri Slaby static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
1875b82154acSJiri Slaby {
1876b82154acSJiri Slaby 	struct tty_struct *tty;
187752494eebSPeter Hurley 	int retval;
1878b82154acSJiri Slaby 
1879b82154acSJiri Slaby 	if (device != MKDEV(TTYAUX_MAJOR, 0))
1880b82154acSJiri Slaby 		return NULL;
1881b82154acSJiri Slaby 
1882b82154acSJiri Slaby 	tty = get_current_tty();
1883b82154acSJiri Slaby 	if (!tty)
1884b82154acSJiri Slaby 		return ERR_PTR(-ENXIO);
1885b82154acSJiri Slaby 
1886b82154acSJiri Slaby 	filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
1887b82154acSJiri Slaby 	/* noctty = 1; */
188852494eebSPeter Hurley 	tty_lock(tty);
188952494eebSPeter Hurley 	tty_kref_put(tty);	/* safe to drop the kref now */
189052494eebSPeter Hurley 
189152494eebSPeter Hurley 	retval = tty_reopen(tty);
189252494eebSPeter Hurley 	if (retval < 0) {
189352494eebSPeter Hurley 		tty_unlock(tty);
189452494eebSPeter Hurley 		tty = ERR_PTR(retval);
189552494eebSPeter Hurley 	}
1896b82154acSJiri Slaby 	return tty;
1897b82154acSJiri Slaby }
1898b82154acSJiri Slaby 
1899b82154acSJiri Slaby /**
19005b5e7040SJiri Slaby  * tty_lookup_driver - lookup a tty driver for a given device file
19015b5e7040SJiri Slaby  * @device: device number
19025b5e7040SJiri Slaby  * @filp: file pointer to tty
19035b5e7040SJiri Slaby  * @index: index for the device in the @return driver
19045b5e7040SJiri Slaby  *
1905796a75a9SJiri Slaby  * If returned value is not erroneous, the caller is responsible to decrement
1906796a75a9SJiri Slaby  * the refcount by tty_driver_kref_put().
19075b5e7040SJiri Slaby  *
1908796a75a9SJiri Slaby  * Locking: %tty_mutex protects get_tty_driver()
1909796a75a9SJiri Slaby  *
1910796a75a9SJiri Slaby  * Return: driver for this inode (with increased refcount)
19115b5e7040SJiri Slaby  */
tty_lookup_driver(dev_t device,struct file * filp,int * index)19125b5e7040SJiri Slaby static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
191311e1d4aaSPeter Hurley 		int *index)
19145b5e7040SJiri Slaby {
19158c8af41dSLin Yi 	struct tty_driver *driver = NULL;
19165b5e7040SJiri Slaby 
19172cd0050cSJiri Slaby 	switch (device) {
19185b5e7040SJiri Slaby #ifdef CONFIG_VT
19192cd0050cSJiri Slaby 	case MKDEV(TTY_MAJOR, 0): {
19205b5e7040SJiri Slaby 		extern struct tty_driver *console_driver;
1921e73b2407SXiaofei Tan 
19225b5e7040SJiri Slaby 		driver = tty_driver_kref_get(console_driver);
19235b5e7040SJiri Slaby 		*index = fg_console;
19242cd0050cSJiri Slaby 		break;
19255b5e7040SJiri Slaby 	}
19265b5e7040SJiri Slaby #endif
19272cd0050cSJiri Slaby 	case MKDEV(TTYAUX_MAJOR, 1): {
19285b5e7040SJiri Slaby 		struct tty_driver *console_driver = console_device(index);
1929e73b2407SXiaofei Tan 
19305b5e7040SJiri Slaby 		if (console_driver) {
19315b5e7040SJiri Slaby 			driver = tty_driver_kref_get(console_driver);
193212e84c71SOkash Khawaja 			if (driver && filp) {
19335b5e7040SJiri Slaby 				/* Don't let /dev/console block */
19345b5e7040SJiri Slaby 				filp->f_flags |= O_NONBLOCK;
19352cd0050cSJiri Slaby 				break;
19365b5e7040SJiri Slaby 			}
19375b5e7040SJiri Slaby 		}
19388c8af41dSLin Yi 		if (driver)
19398c8af41dSLin Yi 			tty_driver_kref_put(driver);
19405b5e7040SJiri Slaby 		return ERR_PTR(-ENODEV);
19415b5e7040SJiri Slaby 	}
19422cd0050cSJiri Slaby 	default:
19435b5e7040SJiri Slaby 		driver = get_tty_driver(device, index);
19445b5e7040SJiri Slaby 		if (!driver)
19455b5e7040SJiri Slaby 			return ERR_PTR(-ENODEV);
19462cd0050cSJiri Slaby 		break;
19472cd0050cSJiri Slaby 	}
19485b5e7040SJiri Slaby 	return driver;
19495b5e7040SJiri Slaby }
19505b5e7040SJiri Slaby 
tty_kopen(dev_t device,int shared)19514ea3cd65SUwe Kleine-König static struct tty_struct *tty_kopen(dev_t device, int shared)
1952a09ac397SOkash Khawaja {
1953a09ac397SOkash Khawaja 	struct tty_struct *tty;
1954f1d31743SUwe Kleine-König 	struct tty_driver *driver;
1955a09ac397SOkash Khawaja 	int index = -1;
1956a09ac397SOkash Khawaja 
1957a09ac397SOkash Khawaja 	mutex_lock(&tty_mutex);
1958a09ac397SOkash Khawaja 	driver = tty_lookup_driver(device, NULL, &index);
1959a09ac397SOkash Khawaja 	if (IS_ERR(driver)) {
1960a09ac397SOkash Khawaja 		mutex_unlock(&tty_mutex);
1961a09ac397SOkash Khawaja 		return ERR_CAST(driver);
1962a09ac397SOkash Khawaja 	}
1963a09ac397SOkash Khawaja 
1964a09ac397SOkash Khawaja 	/* check whether we're reopening an existing tty */
1965a09ac397SOkash Khawaja 	tty = tty_driver_lookup_tty(driver, NULL, index);
19664ea3cd65SUwe Kleine-König 	if (IS_ERR(tty) || shared)
1967a09ac397SOkash Khawaja 		goto out;
1968a09ac397SOkash Khawaja 
1969a09ac397SOkash Khawaja 	if (tty) {
1970a09ac397SOkash Khawaja 		/* drop kref from tty_driver_lookup_tty() */
1971a09ac397SOkash Khawaja 		tty_kref_put(tty);
1972a09ac397SOkash Khawaja 		tty = ERR_PTR(-EBUSY);
1973a09ac397SOkash Khawaja 	} else { /* tty_init_dev returns tty with the tty_lock held */
1974a09ac397SOkash Khawaja 		tty = tty_init_dev(driver, index);
1975a09ac397SOkash Khawaja 		if (IS_ERR(tty))
1976a09ac397SOkash Khawaja 			goto out;
1977a09ac397SOkash Khawaja 		tty_port_set_kopened(tty->port, 1);
1978a09ac397SOkash Khawaja 	}
1979a09ac397SOkash Khawaja out:
1980a09ac397SOkash Khawaja 	mutex_unlock(&tty_mutex);
1981a09ac397SOkash Khawaja 	tty_driver_kref_put(driver);
1982a09ac397SOkash Khawaja 	return tty;
1983a09ac397SOkash Khawaja }
19844ea3cd65SUwe Kleine-König 
19854ea3cd65SUwe Kleine-König /**
19864ea3cd65SUwe Kleine-König  * tty_kopen_exclusive	-	open a tty device for kernel
19874ea3cd65SUwe Kleine-König  * @device: dev_t of device to open
19884ea3cd65SUwe Kleine-König  *
1989796a75a9SJiri Slaby  * Opens tty exclusively for kernel. Performs the driver lookup, makes sure
1990796a75a9SJiri Slaby  * it's not already opened and performs the first-time tty initialization.
19914ea3cd65SUwe Kleine-König  *
1992796a75a9SJiri Slaby  * Claims the global %tty_mutex to serialize:
1993796a75a9SJiri Slaby  *  * concurrent first-time tty initialization
1994796a75a9SJiri Slaby  *  * concurrent tty driver removal w/ lookup
1995796a75a9SJiri Slaby  *  * concurrent tty removal from driver table
19964ea3cd65SUwe Kleine-König  *
1997796a75a9SJiri Slaby  * Return: the locked initialized &tty_struct
19984ea3cd65SUwe Kleine-König  */
tty_kopen_exclusive(dev_t device)19994ea3cd65SUwe Kleine-König struct tty_struct *tty_kopen_exclusive(dev_t device)
20004ea3cd65SUwe Kleine-König {
20014ea3cd65SUwe Kleine-König 	return tty_kopen(device, 0);
20024ea3cd65SUwe Kleine-König }
20034ea3cd65SUwe Kleine-König EXPORT_SYMBOL_GPL(tty_kopen_exclusive);
20044ea3cd65SUwe Kleine-König 
20054ea3cd65SUwe Kleine-König /**
20064ea3cd65SUwe Kleine-König  * tty_kopen_shared	-	open a tty device for shared in-kernel use
20074ea3cd65SUwe Kleine-König  * @device: dev_t of device to open
20084ea3cd65SUwe Kleine-König  *
20094ea3cd65SUwe Kleine-König  * Opens an already existing tty for in-kernel use. Compared to
20104ea3cd65SUwe Kleine-König  * tty_kopen_exclusive() above it doesn't ensure to be the only user.
20114ea3cd65SUwe Kleine-König  *
2012796a75a9SJiri Slaby  * Locking: identical to tty_kopen() above.
20134ea3cd65SUwe Kleine-König  */
tty_kopen_shared(dev_t device)20144ea3cd65SUwe Kleine-König struct tty_struct *tty_kopen_shared(dev_t device)
20154ea3cd65SUwe Kleine-König {
20164ea3cd65SUwe Kleine-König 	return tty_kopen(device, 1);
20174ea3cd65SUwe Kleine-König }
20184ea3cd65SUwe Kleine-König EXPORT_SYMBOL_GPL(tty_kopen_shared);
2019a09ac397SOkash Khawaja 
2020a09ac397SOkash Khawaja /**
2021d6203d0cSPeter Hurley  * tty_open_by_driver	-	open a tty device
2022d6203d0cSPeter Hurley  * @device: dev_t of device to open
2023d6203d0cSPeter Hurley  * @filp: file pointer to tty
2024d6203d0cSPeter Hurley  *
2025796a75a9SJiri Slaby  * Performs the driver lookup, checks for a reopen, or otherwise performs the
2026796a75a9SJiri Slaby  * first-time tty initialization.
2027d6203d0cSPeter Hurley  *
2028d6203d0cSPeter Hurley  *
2029d6203d0cSPeter Hurley  * Claims the global tty_mutex to serialize:
2030796a75a9SJiri Slaby  *  * concurrent first-time tty initialization
2031796a75a9SJiri Slaby  *  * concurrent tty driver removal w/ lookup
2032796a75a9SJiri Slaby  *  * concurrent tty removal from driver table
2033796a75a9SJiri Slaby  *
2034796a75a9SJiri Slaby  * Return: the locked initialized or re-opened &tty_struct
2035d6203d0cSPeter Hurley  */
tty_open_by_driver(dev_t device,struct file * filp)203614ce3848SSudip Mukherjee static struct tty_struct *tty_open_by_driver(dev_t device,
2037d6203d0cSPeter Hurley 					     struct file *filp)
2038d6203d0cSPeter Hurley {
2039d6203d0cSPeter Hurley 	struct tty_struct *tty;
2040d6203d0cSPeter Hurley 	struct tty_driver *driver = NULL;
2041d6203d0cSPeter Hurley 	int index = -1;
2042d6203d0cSPeter Hurley 	int retval;
2043d6203d0cSPeter Hurley 
2044d6203d0cSPeter Hurley 	mutex_lock(&tty_mutex);
2045d6203d0cSPeter Hurley 	driver = tty_lookup_driver(device, filp, &index);
2046d6203d0cSPeter Hurley 	if (IS_ERR(driver)) {
2047d6203d0cSPeter Hurley 		mutex_unlock(&tty_mutex);
2048d6203d0cSPeter Hurley 		return ERR_CAST(driver);
2049d6203d0cSPeter Hurley 	}
2050d6203d0cSPeter Hurley 
2051d6203d0cSPeter Hurley 	/* check whether we're reopening an existing tty */
20528ead9dd5SLinus Torvalds 	tty = tty_driver_lookup_tty(driver, filp, index);
2053d6203d0cSPeter Hurley 	if (IS_ERR(tty)) {
2054d6203d0cSPeter Hurley 		mutex_unlock(&tty_mutex);
2055d6203d0cSPeter Hurley 		goto out;
2056d6203d0cSPeter Hurley 	}
2057d6203d0cSPeter Hurley 
2058d6203d0cSPeter Hurley 	if (tty) {
2059a09ac397SOkash Khawaja 		if (tty_port_kopened(tty->port)) {
2060a09ac397SOkash Khawaja 			tty_kref_put(tty);
2061a09ac397SOkash Khawaja 			mutex_unlock(&tty_mutex);
2062a09ac397SOkash Khawaja 			tty = ERR_PTR(-EBUSY);
2063a09ac397SOkash Khawaja 			goto out;
2064a09ac397SOkash Khawaja 		}
2065d6203d0cSPeter Hurley 		mutex_unlock(&tty_mutex);
2066d6203d0cSPeter Hurley 		retval = tty_lock_interruptible(tty);
20675e00bbfbSPeter Hurley 		tty_kref_put(tty);  /* drop kref from tty_driver_lookup_tty() */
2068d6203d0cSPeter Hurley 		if (retval) {
2069d6203d0cSPeter Hurley 			if (retval == -EINTR)
2070d6203d0cSPeter Hurley 				retval = -ERESTARTSYS;
2071d6203d0cSPeter Hurley 			tty = ERR_PTR(retval);
2072d6203d0cSPeter Hurley 			goto out;
2073d6203d0cSPeter Hurley 		}
2074d6203d0cSPeter Hurley 		retval = tty_reopen(tty);
2075d6203d0cSPeter Hurley 		if (retval < 0) {
2076d6203d0cSPeter Hurley 			tty_unlock(tty);
2077d6203d0cSPeter Hurley 			tty = ERR_PTR(retval);
2078d6203d0cSPeter Hurley 		}
2079d6203d0cSPeter Hurley 	} else { /* Returns with the tty_lock held for now */
2080d6203d0cSPeter Hurley 		tty = tty_init_dev(driver, index);
2081d6203d0cSPeter Hurley 		mutex_unlock(&tty_mutex);
2082d6203d0cSPeter Hurley 	}
2083d6203d0cSPeter Hurley out:
2084d6203d0cSPeter Hurley 	tty_driver_kref_put(driver);
2085d6203d0cSPeter Hurley 	return tty;
2086d6203d0cSPeter Hurley }
2087d6203d0cSPeter Hurley 
2088d6203d0cSPeter Hurley /**
208996fd7ce5SGreg Kroah-Hartman  * tty_open	-	open a tty device
209096fd7ce5SGreg Kroah-Hartman  * @inode: inode of device file
209196fd7ce5SGreg Kroah-Hartman  * @filp: file pointer to tty
209296fd7ce5SGreg Kroah-Hartman  *
2093796a75a9SJiri Slaby  * tty_open() and tty_release() keep up the tty count that contains the number
2094796a75a9SJiri Slaby  * of opens done on a tty. We cannot use the inode-count, as different inodes
2095796a75a9SJiri Slaby  * might point to the same tty.
209696fd7ce5SGreg Kroah-Hartman  *
2097796a75a9SJiri Slaby  * Open-counting is needed for pty masters, as well as for keeping track of
2098796a75a9SJiri Slaby  * serial lines: DTR is dropped when the last close happens.
209996fd7ce5SGreg Kroah-Hartman  * (This is not done solely through tty->count, now.  - Ted 1/27/92)
210096fd7ce5SGreg Kroah-Hartman  *
2101796a75a9SJiri Slaby  * The termios state of a pty is reset on the first open so that settings don't
2102796a75a9SJiri Slaby  * persist across reuse.
210396fd7ce5SGreg Kroah-Hartman  *
2104796a75a9SJiri Slaby  * Locking:
2105796a75a9SJiri Slaby  *  * %tty_mutex protects tty, tty_lookup_driver() and tty_init_dev().
2106796a75a9SJiri Slaby  *  * @tty->count should protect the rest.
2107796a75a9SJiri Slaby  *  * ->siglock protects ->signal/->sighand
210889c8d91eSAlan Cox  *
2109796a75a9SJiri Slaby  * Note: the tty_unlock/lock cases without a ref are only safe due to %tty_mutex
211096fd7ce5SGreg Kroah-Hartman  */
tty_open(struct inode * inode,struct file * filp)211196fd7ce5SGreg Kroah-Hartman static int tty_open(struct inode *inode, struct file *filp)
211296fd7ce5SGreg Kroah-Hartman {
2113b82154acSJiri Slaby 	struct tty_struct *tty;
211496fd7ce5SGreg Kroah-Hartman 	int noctty, retval;
211596fd7ce5SGreg Kroah-Hartman 	dev_t device = inode->i_rdev;
211696fd7ce5SGreg Kroah-Hartman 	unsigned saved_flags = filp->f_flags;
211796fd7ce5SGreg Kroah-Hartman 
211896fd7ce5SGreg Kroah-Hartman 	nonseekable_open(inode, filp);
211996fd7ce5SGreg Kroah-Hartman 
212096fd7ce5SGreg Kroah-Hartman retry_open:
2121fa90e1c9SJiri Slaby 	retval = tty_alloc_file(filp);
2122fa90e1c9SJiri Slaby 	if (retval)
2123fa90e1c9SJiri Slaby 		return -ENOMEM;
2124fa90e1c9SJiri Slaby 
2125b82154acSJiri Slaby 	tty = tty_open_current_tty(device, filp);
2126d6203d0cSPeter Hurley 	if (!tty)
212714ce3848SSudip Mukherjee 		tty = tty_open_by_driver(device, filp);
212852494eebSPeter Hurley 
212996fd7ce5SGreg Kroah-Hartman 	if (IS_ERR(tty)) {
2130d6203d0cSPeter Hurley 		tty_free_file(filp);
2131ba5db448SJiri Slaby 		retval = PTR_ERR(tty);
21327f22f6c9SPeter Hurley 		if (retval != -EAGAIN || signal_pending(current))
2133d6203d0cSPeter Hurley 			return retval;
21347f22f6c9SPeter Hurley 		schedule();
21357f22f6c9SPeter Hurley 		goto retry_open;
213696fd7ce5SGreg Kroah-Hartman 	}
213796fd7ce5SGreg Kroah-Hartman 
2138fa90e1c9SJiri Slaby 	tty_add_file(tty, filp);
213996fd7ce5SGreg Kroah-Hartman 
21409de44bd6SJiri Slaby 	check_tty_count(tty, __func__);
2141d435cefeSPeter Hurley 	tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
2142accff793SPeter Hurley 
214396fd7ce5SGreg Kroah-Hartman 	if (tty->ops->open)
214496fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->open(tty, filp);
214596fd7ce5SGreg Kroah-Hartman 	else
214696fd7ce5SGreg Kroah-Hartman 		retval = -ENODEV;
214796fd7ce5SGreg Kroah-Hartman 	filp->f_flags = saved_flags;
214896fd7ce5SGreg Kroah-Hartman 
214996fd7ce5SGreg Kroah-Hartman 	if (retval) {
2150d435cefeSPeter Hurley 		tty_debug_hangup(tty, "open error %d, releasing\n", retval);
2151accff793SPeter Hurley 
215289c8d91eSAlan Cox 		tty_unlock(tty); /* need to call tty_release without BTM */
215396fd7ce5SGreg Kroah-Hartman 		tty_release(inode, filp);
215496fd7ce5SGreg Kroah-Hartman 		if (retval != -ERESTARTSYS)
215596fd7ce5SGreg Kroah-Hartman 			return retval;
215696fd7ce5SGreg Kroah-Hartman 
215796fd7ce5SGreg Kroah-Hartman 		if (signal_pending(current))
215896fd7ce5SGreg Kroah-Hartman 			return retval;
215996fd7ce5SGreg Kroah-Hartman 
216096fd7ce5SGreg Kroah-Hartman 		schedule();
216196fd7ce5SGreg Kroah-Hartman 		/*
216296fd7ce5SGreg Kroah-Hartman 		 * Need to reset f_op in case a hangup happened.
216396fd7ce5SGreg Kroah-Hartman 		 */
21641256937fSPeter Hurley 		if (tty_hung_up_p(filp))
216596fd7ce5SGreg Kroah-Hartman 			filp->f_op = &tty_fops;
216696fd7ce5SGreg Kroah-Hartman 		goto retry_open;
216796fd7ce5SGreg Kroah-Hartman 	}
2168d4855e1fSPeter Hurley 	clear_bit(TTY_HUPPED, &tty->flags);
216996fd7ce5SGreg Kroah-Hartman 
217011e1d4aaSPeter Hurley 	noctty = (filp->f_flags & O_NOCTTY) ||
2171da5a0fc6SPeter Hurley 		 (IS_ENABLED(CONFIG_VT) && device == MKDEV(TTY_MAJOR, 0)) ||
217211e1d4aaSPeter Hurley 		 device == MKDEV(TTYAUX_MAJOR, 1) ||
217311e1d4aaSPeter Hurley 		 (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
217411e1d4aaSPeter Hurley 		  tty->driver->subtype == PTY_TYPE_MASTER);
2175a1235b3eSNicolas Pitre 	if (!noctty)
2176a1235b3eSNicolas Pitre 		tty_open_proc_set_tty(filp, tty);
217789c8d91eSAlan Cox 	tty_unlock(tty);
217896fd7ce5SGreg Kroah-Hartman 	return 0;
217996fd7ce5SGreg Kroah-Hartman }
218096fd7ce5SGreg Kroah-Hartman 
218196fd7ce5SGreg Kroah-Hartman 
218296fd7ce5SGreg Kroah-Hartman /**
218396fd7ce5SGreg Kroah-Hartman  * tty_poll	-	check tty status
218496fd7ce5SGreg Kroah-Hartman  * @filp: file being polled
218596fd7ce5SGreg Kroah-Hartman  * @wait: poll wait structures to update
218696fd7ce5SGreg Kroah-Hartman  *
2187796a75a9SJiri Slaby  * Call the line discipline polling method to obtain the poll status of the
2188796a75a9SJiri Slaby  * device.
218996fd7ce5SGreg Kroah-Hartman  *
2190796a75a9SJiri Slaby  * Locking: locks called line discipline but ldisc poll method may be
2191796a75a9SJiri Slaby  * re-entered freely by other callers.
219296fd7ce5SGreg Kroah-Hartman  */
tty_poll(struct file * filp,poll_table * wait)2193afc9a42bSAl Viro static __poll_t tty_poll(struct file *filp, poll_table *wait)
219496fd7ce5SGreg Kroah-Hartman {
219596fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(filp);
219696fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
2197e6c8adcaSAl Viro 	__poll_t ret = 0;
219896fd7ce5SGreg Kroah-Hartman 
21996131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(filp), "tty_poll"))
220096fd7ce5SGreg Kroah-Hartman 		return 0;
220196fd7ce5SGreg Kroah-Hartman 
220296fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
2203e55afd11SPeter Hurley 	if (!ld)
2204e55afd11SPeter Hurley 		return hung_up_tty_poll(filp, wait);
220596fd7ce5SGreg Kroah-Hartman 	if (ld->ops->poll)
2206c961bfb1SPeter Hurley 		ret = ld->ops->poll(tty, filp, wait);
220796fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
220896fd7ce5SGreg Kroah-Hartman 	return ret;
220996fd7ce5SGreg Kroah-Hartman }
221096fd7ce5SGreg Kroah-Hartman 
__tty_fasync(int fd,struct file * filp,int on)221196fd7ce5SGreg Kroah-Hartman static int __tty_fasync(int fd, struct file *filp, int on)
221296fd7ce5SGreg Kroah-Hartman {
221396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(filp);
221496fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
221596fd7ce5SGreg Kroah-Hartman 	int retval = 0;
221696fd7ce5SGreg Kroah-Hartman 
22176131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
221896fd7ce5SGreg Kroah-Hartman 		goto out;
221996fd7ce5SGreg Kroah-Hartman 
222096fd7ce5SGreg Kroah-Hartman 	retval = fasync_helper(fd, filp, on, &tty->fasync);
222196fd7ce5SGreg Kroah-Hartman 	if (retval <= 0)
222296fd7ce5SGreg Kroah-Hartman 		goto out;
222396fd7ce5SGreg Kroah-Hartman 
222496fd7ce5SGreg Kroah-Hartman 	if (on) {
222596fd7ce5SGreg Kroah-Hartman 		enum pid_type type;
222696fd7ce5SGreg Kroah-Hartman 		struct pid *pid;
2227f6c8dbe6SPeter Hurley 
222864d608dbSJiri Slaby 		spin_lock_irqsave(&tty->ctrl.lock, flags);
222964d608dbSJiri Slaby 		if (tty->ctrl.pgrp) {
223064d608dbSJiri Slaby 			pid = tty->ctrl.pgrp;
223196fd7ce5SGreg Kroah-Hartman 			type = PIDTYPE_PGID;
223296fd7ce5SGreg Kroah-Hartman 		} else {
223396fd7ce5SGreg Kroah-Hartman 			pid = task_pid(current);
223401919134SEric W. Biederman 			type = PIDTYPE_TGID;
223596fd7ce5SGreg Kroah-Hartman 		}
223696fd7ce5SGreg Kroah-Hartman 		get_pid(pid);
223764d608dbSJiri Slaby 		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
2238e0b93eddSJeff Layton 		__f_setown(filp, pid, type, 0);
223996fd7ce5SGreg Kroah-Hartman 		put_pid(pid);
2240e0b93eddSJeff Layton 		retval = 0;
224196fd7ce5SGreg Kroah-Hartman 	}
224296fd7ce5SGreg Kroah-Hartman out:
224396fd7ce5SGreg Kroah-Hartman 	return retval;
224496fd7ce5SGreg Kroah-Hartman }
224596fd7ce5SGreg Kroah-Hartman 
tty_fasync(int fd,struct file * filp,int on)224696fd7ce5SGreg Kroah-Hartman static int tty_fasync(int fd, struct file *filp, int on)
224796fd7ce5SGreg Kroah-Hartman {
224889c8d91eSAlan Cox 	struct tty_struct *tty = file_tty(filp);
2249a8f3a297SPeter Hurley 	int retval = -ENOTTY;
225089c8d91eSAlan Cox 
225189c8d91eSAlan Cox 	tty_lock(tty);
2252a8f3a297SPeter Hurley 	if (!tty_hung_up_p(filp))
225396fd7ce5SGreg Kroah-Hartman 		retval = __tty_fasync(fd, filp, on);
225489c8d91eSAlan Cox 	tty_unlock(tty);
225589c8d91eSAlan Cox 
225696fd7ce5SGreg Kroah-Hartman 	return retval;
225796fd7ce5SGreg Kroah-Hartman }
225896fd7ce5SGreg Kroah-Hartman 
22595c30f3e4SKees Cook static bool tty_legacy_tiocsti __read_mostly = IS_ENABLED(CONFIG_LEGACY_TIOCSTI);
226096fd7ce5SGreg Kroah-Hartman /**
226196fd7ce5SGreg Kroah-Hartman  * tiocsti		-	fake input character
226296fd7ce5SGreg Kroah-Hartman  * @tty: tty to fake input into
226396fd7ce5SGreg Kroah-Hartman  * @p: pointer to character
226496fd7ce5SGreg Kroah-Hartman  *
2265796a75a9SJiri Slaby  * Fake input to a tty device. Does the necessary locking and input management.
226696fd7ce5SGreg Kroah-Hartman  *
226796fd7ce5SGreg Kroah-Hartman  * FIXME: does not honour flow control ??
226896fd7ce5SGreg Kroah-Hartman  *
226996fd7ce5SGreg Kroah-Hartman  * Locking:
2270796a75a9SJiri Slaby  *  * Called functions take tty_ldiscs_lock
2271796a75a9SJiri Slaby  *  * current->signal->tty check is safe without locks
227296fd7ce5SGreg Kroah-Hartman  */
tiocsti(struct tty_struct * tty,char __user * p)227396fd7ce5SGreg Kroah-Hartman static int tiocsti(struct tty_struct *tty, char __user *p)
227496fd7ce5SGreg Kroah-Hartman {
227596fd7ce5SGreg Kroah-Hartman 	char ch, mbz = 0;
227696fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
227796fd7ce5SGreg Kroah-Hartman 
2278690c8b80SSamuel Thibault 	if (!tty_legacy_tiocsti && !capable(CAP_SYS_ADMIN))
227983efeeebSKees Cook 		return -EIO;
228083efeeebSKees Cook 
228196fd7ce5SGreg Kroah-Hartman 	if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
228296fd7ce5SGreg Kroah-Hartman 		return -EPERM;
228396fd7ce5SGreg Kroah-Hartman 	if (get_user(ch, p))
228496fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
228596fd7ce5SGreg Kroah-Hartman 	tty_audit_tiocsti(tty, ch);
228696fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
2287e55afd11SPeter Hurley 	if (!ld)
2288e55afd11SPeter Hurley 		return -EIO;
2289bb2853a6SNguyen Dinh Phi 	tty_buffer_lock_exclusive(tty->port);
229027cfb3a5SGreg Kroah-Hartman 	if (ld->ops->receive_buf)
229196fd7ce5SGreg Kroah-Hartman 		ld->ops->receive_buf(tty, &ch, &mbz, 1);
2292bb2853a6SNguyen Dinh Phi 	tty_buffer_unlock_exclusive(tty->port);
229396fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
229496fd7ce5SGreg Kroah-Hartman 	return 0;
229596fd7ce5SGreg Kroah-Hartman }
229696fd7ce5SGreg Kroah-Hartman 
229796fd7ce5SGreg Kroah-Hartman /**
229896fd7ce5SGreg Kroah-Hartman  * tiocgwinsz		-	implement window query ioctl
2299fa441954SJiri Slaby  * @tty: tty
230096fd7ce5SGreg Kroah-Hartman  * @arg: user buffer for result
230196fd7ce5SGreg Kroah-Hartman  *
230296fd7ce5SGreg Kroah-Hartman  * Copies the kernel idea of the window size into the user buffer.
230396fd7ce5SGreg Kroah-Hartman  *
2304796a75a9SJiri Slaby  * Locking: @tty->winsize_mutex is taken to ensure the winsize data is
2305796a75a9SJiri Slaby  * consistent.
230696fd7ce5SGreg Kroah-Hartman  */
tiocgwinsz(struct tty_struct * tty,struct winsize __user * arg)230796fd7ce5SGreg Kroah-Hartman static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
230896fd7ce5SGreg Kroah-Hartman {
230996fd7ce5SGreg Kroah-Hartman 	int err;
231096fd7ce5SGreg Kroah-Hartman 
2311dee4a0beSPeter Hurley 	mutex_lock(&tty->winsize_mutex);
231296fd7ce5SGreg Kroah-Hartman 	err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
2313dee4a0beSPeter Hurley 	mutex_unlock(&tty->winsize_mutex);
231496fd7ce5SGreg Kroah-Hartman 
231596fd7ce5SGreg Kroah-Hartman 	return err ? -EFAULT : 0;
231696fd7ce5SGreg Kroah-Hartman }
231796fd7ce5SGreg Kroah-Hartman 
231896fd7ce5SGreg Kroah-Hartman /**
231996fd7ce5SGreg Kroah-Hartman  * tty_do_resize	-	resize event
232096fd7ce5SGreg Kroah-Hartman  * @tty: tty being resized
2321fa441954SJiri Slaby  * @ws: new dimensions
232296fd7ce5SGreg Kroah-Hartman  *
2323796a75a9SJiri Slaby  * Update the termios variables and send the necessary signals to peform a
2324796a75a9SJiri Slaby  * terminal resize correctly.
232596fd7ce5SGreg Kroah-Hartman  */
tty_do_resize(struct tty_struct * tty,struct winsize * ws)232696fd7ce5SGreg Kroah-Hartman int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
232796fd7ce5SGreg Kroah-Hartman {
232896fd7ce5SGreg Kroah-Hartman 	struct pid *pgrp;
232996fd7ce5SGreg Kroah-Hartman 
233096fd7ce5SGreg Kroah-Hartman 	/* Lock the tty */
2331dee4a0beSPeter Hurley 	mutex_lock(&tty->winsize_mutex);
233296fd7ce5SGreg Kroah-Hartman 	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
233396fd7ce5SGreg Kroah-Hartman 		goto done;
233496fd7ce5SGreg Kroah-Hartman 
23355b239542SPeter Hurley 	/* Signal the foreground process group */
23365b239542SPeter Hurley 	pgrp = tty_get_pgrp(tty);
233796fd7ce5SGreg Kroah-Hartman 	if (pgrp)
233896fd7ce5SGreg Kroah-Hartman 		kill_pgrp(pgrp, SIGWINCH, 1);
233996fd7ce5SGreg Kroah-Hartman 	put_pid(pgrp);
234096fd7ce5SGreg Kroah-Hartman 
234196fd7ce5SGreg Kroah-Hartman 	tty->winsize = *ws;
234296fd7ce5SGreg Kroah-Hartman done:
2343dee4a0beSPeter Hurley 	mutex_unlock(&tty->winsize_mutex);
234496fd7ce5SGreg Kroah-Hartman 	return 0;
234596fd7ce5SGreg Kroah-Hartman }
23464d334fd1SMartin Schwidefsky EXPORT_SYMBOL(tty_do_resize);
234796fd7ce5SGreg Kroah-Hartman 
234896fd7ce5SGreg Kroah-Hartman /**
234996fd7ce5SGreg Kroah-Hartman  * tiocswinsz		-	implement window size set ioctl
2350fa441954SJiri Slaby  * @tty: tty side of tty
235196fd7ce5SGreg Kroah-Hartman  * @arg: user buffer for result
235296fd7ce5SGreg Kroah-Hartman  *
2353796a75a9SJiri Slaby  * Copies the user idea of the window size to the kernel. Traditionally this is
2354796a75a9SJiri Slaby  * just advisory information but for the Linux console it actually has driver
2355796a75a9SJiri Slaby  * level meaning and triggers a VC resize.
235696fd7ce5SGreg Kroah-Hartman  *
235796fd7ce5SGreg Kroah-Hartman  * Locking:
2358796a75a9SJiri Slaby  *	Driver dependent. The default do_resize method takes the tty termios
2359796a75a9SJiri Slaby  *	mutex and ctrl.lock. The console takes its own lock then calls into the
2360796a75a9SJiri Slaby  *	default method.
236196fd7ce5SGreg Kroah-Hartman  */
tiocswinsz(struct tty_struct * tty,struct winsize __user * arg)236296fd7ce5SGreg Kroah-Hartman static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
236396fd7ce5SGreg Kroah-Hartman {
236496fd7ce5SGreg Kroah-Hartman 	struct winsize tmp_ws;
2365e73b2407SXiaofei Tan 
236696fd7ce5SGreg Kroah-Hartman 	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
236796fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
236896fd7ce5SGreg Kroah-Hartman 
236996fd7ce5SGreg Kroah-Hartman 	if (tty->ops->resize)
237096fd7ce5SGreg Kroah-Hartman 		return tty->ops->resize(tty, &tmp_ws);
237196fd7ce5SGreg Kroah-Hartman 	else
237296fd7ce5SGreg Kroah-Hartman 		return tty_do_resize(tty, &tmp_ws);
237396fd7ce5SGreg Kroah-Hartman }
237496fd7ce5SGreg Kroah-Hartman 
237596fd7ce5SGreg Kroah-Hartman /**
237696fd7ce5SGreg Kroah-Hartman  * tioccons	-	allow admin to move logical console
237796fd7ce5SGreg Kroah-Hartman  * @file: the file to become console
237896fd7ce5SGreg Kroah-Hartman  *
2379796a75a9SJiri Slaby  * Allow the administrator to move the redirected console device.
238096fd7ce5SGreg Kroah-Hartman  *
238196fd7ce5SGreg Kroah-Hartman  * Locking: uses redirect_lock to guard the redirect information
238296fd7ce5SGreg Kroah-Hartman  */
tioccons(struct file * file)238396fd7ce5SGreg Kroah-Hartman static int tioccons(struct file *file)
238496fd7ce5SGreg Kroah-Hartman {
238596fd7ce5SGreg Kroah-Hartman 	if (!capable(CAP_SYS_ADMIN))
238696fd7ce5SGreg Kroah-Hartman 		return -EPERM;
23879bb48c82SLinus Torvalds 	if (file->f_op->write_iter == redirected_tty_write) {
238896fd7ce5SGreg Kroah-Hartman 		struct file *f;
2389e73b2407SXiaofei Tan 
239096fd7ce5SGreg Kroah-Hartman 		spin_lock(&redirect_lock);
239196fd7ce5SGreg Kroah-Hartman 		f = redirect;
239296fd7ce5SGreg Kroah-Hartman 		redirect = NULL;
239396fd7ce5SGreg Kroah-Hartman 		spin_unlock(&redirect_lock);
239496fd7ce5SGreg Kroah-Hartman 		if (f)
239596fd7ce5SGreg Kroah-Hartman 			fput(f);
239696fd7ce5SGreg Kroah-Hartman 		return 0;
239796fd7ce5SGreg Kroah-Hartman 	}
2398a9cbbb80SLinus Torvalds 	if (file->f_op->write_iter != tty_write)
2399a9cbbb80SLinus Torvalds 		return -ENOTTY;
2400a9cbbb80SLinus Torvalds 	if (!(file->f_mode & FMODE_WRITE))
2401a9cbbb80SLinus Torvalds 		return -EBADF;
2402a9cbbb80SLinus Torvalds 	if (!(file->f_mode & FMODE_CAN_WRITE))
2403a9cbbb80SLinus Torvalds 		return -EINVAL;
240496fd7ce5SGreg Kroah-Hartman 	spin_lock(&redirect_lock);
240596fd7ce5SGreg Kroah-Hartman 	if (redirect) {
240696fd7ce5SGreg Kroah-Hartman 		spin_unlock(&redirect_lock);
240796fd7ce5SGreg Kroah-Hartman 		return -EBUSY;
240896fd7ce5SGreg Kroah-Hartman 	}
2409cb0942b8SAl Viro 	redirect = get_file(file);
241096fd7ce5SGreg Kroah-Hartman 	spin_unlock(&redirect_lock);
241196fd7ce5SGreg Kroah-Hartman 	return 0;
241296fd7ce5SGreg Kroah-Hartman }
241396fd7ce5SGreg Kroah-Hartman 
241496fd7ce5SGreg Kroah-Hartman /**
241596fd7ce5SGreg Kroah-Hartman  * tiocsetd	-	set line discipline
241696fd7ce5SGreg Kroah-Hartman  * @tty: tty device
241796fd7ce5SGreg Kroah-Hartman  * @p: pointer to user data
241896fd7ce5SGreg Kroah-Hartman  *
241996fd7ce5SGreg Kroah-Hartman  * Set the line discipline according to user request.
242096fd7ce5SGreg Kroah-Hartman  *
2421796a75a9SJiri Slaby  * Locking: see tty_set_ldisc(), this function is just a helper
242296fd7ce5SGreg Kroah-Hartman  */
tiocsetd(struct tty_struct * tty,int __user * p)242396fd7ce5SGreg Kroah-Hartman static int tiocsetd(struct tty_struct *tty, int __user *p)
242496fd7ce5SGreg Kroah-Hartman {
2425c12da96fSPeter Hurley 	int disc;
242696fd7ce5SGreg Kroah-Hartman 	int ret;
242796fd7ce5SGreg Kroah-Hartman 
2428c12da96fSPeter Hurley 	if (get_user(disc, p))
242996fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
243096fd7ce5SGreg Kroah-Hartman 
2431c12da96fSPeter Hurley 	ret = tty_set_ldisc(tty, disc);
243296fd7ce5SGreg Kroah-Hartman 
243396fd7ce5SGreg Kroah-Hartman 	return ret;
243496fd7ce5SGreg Kroah-Hartman }
243596fd7ce5SGreg Kroah-Hartman 
243696fd7ce5SGreg Kroah-Hartman /**
24375c17c861SPeter Hurley  * tiocgetd	-	get line discipline
24385c17c861SPeter Hurley  * @tty: tty device
24395c17c861SPeter Hurley  * @p: pointer to user data
24405c17c861SPeter Hurley  *
24415c17c861SPeter Hurley  * Retrieves the line discipline id directly from the ldisc.
24425c17c861SPeter Hurley  *
2443796a75a9SJiri Slaby  * Locking: waits for ldisc reference (in case the line discipline is changing
2444796a75a9SJiri Slaby  * or the @tty is being hungup)
24455c17c861SPeter Hurley  */
tiocgetd(struct tty_struct * tty,int __user * p)24465c17c861SPeter Hurley static int tiocgetd(struct tty_struct *tty, int __user *p)
24475c17c861SPeter Hurley {
24485c17c861SPeter Hurley 	struct tty_ldisc *ld;
24495c17c861SPeter Hurley 	int ret;
24505c17c861SPeter Hurley 
24515c17c861SPeter Hurley 	ld = tty_ldisc_ref_wait(tty);
2452e55afd11SPeter Hurley 	if (!ld)
2453e55afd11SPeter Hurley 		return -EIO;
24545c17c861SPeter Hurley 	ret = put_user(ld->ops->num, p);
24555c17c861SPeter Hurley 	tty_ldisc_deref(ld);
24565c17c861SPeter Hurley 	return ret;
24575c17c861SPeter Hurley }
24585c17c861SPeter Hurley 
24595c17c861SPeter Hurley /**
246096fd7ce5SGreg Kroah-Hartman  * send_break	-	performed time break
246196fd7ce5SGreg Kroah-Hartman  * @tty: device to break on
246296fd7ce5SGreg Kroah-Hartman  * @duration: timeout in mS
246396fd7ce5SGreg Kroah-Hartman  *
2464796a75a9SJiri Slaby  * Perform a timed break on hardware that lacks its own driver level timed
2465796a75a9SJiri Slaby  * break functionality.
246696fd7ce5SGreg Kroah-Hartman  *
246796fd7ce5SGreg Kroah-Hartman  * Locking:
2468796a75a9SJiri Slaby  *	@tty->atomic_write_lock serializes
246996fd7ce5SGreg Kroah-Hartman  */
send_break(struct tty_struct * tty,unsigned int duration)247096fd7ce5SGreg Kroah-Hartman static int send_break(struct tty_struct *tty, unsigned int duration)
247196fd7ce5SGreg Kroah-Hartman {
247296fd7ce5SGreg Kroah-Hartman 	int retval;
247396fd7ce5SGreg Kroah-Hartman 
247496fd7ce5SGreg Kroah-Hartman 	if (tty->ops->break_ctl == NULL)
247596fd7ce5SGreg Kroah-Hartman 		return 0;
247696fd7ce5SGreg Kroah-Hartman 
247796fd7ce5SGreg Kroah-Hartman 	if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
2478ecf27e47SJiri Slaby (SUSE) 		return tty->ops->break_ctl(tty, duration);
2479ecf27e47SJiri Slaby (SUSE) 
248096fd7ce5SGreg Kroah-Hartman 	/* Do the work ourselves */
2481af815336SJiri Slaby (SUSE) 	if (tty_write_lock(tty, false) < 0)
248296fd7ce5SGreg Kroah-Hartman 		return -EINTR;
2483ecf27e47SJiri Slaby (SUSE) 
248496fd7ce5SGreg Kroah-Hartman 	retval = tty->ops->break_ctl(tty, -1);
24852433f050SJiri Slaby (SUSE) 	if (!retval) {
248696fd7ce5SGreg Kroah-Hartman 		msleep_interruptible(duration);
248796fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->break_ctl(tty, 0);
2488*19e321c3SOliver Neukum 	} else if (retval == -EOPNOTSUPP) {
2489*19e321c3SOliver Neukum 		/* some drivers can tell only dynamically */
2490*19e321c3SOliver Neukum 		retval = 0;
24912433f050SJiri Slaby (SUSE) 	}
249296fd7ce5SGreg Kroah-Hartman 	tty_write_unlock(tty);
2493ecf27e47SJiri Slaby (SUSE) 
249496fd7ce5SGreg Kroah-Hartman 	if (signal_pending(current))
249596fd7ce5SGreg Kroah-Hartman 		retval = -EINTR;
2496ecf27e47SJiri Slaby (SUSE) 
249796fd7ce5SGreg Kroah-Hartman 	return retval;
249896fd7ce5SGreg Kroah-Hartman }
249996fd7ce5SGreg Kroah-Hartman 
250096fd7ce5SGreg Kroah-Hartman /**
250196fd7ce5SGreg Kroah-Hartman  * tty_tiocmget		-	get modem status
250296fd7ce5SGreg Kroah-Hartman  * @tty: tty device
250396fd7ce5SGreg Kroah-Hartman  * @p: pointer to result
250496fd7ce5SGreg Kroah-Hartman  *
2505796a75a9SJiri Slaby  * Obtain the modem status bits from the tty driver if the feature is
2506796a75a9SJiri Slaby  * supported. Return -%ENOTTY if it is not available.
250796fd7ce5SGreg Kroah-Hartman  *
250896fd7ce5SGreg Kroah-Hartman  * Locking: none (up to the driver)
250996fd7ce5SGreg Kroah-Hartman  */
tty_tiocmget(struct tty_struct * tty,int __user * p)251060b33c13SAlan Cox static int tty_tiocmget(struct tty_struct *tty, int __user *p)
251196fd7ce5SGreg Kroah-Hartman {
25121b8b2086SJohan Hovold 	int retval = -ENOTTY;
251396fd7ce5SGreg Kroah-Hartman 
251496fd7ce5SGreg Kroah-Hartman 	if (tty->ops->tiocmget) {
251560b33c13SAlan Cox 		retval = tty->ops->tiocmget(tty);
251696fd7ce5SGreg Kroah-Hartman 
251796fd7ce5SGreg Kroah-Hartman 		if (retval >= 0)
251896fd7ce5SGreg Kroah-Hartman 			retval = put_user(retval, p);
251996fd7ce5SGreg Kroah-Hartman 	}
252096fd7ce5SGreg Kroah-Hartman 	return retval;
252196fd7ce5SGreg Kroah-Hartman }
252296fd7ce5SGreg Kroah-Hartman 
252396fd7ce5SGreg Kroah-Hartman /**
252496fd7ce5SGreg Kroah-Hartman  * tty_tiocmset		-	set modem status
252596fd7ce5SGreg Kroah-Hartman  * @tty: tty device
252696fd7ce5SGreg Kroah-Hartman  * @cmd: command - clear bits, set bits or set all
252796fd7ce5SGreg Kroah-Hartman  * @p: pointer to desired bits
252896fd7ce5SGreg Kroah-Hartman  *
252996fd7ce5SGreg Kroah-Hartman  * Set the modem status bits from the tty driver if the feature
2530796a75a9SJiri Slaby  * is supported. Return -%ENOTTY if it is not available.
253196fd7ce5SGreg Kroah-Hartman  *
253296fd7ce5SGreg Kroah-Hartman  * Locking: none (up to the driver)
253396fd7ce5SGreg Kroah-Hartman  */
tty_tiocmset(struct tty_struct * tty,unsigned int cmd,unsigned __user * p)253420b9d177SAlan Cox static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd,
253596fd7ce5SGreg Kroah-Hartman 	     unsigned __user *p)
253696fd7ce5SGreg Kroah-Hartman {
253796fd7ce5SGreg Kroah-Hartman 	int retval;
253896fd7ce5SGreg Kroah-Hartman 	unsigned int set, clear, val;
253996fd7ce5SGreg Kroah-Hartman 
254096fd7ce5SGreg Kroah-Hartman 	if (tty->ops->tiocmset == NULL)
25411b8b2086SJohan Hovold 		return -ENOTTY;
254296fd7ce5SGreg Kroah-Hartman 
254396fd7ce5SGreg Kroah-Hartman 	retval = get_user(val, p);
254496fd7ce5SGreg Kroah-Hartman 	if (retval)
254596fd7ce5SGreg Kroah-Hartman 		return retval;
254696fd7ce5SGreg Kroah-Hartman 	set = clear = 0;
254796fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
254896fd7ce5SGreg Kroah-Hartman 	case TIOCMBIS:
254996fd7ce5SGreg Kroah-Hartman 		set = val;
255096fd7ce5SGreg Kroah-Hartman 		break;
255196fd7ce5SGreg Kroah-Hartman 	case TIOCMBIC:
255296fd7ce5SGreg Kroah-Hartman 		clear = val;
255396fd7ce5SGreg Kroah-Hartman 		break;
255496fd7ce5SGreg Kroah-Hartman 	case TIOCMSET:
255596fd7ce5SGreg Kroah-Hartman 		set = val;
255696fd7ce5SGreg Kroah-Hartman 		clear = ~val;
255796fd7ce5SGreg Kroah-Hartman 		break;
255896fd7ce5SGreg Kroah-Hartman 	}
255996fd7ce5SGreg Kroah-Hartman 	set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
256096fd7ce5SGreg Kroah-Hartman 	clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
256120b9d177SAlan Cox 	return tty->ops->tiocmset(tty, set, clear);
256296fd7ce5SGreg Kroah-Hartman }
256396fd7ce5SGreg Kroah-Hartman 
2564d20c219cSUwe Kleine-König /**
2565d20c219cSUwe Kleine-König  * tty_get_icount	-	get tty statistics
2566d20c219cSUwe Kleine-König  * @tty: tty device
2567d20c219cSUwe Kleine-König  * @icount: output parameter
2568d20c219cSUwe Kleine-König  *
2569796a75a9SJiri Slaby  * Gets a copy of the @tty's icount statistics.
2570d20c219cSUwe Kleine-König  *
2571d20c219cSUwe Kleine-König  * Locking: none (up to the driver)
2572d20c219cSUwe Kleine-König  */
tty_get_icount(struct tty_struct * tty,struct serial_icounter_struct * icount)2573d20c219cSUwe Kleine-König int tty_get_icount(struct tty_struct *tty,
2574d20c219cSUwe Kleine-König 		   struct serial_icounter_struct *icount)
2575d20c219cSUwe Kleine-König {
2576d20c219cSUwe Kleine-König 	memset(icount, 0, sizeof(*icount));
2577d20c219cSUwe Kleine-König 
2578d20c219cSUwe Kleine-König 	if (tty->ops->get_icount)
2579d20c219cSUwe Kleine-König 		return tty->ops->get_icount(tty, icount);
2580d20c219cSUwe Kleine-König 	else
25811b8b2086SJohan Hovold 		return -ENOTTY;
2582d20c219cSUwe Kleine-König }
2583d20c219cSUwe Kleine-König EXPORT_SYMBOL_GPL(tty_get_icount);
2584d20c219cSUwe Kleine-König 
tty_tiocgicount(struct tty_struct * tty,void __user * arg)258596fd7ce5SGreg Kroah-Hartman static int tty_tiocgicount(struct tty_struct *tty, void __user *arg)
258696fd7ce5SGreg Kroah-Hartman {
258796fd7ce5SGreg Kroah-Hartman 	struct serial_icounter_struct icount;
2588d20c219cSUwe Kleine-König 	int retval;
2589d20c219cSUwe Kleine-König 
2590d20c219cSUwe Kleine-König 	retval = tty_get_icount(tty, &icount);
259196fd7ce5SGreg Kroah-Hartman 	if (retval != 0)
259296fd7ce5SGreg Kroah-Hartman 		return retval;
2593d20c219cSUwe Kleine-König 
259496fd7ce5SGreg Kroah-Hartman 	if (copy_to_user(arg, &icount, sizeof(icount)))
259596fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
259696fd7ce5SGreg Kroah-Hartman 	return 0;
259796fd7ce5SGreg Kroah-Hartman }
259896fd7ce5SGreg Kroah-Hartman 
tty_set_serial(struct tty_struct * tty,struct serial_struct * ss)2599885c77d5SJohan Hovold static int tty_set_serial(struct tty_struct *tty, struct serial_struct *ss)
26008a8ae62fSJiri Slaby {
26018a8ae62fSJiri Slaby 	char comm[TASK_COMM_LEN];
26028a8ae62fSJiri Slaby 	int flags;
26038a8ae62fSJiri Slaby 
2604885c77d5SJohan Hovold 	flags = ss->flags & ASYNC_DEPRECATED;
26058a8ae62fSJiri Slaby 
26061b7bc6b1SJohan Hovold 	if (flags)
26071b7bc6b1SJohan Hovold 		pr_warn_ratelimited("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
26088a8ae62fSJiri Slaby 				__func__, get_task_comm(comm, current), flags);
2609885c77d5SJohan Hovold 
26102f46a2c1SAl Viro 	if (!tty->ops->set_serial)
2611930236a3SAl Viro 		return -ENOTTY;
2612885c77d5SJohan Hovold 
2613885c77d5SJohan Hovold 	return tty->ops->set_serial(tty, ss);
2614885c77d5SJohan Hovold }
2615885c77d5SJohan Hovold 
tty_tiocsserial(struct tty_struct * tty,struct serial_struct __user * ss)2616885c77d5SJohan Hovold static int tty_tiocsserial(struct tty_struct *tty, struct serial_struct __user *ss)
2617885c77d5SJohan Hovold {
2618885c77d5SJohan Hovold 	struct serial_struct v;
2619885c77d5SJohan Hovold 
2620885c77d5SJohan Hovold 	if (copy_from_user(&v, ss, sizeof(*ss)))
2621885c77d5SJohan Hovold 		return -EFAULT;
2622885c77d5SJohan Hovold 
2623885c77d5SJohan Hovold 	return tty_set_serial(tty, &v);
26242f46a2c1SAl Viro }
26252f46a2c1SAl Viro 
tty_tiocgserial(struct tty_struct * tty,struct serial_struct __user * ss)26262f46a2c1SAl Viro static int tty_tiocgserial(struct tty_struct *tty, struct serial_struct __user *ss)
26272f46a2c1SAl Viro {
26282f46a2c1SAl Viro 	struct serial_struct v;
26292f46a2c1SAl Viro 	int err;
26302f46a2c1SAl Viro 
2631a3241891SGustavo A. R. Silva 	memset(&v, 0, sizeof(v));
26322f46a2c1SAl Viro 	if (!tty->ops->get_serial)
2633930236a3SAl Viro 		return -ENOTTY;
26342f46a2c1SAl Viro 	err = tty->ops->get_serial(tty, &v);
2635a3241891SGustavo A. R. Silva 	if (!err && copy_to_user(ss, &v, sizeof(v)))
26362f46a2c1SAl Viro 		err = -EFAULT;
26372f46a2c1SAl Viro 	return err;
26388a8ae62fSJiri Slaby }
26398a8ae62fSJiri Slaby 
26408f166e00SPeter Hurley /*
26418f166e00SPeter Hurley  * if pty, return the slave side (real_tty)
26428f166e00SPeter Hurley  * otherwise, return self
26438f166e00SPeter Hurley  */
tty_pair_get_tty(struct tty_struct * tty)26448f166e00SPeter Hurley static struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
264596fd7ce5SGreg Kroah-Hartman {
264696fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
264796fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_MASTER)
264896fd7ce5SGreg Kroah-Hartman 		tty = tty->link;
264996fd7ce5SGreg Kroah-Hartman 	return tty;
265096fd7ce5SGreg Kroah-Hartman }
265196fd7ce5SGreg Kroah-Hartman 
265296fd7ce5SGreg Kroah-Hartman /*
265396fd7ce5SGreg Kroah-Hartman  * Split this up, as gcc can choke on it otherwise..
265496fd7ce5SGreg Kroah-Hartman  */
tty_ioctl(struct file * file,unsigned int cmd,unsigned long arg)265596fd7ce5SGreg Kroah-Hartman long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
265696fd7ce5SGreg Kroah-Hartman {
265796fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
265896fd7ce5SGreg Kroah-Hartman 	struct tty_struct *real_tty;
265996fd7ce5SGreg Kroah-Hartman 	void __user *p = (void __user *)arg;
266096fd7ce5SGreg Kroah-Hartman 	int retval;
266196fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
266296fd7ce5SGreg Kroah-Hartman 
26636131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
266496fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
266596fd7ce5SGreg Kroah-Hartman 
266696fd7ce5SGreg Kroah-Hartman 	real_tty = tty_pair_get_tty(tty);
266796fd7ce5SGreg Kroah-Hartman 
266896fd7ce5SGreg Kroah-Hartman 	/*
266996fd7ce5SGreg Kroah-Hartman 	 * Factor out some common prep work
267096fd7ce5SGreg Kroah-Hartman 	 */
267196fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
267296fd7ce5SGreg Kroah-Hartman 	case TIOCSETD:
267396fd7ce5SGreg Kroah-Hartman 	case TIOCSBRK:
267496fd7ce5SGreg Kroah-Hartman 	case TIOCCBRK:
267596fd7ce5SGreg Kroah-Hartman 	case TCSBRK:
267696fd7ce5SGreg Kroah-Hartman 	case TCSBRKP:
267796fd7ce5SGreg Kroah-Hartman 		retval = tty_check_change(tty);
267896fd7ce5SGreg Kroah-Hartman 		if (retval)
267996fd7ce5SGreg Kroah-Hartman 			return retval;
268096fd7ce5SGreg Kroah-Hartman 		if (cmd != TIOCCBRK) {
268196fd7ce5SGreg Kroah-Hartman 			tty_wait_until_sent(tty, 0);
268296fd7ce5SGreg Kroah-Hartman 			if (signal_pending(current))
268396fd7ce5SGreg Kroah-Hartman 				return -EINTR;
268496fd7ce5SGreg Kroah-Hartman 		}
268596fd7ce5SGreg Kroah-Hartman 		break;
268696fd7ce5SGreg Kroah-Hartman 	}
268796fd7ce5SGreg Kroah-Hartman 
268896fd7ce5SGreg Kroah-Hartman 	/*
268996fd7ce5SGreg Kroah-Hartman 	 *	Now do the stuff.
269096fd7ce5SGreg Kroah-Hartman 	 */
269196fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
269296fd7ce5SGreg Kroah-Hartman 	case TIOCSTI:
269396fd7ce5SGreg Kroah-Hartman 		return tiocsti(tty, p);
269496fd7ce5SGreg Kroah-Hartman 	case TIOCGWINSZ:
269596fd7ce5SGreg Kroah-Hartman 		return tiocgwinsz(real_tty, p);
269696fd7ce5SGreg Kroah-Hartman 	case TIOCSWINSZ:
269796fd7ce5SGreg Kroah-Hartman 		return tiocswinsz(real_tty, p);
269896fd7ce5SGreg Kroah-Hartman 	case TIOCCONS:
269996fd7ce5SGreg Kroah-Hartman 		return real_tty != tty ? -EINVAL : tioccons(file);
270096fd7ce5SGreg Kroah-Hartman 	case TIOCEXCL:
270196fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_EXCLUSIVE, &tty->flags);
270296fd7ce5SGreg Kroah-Hartman 		return 0;
270396fd7ce5SGreg Kroah-Hartman 	case TIOCNXCL:
270496fd7ce5SGreg Kroah-Hartman 		clear_bit(TTY_EXCLUSIVE, &tty->flags);
270596fd7ce5SGreg Kroah-Hartman 		return 0;
270684fd7bdfSCyrill Gorcunov 	case TIOCGEXCL:
270784fd7bdfSCyrill Gorcunov 	{
270884fd7bdfSCyrill Gorcunov 		int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
2709e73b2407SXiaofei Tan 
271084fd7bdfSCyrill Gorcunov 		return put_user(excl, (int __user *)p);
271184fd7bdfSCyrill Gorcunov 	}
271296fd7ce5SGreg Kroah-Hartman 	case TIOCGETD:
27135c17c861SPeter Hurley 		return tiocgetd(tty, p);
271496fd7ce5SGreg Kroah-Hartman 	case TIOCSETD:
271596fd7ce5SGreg Kroah-Hartman 		return tiocsetd(tty, p);
27163c95c985SKay Sievers 	case TIOCVHANGUP:
27173c95c985SKay Sievers 		if (!capable(CAP_SYS_ADMIN))
27183c95c985SKay Sievers 			return -EPERM;
27193c95c985SKay Sievers 		tty_vhangup(tty);
27203c95c985SKay Sievers 		return 0;
2721b7b8de08SWerner Fink 	case TIOCGDEV:
2722b7b8de08SWerner Fink 	{
2723b7b8de08SWerner Fink 		unsigned int ret = new_encode_dev(tty_devnum(real_tty));
2724e73b2407SXiaofei Tan 
2725b7b8de08SWerner Fink 		return put_user(ret, (unsigned int __user *)p);
2726b7b8de08SWerner Fink 	}
272796fd7ce5SGreg Kroah-Hartman 	/*
272896fd7ce5SGreg Kroah-Hartman 	 * Break handling
272996fd7ce5SGreg Kroah-Hartman 	 */
273096fd7ce5SGreg Kroah-Hartman 	case TIOCSBRK:	/* Turn break on, unconditionally */
273196fd7ce5SGreg Kroah-Hartman 		if (tty->ops->break_ctl)
273296fd7ce5SGreg Kroah-Hartman 			return tty->ops->break_ctl(tty, -1);
273396fd7ce5SGreg Kroah-Hartman 		return 0;
273496fd7ce5SGreg Kroah-Hartman 	case TIOCCBRK:	/* Turn break off, unconditionally */
273596fd7ce5SGreg Kroah-Hartman 		if (tty->ops->break_ctl)
273696fd7ce5SGreg Kroah-Hartman 			return tty->ops->break_ctl(tty, 0);
273796fd7ce5SGreg Kroah-Hartman 		return 0;
273896fd7ce5SGreg Kroah-Hartman 	case TCSBRK:   /* SVID version: non-zero arg --> no break */
273996fd7ce5SGreg Kroah-Hartman 		/* non-zero arg means wait for all output data
274096fd7ce5SGreg Kroah-Hartman 		 * to be sent (performed above) but don't send break.
274196fd7ce5SGreg Kroah-Hartman 		 * This is used by the tcdrain() termios function.
274296fd7ce5SGreg Kroah-Hartman 		 */
274396fd7ce5SGreg Kroah-Hartman 		if (!arg)
274496fd7ce5SGreg Kroah-Hartman 			return send_break(tty, 250);
274596fd7ce5SGreg Kroah-Hartman 		return 0;
274696fd7ce5SGreg Kroah-Hartman 	case TCSBRKP:	/* support for POSIX tcsendbreak() */
274796fd7ce5SGreg Kroah-Hartman 		return send_break(tty, arg ? arg*100 : 250);
274896fd7ce5SGreg Kroah-Hartman 
274996fd7ce5SGreg Kroah-Hartman 	case TIOCMGET:
275060b33c13SAlan Cox 		return tty_tiocmget(tty, p);
275196fd7ce5SGreg Kroah-Hartman 	case TIOCMSET:
275296fd7ce5SGreg Kroah-Hartman 	case TIOCMBIC:
275396fd7ce5SGreg Kroah-Hartman 	case TIOCMBIS:
275420b9d177SAlan Cox 		return tty_tiocmset(tty, cmd, p);
275596fd7ce5SGreg Kroah-Hartman 	case TIOCGICOUNT:
2756a3096199SAl Viro 		return tty_tiocgicount(tty, p);
275796fd7ce5SGreg Kroah-Hartman 	case TCFLSH:
275896fd7ce5SGreg Kroah-Hartman 		switch (arg) {
275996fd7ce5SGreg Kroah-Hartman 		case TCIFLUSH:
276096fd7ce5SGreg Kroah-Hartman 		case TCIOFLUSH:
276196fd7ce5SGreg Kroah-Hartman 		/* flush tty buffer and allow ldisc to process ioctl */
276286c80a8eSPeter Hurley 			tty_buffer_flush(tty, NULL);
276396fd7ce5SGreg Kroah-Hartman 			break;
276496fd7ce5SGreg Kroah-Hartman 		}
276596fd7ce5SGreg Kroah-Hartman 		break;
27668a8ae62fSJiri Slaby 	case TIOCSSERIAL:
2767930236a3SAl Viro 		return tty_tiocsserial(tty, p);
27682f46a2c1SAl Viro 	case TIOCGSERIAL:
2769930236a3SAl Viro 		return tty_tiocgserial(tty, p);
2770311fc65cSEric W. Biederman 	case TIOCGPTPEER:
2771311fc65cSEric W. Biederman 		/* Special because the struct file is needed */
2772311fc65cSEric W. Biederman 		return ptm_open_peer(file, tty, (int)arg);
2773a1235b3eSNicolas Pitre 	default:
2774a1235b3eSNicolas Pitre 		retval = tty_jobctrl_ioctl(tty, real_tty, file, cmd, arg);
2775a1235b3eSNicolas Pitre 		if (retval != -ENOIOCTLCMD)
2776a1235b3eSNicolas Pitre 			return retval;
277796fd7ce5SGreg Kroah-Hartman 	}
277896fd7ce5SGreg Kroah-Hartman 	if (tty->ops->ioctl) {
2779c961bfb1SPeter Hurley 		retval = tty->ops->ioctl(tty, cmd, arg);
278096fd7ce5SGreg Kroah-Hartman 		if (retval != -ENOIOCTLCMD)
278196fd7ce5SGreg Kroah-Hartman 			return retval;
278296fd7ce5SGreg Kroah-Hartman 	}
278396fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
2784e55afd11SPeter Hurley 	if (!ld)
2785e55afd11SPeter Hurley 		return hung_up_tty_ioctl(file, cmd, arg);
278696fd7ce5SGreg Kroah-Hartman 	retval = -EINVAL;
278796fd7ce5SGreg Kroah-Hartman 	if (ld->ops->ioctl) {
2788d78328bcSJiri Slaby 		retval = ld->ops->ioctl(tty, cmd, arg);
278996fd7ce5SGreg Kroah-Hartman 		if (retval == -ENOIOCTLCMD)
2790bbb63c51SWanlong Gao 			retval = -ENOTTY;
279196fd7ce5SGreg Kroah-Hartman 	}
279296fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
279396fd7ce5SGreg Kroah-Hartman 	return retval;
279496fd7ce5SGreg Kroah-Hartman }
279596fd7ce5SGreg Kroah-Hartman 
279696fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_COMPAT
279777654350SAl Viro 
279877654350SAl Viro struct serial_struct32 {
279977654350SAl Viro 	compat_int_t    type;
280077654350SAl Viro 	compat_int_t    line;
280177654350SAl Viro 	compat_uint_t   port;
280277654350SAl Viro 	compat_int_t    irq;
280377654350SAl Viro 	compat_int_t    flags;
280477654350SAl Viro 	compat_int_t    xmit_fifo_size;
280577654350SAl Viro 	compat_int_t    custom_divisor;
280677654350SAl Viro 	compat_int_t    baud_base;
280777654350SAl Viro 	unsigned short  close_delay;
280877654350SAl Viro 	char    io_type;
2809e5b9f4b1SGustavo A. R. Silva 	char    reserved_char;
281077654350SAl Viro 	compat_int_t    hub6;
281177654350SAl Viro 	unsigned short  closing_wait; /* time to wait before closing */
281277654350SAl Viro 	unsigned short  closing_wait2; /* no longer used... */
281377654350SAl Viro 	compat_uint_t   iomem_base;
281477654350SAl Viro 	unsigned short  iomem_reg_shift;
281577654350SAl Viro 	unsigned int    port_high;
281677654350SAl Viro 	/* compat_ulong_t  iomap_base FIXME */
2817e5b9f4b1SGustavo A. R. Silva 	compat_int_t    reserved;
281877654350SAl Viro };
281977654350SAl Viro 
compat_tty_tiocsserial(struct tty_struct * tty,struct serial_struct32 __user * ss)282077654350SAl Viro static int compat_tty_tiocsserial(struct tty_struct *tty,
282177654350SAl Viro 		struct serial_struct32 __user *ss)
282277654350SAl Viro {
282377654350SAl Viro 	struct serial_struct32 v32;
282477654350SAl Viro 	struct serial_struct v;
282577654350SAl Viro 
2826a3241891SGustavo A. R. Silva 	if (copy_from_user(&v32, ss, sizeof(*ss)))
282777654350SAl Viro 		return -EFAULT;
282877654350SAl Viro 
282977654350SAl Viro 	memcpy(&v, &v32, offsetof(struct serial_struct32, iomem_base));
283077654350SAl Viro 	v.iomem_base = compat_ptr(v32.iomem_base);
283177654350SAl Viro 	v.iomem_reg_shift = v32.iomem_reg_shift;
283277654350SAl Viro 	v.port_high = v32.port_high;
283377654350SAl Viro 	v.iomap_base = 0;
283477654350SAl Viro 
2835885c77d5SJohan Hovold 	return tty_set_serial(tty, &v);
283677654350SAl Viro }
283777654350SAl Viro 
compat_tty_tiocgserial(struct tty_struct * tty,struct serial_struct32 __user * ss)283877654350SAl Viro static int compat_tty_tiocgserial(struct tty_struct *tty,
283977654350SAl Viro 			struct serial_struct32 __user *ss)
284077654350SAl Viro {
284177654350SAl Viro 	struct serial_struct32 v32;
284277654350SAl Viro 	struct serial_struct v;
284377654350SAl Viro 	int err;
284417329563SEric Biggers 
284517329563SEric Biggers 	memset(&v, 0, sizeof(v));
284617329563SEric Biggers 	memset(&v32, 0, sizeof(v32));
284777654350SAl Viro 
28486e622cd8SEric Biggers 	if (!tty->ops->get_serial)
284977654350SAl Viro 		return -ENOTTY;
285077654350SAl Viro 	err = tty->ops->get_serial(tty, &v);
285177654350SAl Viro 	if (!err) {
285277654350SAl Viro 		memcpy(&v32, &v, offsetof(struct serial_struct32, iomem_base));
285377654350SAl Viro 		v32.iomem_base = (unsigned long)v.iomem_base >> 32 ?
285477654350SAl Viro 			0xfffffff : ptr_to_compat(v.iomem_base);
285577654350SAl Viro 		v32.iomem_reg_shift = v.iomem_reg_shift;
285677654350SAl Viro 		v32.port_high = v.port_high;
2857a3241891SGustavo A. R. Silva 		if (copy_to_user(ss, &v32, sizeof(v32)))
285877654350SAl Viro 			err = -EFAULT;
285977654350SAl Viro 	}
286077654350SAl Viro 	return err;
286177654350SAl Viro }
tty_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)286296fd7ce5SGreg Kroah-Hartman static long tty_compat_ioctl(struct file *file, unsigned int cmd,
286396fd7ce5SGreg Kroah-Hartman 				unsigned long arg)
286496fd7ce5SGreg Kroah-Hartman {
286596fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
286696fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
286796fd7ce5SGreg Kroah-Hartman 	int retval = -ENOIOCTLCMD;
286896fd7ce5SGreg Kroah-Hartman 
2869e2112038SAl Viro 	switch (cmd) {
2870c7dc504eSArnd Bergmann 	case TIOCOUTQ:
2871e2112038SAl Viro 	case TIOCSTI:
2872e2112038SAl Viro 	case TIOCGWINSZ:
2873e2112038SAl Viro 	case TIOCSWINSZ:
2874e2112038SAl Viro 	case TIOCGEXCL:
2875e2112038SAl Viro 	case TIOCGETD:
2876e2112038SAl Viro 	case TIOCSETD:
2877e2112038SAl Viro 	case TIOCGDEV:
2878e2112038SAl Viro 	case TIOCMGET:
2879e2112038SAl Viro 	case TIOCMSET:
2880e2112038SAl Viro 	case TIOCMBIC:
2881e2112038SAl Viro 	case TIOCMBIS:
2882e2112038SAl Viro 	case TIOCGICOUNT:
2883e2112038SAl Viro 	case TIOCGPGRP:
2884e2112038SAl Viro 	case TIOCSPGRP:
2885e2112038SAl Viro 	case TIOCGSID:
2886e2112038SAl Viro 	case TIOCSERGETLSR:
2887e2112038SAl Viro 	case TIOCGRS485:
2888e2112038SAl Viro 	case TIOCSRS485:
2889e2112038SAl Viro #ifdef TIOCGETP
2890e2112038SAl Viro 	case TIOCGETP:
2891e2112038SAl Viro 	case TIOCSETP:
2892e2112038SAl Viro 	case TIOCSETN:
2893e2112038SAl Viro #endif
2894e2112038SAl Viro #ifdef TIOCGETC
2895e2112038SAl Viro 	case TIOCGETC:
2896e2112038SAl Viro 	case TIOCSETC:
2897e2112038SAl Viro #endif
2898e2112038SAl Viro #ifdef TIOCGLTC
2899e2112038SAl Viro 	case TIOCGLTC:
2900e2112038SAl Viro 	case TIOCSLTC:
2901e2112038SAl Viro #endif
2902e2112038SAl Viro 	case TCSETSF:
2903e2112038SAl Viro 	case TCSETSW:
2904e2112038SAl Viro 	case TCSETS:
2905e2112038SAl Viro 	case TCGETS:
2906e2112038SAl Viro #ifdef TCGETS2
2907e2112038SAl Viro 	case TCGETS2:
2908e2112038SAl Viro 	case TCSETSF2:
2909e2112038SAl Viro 	case TCSETSW2:
2910e2112038SAl Viro 	case TCSETS2:
2911e2112038SAl Viro #endif
2912e2112038SAl Viro 	case TCGETA:
2913e2112038SAl Viro 	case TCSETAF:
2914e2112038SAl Viro 	case TCSETAW:
2915e2112038SAl Viro 	case TCSETA:
2916e2112038SAl Viro 	case TIOCGLCKTRMIOS:
2917e2112038SAl Viro 	case TIOCSLCKTRMIOS:
2918e2112038SAl Viro #ifdef TCGETX
2919e2112038SAl Viro 	case TCGETX:
2920e2112038SAl Viro 	case TCSETX:
2921e2112038SAl Viro 	case TCSETXW:
2922e2112038SAl Viro 	case TCSETXF:
2923e2112038SAl Viro #endif
2924e2112038SAl Viro 	case TIOCGSOFTCAR:
2925e2112038SAl Viro 	case TIOCSSOFTCAR:
2926b7aff093SArnd Bergmann 
2927b7aff093SArnd Bergmann 	case PPPIOCGCHAN:
2928b7aff093SArnd Bergmann 	case PPPIOCGUNIT:
2929e2112038SAl Viro 		return tty_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
2930e2112038SAl Viro 	case TIOCCONS:
2931e2112038SAl Viro 	case TIOCEXCL:
2932e2112038SAl Viro 	case TIOCNXCL:
2933e2112038SAl Viro 	case TIOCVHANGUP:
2934e2112038SAl Viro 	case TIOCSBRK:
2935e2112038SAl Viro 	case TIOCCBRK:
2936e2112038SAl Viro 	case TCSBRK:
2937e2112038SAl Viro 	case TCSBRKP:
2938e2112038SAl Viro 	case TCFLSH:
2939e2112038SAl Viro 	case TIOCGPTPEER:
2940e2112038SAl Viro 	case TIOCNOTTY:
2941e2112038SAl Viro 	case TIOCSCTTY:
2942e2112038SAl Viro 	case TCXONC:
2943e2112038SAl Viro 	case TIOCMIWAIT:
2944e2112038SAl Viro 	case TIOCSERCONFIG:
2945e2112038SAl Viro 		return tty_ioctl(file, cmd, arg);
2946e2112038SAl Viro 	}
2947e2112038SAl Viro 
29486131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
294996fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
295096fd7ce5SGreg Kroah-Hartman 
295177654350SAl Viro 	switch (cmd) {
295277654350SAl Viro 	case TIOCSSERIAL:
295377654350SAl Viro 		return compat_tty_tiocsserial(tty, compat_ptr(arg));
295477654350SAl Viro 	case TIOCGSERIAL:
295577654350SAl Viro 		return compat_tty_tiocgserial(tty, compat_ptr(arg));
295677654350SAl Viro 	}
295796fd7ce5SGreg Kroah-Hartman 	if (tty->ops->compat_ioctl) {
2958c961bfb1SPeter Hurley 		retval = tty->ops->compat_ioctl(tty, cmd, arg);
295996fd7ce5SGreg Kroah-Hartman 		if (retval != -ENOIOCTLCMD)
296096fd7ce5SGreg Kroah-Hartman 			return retval;
296196fd7ce5SGreg Kroah-Hartman 	}
296296fd7ce5SGreg Kroah-Hartman 
296396fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
2964e55afd11SPeter Hurley 	if (!ld)
2965e55afd11SPeter Hurley 		return hung_up_tty_compat_ioctl(file, cmd, arg);
296696fd7ce5SGreg Kroah-Hartman 	if (ld->ops->compat_ioctl)
2967d78328bcSJiri Slaby 		retval = ld->ops->compat_ioctl(tty, cmd, arg);
2968f0193d3eSAl Viro 	if (retval == -ENOIOCTLCMD && ld->ops->ioctl)
2969d78328bcSJiri Slaby 		retval = ld->ops->ioctl(tty, (unsigned long)compat_ptr(cmd),
2970d78328bcSJiri Slaby 				arg);
297196fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
297296fd7ce5SGreg Kroah-Hartman 
297396fd7ce5SGreg Kroah-Hartman 	return retval;
297496fd7ce5SGreg Kroah-Hartman }
297596fd7ce5SGreg Kroah-Hartman #endif
297696fd7ce5SGreg Kroah-Hartman 
this_tty(const void * t,struct file * file,unsigned fd)2977c3c073f8SAl Viro static int this_tty(const void *t, struct file *file, unsigned fd)
2978c3c073f8SAl Viro {
2979dd78b0c4SLinus Torvalds 	if (likely(file->f_op->read_iter != tty_read))
2980c3c073f8SAl Viro 		return 0;
2981c3c073f8SAl Viro 	return file_tty(file) != t ? 0 : fd + 1;
2982c3c073f8SAl Viro }
2983c3c073f8SAl Viro 
298496fd7ce5SGreg Kroah-Hartman /*
298596fd7ce5SGreg Kroah-Hartman  * This implements the "Secure Attention Key" ---  the idea is to
298696fd7ce5SGreg Kroah-Hartman  * prevent trojan horses by killing all processes associated with this
298796fd7ce5SGreg Kroah-Hartman  * tty when the user hits the "Secure Attention Key".  Required for
298896fd7ce5SGreg Kroah-Hartman  * super-paranoid applications --- see the Orange Book for more details.
298996fd7ce5SGreg Kroah-Hartman  *
299096fd7ce5SGreg Kroah-Hartman  * This code could be nicer; ideally it should send a HUP, wait a few
299196fd7ce5SGreg Kroah-Hartman  * seconds, then send a INT, and then a KILL signal.  But you then
299296fd7ce5SGreg Kroah-Hartman  * have to coordinate with the init process, since all processes associated
299396fd7ce5SGreg Kroah-Hartman  * with the current tty must be dead before the new getty is allowed
299496fd7ce5SGreg Kroah-Hartman  * to spawn.
299596fd7ce5SGreg Kroah-Hartman  *
299696fd7ce5SGreg Kroah-Hartman  * Now, if it would be correct ;-/ The current code has a nasty hole -
299796fd7ce5SGreg Kroah-Hartman  * it doesn't catch files in flight. We may send the descriptor to ourselves
299896fd7ce5SGreg Kroah-Hartman  * via AF_UNIX socket, close it and later fetch from socket. FIXME.
299996fd7ce5SGreg Kroah-Hartman  *
300096fd7ce5SGreg Kroah-Hartman  * Nasty bug: do_SAK is being called in interrupt context.  This can
300196fd7ce5SGreg Kroah-Hartman  * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
300296fd7ce5SGreg Kroah-Hartman  */
__do_SAK(struct tty_struct * tty)300396fd7ce5SGreg Kroah-Hartman void __do_SAK(struct tty_struct *tty)
300496fd7ce5SGreg Kroah-Hartman {
300596fd7ce5SGreg Kroah-Hartman 	struct task_struct *g, *p;
300696fd7ce5SGreg Kroah-Hartman 	struct pid *session;
300796fd7ce5SGreg Kroah-Hartman 	int i;
3008c8bcd9c5SJann Horn 	unsigned long flags;
300996fd7ce5SGreg Kroah-Hartman 
301064d608dbSJiri Slaby 	spin_lock_irqsave(&tty->ctrl.lock, flags);
301164d608dbSJiri Slaby 	session = get_pid(tty->ctrl.session);
301264d608dbSJiri Slaby 	spin_unlock_irqrestore(&tty->ctrl.lock, flags);
301396fd7ce5SGreg Kroah-Hartman 
301496fd7ce5SGreg Kroah-Hartman 	tty_ldisc_flush(tty);
301596fd7ce5SGreg Kroah-Hartman 
301696fd7ce5SGreg Kroah-Hartman 	tty_driver_flush_buffer(tty);
301796fd7ce5SGreg Kroah-Hartman 
301896fd7ce5SGreg Kroah-Hartman 	read_lock(&tasklist_lock);
301996fd7ce5SGreg Kroah-Hartman 	/* Kill the entire session */
302096fd7ce5SGreg Kroah-Hartman 	do_each_pid_task(session, PIDTYPE_SID, p) {
30219b42bb75SPeter Hurley 		tty_notice(tty, "SAK: killed process %d (%s): by session\n",
302296fd7ce5SGreg Kroah-Hartman 			   task_pid_nr(p), p->comm);
3023a8ebd171SEric W. Biederman 		group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p, PIDTYPE_SID);
302496fd7ce5SGreg Kroah-Hartman 	} while_each_pid_task(session, PIDTYPE_SID, p);
30259b42bb75SPeter Hurley 
30269b42bb75SPeter Hurley 	/* Now kill any processes that happen to have the tty open */
30275ffd2c37SOleg Nesterov 	for_each_process_thread(g, p) {
302896fd7ce5SGreg Kroah-Hartman 		if (p->signal->tty == tty) {
30299b42bb75SPeter Hurley 			tty_notice(tty, "SAK: killed process %d (%s): by controlling tty\n",
303096fd7ce5SGreg Kroah-Hartman 				   task_pid_nr(p), p->comm);
3031954a0881SJiri Slaby 			group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p,
3032954a0881SJiri Slaby 					PIDTYPE_SID);
303396fd7ce5SGreg Kroah-Hartman 			continue;
303496fd7ce5SGreg Kroah-Hartman 		}
303596fd7ce5SGreg Kroah-Hartman 		task_lock(p);
3036c3c073f8SAl Viro 		i = iterate_fd(p->files, 0, this_tty, tty);
3037c3c073f8SAl Viro 		if (i != 0) {
30389b42bb75SPeter Hurley 			tty_notice(tty, "SAK: killed process %d (%s): by fd#%d\n",
3039c3c073f8SAl Viro 				   task_pid_nr(p), p->comm, i - 1);
3040954a0881SJiri Slaby 			group_send_sig_info(SIGKILL, SEND_SIG_PRIV, p,
3041954a0881SJiri Slaby 					PIDTYPE_SID);
304296fd7ce5SGreg Kroah-Hartman 		}
304396fd7ce5SGreg Kroah-Hartman 		task_unlock(p);
30445ffd2c37SOleg Nesterov 	}
304596fd7ce5SGreg Kroah-Hartman 	read_unlock(&tasklist_lock);
3046c8bcd9c5SJann Horn 	put_pid(session);
304796fd7ce5SGreg Kroah-Hartman }
304896fd7ce5SGreg Kroah-Hartman 
do_SAK_work(struct work_struct * work)304996fd7ce5SGreg Kroah-Hartman static void do_SAK_work(struct work_struct *work)
305096fd7ce5SGreg Kroah-Hartman {
305196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty =
305296fd7ce5SGreg Kroah-Hartman 		container_of(work, struct tty_struct, SAK_work);
305396fd7ce5SGreg Kroah-Hartman 	__do_SAK(tty);
305496fd7ce5SGreg Kroah-Hartman }
305596fd7ce5SGreg Kroah-Hartman 
305696fd7ce5SGreg Kroah-Hartman /*
305796fd7ce5SGreg Kroah-Hartman  * The tq handling here is a little racy - tty->SAK_work may already be queued.
305896fd7ce5SGreg Kroah-Hartman  * Fortunately we don't need to worry, because if ->SAK_work is already queued,
305996fd7ce5SGreg Kroah-Hartman  * the values which we write to it will be identical to the values which it
306096fd7ce5SGreg Kroah-Hartman  * already has. --akpm
306196fd7ce5SGreg Kroah-Hartman  */
do_SAK(struct tty_struct * tty)306296fd7ce5SGreg Kroah-Hartman void do_SAK(struct tty_struct *tty)
306396fd7ce5SGreg Kroah-Hartman {
306496fd7ce5SGreg Kroah-Hartman 	if (!tty)
306596fd7ce5SGreg Kroah-Hartman 		return;
306696fd7ce5SGreg Kroah-Hartman 	schedule_work(&tty->SAK_work);
306796fd7ce5SGreg Kroah-Hartman }
306896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(do_SAK);
306996fd7ce5SGreg Kroah-Hartman 
307096fd7ce5SGreg Kroah-Hartman /* Must put_device() after it's unused! */
tty_get_device(struct tty_struct * tty)307196fd7ce5SGreg Kroah-Hartman static struct device *tty_get_device(struct tty_struct *tty)
307296fd7ce5SGreg Kroah-Hartman {
307396fd7ce5SGreg Kroah-Hartman 	dev_t devt = tty_devnum(tty);
3074e73b2407SXiaofei Tan 
3075862d8312SGreg Kroah-Hartman 	return class_find_device_by_devt(&tty_class, devt);
307696fd7ce5SGreg Kroah-Hartman }
307796fd7ce5SGreg Kroah-Hartman 
307896fd7ce5SGreg Kroah-Hartman 
3079796a75a9SJiri Slaby /**
3080796a75a9SJiri Slaby  * alloc_tty_struct - allocate a new tty
3081796a75a9SJiri Slaby  * @driver: driver which will handle the returned tty
3082796a75a9SJiri Slaby  * @idx: minor of the tty
308396fd7ce5SGreg Kroah-Hartman  *
30842c964a2fSRasmus Villemoes  * This subroutine allocates and initializes a tty structure.
308596fd7ce5SGreg Kroah-Hartman  *
3086796a75a9SJiri Slaby  * Locking: none - @tty in question is not exposed at this point
308796fd7ce5SGreg Kroah-Hartman  */
alloc_tty_struct(struct tty_driver * driver,int idx)30882c964a2fSRasmus Villemoes struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
308996fd7ce5SGreg Kroah-Hartman {
30902c964a2fSRasmus Villemoes 	struct tty_struct *tty;
30912c964a2fSRasmus Villemoes 
3092c72d8592SVasily Averin 	tty = kzalloc(sizeof(*tty), GFP_KERNEL_ACCOUNT);
30932c964a2fSRasmus Villemoes 	if (!tty)
30942c964a2fSRasmus Villemoes 		return NULL;
30952c964a2fSRasmus Villemoes 
309696fd7ce5SGreg Kroah-Hartman 	kref_init(&tty->kref);
3097903f9db1STetsuo Handa 	if (tty_ldisc_init(tty)) {
3098903f9db1STetsuo Handa 		kfree(tty);
3099903f9db1STetsuo Handa 		return NULL;
3100903f9db1STetsuo Handa 	}
310164d608dbSJiri Slaby 	tty->ctrl.session = NULL;
310264d608dbSJiri Slaby 	tty->ctrl.pgrp = NULL;
310389c8d91eSAlan Cox 	mutex_init(&tty->legacy_mutex);
3104d8c1f929SPeter Hurley 	mutex_init(&tty->throttle_mutex);
31056a1c0680SPeter Hurley 	init_rwsem(&tty->termios_rwsem);
3106dee4a0beSPeter Hurley 	mutex_init(&tty->winsize_mutex);
310736697529SPeter Hurley 	init_ldsem(&tty->ldisc_sem);
310896fd7ce5SGreg Kroah-Hartman 	init_waitqueue_head(&tty->write_wait);
310996fd7ce5SGreg Kroah-Hartman 	init_waitqueue_head(&tty->read_wait);
311096fd7ce5SGreg Kroah-Hartman 	INIT_WORK(&tty->hangup_work, do_tty_hangup);
311196fd7ce5SGreg Kroah-Hartman 	mutex_init(&tty->atomic_write_lock);
311264d608dbSJiri Slaby 	spin_lock_init(&tty->ctrl.lock);
31136e94dbc7SJiri Slaby 	spin_lock_init(&tty->flow.lock);
31144a510969SPeter Hurley 	spin_lock_init(&tty->files_lock);
311596fd7ce5SGreg Kroah-Hartman 	INIT_LIST_HEAD(&tty->tty_files);
311696fd7ce5SGreg Kroah-Hartman 	INIT_WORK(&tty->SAK_work, do_SAK_work);
311796fd7ce5SGreg Kroah-Hartman 
311896fd7ce5SGreg Kroah-Hartman 	tty->driver = driver;
311996fd7ce5SGreg Kroah-Hartman 	tty->ops = driver->ops;
312096fd7ce5SGreg Kroah-Hartman 	tty->index = idx;
312196fd7ce5SGreg Kroah-Hartman 	tty_line_name(driver, idx, tty->name);
312296fd7ce5SGreg Kroah-Hartman 	tty->dev = tty_get_device(tty);
31232c964a2fSRasmus Villemoes 
31242c964a2fSRasmus Villemoes 	return tty;
312596fd7ce5SGreg Kroah-Hartman }
312696fd7ce5SGreg Kroah-Hartman 
312796fd7ce5SGreg Kroah-Hartman /**
312896fd7ce5SGreg Kroah-Hartman  * tty_put_char	- write one character to a tty
312996fd7ce5SGreg Kroah-Hartman  * @tty: tty
3130796a75a9SJiri Slaby  * @ch: character to write
313196fd7ce5SGreg Kroah-Hartman  *
3132796a75a9SJiri Slaby  * Write one byte to the @tty using the provided @tty->ops->put_char() method
3133796a75a9SJiri Slaby  * if present.
313496fd7ce5SGreg Kroah-Hartman  *
313596fd7ce5SGreg Kroah-Hartman  * Note: the specific put_char operation in the driver layer may go
313696fd7ce5SGreg Kroah-Hartman  * away soon. Don't call it directly, use this method
3137796a75a9SJiri Slaby  *
3138796a75a9SJiri Slaby  * Return: the number of characters successfully output.
313996fd7ce5SGreg Kroah-Hartman  */
tty_put_char(struct tty_struct * tty,unsigned char ch)314096fd7ce5SGreg Kroah-Hartman int tty_put_char(struct tty_struct *tty, unsigned char ch)
314196fd7ce5SGreg Kroah-Hartman {
314296fd7ce5SGreg Kroah-Hartman 	if (tty->ops->put_char)
314396fd7ce5SGreg Kroah-Hartman 		return tty->ops->put_char(tty, ch);
314496fd7ce5SGreg Kroah-Hartman 	return tty->ops->write(tty, &ch, 1);
314596fd7ce5SGreg Kroah-Hartman }
314696fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_put_char);
314796fd7ce5SGreg Kroah-Hartman 
tty_cdev_add(struct tty_driver * driver,dev_t dev,unsigned int index,unsigned int count)31487e73eca6SJiri Slaby static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
31497e73eca6SJiri Slaby 		unsigned int index, unsigned int count)
31507e73eca6SJiri Slaby {
3151c1a752baSLeon Yu 	int err;
3152c1a752baSLeon Yu 
31537e73eca6SJiri Slaby 	/* init here, since reused cdevs cause crashes */
3154a3a10ce3SRichard Watts 	driver->cdevs[index] = cdev_alloc();
3155a3a10ce3SRichard Watts 	if (!driver->cdevs[index])
3156a3a10ce3SRichard Watts 		return -ENOMEM;
3157c1a752baSLeon Yu 	driver->cdevs[index]->ops = &tty_fops;
3158a3a10ce3SRichard Watts 	driver->cdevs[index]->owner = driver->owner;
3159c1a752baSLeon Yu 	err = cdev_add(driver->cdevs[index], dev, count);
3160c1a752baSLeon Yu 	if (err)
3161c1a752baSLeon Yu 		kobject_put(&driver->cdevs[index]->kobj);
3162c1a752baSLeon Yu 	return err;
31637e73eca6SJiri Slaby }
31647e73eca6SJiri Slaby 
316596fd7ce5SGreg Kroah-Hartman /**
316696fd7ce5SGreg Kroah-Hartman  * tty_register_device - register a tty device
316796fd7ce5SGreg Kroah-Hartman  * @driver: the tty driver that describes the tty device
316896fd7ce5SGreg Kroah-Hartman  * @index: the index in the tty driver for this tty device
316996fd7ce5SGreg Kroah-Hartman  * @device: a struct device that is associated with this tty device.
317096fd7ce5SGreg Kroah-Hartman  *	This field is optional, if there is no known struct device
317196fd7ce5SGreg Kroah-Hartman  *	for this tty device it can be set to NULL safely.
317296fd7ce5SGreg Kroah-Hartman  *
317396fd7ce5SGreg Kroah-Hartman  * This call is required to be made to register an individual tty device
3174796a75a9SJiri Slaby  * if the tty driver's flags have the %TTY_DRIVER_DYNAMIC_DEV bit set.  If
317596fd7ce5SGreg Kroah-Hartman  * that bit is not set, this function should not be called by a tty
317696fd7ce5SGreg Kroah-Hartman  * driver.
317796fd7ce5SGreg Kroah-Hartman  *
317896fd7ce5SGreg Kroah-Hartman  * Locking: ??
3179796a75a9SJiri Slaby  *
3180796a75a9SJiri Slaby  * Return: A pointer to the struct device for this tty device (or
3181796a75a9SJiri Slaby  * ERR_PTR(-EFOO) on error).
318296fd7ce5SGreg Kroah-Hartman  */
tty_register_device(struct tty_driver * driver,unsigned index,struct device * device)318396fd7ce5SGreg Kroah-Hartman struct device *tty_register_device(struct tty_driver *driver, unsigned index,
318496fd7ce5SGreg Kroah-Hartman 				   struct device *device)
318596fd7ce5SGreg Kroah-Hartman {
31866915c0e4STomas Hlavacek 	return tty_register_device_attr(driver, index, device, NULL, NULL);
31876915c0e4STomas Hlavacek }
31886915c0e4STomas Hlavacek EXPORT_SYMBOL(tty_register_device);
31896915c0e4STomas Hlavacek 
tty_device_create_release(struct device * dev)3190b1b79916STomas Hlavacek static void tty_device_create_release(struct device *dev)
3191b1b79916STomas Hlavacek {
319283db1df4SPeter Hurley 	dev_dbg(dev, "releasing...\n");
3193b1b79916STomas Hlavacek 	kfree(dev);
3194b1b79916STomas Hlavacek }
3195b1b79916STomas Hlavacek 
31966915c0e4STomas Hlavacek /**
31976915c0e4STomas Hlavacek  * tty_register_device_attr - register a tty device
31986915c0e4STomas Hlavacek  * @driver: the tty driver that describes the tty device
31996915c0e4STomas Hlavacek  * @index: the index in the tty driver for this tty device
32006915c0e4STomas Hlavacek  * @device: a struct device that is associated with this tty device.
32016915c0e4STomas Hlavacek  *	This field is optional, if there is no known struct device
3202796a75a9SJiri Slaby  *	for this tty device it can be set to %NULL safely.
32036915c0e4STomas Hlavacek  * @drvdata: Driver data to be set to device.
32046915c0e4STomas Hlavacek  * @attr_grp: Attribute group to be set on device.
32056915c0e4STomas Hlavacek  *
3206796a75a9SJiri Slaby  * This call is required to be made to register an individual tty device if the
3207796a75a9SJiri Slaby  * tty driver's flags have the %TTY_DRIVER_DYNAMIC_DEV bit set. If that bit is
3208796a75a9SJiri Slaby  * not set, this function should not be called by a tty driver.
32096915c0e4STomas Hlavacek  *
32106915c0e4STomas Hlavacek  * Locking: ??
3211796a75a9SJiri Slaby  *
3212796a75a9SJiri Slaby  * Return: A pointer to the struct device for this tty device (or
3213796a75a9SJiri Slaby  * ERR_PTR(-EFOO) on error).
32146915c0e4STomas Hlavacek  */
tty_register_device_attr(struct tty_driver * driver,unsigned index,struct device * device,void * drvdata,const struct attribute_group ** attr_grp)32156915c0e4STomas Hlavacek struct device *tty_register_device_attr(struct tty_driver *driver,
32166915c0e4STomas Hlavacek 				   unsigned index, struct device *device,
32176915c0e4STomas Hlavacek 				   void *drvdata,
32186915c0e4STomas Hlavacek 				   const struct attribute_group **attr_grp)
32196915c0e4STomas Hlavacek {
322096fd7ce5SGreg Kroah-Hartman 	char name[64];
32216915c0e4STomas Hlavacek 	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
322293857eddSJohan Hovold 	struct ktermios *tp;
32236a7e6f78SJohan Hovold 	struct device *dev;
32246a7e6f78SJohan Hovold 	int retval;
322596fd7ce5SGreg Kroah-Hartman 
322696fd7ce5SGreg Kroah-Hartman 	if (index >= driver->num) {
3227656fb867SPeter Hurley 		pr_err("%s: Attempt to register invalid tty line number (%d)\n",
3228656fb867SPeter Hurley 		       driver->name, index);
322996fd7ce5SGreg Kroah-Hartman 		return ERR_PTR(-EINVAL);
323096fd7ce5SGreg Kroah-Hartman 	}
323196fd7ce5SGreg Kroah-Hartman 
323296fd7ce5SGreg Kroah-Hartman 	if (driver->type == TTY_DRIVER_TYPE_PTY)
323396fd7ce5SGreg Kroah-Hartman 		pty_line_name(driver, index, name);
323496fd7ce5SGreg Kroah-Hartman 	else
323596fd7ce5SGreg Kroah-Hartman 		tty_line_name(driver, index, name);
323696fd7ce5SGreg Kroah-Hartman 
32376915c0e4STomas Hlavacek 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
32386a7e6f78SJohan Hovold 	if (!dev)
32396a7e6f78SJohan Hovold 		return ERR_PTR(-ENOMEM);
32406915c0e4STomas Hlavacek 
32416915c0e4STomas Hlavacek 	dev->devt = devt;
3242862d8312SGreg Kroah-Hartman 	dev->class = &tty_class;
32436915c0e4STomas Hlavacek 	dev->parent = device;
3244b1b79916STomas Hlavacek 	dev->release = tty_device_create_release;
32456915c0e4STomas Hlavacek 	dev_set_name(dev, "%s", name);
32466915c0e4STomas Hlavacek 	dev->groups = attr_grp;
32476915c0e4STomas Hlavacek 	dev_set_drvdata(dev, drvdata);
32486915c0e4STomas Hlavacek 
32496a7e6f78SJohan Hovold 	dev_set_uevent_suppress(dev, 1);
32506a7e6f78SJohan Hovold 
32516915c0e4STomas Hlavacek 	retval = device_register(dev);
32526915c0e4STomas Hlavacek 	if (retval)
32536a7e6f78SJohan Hovold 		goto err_put;
32546a7e6f78SJohan Hovold 
32556a7e6f78SJohan Hovold 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
325693857eddSJohan Hovold 		/*
325793857eddSJohan Hovold 		 * Free any saved termios data so that the termios state is
325893857eddSJohan Hovold 		 * reset when reusing a minor number.
325993857eddSJohan Hovold 		 */
326093857eddSJohan Hovold 		tp = driver->termios[index];
326193857eddSJohan Hovold 		if (tp) {
326293857eddSJohan Hovold 			driver->termios[index] = NULL;
326393857eddSJohan Hovold 			kfree(tp);
326493857eddSJohan Hovold 		}
326593857eddSJohan Hovold 
32666a7e6f78SJohan Hovold 		retval = tty_cdev_add(driver, devt, index, 1);
32676a7e6f78SJohan Hovold 		if (retval)
32686a7e6f78SJohan Hovold 			goto err_del;
32696a7e6f78SJohan Hovold 	}
32706a7e6f78SJohan Hovold 
32716a7e6f78SJohan Hovold 	dev_set_uevent_suppress(dev, 0);
32726a7e6f78SJohan Hovold 	kobject_uevent(&dev->kobj, KOBJ_ADD);
32736915c0e4STomas Hlavacek 
32746915c0e4STomas Hlavacek 	return dev;
32756915c0e4STomas Hlavacek 
32766a7e6f78SJohan Hovold err_del:
32776a7e6f78SJohan Hovold 	device_del(dev);
32786a7e6f78SJohan Hovold err_put:
32796915c0e4STomas Hlavacek 	put_device(dev);
32806a7e6f78SJohan Hovold 
32816915c0e4STomas Hlavacek 	return ERR_PTR(retval);
32826915c0e4STomas Hlavacek }
32836915c0e4STomas Hlavacek EXPORT_SYMBOL_GPL(tty_register_device_attr);
328496fd7ce5SGreg Kroah-Hartman 
328596fd7ce5SGreg Kroah-Hartman /**
328696fd7ce5SGreg Kroah-Hartman  * tty_unregister_device - unregister a tty device
328796fd7ce5SGreg Kroah-Hartman  * @driver: the tty driver that describes the tty device
328896fd7ce5SGreg Kroah-Hartman  * @index: the index in the tty driver for this tty device
328996fd7ce5SGreg Kroah-Hartman  *
329096fd7ce5SGreg Kroah-Hartman  * If a tty device is registered with a call to tty_register_device() then
329196fd7ce5SGreg Kroah-Hartman  * this function must be called when the tty device is gone.
329296fd7ce5SGreg Kroah-Hartman  *
329396fd7ce5SGreg Kroah-Hartman  * Locking: ??
329496fd7ce5SGreg Kroah-Hartman  */
tty_unregister_device(struct tty_driver * driver,unsigned index)329596fd7ce5SGreg Kroah-Hartman void tty_unregister_device(struct tty_driver *driver, unsigned index)
329696fd7ce5SGreg Kroah-Hartman {
3297862d8312SGreg Kroah-Hartman 	device_destroy(&tty_class, MKDEV(driver->major, driver->minor_start) + index);
3298a3a10ce3SRichard Watts 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
3299a3a10ce3SRichard Watts 		cdev_del(driver->cdevs[index]);
3300a3a10ce3SRichard Watts 		driver->cdevs[index] = NULL;
3301a3a10ce3SRichard Watts 	}
330296fd7ce5SGreg Kroah-Hartman }
330396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_unregister_device);
330496fd7ce5SGreg Kroah-Hartman 
33057f0bc6a6SJiri Slaby /**
33067f0bc6a6SJiri Slaby  * __tty_alloc_driver -- allocate tty driver
33077f0bc6a6SJiri Slaby  * @lines: count of lines this driver can handle at most
330887838ae3SThadeu Lima de Souza Cascardo  * @owner: module which is responsible for this driver
3309796a75a9SJiri Slaby  * @flags: some of %TTY_DRIVER_ flags, will be set in driver->flags
33107f0bc6a6SJiri Slaby  *
33117f0bc6a6SJiri Slaby  * This should not be called directly, some of the provided macros should be
3312796a75a9SJiri Slaby  * used instead. Use IS_ERR() and friends on @retval.
33137f0bc6a6SJiri Slaby  */
__tty_alloc_driver(unsigned int lines,struct module * owner,unsigned long flags)33147f0bc6a6SJiri Slaby struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
33157f0bc6a6SJiri Slaby 		unsigned long flags)
331696fd7ce5SGreg Kroah-Hartman {
331796fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver;
33187e73eca6SJiri Slaby 	unsigned int cdevs = 1;
331916a02081SJiri Slaby 	int err;
332096fd7ce5SGreg Kroah-Hartman 
33210019b408SJiri Slaby 	if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
33227f0bc6a6SJiri Slaby 		return ERR_PTR(-EINVAL);
33237f0bc6a6SJiri Slaby 
3324a3241891SGustavo A. R. Silva 	driver = kzalloc(sizeof(*driver), GFP_KERNEL);
33257f0bc6a6SJiri Slaby 	if (!driver)
33267f0bc6a6SJiri Slaby 		return ERR_PTR(-ENOMEM);
33277f0bc6a6SJiri Slaby 
332896fd7ce5SGreg Kroah-Hartman 	kref_init(&driver->kref);
332996fd7ce5SGreg Kroah-Hartman 	driver->num = lines;
33301a54a76dSJiri Slaby 	driver->owner = owner;
33317f0bc6a6SJiri Slaby 	driver->flags = flags;
333216a02081SJiri Slaby 
333316a02081SJiri Slaby 	if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
333416a02081SJiri Slaby 		driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
333516a02081SJiri Slaby 				GFP_KERNEL);
333616a02081SJiri Slaby 		driver->termios = kcalloc(lines, sizeof(*driver->termios),
333716a02081SJiri Slaby 				GFP_KERNEL);
333816a02081SJiri Slaby 		if (!driver->ttys || !driver->termios) {
333916a02081SJiri Slaby 			err = -ENOMEM;
334016a02081SJiri Slaby 			goto err_free_all;
334116a02081SJiri Slaby 		}
334216a02081SJiri Slaby 	}
334316a02081SJiri Slaby 
334416a02081SJiri Slaby 	if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
334516a02081SJiri Slaby 		driver->ports = kcalloc(lines, sizeof(*driver->ports),
334616a02081SJiri Slaby 				GFP_KERNEL);
334716a02081SJiri Slaby 		if (!driver->ports) {
334816a02081SJiri Slaby 			err = -ENOMEM;
334916a02081SJiri Slaby 			goto err_free_all;
335016a02081SJiri Slaby 		}
33517e73eca6SJiri Slaby 		cdevs = lines;
33527e73eca6SJiri Slaby 	}
33537e73eca6SJiri Slaby 
33547e73eca6SJiri Slaby 	driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);
33557e73eca6SJiri Slaby 	if (!driver->cdevs) {
33567e73eca6SJiri Slaby 		err = -ENOMEM;
33577e73eca6SJiri Slaby 		goto err_free_all;
335816a02081SJiri Slaby 	}
33597f0bc6a6SJiri Slaby 
336096fd7ce5SGreg Kroah-Hartman 	return driver;
336116a02081SJiri Slaby err_free_all:
336216a02081SJiri Slaby 	kfree(driver->ports);
336316a02081SJiri Slaby 	kfree(driver->ttys);
336416a02081SJiri Slaby 	kfree(driver->termios);
3365a3a10ce3SRichard Watts 	kfree(driver->cdevs);
336616a02081SJiri Slaby 	kfree(driver);
336716a02081SJiri Slaby 	return ERR_PTR(err);
336896fd7ce5SGreg Kroah-Hartman }
33697f0bc6a6SJiri Slaby EXPORT_SYMBOL(__tty_alloc_driver);
337096fd7ce5SGreg Kroah-Hartman 
destruct_tty_driver(struct kref * kref)337196fd7ce5SGreg Kroah-Hartman static void destruct_tty_driver(struct kref *kref)
337296fd7ce5SGreg Kroah-Hartman {
337396fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver = container_of(kref, struct tty_driver, kref);
337496fd7ce5SGreg Kroah-Hartman 	int i;
337596fd7ce5SGreg Kroah-Hartman 	struct ktermios *tp;
337696fd7ce5SGreg Kroah-Hartman 
337796fd7ce5SGreg Kroah-Hartman 	if (driver->flags & TTY_DRIVER_INSTALLED) {
337896fd7ce5SGreg Kroah-Hartman 		for (i = 0; i < driver->num; i++) {
337996fd7ce5SGreg Kroah-Hartman 			tp = driver->termios[i];
338096fd7ce5SGreg Kroah-Hartman 			if (tp) {
338196fd7ce5SGreg Kroah-Hartman 				driver->termios[i] = NULL;
338296fd7ce5SGreg Kroah-Hartman 				kfree(tp);
338396fd7ce5SGreg Kroah-Hartman 			}
338496fd7ce5SGreg Kroah-Hartman 			if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
338596fd7ce5SGreg Kroah-Hartman 				tty_unregister_device(driver, i);
338696fd7ce5SGreg Kroah-Hartman 		}
338796fd7ce5SGreg Kroah-Hartman 		proc_tty_unregister_driver(driver);
33887e73eca6SJiri Slaby 		if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)
3389a3a10ce3SRichard Watts 			cdev_del(driver->cdevs[0]);
339096fd7ce5SGreg Kroah-Hartman 	}
33917e73eca6SJiri Slaby 	kfree(driver->cdevs);
339204831dc1SJiri Slaby 	kfree(driver->ports);
339316a02081SJiri Slaby 	kfree(driver->termios);
339416a02081SJiri Slaby 	kfree(driver->ttys);
339596fd7ce5SGreg Kroah-Hartman 	kfree(driver);
339696fd7ce5SGreg Kroah-Hartman }
339796fd7ce5SGreg Kroah-Hartman 
339838581283SJiri Slaby /**
339938581283SJiri Slaby  * tty_driver_kref_put -- drop a reference to a tty driver
340038581283SJiri Slaby  * @driver: driver of which to drop the reference
340138581283SJiri Slaby  *
340238581283SJiri Slaby  * The final put will destroy and free up the driver.
340338581283SJiri Slaby  */
tty_driver_kref_put(struct tty_driver * driver)340496fd7ce5SGreg Kroah-Hartman void tty_driver_kref_put(struct tty_driver *driver)
340596fd7ce5SGreg Kroah-Hartman {
340696fd7ce5SGreg Kroah-Hartman 	kref_put(&driver->kref, destruct_tty_driver);
340796fd7ce5SGreg Kroah-Hartman }
340896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_driver_kref_put);
340996fd7ce5SGreg Kroah-Hartman 
341038581283SJiri Slaby /**
341138581283SJiri Slaby  * tty_register_driver -- register a tty driver
341238581283SJiri Slaby  * @driver: driver to register
341338581283SJiri Slaby  *
341496fd7ce5SGreg Kroah-Hartman  * Called by a tty driver to register itself.
341596fd7ce5SGreg Kroah-Hartman  */
tty_register_driver(struct tty_driver * driver)341696fd7ce5SGreg Kroah-Hartman int tty_register_driver(struct tty_driver *driver)
341796fd7ce5SGreg Kroah-Hartman {
341896fd7ce5SGreg Kroah-Hartman 	int error;
341996fd7ce5SGreg Kroah-Hartman 	int i;
342096fd7ce5SGreg Kroah-Hartman 	dev_t dev;
342196fd7ce5SGreg Kroah-Hartman 	struct device *d;
342296fd7ce5SGreg Kroah-Hartman 
342396fd7ce5SGreg Kroah-Hartman 	if (!driver->major) {
342496fd7ce5SGreg Kroah-Hartman 		error = alloc_chrdev_region(&dev, driver->minor_start,
342596fd7ce5SGreg Kroah-Hartman 						driver->num, driver->name);
342696fd7ce5SGreg Kroah-Hartman 		if (!error) {
342796fd7ce5SGreg Kroah-Hartman 			driver->major = MAJOR(dev);
342896fd7ce5SGreg Kroah-Hartman 			driver->minor_start = MINOR(dev);
342996fd7ce5SGreg Kroah-Hartman 		}
343096fd7ce5SGreg Kroah-Hartman 	} else {
343196fd7ce5SGreg Kroah-Hartman 		dev = MKDEV(driver->major, driver->minor_start);
343296fd7ce5SGreg Kroah-Hartman 		error = register_chrdev_region(dev, driver->num, driver->name);
343396fd7ce5SGreg Kroah-Hartman 	}
34349bb8a3d4SJiri Slaby 	if (error < 0)
343516a02081SJiri Slaby 		goto err;
343696fd7ce5SGreg Kroah-Hartman 
34377e73eca6SJiri Slaby 	if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
34387e73eca6SJiri Slaby 		error = tty_cdev_add(driver, dev, 0, driver->num);
34399bb8a3d4SJiri Slaby 		if (error)
34409bb8a3d4SJiri Slaby 			goto err_unreg_char;
34417e73eca6SJiri Slaby 	}
344296fd7ce5SGreg Kroah-Hartman 
344396fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
344496fd7ce5SGreg Kroah-Hartman 	list_add(&driver->tty_drivers, &tty_drivers);
344596fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
344696fd7ce5SGreg Kroah-Hartman 
344796fd7ce5SGreg Kroah-Hartman 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
344896fd7ce5SGreg Kroah-Hartman 		for (i = 0; i < driver->num; i++) {
344996fd7ce5SGreg Kroah-Hartman 			d = tty_register_device(driver, i, NULL);
345096fd7ce5SGreg Kroah-Hartman 			if (IS_ERR(d)) {
345196fd7ce5SGreg Kroah-Hartman 				error = PTR_ERR(d);
345216a02081SJiri Slaby 				goto err_unreg_devs;
345396fd7ce5SGreg Kroah-Hartman 			}
345496fd7ce5SGreg Kroah-Hartman 		}
345596fd7ce5SGreg Kroah-Hartman 	}
345696fd7ce5SGreg Kroah-Hartman 	proc_tty_register_driver(driver);
345796fd7ce5SGreg Kroah-Hartman 	driver->flags |= TTY_DRIVER_INSTALLED;
345896fd7ce5SGreg Kroah-Hartman 	return 0;
345996fd7ce5SGreg Kroah-Hartman 
346016a02081SJiri Slaby err_unreg_devs:
346196fd7ce5SGreg Kroah-Hartman 	for (i--; i >= 0; i--)
346296fd7ce5SGreg Kroah-Hartman 		tty_unregister_device(driver, i);
346396fd7ce5SGreg Kroah-Hartman 
346496fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
346596fd7ce5SGreg Kroah-Hartman 	list_del(&driver->tty_drivers);
346696fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
346796fd7ce5SGreg Kroah-Hartman 
34689bb8a3d4SJiri Slaby err_unreg_char:
346996fd7ce5SGreg Kroah-Hartman 	unregister_chrdev_region(dev, driver->num);
347016a02081SJiri Slaby err:
347196fd7ce5SGreg Kroah-Hartman 	return error;
347296fd7ce5SGreg Kroah-Hartman }
347396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_register_driver);
347496fd7ce5SGreg Kroah-Hartman 
347538581283SJiri Slaby /**
347638581283SJiri Slaby  * tty_unregister_driver -- unregister a tty driver
347738581283SJiri Slaby  * @driver: driver to unregister
347838581283SJiri Slaby  *
347996fd7ce5SGreg Kroah-Hartman  * Called by a tty driver to unregister itself.
348096fd7ce5SGreg Kroah-Hartman  */
tty_unregister_driver(struct tty_driver * driver)3481a872ab4dSJiri Slaby void tty_unregister_driver(struct tty_driver *driver)
348296fd7ce5SGreg Kroah-Hartman {
348396fd7ce5SGreg Kroah-Hartman 	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
348496fd7ce5SGreg Kroah-Hartman 				driver->num);
348596fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
348696fd7ce5SGreg Kroah-Hartman 	list_del(&driver->tty_drivers);
348796fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
348896fd7ce5SGreg Kroah-Hartman }
348996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_unregister_driver);
349096fd7ce5SGreg Kroah-Hartman 
tty_devnum(struct tty_struct * tty)349196fd7ce5SGreg Kroah-Hartman dev_t tty_devnum(struct tty_struct *tty)
349296fd7ce5SGreg Kroah-Hartman {
349396fd7ce5SGreg Kroah-Hartman 	return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
349496fd7ce5SGreg Kroah-Hartman }
349596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_devnum);
349696fd7ce5SGreg Kroah-Hartman 
tty_default_fops(struct file_operations * fops)349796fd7ce5SGreg Kroah-Hartman void tty_default_fops(struct file_operations *fops)
349896fd7ce5SGreg Kroah-Hartman {
349996fd7ce5SGreg Kroah-Hartman 	*fops = tty_fops;
350096fd7ce5SGreg Kroah-Hartman }
350196fd7ce5SGreg Kroah-Hartman 
tty_devnode(const struct device * dev,umode_t * mode)3502ff62b8e6SGreg Kroah-Hartman static char *tty_devnode(const struct device *dev, umode_t *mode)
350396fd7ce5SGreg Kroah-Hartman {
350496fd7ce5SGreg Kroah-Hartman 	if (!mode)
350596fd7ce5SGreg Kroah-Hartman 		return NULL;
350696fd7ce5SGreg Kroah-Hartman 	if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
350796fd7ce5SGreg Kroah-Hartman 	    dev->devt == MKDEV(TTYAUX_MAJOR, 2))
350896fd7ce5SGreg Kroah-Hartman 		*mode = 0666;
350996fd7ce5SGreg Kroah-Hartman 	return NULL;
351096fd7ce5SGreg Kroah-Hartman }
351196fd7ce5SGreg Kroah-Hartman 
3512862d8312SGreg Kroah-Hartman const struct class tty_class = {
3513862d8312SGreg Kroah-Hartman 	.name		= "tty",
3514862d8312SGreg Kroah-Hartman 	.devnode	= tty_devnode,
3515862d8312SGreg Kroah-Hartman };
3516862d8312SGreg Kroah-Hartman 
tty_class_init(void)351796fd7ce5SGreg Kroah-Hartman static int __init tty_class_init(void)
351896fd7ce5SGreg Kroah-Hartman {
3519862d8312SGreg Kroah-Hartman 	return class_register(&tty_class);
352096fd7ce5SGreg Kroah-Hartman }
352196fd7ce5SGreg Kroah-Hartman 
352296fd7ce5SGreg Kroah-Hartman postcore_initcall(tty_class_init);
352396fd7ce5SGreg Kroah-Hartman 
352496fd7ce5SGreg Kroah-Hartman /* 3/2004 jmc: why do these devices exist? */
352596fd7ce5SGreg Kroah-Hartman static struct cdev tty_cdev, console_cdev;
352696fd7ce5SGreg Kroah-Hartman 
show_cons_active(struct device * dev,struct device_attribute * attr,char * buf)3527fbc92a34SKay Sievers static ssize_t show_cons_active(struct device *dev,
3528fbc92a34SKay Sievers 				struct device_attribute *attr, char *buf)
3529fbc92a34SKay Sievers {
3530fbc92a34SKay Sievers 	struct console *cs[16];
3531fbc92a34SKay Sievers 	int i = 0;
3532fbc92a34SKay Sievers 	struct console *c;
3533fbc92a34SKay Sievers 	ssize_t count = 0;
3534fbc92a34SKay Sievers 
3535d25a2e74SJohn Ogness 	/*
35367c2af0f6SJohn Ogness 	 * Hold the console_list_lock to guarantee that no consoles are
3537d25a2e74SJohn Ogness 	 * unregistered until all console processing is complete.
3538d25a2e74SJohn Ogness 	 * This also allows safe traversal of the console list and
3539d25a2e74SJohn Ogness 	 * race-free reading of @flags.
3540d25a2e74SJohn Ogness 	 */
35417c2af0f6SJohn Ogness 	console_list_lock();
35427c2af0f6SJohn Ogness 
3543a2a6a822SKay Sievers 	for_each_console(c) {
3544fbc92a34SKay Sievers 		if (!c->device)
3545fbc92a34SKay Sievers 			continue;
3546fbc92a34SKay Sievers 		if (!c->write)
3547fbc92a34SKay Sievers 			continue;
3548fbc92a34SKay Sievers 		if ((c->flags & CON_ENABLED) == 0)
3549fbc92a34SKay Sievers 			continue;
3550fbc92a34SKay Sievers 		cs[i++] = c;
3551fbc92a34SKay Sievers 		if (i >= ARRAY_SIZE(cs))
3552fbc92a34SKay Sievers 			break;
3553fbc92a34SKay Sievers 	}
35547c2af0f6SJohn Ogness 
35557c2af0f6SJohn Ogness 	/*
35567c2af0f6SJohn Ogness 	 * Take console_lock to serialize device() callback with
35577c2af0f6SJohn Ogness 	 * other console operations. For example, fg_console is
35587c2af0f6SJohn Ogness 	 * modified under console_lock when switching vt.
35597c2af0f6SJohn Ogness 	 */
35607c2af0f6SJohn Ogness 	console_lock();
3561723abd87SHannes Reinecke 	while (i--) {
3562723abd87SHannes Reinecke 		int index = cs[i]->index;
3563723abd87SHannes Reinecke 		struct tty_driver *drv = cs[i]->device(cs[i], &index);
3564723abd87SHannes Reinecke 
3565723abd87SHannes Reinecke 		/* don't resolve tty0 as some programs depend on it */
3566723abd87SHannes Reinecke 		if (drv && (cs[i]->index > 0 || drv->major != TTY_MAJOR))
3567723abd87SHannes Reinecke 			count += tty_line_name(drv, index, buf + count);
3568723abd87SHannes Reinecke 		else
3569723abd87SHannes Reinecke 			count += sprintf(buf + count, "%s%d",
3570723abd87SHannes Reinecke 					 cs[i]->name, cs[i]->index);
3571723abd87SHannes Reinecke 
3572723abd87SHannes Reinecke 		count += sprintf(buf + count, "%c", i ? ' ':'\n');
3573723abd87SHannes Reinecke 	}
3574ac751efaSTorben Hohn 	console_unlock();
3575fbc92a34SKay Sievers 
35767c2af0f6SJohn Ogness 	console_list_unlock();
35777c2af0f6SJohn Ogness 
3578fbc92a34SKay Sievers 	return count;
3579fbc92a34SKay Sievers }
3580fbc92a34SKay Sievers static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
3581fbc92a34SKay Sievers 
35821083a7beSTakashi Iwai static struct attribute *cons_dev_attrs[] = {
35831083a7beSTakashi Iwai 	&dev_attr_active.attr,
35841083a7beSTakashi Iwai 	NULL
35851083a7beSTakashi Iwai };
35861083a7beSTakashi Iwai 
35871083a7beSTakashi Iwai ATTRIBUTE_GROUPS(cons_dev);
35881083a7beSTakashi Iwai 
3589fbc92a34SKay Sievers static struct device *consdev;
3590fbc92a34SKay Sievers 
console_sysfs_notify(void)3591fbc92a34SKay Sievers void console_sysfs_notify(void)
3592fbc92a34SKay Sievers {
3593fbc92a34SKay Sievers 	if (consdev)
3594fbc92a34SKay Sievers 		sysfs_notify(&consdev->kobj, NULL, "active");
3595fbc92a34SKay Sievers }
3596fbc92a34SKay Sievers 
35975fd8c2d3SKees Cook static struct ctl_table tty_table[] = {
35985fd8c2d3SKees Cook 	{
359983efeeebSKees Cook 		.procname	= "legacy_tiocsti",
360083efeeebSKees Cook 		.data		= &tty_legacy_tiocsti,
360183efeeebSKees Cook 		.maxlen		= sizeof(tty_legacy_tiocsti),
360283efeeebSKees Cook 		.mode		= 0644,
360383efeeebSKees Cook 		.proc_handler	= proc_dobool,
360483efeeebSKees Cook 	},
360583efeeebSKees Cook 	{
36065fd8c2d3SKees Cook 		.procname	= "ldisc_autoload",
36075fd8c2d3SKees Cook 		.data		= &tty_ldisc_autoload,
36085fd8c2d3SKees Cook 		.maxlen		= sizeof(tty_ldisc_autoload),
36095fd8c2d3SKees Cook 		.mode		= 0644,
36105fd8c2d3SKees Cook 		.proc_handler	= proc_dointvec,
36115fd8c2d3SKees Cook 		.extra1		= SYSCTL_ZERO,
36125fd8c2d3SKees Cook 		.extra2		= SYSCTL_ONE,
36135fd8c2d3SKees Cook 	},
36145fd8c2d3SKees Cook 	{ }
36155fd8c2d3SKees Cook };
36165fd8c2d3SKees Cook 
361796fd7ce5SGreg Kroah-Hartman /*
361896fd7ce5SGreg Kroah-Hartman  * Ok, now we can initialize the rest of the tty devices and can count
361996fd7ce5SGreg Kroah-Hartman  * on memory allocations, interrupts etc..
362096fd7ce5SGreg Kroah-Hartman  */
tty_init(void)362196fd7ce5SGreg Kroah-Hartman int __init tty_init(void)
362296fd7ce5SGreg Kroah-Hartman {
36236d12b774SLuis Chamberlain 	register_sysctl_init("dev/tty", tty_table);
362496fd7ce5SGreg Kroah-Hartman 	cdev_init(&tty_cdev, &tty_fops);
362596fd7ce5SGreg Kroah-Hartman 	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
362696fd7ce5SGreg Kroah-Hartman 	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
362796fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register /dev/tty driver\n");
3628862d8312SGreg Kroah-Hartman 	device_create(&tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
362996fd7ce5SGreg Kroah-Hartman 
363096fd7ce5SGreg Kroah-Hartman 	cdev_init(&console_cdev, &console_fops);
363196fd7ce5SGreg Kroah-Hartman 	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
363296fd7ce5SGreg Kroah-Hartman 	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
363396fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register /dev/console driver\n");
3634862d8312SGreg Kroah-Hartman 	consdev = device_create_with_groups(&tty_class, NULL,
36351083a7beSTakashi Iwai 					    MKDEV(TTYAUX_MAJOR, 1), NULL,
36361083a7beSTakashi Iwai 					    cons_dev_groups, "console");
3637fbc92a34SKay Sievers 	if (IS_ERR(consdev))
3638fbc92a34SKay Sievers 		consdev = NULL;
363996fd7ce5SGreg Kroah-Hartman 
364096fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_VT
364196fd7ce5SGreg Kroah-Hartman 	vty_init(&console_fops);
364296fd7ce5SGreg Kroah-Hartman #endif
364396fd7ce5SGreg Kroah-Hartman 	return 0;
364496fd7ce5SGreg Kroah-Hartman }
3645