xref: /openbmc/linux/drivers/tty/tty_io.c (revision 4f98d467)
196fd7ce5SGreg Kroah-Hartman /*
296fd7ce5SGreg Kroah-Hartman  *  Copyright (C) 1991, 1992  Linus Torvalds
396fd7ce5SGreg Kroah-Hartman  */
496fd7ce5SGreg Kroah-Hartman 
596fd7ce5SGreg Kroah-Hartman /*
696fd7ce5SGreg Kroah-Hartman  * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
796fd7ce5SGreg Kroah-Hartman  * or rs-channels. It also implements echoing, cooked mode etc.
896fd7ce5SGreg Kroah-Hartman  *
996fd7ce5SGreg Kroah-Hartman  * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
1096fd7ce5SGreg Kroah-Hartman  *
1196fd7ce5SGreg Kroah-Hartman  * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
1296fd7ce5SGreg Kroah-Hartman  * tty_struct and tty_queue structures.  Previously there was an array
1396fd7ce5SGreg Kroah-Hartman  * of 256 tty_struct's which was statically allocated, and the
1496fd7ce5SGreg Kroah-Hartman  * tty_queue structures were allocated at boot time.  Both are now
1596fd7ce5SGreg Kroah-Hartman  * dynamically allocated only when the tty is open.
1696fd7ce5SGreg Kroah-Hartman  *
1796fd7ce5SGreg Kroah-Hartman  * Also restructured routines so that there is more of a separation
1896fd7ce5SGreg Kroah-Hartman  * between the high-level tty routines (tty_io.c and tty_ioctl.c) and
1996fd7ce5SGreg Kroah-Hartman  * the low-level tty routines (serial.c, pty.c, console.c).  This
2096fd7ce5SGreg Kroah-Hartman  * makes for cleaner and more compact code.  -TYT, 9/17/92
2196fd7ce5SGreg Kroah-Hartman  *
2296fd7ce5SGreg Kroah-Hartman  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
2396fd7ce5SGreg Kroah-Hartman  * which can be dynamically activated and de-activated by the line
2496fd7ce5SGreg Kroah-Hartman  * discipline handling modules (like SLIP).
2596fd7ce5SGreg Kroah-Hartman  *
2696fd7ce5SGreg Kroah-Hartman  * NOTE: pay no attention to the line discipline code (yet); its
2796fd7ce5SGreg Kroah-Hartman  * interface is still subject to change in this version...
2896fd7ce5SGreg Kroah-Hartman  * -- TYT, 1/31/92
2996fd7ce5SGreg Kroah-Hartman  *
3096fd7ce5SGreg Kroah-Hartman  * Added functionality to the OPOST tty handling.  No delays, but all
3196fd7ce5SGreg Kroah-Hartman  * other bits should be there.
3296fd7ce5SGreg Kroah-Hartman  *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
3396fd7ce5SGreg Kroah-Hartman  *
3496fd7ce5SGreg Kroah-Hartman  * Rewrote canonical mode and added more termios flags.
3596fd7ce5SGreg Kroah-Hartman  * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
3696fd7ce5SGreg Kroah-Hartman  *
3796fd7ce5SGreg Kroah-Hartman  * Reorganized FASYNC support so mouse code can share it.
3896fd7ce5SGreg Kroah-Hartman  *	-- ctm@ardi.com, 9Sep95
3996fd7ce5SGreg Kroah-Hartman  *
4096fd7ce5SGreg Kroah-Hartman  * New TIOCLINUX variants added.
4196fd7ce5SGreg Kroah-Hartman  *	-- mj@k332.feld.cvut.cz, 19-Nov-95
4296fd7ce5SGreg Kroah-Hartman  *
4396fd7ce5SGreg Kroah-Hartman  * Restrict vt switching via ioctl()
4496fd7ce5SGreg Kroah-Hartman  *      -- grif@cs.ucr.edu, 5-Dec-95
4596fd7ce5SGreg Kroah-Hartman  *
4696fd7ce5SGreg Kroah-Hartman  * Move console and virtual terminal code to more appropriate files,
4796fd7ce5SGreg Kroah-Hartman  * implement CONFIG_VT and generalize console device interface.
4896fd7ce5SGreg Kroah-Hartman  *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97
4996fd7ce5SGreg Kroah-Hartman  *
5096fd7ce5SGreg Kroah-Hartman  * Rewrote tty_init_dev and tty_release_dev to eliminate races.
5196fd7ce5SGreg Kroah-Hartman  *	-- Bill Hawes <whawes@star.net>, June 97
5296fd7ce5SGreg Kroah-Hartman  *
5396fd7ce5SGreg Kroah-Hartman  * Added devfs support.
5496fd7ce5SGreg Kroah-Hartman  *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998
5596fd7ce5SGreg Kroah-Hartman  *
5696fd7ce5SGreg Kroah-Hartman  * Added support for a Unix98-style ptmx device.
5796fd7ce5SGreg Kroah-Hartman  *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
5896fd7ce5SGreg Kroah-Hartman  *
5996fd7ce5SGreg Kroah-Hartman  * Reduced memory usage for older ARM systems
6096fd7ce5SGreg Kroah-Hartman  *      -- Russell King <rmk@arm.linux.org.uk>
6196fd7ce5SGreg Kroah-Hartman  *
6296fd7ce5SGreg Kroah-Hartman  * Move do_SAK() into process context.  Less stack use in devfs functions.
6396fd7ce5SGreg Kroah-Hartman  * alloc_tty_struct() always uses kmalloc()
6496fd7ce5SGreg Kroah-Hartman  *			 -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01
6596fd7ce5SGreg Kroah-Hartman  */
6696fd7ce5SGreg Kroah-Hartman 
6796fd7ce5SGreg Kroah-Hartman #include <linux/types.h>
6896fd7ce5SGreg Kroah-Hartman #include <linux/major.h>
6996fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
7096fd7ce5SGreg Kroah-Hartman #include <linux/signal.h>
7196fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
7296fd7ce5SGreg Kroah-Hartman #include <linux/sched.h>
7396fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h>
7496fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
7596fd7ce5SGreg Kroah-Hartman #include <linux/tty_driver.h>
7696fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h>
7796fd7ce5SGreg Kroah-Hartman #include <linux/devpts_fs.h>
7896fd7ce5SGreg Kroah-Hartman #include <linux/file.h>
7996fd7ce5SGreg Kroah-Hartman #include <linux/fdtable.h>
8096fd7ce5SGreg Kroah-Hartman #include <linux/console.h>
8196fd7ce5SGreg Kroah-Hartman #include <linux/timer.h>
8296fd7ce5SGreg Kroah-Hartman #include <linux/ctype.h>
8396fd7ce5SGreg Kroah-Hartman #include <linux/kd.h>
8496fd7ce5SGreg Kroah-Hartman #include <linux/mm.h>
8596fd7ce5SGreg Kroah-Hartman #include <linux/string.h>
8696fd7ce5SGreg Kroah-Hartman #include <linux/slab.h>
8796fd7ce5SGreg Kroah-Hartman #include <linux/poll.h>
8896fd7ce5SGreg Kroah-Hartman #include <linux/proc_fs.h>
8996fd7ce5SGreg Kroah-Hartman #include <linux/init.h>
9096fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
9196fd7ce5SGreg Kroah-Hartman #include <linux/device.h>
9296fd7ce5SGreg Kroah-Hartman #include <linux/wait.h>
9396fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
9496fd7ce5SGreg Kroah-Hartman #include <linux/delay.h>
9596fd7ce5SGreg Kroah-Hartman #include <linux/seq_file.h>
9696fd7ce5SGreg Kroah-Hartman #include <linux/serial.h>
975a3c6b25SManuel Zerpies #include <linux/ratelimit.h>
9896fd7ce5SGreg Kroah-Hartman 
9996fd7ce5SGreg Kroah-Hartman #include <linux/uaccess.h>
10096fd7ce5SGreg Kroah-Hartman 
10196fd7ce5SGreg Kroah-Hartman #include <linux/kbd_kern.h>
10296fd7ce5SGreg Kroah-Hartman #include <linux/vt_kern.h>
10396fd7ce5SGreg Kroah-Hartman #include <linux/selection.h>
10496fd7ce5SGreg Kroah-Hartman 
10596fd7ce5SGreg Kroah-Hartman #include <linux/kmod.h>
10696fd7ce5SGreg Kroah-Hartman #include <linux/nsproxy.h>
10796fd7ce5SGreg Kroah-Hartman 
10896fd7ce5SGreg Kroah-Hartman #undef TTY_DEBUG_HANGUP
10996fd7ce5SGreg Kroah-Hartman 
11096fd7ce5SGreg Kroah-Hartman #define TTY_PARANOIA_CHECK 1
11196fd7ce5SGreg Kroah-Hartman #define CHECK_TTY_COUNT 1
11296fd7ce5SGreg Kroah-Hartman 
11396fd7ce5SGreg Kroah-Hartman struct ktermios tty_std_termios = {	/* for the benefit of tty drivers  */
11496fd7ce5SGreg Kroah-Hartman 	.c_iflag = ICRNL | IXON,
11596fd7ce5SGreg Kroah-Hartman 	.c_oflag = OPOST | ONLCR,
11696fd7ce5SGreg Kroah-Hartman 	.c_cflag = B38400 | CS8 | CREAD | HUPCL,
11796fd7ce5SGreg Kroah-Hartman 	.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
11896fd7ce5SGreg Kroah-Hartman 		   ECHOCTL | ECHOKE | IEXTEN,
11996fd7ce5SGreg Kroah-Hartman 	.c_cc = INIT_C_CC,
12096fd7ce5SGreg Kroah-Hartman 	.c_ispeed = 38400,
12196fd7ce5SGreg Kroah-Hartman 	.c_ospeed = 38400
12296fd7ce5SGreg Kroah-Hartman };
12396fd7ce5SGreg Kroah-Hartman 
12496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_std_termios);
12596fd7ce5SGreg Kroah-Hartman 
12696fd7ce5SGreg Kroah-Hartman /* This list gets poked at by procfs and various bits of boot up code. This
12796fd7ce5SGreg Kroah-Hartman    could do with some rationalisation such as pulling the tty proc function
12896fd7ce5SGreg Kroah-Hartman    into this file */
12996fd7ce5SGreg Kroah-Hartman 
13096fd7ce5SGreg Kroah-Hartman LIST_HEAD(tty_drivers);			/* linked list of tty drivers */
13196fd7ce5SGreg Kroah-Hartman 
13296fd7ce5SGreg Kroah-Hartman /* Mutex to protect creating and releasing a tty. This is shared with
13396fd7ce5SGreg Kroah-Hartman    vt.c for deeply disgusting hack reasons */
13496fd7ce5SGreg Kroah-Hartman DEFINE_MUTEX(tty_mutex);
13596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_mutex);
13696fd7ce5SGreg Kroah-Hartman 
13796fd7ce5SGreg Kroah-Hartman /* Spinlock to protect the tty->tty_files list */
13896fd7ce5SGreg Kroah-Hartman DEFINE_SPINLOCK(tty_files_lock);
13996fd7ce5SGreg Kroah-Hartman 
14096fd7ce5SGreg Kroah-Hartman static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
14196fd7ce5SGreg Kroah-Hartman static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
14296fd7ce5SGreg Kroah-Hartman ssize_t redirected_tty_write(struct file *, const char __user *,
14396fd7ce5SGreg Kroah-Hartman 							size_t, loff_t *);
14496fd7ce5SGreg Kroah-Hartman static unsigned int tty_poll(struct file *, poll_table *);
14596fd7ce5SGreg Kroah-Hartman static int tty_open(struct inode *, struct file *);
14696fd7ce5SGreg Kroah-Hartman long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
14796fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_COMPAT
14896fd7ce5SGreg Kroah-Hartman static long tty_compat_ioctl(struct file *file, unsigned int cmd,
14996fd7ce5SGreg Kroah-Hartman 				unsigned long arg);
15096fd7ce5SGreg Kroah-Hartman #else
15196fd7ce5SGreg Kroah-Hartman #define tty_compat_ioctl NULL
15296fd7ce5SGreg Kroah-Hartman #endif
15396fd7ce5SGreg Kroah-Hartman static int __tty_fasync(int fd, struct file *filp, int on);
15496fd7ce5SGreg Kroah-Hartman static int tty_fasync(int fd, struct file *filp, int on);
15596fd7ce5SGreg Kroah-Hartman static void release_tty(struct tty_struct *tty, int idx);
15696fd7ce5SGreg Kroah-Hartman static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
15796fd7ce5SGreg Kroah-Hartman static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
15896fd7ce5SGreg Kroah-Hartman 
15996fd7ce5SGreg Kroah-Hartman /**
16096fd7ce5SGreg Kroah-Hartman  *	alloc_tty_struct	-	allocate a tty object
16196fd7ce5SGreg Kroah-Hartman  *
16296fd7ce5SGreg Kroah-Hartman  *	Return a new empty tty structure. The data fields have not
16396fd7ce5SGreg Kroah-Hartman  *	been initialized in any way but has been zeroed
16496fd7ce5SGreg Kroah-Hartman  *
16596fd7ce5SGreg Kroah-Hartman  *	Locking: none
16696fd7ce5SGreg Kroah-Hartman  */
16796fd7ce5SGreg Kroah-Hartman 
16896fd7ce5SGreg Kroah-Hartman struct tty_struct *alloc_tty_struct(void)
16996fd7ce5SGreg Kroah-Hartman {
17096fd7ce5SGreg Kroah-Hartman 	return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
17196fd7ce5SGreg Kroah-Hartman }
17296fd7ce5SGreg Kroah-Hartman 
17396fd7ce5SGreg Kroah-Hartman /**
17496fd7ce5SGreg Kroah-Hartman  *	free_tty_struct		-	free a disused tty
17596fd7ce5SGreg Kroah-Hartman  *	@tty: tty struct to free
17696fd7ce5SGreg Kroah-Hartman  *
17796fd7ce5SGreg Kroah-Hartman  *	Free the write buffers, tty queue and tty memory itself.
17896fd7ce5SGreg Kroah-Hartman  *
17996fd7ce5SGreg Kroah-Hartman  *	Locking: none. Must be called after tty is definitely unused
18096fd7ce5SGreg Kroah-Hartman  */
18196fd7ce5SGreg Kroah-Hartman 
18296fd7ce5SGreg Kroah-Hartman void free_tty_struct(struct tty_struct *tty)
18396fd7ce5SGreg Kroah-Hartman {
184dc6802a7SDan Carpenter 	if (!tty)
185dc6802a7SDan Carpenter 		return;
18696fd7ce5SGreg Kroah-Hartman 	if (tty->dev)
18796fd7ce5SGreg Kroah-Hartman 		put_device(tty->dev);
18896fd7ce5SGreg Kroah-Hartman 	kfree(tty->write_buf);
18989c8d91eSAlan Cox 	tty->magic = 0xDEADDEAD;
19096fd7ce5SGreg Kroah-Hartman 	kfree(tty);
19196fd7ce5SGreg Kroah-Hartman }
19296fd7ce5SGreg Kroah-Hartman 
19396fd7ce5SGreg Kroah-Hartman static inline struct tty_struct *file_tty(struct file *file)
19496fd7ce5SGreg Kroah-Hartman {
19596fd7ce5SGreg Kroah-Hartman 	return ((struct tty_file_private *)file->private_data)->tty;
19696fd7ce5SGreg Kroah-Hartman }
19796fd7ce5SGreg Kroah-Hartman 
198fa90e1c9SJiri Slaby int tty_alloc_file(struct file *file)
19996fd7ce5SGreg Kroah-Hartman {
20096fd7ce5SGreg Kroah-Hartman 	struct tty_file_private *priv;
20196fd7ce5SGreg Kroah-Hartman 
20296fd7ce5SGreg Kroah-Hartman 	priv = kmalloc(sizeof(*priv), GFP_KERNEL);
20396fd7ce5SGreg Kroah-Hartman 	if (!priv)
20496fd7ce5SGreg Kroah-Hartman 		return -ENOMEM;
20596fd7ce5SGreg Kroah-Hartman 
206fa90e1c9SJiri Slaby 	file->private_data = priv;
207fa90e1c9SJiri Slaby 
208fa90e1c9SJiri Slaby 	return 0;
209fa90e1c9SJiri Slaby }
210fa90e1c9SJiri Slaby 
211fa90e1c9SJiri Slaby /* Associate a new file with the tty structure */
212fa90e1c9SJiri Slaby void tty_add_file(struct tty_struct *tty, struct file *file)
213fa90e1c9SJiri Slaby {
214fa90e1c9SJiri Slaby 	struct tty_file_private *priv = file->private_data;
215fa90e1c9SJiri Slaby 
21696fd7ce5SGreg Kroah-Hartman 	priv->tty = tty;
21796fd7ce5SGreg Kroah-Hartman 	priv->file = file;
21896fd7ce5SGreg Kroah-Hartman 
21996fd7ce5SGreg Kroah-Hartman 	spin_lock(&tty_files_lock);
22096fd7ce5SGreg Kroah-Hartman 	list_add(&priv->list, &tty->tty_files);
22196fd7ce5SGreg Kroah-Hartman 	spin_unlock(&tty_files_lock);
222fa90e1c9SJiri Slaby }
22396fd7ce5SGreg Kroah-Hartman 
224fa90e1c9SJiri Slaby /**
225fa90e1c9SJiri Slaby  * tty_free_file - free file->private_data
226fa90e1c9SJiri Slaby  *
227fa90e1c9SJiri Slaby  * This shall be used only for fail path handling when tty_add_file was not
228fa90e1c9SJiri Slaby  * called yet.
229fa90e1c9SJiri Slaby  */
230fa90e1c9SJiri Slaby void tty_free_file(struct file *file)
231fa90e1c9SJiri Slaby {
232fa90e1c9SJiri Slaby 	struct tty_file_private *priv = file->private_data;
233fa90e1c9SJiri Slaby 
234fa90e1c9SJiri Slaby 	file->private_data = NULL;
235fa90e1c9SJiri Slaby 	kfree(priv);
23696fd7ce5SGreg Kroah-Hartman }
23796fd7ce5SGreg Kroah-Hartman 
23896fd7ce5SGreg Kroah-Hartman /* Delete file from its tty */
2392520e274SJosh Triplett static void tty_del_file(struct file *file)
24096fd7ce5SGreg Kroah-Hartman {
24196fd7ce5SGreg Kroah-Hartman 	struct tty_file_private *priv = file->private_data;
24296fd7ce5SGreg Kroah-Hartman 
24396fd7ce5SGreg Kroah-Hartman 	spin_lock(&tty_files_lock);
24496fd7ce5SGreg Kroah-Hartman 	list_del(&priv->list);
24596fd7ce5SGreg Kroah-Hartman 	spin_unlock(&tty_files_lock);
246fa90e1c9SJiri Slaby 	tty_free_file(file);
24796fd7ce5SGreg Kroah-Hartman }
24896fd7ce5SGreg Kroah-Hartman 
24996fd7ce5SGreg Kroah-Hartman 
25096fd7ce5SGreg Kroah-Hartman #define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)
25196fd7ce5SGreg Kroah-Hartman 
25296fd7ce5SGreg Kroah-Hartman /**
25396fd7ce5SGreg Kroah-Hartman  *	tty_name	-	return tty naming
25496fd7ce5SGreg Kroah-Hartman  *	@tty: tty structure
25596fd7ce5SGreg Kroah-Hartman  *	@buf: buffer for output
25696fd7ce5SGreg Kroah-Hartman  *
25796fd7ce5SGreg Kroah-Hartman  *	Convert a tty structure into a name. The name reflects the kernel
25896fd7ce5SGreg Kroah-Hartman  *	naming policy and if udev is in use may not reflect user space
25996fd7ce5SGreg Kroah-Hartman  *
26096fd7ce5SGreg Kroah-Hartman  *	Locking: none
26196fd7ce5SGreg Kroah-Hartman  */
26296fd7ce5SGreg Kroah-Hartman 
26396fd7ce5SGreg Kroah-Hartman char *tty_name(struct tty_struct *tty, char *buf)
26496fd7ce5SGreg Kroah-Hartman {
26596fd7ce5SGreg Kroah-Hartman 	if (!tty) /* Hmm.  NULL pointer.  That's fun. */
26696fd7ce5SGreg Kroah-Hartman 		strcpy(buf, "NULL tty");
26796fd7ce5SGreg Kroah-Hartman 	else
26896fd7ce5SGreg Kroah-Hartman 		strcpy(buf, tty->name);
26996fd7ce5SGreg Kroah-Hartman 	return buf;
27096fd7ce5SGreg Kroah-Hartman }
27196fd7ce5SGreg Kroah-Hartman 
27296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_name);
27396fd7ce5SGreg Kroah-Hartman 
27496fd7ce5SGreg Kroah-Hartman int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
27596fd7ce5SGreg Kroah-Hartman 			      const char *routine)
27696fd7ce5SGreg Kroah-Hartman {
27796fd7ce5SGreg Kroah-Hartman #ifdef TTY_PARANOIA_CHECK
27896fd7ce5SGreg Kroah-Hartman 	if (!tty) {
27996fd7ce5SGreg Kroah-Hartman 		printk(KERN_WARNING
28096fd7ce5SGreg Kroah-Hartman 			"null TTY for (%d:%d) in %s\n",
28196fd7ce5SGreg Kroah-Hartman 			imajor(inode), iminor(inode), routine);
28296fd7ce5SGreg Kroah-Hartman 		return 1;
28396fd7ce5SGreg Kroah-Hartman 	}
28496fd7ce5SGreg Kroah-Hartman 	if (tty->magic != TTY_MAGIC) {
28596fd7ce5SGreg Kroah-Hartman 		printk(KERN_WARNING
28696fd7ce5SGreg Kroah-Hartman 			"bad magic number for tty struct (%d:%d) in %s\n",
28796fd7ce5SGreg Kroah-Hartman 			imajor(inode), iminor(inode), routine);
28896fd7ce5SGreg Kroah-Hartman 		return 1;
28996fd7ce5SGreg Kroah-Hartman 	}
29096fd7ce5SGreg Kroah-Hartman #endif
29196fd7ce5SGreg Kroah-Hartman 	return 0;
29296fd7ce5SGreg Kroah-Hartman }
29396fd7ce5SGreg Kroah-Hartman 
29496fd7ce5SGreg Kroah-Hartman static int check_tty_count(struct tty_struct *tty, const char *routine)
29596fd7ce5SGreg Kroah-Hartman {
29696fd7ce5SGreg Kroah-Hartman #ifdef CHECK_TTY_COUNT
29796fd7ce5SGreg Kroah-Hartman 	struct list_head *p;
29896fd7ce5SGreg Kroah-Hartman 	int count = 0;
29996fd7ce5SGreg Kroah-Hartman 
30096fd7ce5SGreg Kroah-Hartman 	spin_lock(&tty_files_lock);
30196fd7ce5SGreg Kroah-Hartman 	list_for_each(p, &tty->tty_files) {
30296fd7ce5SGreg Kroah-Hartman 		count++;
30396fd7ce5SGreg Kroah-Hartman 	}
30496fd7ce5SGreg Kroah-Hartman 	spin_unlock(&tty_files_lock);
30596fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
30696fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_SLAVE &&
30796fd7ce5SGreg Kroah-Hartman 	    tty->link && tty->link->count)
30896fd7ce5SGreg Kroah-Hartman 		count++;
30996fd7ce5SGreg Kroah-Hartman 	if (tty->count != count) {
31096fd7ce5SGreg Kroah-Hartman 		printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
31196fd7ce5SGreg Kroah-Hartman 				    "!= #fd's(%d) in %s\n",
31296fd7ce5SGreg Kroah-Hartman 		       tty->name, tty->count, count, routine);
31396fd7ce5SGreg Kroah-Hartman 		return count;
31496fd7ce5SGreg Kroah-Hartman 	}
31596fd7ce5SGreg Kroah-Hartman #endif
31696fd7ce5SGreg Kroah-Hartman 	return 0;
31796fd7ce5SGreg Kroah-Hartman }
31896fd7ce5SGreg Kroah-Hartman 
31996fd7ce5SGreg Kroah-Hartman /**
32096fd7ce5SGreg Kroah-Hartman  *	get_tty_driver		-	find device of a tty
32196fd7ce5SGreg Kroah-Hartman  *	@dev_t: device identifier
32296fd7ce5SGreg Kroah-Hartman  *	@index: returns the index of the tty
32396fd7ce5SGreg Kroah-Hartman  *
32496fd7ce5SGreg Kroah-Hartman  *	This routine returns a tty driver structure, given a device number
32596fd7ce5SGreg Kroah-Hartman  *	and also passes back the index number.
32696fd7ce5SGreg Kroah-Hartman  *
32796fd7ce5SGreg Kroah-Hartman  *	Locking: caller must hold tty_mutex
32896fd7ce5SGreg Kroah-Hartman  */
32996fd7ce5SGreg Kroah-Hartman 
33096fd7ce5SGreg Kroah-Hartman static struct tty_driver *get_tty_driver(dev_t device, int *index)
33196fd7ce5SGreg Kroah-Hartman {
33296fd7ce5SGreg Kroah-Hartman 	struct tty_driver *p;
33396fd7ce5SGreg Kroah-Hartman 
33496fd7ce5SGreg Kroah-Hartman 	list_for_each_entry(p, &tty_drivers, tty_drivers) {
33596fd7ce5SGreg Kroah-Hartman 		dev_t base = MKDEV(p->major, p->minor_start);
33696fd7ce5SGreg Kroah-Hartman 		if (device < base || device >= base + p->num)
33796fd7ce5SGreg Kroah-Hartman 			continue;
33896fd7ce5SGreg Kroah-Hartman 		*index = device - base;
33996fd7ce5SGreg Kroah-Hartman 		return tty_driver_kref_get(p);
34096fd7ce5SGreg Kroah-Hartman 	}
34196fd7ce5SGreg Kroah-Hartman 	return NULL;
34296fd7ce5SGreg Kroah-Hartman }
34396fd7ce5SGreg Kroah-Hartman 
34496fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_CONSOLE_POLL
34596fd7ce5SGreg Kroah-Hartman 
34696fd7ce5SGreg Kroah-Hartman /**
34796fd7ce5SGreg Kroah-Hartman  *	tty_find_polling_driver	-	find device of a polled tty
34896fd7ce5SGreg Kroah-Hartman  *	@name: name string to match
34996fd7ce5SGreg Kroah-Hartman  *	@line: pointer to resulting tty line nr
35096fd7ce5SGreg Kroah-Hartman  *
35196fd7ce5SGreg Kroah-Hartman  *	This routine returns a tty driver structure, given a name
35296fd7ce5SGreg Kroah-Hartman  *	and the condition that the tty driver is capable of polled
35396fd7ce5SGreg Kroah-Hartman  *	operation.
35496fd7ce5SGreg Kroah-Hartman  */
35596fd7ce5SGreg Kroah-Hartman struct tty_driver *tty_find_polling_driver(char *name, int *line)
35696fd7ce5SGreg Kroah-Hartman {
35796fd7ce5SGreg Kroah-Hartman 	struct tty_driver *p, *res = NULL;
35896fd7ce5SGreg Kroah-Hartman 	int tty_line = 0;
35996fd7ce5SGreg Kroah-Hartman 	int len;
36096fd7ce5SGreg Kroah-Hartman 	char *str, *stp;
36196fd7ce5SGreg Kroah-Hartman 
36296fd7ce5SGreg Kroah-Hartman 	for (str = name; *str; str++)
36396fd7ce5SGreg Kroah-Hartman 		if ((*str >= '0' && *str <= '9') || *str == ',')
36496fd7ce5SGreg Kroah-Hartman 			break;
36596fd7ce5SGreg Kroah-Hartman 	if (!*str)
36696fd7ce5SGreg Kroah-Hartman 		return NULL;
36796fd7ce5SGreg Kroah-Hartman 
36896fd7ce5SGreg Kroah-Hartman 	len = str - name;
36996fd7ce5SGreg Kroah-Hartman 	tty_line = simple_strtoul(str, &str, 10);
37096fd7ce5SGreg Kroah-Hartman 
37196fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
37296fd7ce5SGreg Kroah-Hartman 	/* Search through the tty devices to look for a match */
37396fd7ce5SGreg Kroah-Hartman 	list_for_each_entry(p, &tty_drivers, tty_drivers) {
37496fd7ce5SGreg Kroah-Hartman 		if (strncmp(name, p->name, len) != 0)
37596fd7ce5SGreg Kroah-Hartman 			continue;
37696fd7ce5SGreg Kroah-Hartman 		stp = str;
37796fd7ce5SGreg Kroah-Hartman 		if (*stp == ',')
37896fd7ce5SGreg Kroah-Hartman 			stp++;
37996fd7ce5SGreg Kroah-Hartman 		if (*stp == '\0')
38096fd7ce5SGreg Kroah-Hartman 			stp = NULL;
38196fd7ce5SGreg Kroah-Hartman 
38296fd7ce5SGreg Kroah-Hartman 		if (tty_line >= 0 && tty_line < p->num && p->ops &&
38396fd7ce5SGreg Kroah-Hartman 		    p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
38496fd7ce5SGreg Kroah-Hartman 			res = tty_driver_kref_get(p);
38596fd7ce5SGreg Kroah-Hartman 			*line = tty_line;
38696fd7ce5SGreg Kroah-Hartman 			break;
38796fd7ce5SGreg Kroah-Hartman 		}
38896fd7ce5SGreg Kroah-Hartman 	}
38996fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
39096fd7ce5SGreg Kroah-Hartman 
39196fd7ce5SGreg Kroah-Hartman 	return res;
39296fd7ce5SGreg Kroah-Hartman }
39396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_find_polling_driver);
39496fd7ce5SGreg Kroah-Hartman #endif
39596fd7ce5SGreg Kroah-Hartman 
39696fd7ce5SGreg Kroah-Hartman /**
39796fd7ce5SGreg Kroah-Hartman  *	tty_check_change	-	check for POSIX terminal changes
39896fd7ce5SGreg Kroah-Hartman  *	@tty: tty to check
39996fd7ce5SGreg Kroah-Hartman  *
40096fd7ce5SGreg Kroah-Hartman  *	If we try to write to, or set the state of, a terminal and we're
40196fd7ce5SGreg Kroah-Hartman  *	not in the foreground, send a SIGTTOU.  If the signal is blocked or
40296fd7ce5SGreg Kroah-Hartman  *	ignored, go ahead and perform the operation.  (POSIX 7.2)
40396fd7ce5SGreg Kroah-Hartman  *
40496fd7ce5SGreg Kroah-Hartman  *	Locking: ctrl_lock
40596fd7ce5SGreg Kroah-Hartman  */
40696fd7ce5SGreg Kroah-Hartman 
40796fd7ce5SGreg Kroah-Hartman int tty_check_change(struct tty_struct *tty)
40896fd7ce5SGreg Kroah-Hartman {
40996fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
41096fd7ce5SGreg Kroah-Hartman 	int ret = 0;
41196fd7ce5SGreg Kroah-Hartman 
41296fd7ce5SGreg Kroah-Hartman 	if (current->signal->tty != tty)
41396fd7ce5SGreg Kroah-Hartman 		return 0;
41496fd7ce5SGreg Kroah-Hartman 
41596fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&tty->ctrl_lock, flags);
41696fd7ce5SGreg Kroah-Hartman 
41796fd7ce5SGreg Kroah-Hartman 	if (!tty->pgrp) {
41896fd7ce5SGreg Kroah-Hartman 		printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
41996fd7ce5SGreg Kroah-Hartman 		goto out_unlock;
42096fd7ce5SGreg Kroah-Hartman 	}
42196fd7ce5SGreg Kroah-Hartman 	if (task_pgrp(current) == tty->pgrp)
42296fd7ce5SGreg Kroah-Hartman 		goto out_unlock;
42396fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
42496fd7ce5SGreg Kroah-Hartman 	if (is_ignored(SIGTTOU))
42596fd7ce5SGreg Kroah-Hartman 		goto out;
42696fd7ce5SGreg Kroah-Hartman 	if (is_current_pgrp_orphaned()) {
42796fd7ce5SGreg Kroah-Hartman 		ret = -EIO;
42896fd7ce5SGreg Kroah-Hartman 		goto out;
42996fd7ce5SGreg Kroah-Hartman 	}
43096fd7ce5SGreg Kroah-Hartman 	kill_pgrp(task_pgrp(current), SIGTTOU, 1);
43196fd7ce5SGreg Kroah-Hartman 	set_thread_flag(TIF_SIGPENDING);
43296fd7ce5SGreg Kroah-Hartman 	ret = -ERESTARTSYS;
43396fd7ce5SGreg Kroah-Hartman out:
43496fd7ce5SGreg Kroah-Hartman 	return ret;
43596fd7ce5SGreg Kroah-Hartman out_unlock:
43696fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
43796fd7ce5SGreg Kroah-Hartman 	return ret;
43896fd7ce5SGreg Kroah-Hartman }
43996fd7ce5SGreg Kroah-Hartman 
44096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_check_change);
44196fd7ce5SGreg Kroah-Hartman 
44296fd7ce5SGreg Kroah-Hartman static ssize_t hung_up_tty_read(struct file *file, char __user *buf,
44396fd7ce5SGreg Kroah-Hartman 				size_t count, loff_t *ppos)
44496fd7ce5SGreg Kroah-Hartman {
44596fd7ce5SGreg Kroah-Hartman 	return 0;
44696fd7ce5SGreg Kroah-Hartman }
44796fd7ce5SGreg Kroah-Hartman 
44896fd7ce5SGreg Kroah-Hartman static ssize_t hung_up_tty_write(struct file *file, const char __user *buf,
44996fd7ce5SGreg Kroah-Hartman 				 size_t count, loff_t *ppos)
45096fd7ce5SGreg Kroah-Hartman {
45196fd7ce5SGreg Kroah-Hartman 	return -EIO;
45296fd7ce5SGreg Kroah-Hartman }
45396fd7ce5SGreg Kroah-Hartman 
45496fd7ce5SGreg Kroah-Hartman /* No kernel lock held - none needed ;) */
45596fd7ce5SGreg Kroah-Hartman static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
45696fd7ce5SGreg Kroah-Hartman {
45796fd7ce5SGreg Kroah-Hartman 	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
45896fd7ce5SGreg Kroah-Hartman }
45996fd7ce5SGreg Kroah-Hartman 
46096fd7ce5SGreg Kroah-Hartman static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
46196fd7ce5SGreg Kroah-Hartman 		unsigned long arg)
46296fd7ce5SGreg Kroah-Hartman {
46396fd7ce5SGreg Kroah-Hartman 	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
46496fd7ce5SGreg Kroah-Hartman }
46596fd7ce5SGreg Kroah-Hartman 
46696fd7ce5SGreg Kroah-Hartman static long hung_up_tty_compat_ioctl(struct file *file,
46796fd7ce5SGreg Kroah-Hartman 				     unsigned int cmd, unsigned long arg)
46896fd7ce5SGreg Kroah-Hartman {
46996fd7ce5SGreg Kroah-Hartman 	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
47096fd7ce5SGreg Kroah-Hartman }
47196fd7ce5SGreg Kroah-Hartman 
47296fd7ce5SGreg Kroah-Hartman static const struct file_operations tty_fops = {
47396fd7ce5SGreg Kroah-Hartman 	.llseek		= no_llseek,
47496fd7ce5SGreg Kroah-Hartman 	.read		= tty_read,
47596fd7ce5SGreg Kroah-Hartman 	.write		= tty_write,
47696fd7ce5SGreg Kroah-Hartman 	.poll		= tty_poll,
47796fd7ce5SGreg Kroah-Hartman 	.unlocked_ioctl	= tty_ioctl,
47896fd7ce5SGreg Kroah-Hartman 	.compat_ioctl	= tty_compat_ioctl,
47996fd7ce5SGreg Kroah-Hartman 	.open		= tty_open,
48096fd7ce5SGreg Kroah-Hartman 	.release	= tty_release,
48196fd7ce5SGreg Kroah-Hartman 	.fasync		= tty_fasync,
48296fd7ce5SGreg Kroah-Hartman };
48396fd7ce5SGreg Kroah-Hartman 
48496fd7ce5SGreg Kroah-Hartman static const struct file_operations console_fops = {
48596fd7ce5SGreg Kroah-Hartman 	.llseek		= no_llseek,
48696fd7ce5SGreg Kroah-Hartman 	.read		= tty_read,
48796fd7ce5SGreg Kroah-Hartman 	.write		= redirected_tty_write,
48896fd7ce5SGreg Kroah-Hartman 	.poll		= tty_poll,
48996fd7ce5SGreg Kroah-Hartman 	.unlocked_ioctl	= tty_ioctl,
49096fd7ce5SGreg Kroah-Hartman 	.compat_ioctl	= tty_compat_ioctl,
49196fd7ce5SGreg Kroah-Hartman 	.open		= tty_open,
49296fd7ce5SGreg Kroah-Hartman 	.release	= tty_release,
49396fd7ce5SGreg Kroah-Hartman 	.fasync		= tty_fasync,
49496fd7ce5SGreg Kroah-Hartman };
49596fd7ce5SGreg Kroah-Hartman 
49696fd7ce5SGreg Kroah-Hartman static const struct file_operations hung_up_tty_fops = {
49796fd7ce5SGreg Kroah-Hartman 	.llseek		= no_llseek,
49896fd7ce5SGreg Kroah-Hartman 	.read		= hung_up_tty_read,
49996fd7ce5SGreg Kroah-Hartman 	.write		= hung_up_tty_write,
50096fd7ce5SGreg Kroah-Hartman 	.poll		= hung_up_tty_poll,
50196fd7ce5SGreg Kroah-Hartman 	.unlocked_ioctl	= hung_up_tty_ioctl,
50296fd7ce5SGreg Kroah-Hartman 	.compat_ioctl	= hung_up_tty_compat_ioctl,
50396fd7ce5SGreg Kroah-Hartman 	.release	= tty_release,
50496fd7ce5SGreg Kroah-Hartman };
50596fd7ce5SGreg Kroah-Hartman 
50696fd7ce5SGreg Kroah-Hartman static DEFINE_SPINLOCK(redirect_lock);
50796fd7ce5SGreg Kroah-Hartman static struct file *redirect;
50896fd7ce5SGreg Kroah-Hartman 
50996fd7ce5SGreg Kroah-Hartman /**
51096fd7ce5SGreg Kroah-Hartman  *	tty_wakeup	-	request more data
51196fd7ce5SGreg Kroah-Hartman  *	@tty: terminal
51296fd7ce5SGreg Kroah-Hartman  *
51396fd7ce5SGreg Kroah-Hartman  *	Internal and external helper for wakeups of tty. This function
51496fd7ce5SGreg Kroah-Hartman  *	informs the line discipline if present that the driver is ready
51596fd7ce5SGreg Kroah-Hartman  *	to receive more output data.
51696fd7ce5SGreg Kroah-Hartman  */
51796fd7ce5SGreg Kroah-Hartman 
51896fd7ce5SGreg Kroah-Hartman void tty_wakeup(struct tty_struct *tty)
51996fd7ce5SGreg Kroah-Hartman {
52096fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
52196fd7ce5SGreg Kroah-Hartman 
52296fd7ce5SGreg Kroah-Hartman 	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
52396fd7ce5SGreg Kroah-Hartman 		ld = tty_ldisc_ref(tty);
52496fd7ce5SGreg Kroah-Hartman 		if (ld) {
52596fd7ce5SGreg Kroah-Hartman 			if (ld->ops->write_wakeup)
52696fd7ce5SGreg Kroah-Hartman 				ld->ops->write_wakeup(tty);
52796fd7ce5SGreg Kroah-Hartman 			tty_ldisc_deref(ld);
52896fd7ce5SGreg Kroah-Hartman 		}
52996fd7ce5SGreg Kroah-Hartman 	}
53096fd7ce5SGreg Kroah-Hartman 	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
53196fd7ce5SGreg Kroah-Hartman }
53296fd7ce5SGreg Kroah-Hartman 
53396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_wakeup);
53496fd7ce5SGreg Kroah-Hartman 
53596fd7ce5SGreg Kroah-Hartman /**
536ea648a47SPeter Hurley  *	tty_signal_session_leader	- sends SIGHUP to session leader
537f91e2590SPeter Hurley  *	@tty		controlling tty
538f91e2590SPeter Hurley  *	@exit_session	if non-zero, signal all foreground group processes
539ea648a47SPeter Hurley  *
540f91e2590SPeter Hurley  *	Send SIGHUP and SIGCONT to the session leader and its process group.
541f91e2590SPeter Hurley  *	Optionally, signal all processes in the foreground process group.
542ea648a47SPeter Hurley  *
543ea648a47SPeter Hurley  *	Returns the number of processes in the session with this tty
544ea648a47SPeter Hurley  *	as their controlling terminal. This value is used to drop
545ea648a47SPeter Hurley  *	tty references for those processes.
546ea648a47SPeter Hurley  */
547f91e2590SPeter Hurley static int tty_signal_session_leader(struct tty_struct *tty, int exit_session)
548ea648a47SPeter Hurley {
549ea648a47SPeter Hurley 	struct task_struct *p;
550ea648a47SPeter Hurley 	int refs = 0;
551f91e2590SPeter Hurley 	struct pid *tty_pgrp = NULL;
552ea648a47SPeter Hurley 
553ea648a47SPeter Hurley 	read_lock(&tasklist_lock);
554ea648a47SPeter Hurley 	if (tty->session) {
555ea648a47SPeter Hurley 		do_each_pid_task(tty->session, PIDTYPE_SID, p) {
556ea648a47SPeter Hurley 			spin_lock_irq(&p->sighand->siglock);
557ea648a47SPeter Hurley 			if (p->signal->tty == tty) {
558ea648a47SPeter Hurley 				p->signal->tty = NULL;
559ea648a47SPeter Hurley 				/* We defer the dereferences outside fo
560ea648a47SPeter Hurley 				   the tasklist lock */
561ea648a47SPeter Hurley 				refs++;
562ea648a47SPeter Hurley 			}
563ea648a47SPeter Hurley 			if (!p->signal->leader) {
564ea648a47SPeter Hurley 				spin_unlock_irq(&p->sighand->siglock);
565ea648a47SPeter Hurley 				continue;
566ea648a47SPeter Hurley 			}
567ea648a47SPeter Hurley 			__group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);
568ea648a47SPeter Hurley 			__group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);
569ea648a47SPeter Hurley 			put_pid(p->signal->tty_old_pgrp);  /* A noop */
570bc30c3b2SPeter Hurley 			spin_lock(&tty->ctrl_lock);
571f91e2590SPeter Hurley 			tty_pgrp = get_pid(tty->pgrp);
572ea648a47SPeter Hurley 			if (tty->pgrp)
573ea648a47SPeter Hurley 				p->signal->tty_old_pgrp = get_pid(tty->pgrp);
574bc30c3b2SPeter Hurley 			spin_unlock(&tty->ctrl_lock);
575ea648a47SPeter Hurley 			spin_unlock_irq(&p->sighand->siglock);
576ea648a47SPeter Hurley 		} while_each_pid_task(tty->session, PIDTYPE_SID, p);
577ea648a47SPeter Hurley 	}
578ea648a47SPeter Hurley 	read_unlock(&tasklist_lock);
579ea648a47SPeter Hurley 
580f91e2590SPeter Hurley 	if (tty_pgrp) {
581f91e2590SPeter Hurley 		if (exit_session)
582f91e2590SPeter Hurley 			kill_pgrp(tty_pgrp, SIGHUP, exit_session);
583f91e2590SPeter Hurley 		put_pid(tty_pgrp);
584f91e2590SPeter Hurley 	}
585f91e2590SPeter Hurley 
586ea648a47SPeter Hurley 	return refs;
587ea648a47SPeter Hurley }
588ea648a47SPeter Hurley 
589ea648a47SPeter Hurley /**
59096fd7ce5SGreg Kroah-Hartman  *	__tty_hangup		-	actual handler for hangup events
59196fd7ce5SGreg Kroah-Hartman  *	@work: tty device
59296fd7ce5SGreg Kroah-Hartman  *
593ef4f527cSKevin Cernekee  *	This can be called by a "kworker" kernel thread.  That is process
59496fd7ce5SGreg Kroah-Hartman  *	synchronous but doesn't hold any locks, so we need to make sure we
59596fd7ce5SGreg Kroah-Hartman  *	have the appropriate locks for what we're doing.
59696fd7ce5SGreg Kroah-Hartman  *
59796fd7ce5SGreg Kroah-Hartman  *	The hangup event clears any pending redirections onto the hung up
59896fd7ce5SGreg Kroah-Hartman  *	device. It ensures future writes will error and it does the needed
59996fd7ce5SGreg Kroah-Hartman  *	line discipline hangup and signal delivery. The tty object itself
60096fd7ce5SGreg Kroah-Hartman  *	remains intact.
60196fd7ce5SGreg Kroah-Hartman  *
60296fd7ce5SGreg Kroah-Hartman  *	Locking:
60396fd7ce5SGreg Kroah-Hartman  *		BTM
60496fd7ce5SGreg Kroah-Hartman  *		  redirect lock for undoing redirection
60596fd7ce5SGreg Kroah-Hartman  *		  file list lock for manipulating list of ttys
60696fd7ce5SGreg Kroah-Hartman  *		  tty_ldisc_lock from called functions
60796fd7ce5SGreg Kroah-Hartman  *		  termios_mutex resetting termios data
60896fd7ce5SGreg Kroah-Hartman  *		  tasklist_lock to walk task list for hangup event
60996fd7ce5SGreg Kroah-Hartman  *		    ->siglock to protect ->signal/->sighand
61096fd7ce5SGreg Kroah-Hartman  */
611f91e2590SPeter Hurley static void __tty_hangup(struct tty_struct *tty, int exit_session)
61296fd7ce5SGreg Kroah-Hartman {
61396fd7ce5SGreg Kroah-Hartman 	struct file *cons_filp = NULL;
61496fd7ce5SGreg Kroah-Hartman 	struct file *filp, *f = NULL;
61596fd7ce5SGreg Kroah-Hartman 	struct tty_file_private *priv;
61696fd7ce5SGreg Kroah-Hartman 	int    closecount = 0, n;
617ea648a47SPeter Hurley 	int refs;
61896fd7ce5SGreg Kroah-Hartman 
61996fd7ce5SGreg Kroah-Hartman 	if (!tty)
62096fd7ce5SGreg Kroah-Hartman 		return;
62196fd7ce5SGreg Kroah-Hartman 
62296fd7ce5SGreg Kroah-Hartman 
62396fd7ce5SGreg Kroah-Hartman 	spin_lock(&redirect_lock);
62496fd7ce5SGreg Kroah-Hartman 	if (redirect && file_tty(redirect) == tty) {
62596fd7ce5SGreg Kroah-Hartman 		f = redirect;
62696fd7ce5SGreg Kroah-Hartman 		redirect = NULL;
62796fd7ce5SGreg Kroah-Hartman 	}
62896fd7ce5SGreg Kroah-Hartman 	spin_unlock(&redirect_lock);
62996fd7ce5SGreg Kroah-Hartman 
63089c8d91eSAlan Cox 	tty_lock(tty);
63196fd7ce5SGreg Kroah-Hartman 
632acfa747bSJiri Slaby 	/* some functions below drop BTM, so we need this bit */
633acfa747bSJiri Slaby 	set_bit(TTY_HUPPING, &tty->flags);
634acfa747bSJiri Slaby 
63596fd7ce5SGreg Kroah-Hartman 	/* inuse_filps is protected by the single tty lock,
63696fd7ce5SGreg Kroah-Hartman 	   this really needs to change if we want to flush the
63796fd7ce5SGreg Kroah-Hartman 	   workqueue with the lock held */
63896fd7ce5SGreg Kroah-Hartman 	check_tty_count(tty, "tty_hangup");
63996fd7ce5SGreg Kroah-Hartman 
64096fd7ce5SGreg Kroah-Hartman 	spin_lock(&tty_files_lock);
64196fd7ce5SGreg Kroah-Hartman 	/* This breaks for file handles being sent over AF_UNIX sockets ? */
64296fd7ce5SGreg Kroah-Hartman 	list_for_each_entry(priv, &tty->tty_files, list) {
64396fd7ce5SGreg Kroah-Hartman 		filp = priv->file;
64496fd7ce5SGreg Kroah-Hartman 		if (filp->f_op->write == redirected_tty_write)
64596fd7ce5SGreg Kroah-Hartman 			cons_filp = filp;
64696fd7ce5SGreg Kroah-Hartman 		if (filp->f_op->write != tty_write)
64796fd7ce5SGreg Kroah-Hartman 			continue;
64896fd7ce5SGreg Kroah-Hartman 		closecount++;
64996fd7ce5SGreg Kroah-Hartman 		__tty_fasync(-1, filp, 0);	/* can't block */
65096fd7ce5SGreg Kroah-Hartman 		filp->f_op = &hung_up_tty_fops;
65196fd7ce5SGreg Kroah-Hartman 	}
65296fd7ce5SGreg Kroah-Hartman 	spin_unlock(&tty_files_lock);
65396fd7ce5SGreg Kroah-Hartman 
65425fdf243SPeter Hurley 	refs = tty_signal_session_leader(tty, exit_session);
65525fdf243SPeter Hurley 	/* Account for the p->signal references we killed */
65625fdf243SPeter Hurley 	while (refs--)
65725fdf243SPeter Hurley 		tty_kref_put(tty);
65825fdf243SPeter Hurley 
659acfa747bSJiri Slaby 	/*
660acfa747bSJiri Slaby 	 * it drops BTM and thus races with reopen
661acfa747bSJiri Slaby 	 * we protect the race by TTY_HUPPING
662acfa747bSJiri Slaby 	 */
66396fd7ce5SGreg Kroah-Hartman 	tty_ldisc_hangup(tty);
66496fd7ce5SGreg Kroah-Hartman 
66520cc225bSPeter Hurley 	spin_lock_irq(&tty->ctrl_lock);
66696fd7ce5SGreg Kroah-Hartman 	clear_bit(TTY_THROTTLED, &tty->flags);
66796fd7ce5SGreg Kroah-Hartman 	clear_bit(TTY_PUSH, &tty->flags);
66896fd7ce5SGreg Kroah-Hartman 	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
66996fd7ce5SGreg Kroah-Hartman 	put_pid(tty->session);
67096fd7ce5SGreg Kroah-Hartman 	put_pid(tty->pgrp);
67196fd7ce5SGreg Kroah-Hartman 	tty->session = NULL;
67296fd7ce5SGreg Kroah-Hartman 	tty->pgrp = NULL;
67396fd7ce5SGreg Kroah-Hartman 	tty->ctrl_status = 0;
67420cc225bSPeter Hurley 	spin_unlock_irq(&tty->ctrl_lock);
67596fd7ce5SGreg Kroah-Hartman 
67696fd7ce5SGreg Kroah-Hartman 	/*
67796fd7ce5SGreg Kroah-Hartman 	 * If one of the devices matches a console pointer, we
67896fd7ce5SGreg Kroah-Hartman 	 * cannot just call hangup() because that will cause
67996fd7ce5SGreg Kroah-Hartman 	 * tty->count and state->count to go out of sync.
68096fd7ce5SGreg Kroah-Hartman 	 * So we just call close() the right number of times.
68196fd7ce5SGreg Kroah-Hartman 	 */
68296fd7ce5SGreg Kroah-Hartman 	if (cons_filp) {
68396fd7ce5SGreg Kroah-Hartman 		if (tty->ops->close)
68496fd7ce5SGreg Kroah-Hartman 			for (n = 0; n < closecount; n++)
68596fd7ce5SGreg Kroah-Hartman 				tty->ops->close(tty, cons_filp);
68696fd7ce5SGreg Kroah-Hartman 	} else if (tty->ops->hangup)
68796fd7ce5SGreg Kroah-Hartman 		(tty->ops->hangup)(tty);
68896fd7ce5SGreg Kroah-Hartman 	/*
68996fd7ce5SGreg Kroah-Hartman 	 * We don't want to have driver/ldisc interactions beyond
69096fd7ce5SGreg Kroah-Hartman 	 * the ones we did here. The driver layer expects no
69196fd7ce5SGreg Kroah-Hartman 	 * calls after ->hangup() from the ldisc side. However we
69296fd7ce5SGreg Kroah-Hartman 	 * can't yet guarantee all that.
69396fd7ce5SGreg Kroah-Hartman 	 */
69496fd7ce5SGreg Kroah-Hartman 	set_bit(TTY_HUPPED, &tty->flags);
695acfa747bSJiri Slaby 	clear_bit(TTY_HUPPING, &tty->flags);
69696fd7ce5SGreg Kroah-Hartman 
69789c8d91eSAlan Cox 	tty_unlock(tty);
69896fd7ce5SGreg Kroah-Hartman 
69996fd7ce5SGreg Kroah-Hartman 	if (f)
70096fd7ce5SGreg Kroah-Hartman 		fput(f);
70196fd7ce5SGreg Kroah-Hartman }
70296fd7ce5SGreg Kroah-Hartman 
70396fd7ce5SGreg Kroah-Hartman static void do_tty_hangup(struct work_struct *work)
70496fd7ce5SGreg Kroah-Hartman {
70596fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty =
70696fd7ce5SGreg Kroah-Hartman 		container_of(work, struct tty_struct, hangup_work);
70796fd7ce5SGreg Kroah-Hartman 
708f91e2590SPeter Hurley 	__tty_hangup(tty, 0);
70996fd7ce5SGreg Kroah-Hartman }
71096fd7ce5SGreg Kroah-Hartman 
71196fd7ce5SGreg Kroah-Hartman /**
71296fd7ce5SGreg Kroah-Hartman  *	tty_hangup		-	trigger a hangup event
71396fd7ce5SGreg Kroah-Hartman  *	@tty: tty to hangup
71496fd7ce5SGreg Kroah-Hartman  *
71596fd7ce5SGreg Kroah-Hartman  *	A carrier loss (virtual or otherwise) has occurred on this like
71696fd7ce5SGreg Kroah-Hartman  *	schedule a hangup sequence to run after this event.
71796fd7ce5SGreg Kroah-Hartman  */
71896fd7ce5SGreg Kroah-Hartman 
71996fd7ce5SGreg Kroah-Hartman void tty_hangup(struct tty_struct *tty)
72096fd7ce5SGreg Kroah-Hartman {
72196fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
72296fd7ce5SGreg Kroah-Hartman 	char	buf[64];
72396fd7ce5SGreg Kroah-Hartman 	printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
72496fd7ce5SGreg Kroah-Hartman #endif
72596fd7ce5SGreg Kroah-Hartman 	schedule_work(&tty->hangup_work);
72696fd7ce5SGreg Kroah-Hartman }
72796fd7ce5SGreg Kroah-Hartman 
72896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_hangup);
72996fd7ce5SGreg Kroah-Hartman 
73096fd7ce5SGreg Kroah-Hartman /**
73196fd7ce5SGreg Kroah-Hartman  *	tty_vhangup		-	process vhangup
73296fd7ce5SGreg Kroah-Hartman  *	@tty: tty to hangup
73396fd7ce5SGreg Kroah-Hartman  *
73496fd7ce5SGreg Kroah-Hartman  *	The user has asked via system call for the terminal to be hung up.
73596fd7ce5SGreg Kroah-Hartman  *	We do this synchronously so that when the syscall returns the process
73696fd7ce5SGreg Kroah-Hartman  *	is complete. That guarantee is necessary for security reasons.
73796fd7ce5SGreg Kroah-Hartman  */
73896fd7ce5SGreg Kroah-Hartman 
73996fd7ce5SGreg Kroah-Hartman void tty_vhangup(struct tty_struct *tty)
74096fd7ce5SGreg Kroah-Hartman {
74196fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
74296fd7ce5SGreg Kroah-Hartman 	char	buf[64];
74396fd7ce5SGreg Kroah-Hartman 
74496fd7ce5SGreg Kroah-Hartman 	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));
74596fd7ce5SGreg Kroah-Hartman #endif
746f91e2590SPeter Hurley 	__tty_hangup(tty, 0);
74796fd7ce5SGreg Kroah-Hartman }
74896fd7ce5SGreg Kroah-Hartman 
74996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_vhangup);
75096fd7ce5SGreg Kroah-Hartman 
75196fd7ce5SGreg Kroah-Hartman 
75296fd7ce5SGreg Kroah-Hartman /**
75396fd7ce5SGreg Kroah-Hartman  *	tty_vhangup_self	-	process vhangup for own ctty
75496fd7ce5SGreg Kroah-Hartman  *
75596fd7ce5SGreg Kroah-Hartman  *	Perform a vhangup on the current controlling tty
75696fd7ce5SGreg Kroah-Hartman  */
75796fd7ce5SGreg Kroah-Hartman 
75896fd7ce5SGreg Kroah-Hartman void tty_vhangup_self(void)
75996fd7ce5SGreg Kroah-Hartman {
76096fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
76196fd7ce5SGreg Kroah-Hartman 
76296fd7ce5SGreg Kroah-Hartman 	tty = get_current_tty();
76396fd7ce5SGreg Kroah-Hartman 	if (tty) {
76496fd7ce5SGreg Kroah-Hartman 		tty_vhangup(tty);
76596fd7ce5SGreg Kroah-Hartman 		tty_kref_put(tty);
76696fd7ce5SGreg Kroah-Hartman 	}
76796fd7ce5SGreg Kroah-Hartman }
76896fd7ce5SGreg Kroah-Hartman 
76996fd7ce5SGreg Kroah-Hartman /**
770f91e2590SPeter Hurley  *	tty_vhangup_session		-	hangup session leader exit
771f91e2590SPeter Hurley  *	@tty: tty to hangup
772f91e2590SPeter Hurley  *
773f91e2590SPeter Hurley  *	The session leader is exiting and hanging up its controlling terminal.
774f91e2590SPeter Hurley  *	Every process in the foreground process group is signalled SIGHUP.
775f91e2590SPeter Hurley  *
776f91e2590SPeter Hurley  *	We do this synchronously so that when the syscall returns the process
777f91e2590SPeter Hurley  *	is complete. That guarantee is necessary for security reasons.
778f91e2590SPeter Hurley  */
779f91e2590SPeter Hurley 
780f91e2590SPeter Hurley void tty_vhangup_session(struct tty_struct *tty)
781f91e2590SPeter Hurley {
782f91e2590SPeter Hurley #ifdef TTY_DEBUG_HANGUP
783f91e2590SPeter Hurley 	char	buf[64];
784f91e2590SPeter Hurley 
785f91e2590SPeter Hurley 	printk(KERN_DEBUG "%s vhangup session...\n", tty_name(tty, buf));
786f91e2590SPeter Hurley #endif
787f91e2590SPeter Hurley 	__tty_hangup(tty, 1);
788f91e2590SPeter Hurley }
789f91e2590SPeter Hurley 
790f91e2590SPeter Hurley /**
79196fd7ce5SGreg Kroah-Hartman  *	tty_hung_up_p		-	was tty hung up
79296fd7ce5SGreg Kroah-Hartman  *	@filp: file pointer of tty
79396fd7ce5SGreg Kroah-Hartman  *
79496fd7ce5SGreg Kroah-Hartman  *	Return true if the tty has been subject to a vhangup or a carrier
79596fd7ce5SGreg Kroah-Hartman  *	loss
79696fd7ce5SGreg Kroah-Hartman  */
79796fd7ce5SGreg Kroah-Hartman 
79896fd7ce5SGreg Kroah-Hartman int tty_hung_up_p(struct file *filp)
79996fd7ce5SGreg Kroah-Hartman {
80096fd7ce5SGreg Kroah-Hartman 	return (filp->f_op == &hung_up_tty_fops);
80196fd7ce5SGreg Kroah-Hartman }
80296fd7ce5SGreg Kroah-Hartman 
80396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_hung_up_p);
80496fd7ce5SGreg Kroah-Hartman 
80596fd7ce5SGreg Kroah-Hartman static void session_clear_tty(struct pid *session)
80696fd7ce5SGreg Kroah-Hartman {
80796fd7ce5SGreg Kroah-Hartman 	struct task_struct *p;
80896fd7ce5SGreg Kroah-Hartman 	do_each_pid_task(session, PIDTYPE_SID, p) {
80996fd7ce5SGreg Kroah-Hartman 		proc_clear_tty(p);
81096fd7ce5SGreg Kroah-Hartman 	} while_each_pid_task(session, PIDTYPE_SID, p);
81196fd7ce5SGreg Kroah-Hartman }
81296fd7ce5SGreg Kroah-Hartman 
81396fd7ce5SGreg Kroah-Hartman /**
81496fd7ce5SGreg Kroah-Hartman  *	disassociate_ctty	-	disconnect controlling tty
81596fd7ce5SGreg Kroah-Hartman  *	@on_exit: true if exiting so need to "hang up" the session
81696fd7ce5SGreg Kroah-Hartman  *
81796fd7ce5SGreg Kroah-Hartman  *	This function is typically called only by the session leader, when
81896fd7ce5SGreg Kroah-Hartman  *	it wants to disassociate itself from its controlling tty.
81996fd7ce5SGreg Kroah-Hartman  *
82096fd7ce5SGreg Kroah-Hartman  *	It performs the following functions:
82196fd7ce5SGreg Kroah-Hartman  * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group
82296fd7ce5SGreg Kroah-Hartman  * 	(2)  Clears the tty from being controlling the session
82396fd7ce5SGreg Kroah-Hartman  * 	(3)  Clears the controlling tty for all processes in the
82496fd7ce5SGreg Kroah-Hartman  * 		session group.
82596fd7ce5SGreg Kroah-Hartman  *
82696fd7ce5SGreg Kroah-Hartman  *	The argument on_exit is set to 1 if called when a process is
82796fd7ce5SGreg Kroah-Hartman  *	exiting; it is 0 if called by the ioctl TIOCNOTTY.
82896fd7ce5SGreg Kroah-Hartman  *
82996fd7ce5SGreg Kroah-Hartman  *	Locking:
83096fd7ce5SGreg Kroah-Hartman  *		BTM is taken for hysterical raisins, and held when
83196fd7ce5SGreg Kroah-Hartman  *		  called from no_tty().
83296fd7ce5SGreg Kroah-Hartman  *		  tty_mutex is taken to protect tty
83396fd7ce5SGreg Kroah-Hartman  *		  ->siglock is taken to protect ->signal/->sighand
83496fd7ce5SGreg Kroah-Hartman  *		  tasklist_lock is taken to walk process list for sessions
83596fd7ce5SGreg Kroah-Hartman  *		    ->siglock is taken to protect ->signal/->sighand
83696fd7ce5SGreg Kroah-Hartman  */
83796fd7ce5SGreg Kroah-Hartman 
83896fd7ce5SGreg Kroah-Hartman void disassociate_ctty(int on_exit)
83996fd7ce5SGreg Kroah-Hartman {
84096fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
84196fd7ce5SGreg Kroah-Hartman 
84296fd7ce5SGreg Kroah-Hartman 	if (!current->signal->leader)
84396fd7ce5SGreg Kroah-Hartman 		return;
84496fd7ce5SGreg Kroah-Hartman 
84596fd7ce5SGreg Kroah-Hartman 	tty = get_current_tty();
84696fd7ce5SGreg Kroah-Hartman 	if (tty) {
847f91e2590SPeter Hurley 		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) {
848f91e2590SPeter Hurley 			tty_vhangup_session(tty);
849f91e2590SPeter Hurley 		} else {
850f91e2590SPeter Hurley 			struct pid *tty_pgrp = tty_get_pgrp(tty);
8511411dc4aSJiri Slaby 			if (tty_pgrp) {
8521411dc4aSJiri Slaby 				kill_pgrp(tty_pgrp, SIGHUP, on_exit);
8531411dc4aSJiri Slaby 				kill_pgrp(tty_pgrp, SIGCONT, on_exit);
8541411dc4aSJiri Slaby 				put_pid(tty_pgrp);
8551411dc4aSJiri Slaby 			}
856f91e2590SPeter Hurley 		}
857f91e2590SPeter Hurley 		tty_kref_put(tty);
858f91e2590SPeter Hurley 
85996fd7ce5SGreg Kroah-Hartman 	} else if (on_exit) {
86096fd7ce5SGreg Kroah-Hartman 		struct pid *old_pgrp;
86196fd7ce5SGreg Kroah-Hartman 		spin_lock_irq(&current->sighand->siglock);
86296fd7ce5SGreg Kroah-Hartman 		old_pgrp = current->signal->tty_old_pgrp;
86396fd7ce5SGreg Kroah-Hartman 		current->signal->tty_old_pgrp = NULL;
86496fd7ce5SGreg Kroah-Hartman 		spin_unlock_irq(&current->sighand->siglock);
86596fd7ce5SGreg Kroah-Hartman 		if (old_pgrp) {
86696fd7ce5SGreg Kroah-Hartman 			kill_pgrp(old_pgrp, SIGHUP, on_exit);
86796fd7ce5SGreg Kroah-Hartman 			kill_pgrp(old_pgrp, SIGCONT, on_exit);
86896fd7ce5SGreg Kroah-Hartman 			put_pid(old_pgrp);
86996fd7ce5SGreg Kroah-Hartman 		}
87096fd7ce5SGreg Kroah-Hartman 		return;
87196fd7ce5SGreg Kroah-Hartman 	}
87296fd7ce5SGreg Kroah-Hartman 
87396fd7ce5SGreg Kroah-Hartman 	spin_lock_irq(&current->sighand->siglock);
87496fd7ce5SGreg Kroah-Hartman 	put_pid(current->signal->tty_old_pgrp);
87596fd7ce5SGreg Kroah-Hartman 	current->signal->tty_old_pgrp = NULL;
87696fd7ce5SGreg Kroah-Hartman 	spin_unlock_irq(&current->sighand->siglock);
87796fd7ce5SGreg Kroah-Hartman 
87896fd7ce5SGreg Kroah-Hartman 	tty = get_current_tty();
87996fd7ce5SGreg Kroah-Hartman 	if (tty) {
88096fd7ce5SGreg Kroah-Hartman 		unsigned long flags;
88196fd7ce5SGreg Kroah-Hartman 		spin_lock_irqsave(&tty->ctrl_lock, flags);
88296fd7ce5SGreg Kroah-Hartman 		put_pid(tty->session);
88396fd7ce5SGreg Kroah-Hartman 		put_pid(tty->pgrp);
88496fd7ce5SGreg Kroah-Hartman 		tty->session = NULL;
88596fd7ce5SGreg Kroah-Hartman 		tty->pgrp = NULL;
88696fd7ce5SGreg Kroah-Hartman 		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
88796fd7ce5SGreg Kroah-Hartman 		tty_kref_put(tty);
88896fd7ce5SGreg Kroah-Hartman 	} else {
88996fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
89096fd7ce5SGreg Kroah-Hartman 		printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
89196fd7ce5SGreg Kroah-Hartman 		       " = NULL", tty);
89296fd7ce5SGreg Kroah-Hartman #endif
89396fd7ce5SGreg Kroah-Hartman 	}
89496fd7ce5SGreg Kroah-Hartman 
89596fd7ce5SGreg Kroah-Hartman 	/* Now clear signal->tty under the lock */
89696fd7ce5SGreg Kroah-Hartman 	read_lock(&tasklist_lock);
89796fd7ce5SGreg Kroah-Hartman 	session_clear_tty(task_session(current));
89896fd7ce5SGreg Kroah-Hartman 	read_unlock(&tasklist_lock);
89996fd7ce5SGreg Kroah-Hartman }
90096fd7ce5SGreg Kroah-Hartman 
90196fd7ce5SGreg Kroah-Hartman /**
90296fd7ce5SGreg Kroah-Hartman  *
90396fd7ce5SGreg Kroah-Hartman  *	no_tty	- Ensure the current process does not have a controlling tty
90496fd7ce5SGreg Kroah-Hartman  */
90596fd7ce5SGreg Kroah-Hartman void no_tty(void)
90696fd7ce5SGreg Kroah-Hartman {
9073af502b9SAlan Cox 	/* FIXME: Review locking here. The tty_lock never covered any race
9083af502b9SAlan Cox 	   between a new association and proc_clear_tty but possible we need
9093af502b9SAlan Cox 	   to protect against this anyway */
91096fd7ce5SGreg Kroah-Hartman 	struct task_struct *tsk = current;
91196fd7ce5SGreg Kroah-Hartman 	disassociate_ctty(0);
91296fd7ce5SGreg Kroah-Hartman 	proc_clear_tty(tsk);
91396fd7ce5SGreg Kroah-Hartman }
91496fd7ce5SGreg Kroah-Hartman 
91596fd7ce5SGreg Kroah-Hartman 
91696fd7ce5SGreg Kroah-Hartman /**
91796fd7ce5SGreg Kroah-Hartman  *	stop_tty	-	propagate flow control
91896fd7ce5SGreg Kroah-Hartman  *	@tty: tty to stop
91996fd7ce5SGreg Kroah-Hartman  *
92096fd7ce5SGreg Kroah-Hartman  *	Perform flow control to the driver. For PTY/TTY pairs we
92196fd7ce5SGreg Kroah-Hartman  *	must also propagate the TIOCKPKT status. May be called
92296fd7ce5SGreg Kroah-Hartman  *	on an already stopped device and will not re-call the driver
92396fd7ce5SGreg Kroah-Hartman  *	method.
92496fd7ce5SGreg Kroah-Hartman  *
92596fd7ce5SGreg Kroah-Hartman  *	This functionality is used by both the line disciplines for
92696fd7ce5SGreg Kroah-Hartman  *	halting incoming flow and by the driver. It may therefore be
92796fd7ce5SGreg Kroah-Hartman  *	called from any context, may be under the tty atomic_write_lock
92896fd7ce5SGreg Kroah-Hartman  *	but not always.
92996fd7ce5SGreg Kroah-Hartman  *
93096fd7ce5SGreg Kroah-Hartman  *	Locking:
93196fd7ce5SGreg Kroah-Hartman  *		Uses the tty control lock internally
93296fd7ce5SGreg Kroah-Hartman  */
93396fd7ce5SGreg Kroah-Hartman 
93496fd7ce5SGreg Kroah-Hartman void stop_tty(struct tty_struct *tty)
93596fd7ce5SGreg Kroah-Hartman {
93696fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
93796fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&tty->ctrl_lock, flags);
93896fd7ce5SGreg Kroah-Hartman 	if (tty->stopped) {
93996fd7ce5SGreg Kroah-Hartman 		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
94096fd7ce5SGreg Kroah-Hartman 		return;
94196fd7ce5SGreg Kroah-Hartman 	}
94296fd7ce5SGreg Kroah-Hartman 	tty->stopped = 1;
94396fd7ce5SGreg Kroah-Hartman 	if (tty->link && tty->link->packet) {
94496fd7ce5SGreg Kroah-Hartman 		tty->ctrl_status &= ~TIOCPKT_START;
94596fd7ce5SGreg Kroah-Hartman 		tty->ctrl_status |= TIOCPKT_STOP;
94696fd7ce5SGreg Kroah-Hartman 		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
94796fd7ce5SGreg Kroah-Hartman 	}
94896fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
94996fd7ce5SGreg Kroah-Hartman 	if (tty->ops->stop)
95096fd7ce5SGreg Kroah-Hartman 		(tty->ops->stop)(tty);
95196fd7ce5SGreg Kroah-Hartman }
95296fd7ce5SGreg Kroah-Hartman 
95396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(stop_tty);
95496fd7ce5SGreg Kroah-Hartman 
95596fd7ce5SGreg Kroah-Hartman /**
95696fd7ce5SGreg Kroah-Hartman  *	start_tty	-	propagate flow control
95796fd7ce5SGreg Kroah-Hartman  *	@tty: tty to start
95896fd7ce5SGreg Kroah-Hartman  *
95996fd7ce5SGreg Kroah-Hartman  *	Start a tty that has been stopped if at all possible. Perform
96096fd7ce5SGreg Kroah-Hartman  *	any necessary wakeups and propagate the TIOCPKT status. If this
96196fd7ce5SGreg Kroah-Hartman  *	is the tty was previous stopped and is being started then the
96296fd7ce5SGreg Kroah-Hartman  *	driver start method is invoked and the line discipline woken.
96396fd7ce5SGreg Kroah-Hartman  *
96496fd7ce5SGreg Kroah-Hartman  *	Locking:
96596fd7ce5SGreg Kroah-Hartman  *		ctrl_lock
96696fd7ce5SGreg Kroah-Hartman  */
96796fd7ce5SGreg Kroah-Hartman 
96896fd7ce5SGreg Kroah-Hartman void start_tty(struct tty_struct *tty)
96996fd7ce5SGreg Kroah-Hartman {
97096fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
97196fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&tty->ctrl_lock, flags);
97296fd7ce5SGreg Kroah-Hartman 	if (!tty->stopped || tty->flow_stopped) {
97396fd7ce5SGreg Kroah-Hartman 		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
97496fd7ce5SGreg Kroah-Hartman 		return;
97596fd7ce5SGreg Kroah-Hartman 	}
97696fd7ce5SGreg Kroah-Hartman 	tty->stopped = 0;
97796fd7ce5SGreg Kroah-Hartman 	if (tty->link && tty->link->packet) {
97896fd7ce5SGreg Kroah-Hartman 		tty->ctrl_status &= ~TIOCPKT_STOP;
97996fd7ce5SGreg Kroah-Hartman 		tty->ctrl_status |= TIOCPKT_START;
98096fd7ce5SGreg Kroah-Hartman 		wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
98196fd7ce5SGreg Kroah-Hartman 	}
98296fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
98396fd7ce5SGreg Kroah-Hartman 	if (tty->ops->start)
98496fd7ce5SGreg Kroah-Hartman 		(tty->ops->start)(tty);
98596fd7ce5SGreg Kroah-Hartman 	/* If we have a running line discipline it may need kicking */
98696fd7ce5SGreg Kroah-Hartman 	tty_wakeup(tty);
98796fd7ce5SGreg Kroah-Hartman }
98896fd7ce5SGreg Kroah-Hartman 
98996fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(start_tty);
99096fd7ce5SGreg Kroah-Hartman 
99196fd7ce5SGreg Kroah-Hartman /**
99296fd7ce5SGreg Kroah-Hartman  *	tty_read	-	read method for tty device files
99396fd7ce5SGreg Kroah-Hartman  *	@file: pointer to tty file
99496fd7ce5SGreg Kroah-Hartman  *	@buf: user buffer
99596fd7ce5SGreg Kroah-Hartman  *	@count: size of user buffer
99696fd7ce5SGreg Kroah-Hartman  *	@ppos: unused
99796fd7ce5SGreg Kroah-Hartman  *
99896fd7ce5SGreg Kroah-Hartman  *	Perform the read system call function on this terminal device. Checks
99996fd7ce5SGreg Kroah-Hartman  *	for hung up devices before calling the line discipline method.
100096fd7ce5SGreg Kroah-Hartman  *
100196fd7ce5SGreg Kroah-Hartman  *	Locking:
100296fd7ce5SGreg Kroah-Hartman  *		Locks the line discipline internally while needed. Multiple
100396fd7ce5SGreg Kroah-Hartman  *	read calls may be outstanding in parallel.
100496fd7ce5SGreg Kroah-Hartman  */
100596fd7ce5SGreg Kroah-Hartman 
100696fd7ce5SGreg Kroah-Hartman static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
100796fd7ce5SGreg Kroah-Hartman 			loff_t *ppos)
100896fd7ce5SGreg Kroah-Hartman {
100996fd7ce5SGreg Kroah-Hartman 	int i;
101096fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
101196fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
101296fd7ce5SGreg Kroah-Hartman 
10136131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_read"))
101496fd7ce5SGreg Kroah-Hartman 		return -EIO;
101596fd7ce5SGreg Kroah-Hartman 	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
101696fd7ce5SGreg Kroah-Hartman 		return -EIO;
101796fd7ce5SGreg Kroah-Hartman 
101896fd7ce5SGreg Kroah-Hartman 	/* We want to wait for the line discipline to sort out in this
101996fd7ce5SGreg Kroah-Hartman 	   situation */
102096fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
102196fd7ce5SGreg Kroah-Hartman 	if (ld->ops->read)
102296fd7ce5SGreg Kroah-Hartman 		i = (ld->ops->read)(tty, file, buf, count);
102396fd7ce5SGreg Kroah-Hartman 	else
102496fd7ce5SGreg Kroah-Hartman 		i = -EIO;
102596fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
1026b0de59b5SJiri Slaby 
102796fd7ce5SGreg Kroah-Hartman 	return i;
102896fd7ce5SGreg Kroah-Hartman }
102996fd7ce5SGreg Kroah-Hartman 
103096fd7ce5SGreg Kroah-Hartman void tty_write_unlock(struct tty_struct *tty)
103183c67571SJiri Slaby 	__releases(&tty->atomic_write_lock)
103296fd7ce5SGreg Kroah-Hartman {
103396fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty->atomic_write_lock);
103496fd7ce5SGreg Kroah-Hartman 	wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
103596fd7ce5SGreg Kroah-Hartman }
103696fd7ce5SGreg Kroah-Hartman 
103796fd7ce5SGreg Kroah-Hartman int tty_write_lock(struct tty_struct *tty, int ndelay)
103883c67571SJiri Slaby 	__acquires(&tty->atomic_write_lock)
103996fd7ce5SGreg Kroah-Hartman {
104096fd7ce5SGreg Kroah-Hartman 	if (!mutex_trylock(&tty->atomic_write_lock)) {
104196fd7ce5SGreg Kroah-Hartman 		if (ndelay)
104296fd7ce5SGreg Kroah-Hartman 			return -EAGAIN;
104396fd7ce5SGreg Kroah-Hartman 		if (mutex_lock_interruptible(&tty->atomic_write_lock))
104496fd7ce5SGreg Kroah-Hartman 			return -ERESTARTSYS;
104596fd7ce5SGreg Kroah-Hartman 	}
104696fd7ce5SGreg Kroah-Hartman 	return 0;
104796fd7ce5SGreg Kroah-Hartman }
104896fd7ce5SGreg Kroah-Hartman 
104996fd7ce5SGreg Kroah-Hartman /*
105096fd7ce5SGreg Kroah-Hartman  * Split writes up in sane blocksizes to avoid
105196fd7ce5SGreg Kroah-Hartman  * denial-of-service type attacks
105296fd7ce5SGreg Kroah-Hartman  */
105396fd7ce5SGreg Kroah-Hartman static inline ssize_t do_tty_write(
105496fd7ce5SGreg Kroah-Hartman 	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
105596fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty,
105696fd7ce5SGreg Kroah-Hartman 	struct file *file,
105796fd7ce5SGreg Kroah-Hartman 	const char __user *buf,
105896fd7ce5SGreg Kroah-Hartman 	size_t count)
105996fd7ce5SGreg Kroah-Hartman {
106096fd7ce5SGreg Kroah-Hartman 	ssize_t ret, written = 0;
106196fd7ce5SGreg Kroah-Hartman 	unsigned int chunk;
106296fd7ce5SGreg Kroah-Hartman 
106396fd7ce5SGreg Kroah-Hartman 	ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
106496fd7ce5SGreg Kroah-Hartman 	if (ret < 0)
106596fd7ce5SGreg Kroah-Hartman 		return ret;
106696fd7ce5SGreg Kroah-Hartman 
106796fd7ce5SGreg Kroah-Hartman 	/*
106896fd7ce5SGreg Kroah-Hartman 	 * We chunk up writes into a temporary buffer. This
106996fd7ce5SGreg Kroah-Hartman 	 * simplifies low-level drivers immensely, since they
107096fd7ce5SGreg Kroah-Hartman 	 * don't have locking issues and user mode accesses.
107196fd7ce5SGreg Kroah-Hartman 	 *
107296fd7ce5SGreg Kroah-Hartman 	 * But if TTY_NO_WRITE_SPLIT is set, we should use a
107396fd7ce5SGreg Kroah-Hartman 	 * big chunk-size..
107496fd7ce5SGreg Kroah-Hartman 	 *
107596fd7ce5SGreg Kroah-Hartman 	 * The default chunk-size is 2kB, because the NTTY
107696fd7ce5SGreg Kroah-Hartman 	 * layer has problems with bigger chunks. It will
107796fd7ce5SGreg Kroah-Hartman 	 * claim to be able to handle more characters than
107896fd7ce5SGreg Kroah-Hartman 	 * it actually does.
107996fd7ce5SGreg Kroah-Hartman 	 *
108096fd7ce5SGreg Kroah-Hartman 	 * FIXME: This can probably go away now except that 64K chunks
108196fd7ce5SGreg Kroah-Hartman 	 * are too likely to fail unless switched to vmalloc...
108296fd7ce5SGreg Kroah-Hartman 	 */
108396fd7ce5SGreg Kroah-Hartman 	chunk = 2048;
108496fd7ce5SGreg Kroah-Hartman 	if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
108596fd7ce5SGreg Kroah-Hartman 		chunk = 65536;
108696fd7ce5SGreg Kroah-Hartman 	if (count < chunk)
108796fd7ce5SGreg Kroah-Hartman 		chunk = count;
108896fd7ce5SGreg Kroah-Hartman 
108996fd7ce5SGreg Kroah-Hartman 	/* write_buf/write_cnt is protected by the atomic_write_lock mutex */
109096fd7ce5SGreg Kroah-Hartman 	if (tty->write_cnt < chunk) {
109196fd7ce5SGreg Kroah-Hartman 		unsigned char *buf_chunk;
109296fd7ce5SGreg Kroah-Hartman 
109396fd7ce5SGreg Kroah-Hartman 		if (chunk < 1024)
109496fd7ce5SGreg Kroah-Hartman 			chunk = 1024;
109596fd7ce5SGreg Kroah-Hartman 
109696fd7ce5SGreg Kroah-Hartman 		buf_chunk = kmalloc(chunk, GFP_KERNEL);
109796fd7ce5SGreg Kroah-Hartman 		if (!buf_chunk) {
109896fd7ce5SGreg Kroah-Hartman 			ret = -ENOMEM;
109996fd7ce5SGreg Kroah-Hartman 			goto out;
110096fd7ce5SGreg Kroah-Hartman 		}
110196fd7ce5SGreg Kroah-Hartman 		kfree(tty->write_buf);
110296fd7ce5SGreg Kroah-Hartman 		tty->write_cnt = chunk;
110396fd7ce5SGreg Kroah-Hartman 		tty->write_buf = buf_chunk;
110496fd7ce5SGreg Kroah-Hartman 	}
110596fd7ce5SGreg Kroah-Hartman 
110696fd7ce5SGreg Kroah-Hartman 	/* Do the write .. */
110796fd7ce5SGreg Kroah-Hartman 	for (;;) {
110896fd7ce5SGreg Kroah-Hartman 		size_t size = count;
110996fd7ce5SGreg Kroah-Hartman 		if (size > chunk)
111096fd7ce5SGreg Kroah-Hartman 			size = chunk;
111196fd7ce5SGreg Kroah-Hartman 		ret = -EFAULT;
111296fd7ce5SGreg Kroah-Hartman 		if (copy_from_user(tty->write_buf, buf, size))
111396fd7ce5SGreg Kroah-Hartman 			break;
111496fd7ce5SGreg Kroah-Hartman 		ret = write(tty, file, tty->write_buf, size);
111596fd7ce5SGreg Kroah-Hartman 		if (ret <= 0)
111696fd7ce5SGreg Kroah-Hartman 			break;
111796fd7ce5SGreg Kroah-Hartman 		written += ret;
111896fd7ce5SGreg Kroah-Hartman 		buf += ret;
111996fd7ce5SGreg Kroah-Hartman 		count -= ret;
112096fd7ce5SGreg Kroah-Hartman 		if (!count)
112196fd7ce5SGreg Kroah-Hartman 			break;
112296fd7ce5SGreg Kroah-Hartman 		ret = -ERESTARTSYS;
112396fd7ce5SGreg Kroah-Hartman 		if (signal_pending(current))
112496fd7ce5SGreg Kroah-Hartman 			break;
112596fd7ce5SGreg Kroah-Hartman 		cond_resched();
112696fd7ce5SGreg Kroah-Hartman 	}
1127b0de59b5SJiri Slaby 	if (written)
112896fd7ce5SGreg Kroah-Hartman 		ret = written;
112996fd7ce5SGreg Kroah-Hartman out:
113096fd7ce5SGreg Kroah-Hartman 	tty_write_unlock(tty);
113196fd7ce5SGreg Kroah-Hartman 	return ret;
113296fd7ce5SGreg Kroah-Hartman }
113396fd7ce5SGreg Kroah-Hartman 
113496fd7ce5SGreg Kroah-Hartman /**
113596fd7ce5SGreg Kroah-Hartman  * tty_write_message - write a message to a certain tty, not just the console.
113696fd7ce5SGreg Kroah-Hartman  * @tty: the destination tty_struct
113796fd7ce5SGreg Kroah-Hartman  * @msg: the message to write
113896fd7ce5SGreg Kroah-Hartman  *
113996fd7ce5SGreg Kroah-Hartman  * This is used for messages that need to be redirected to a specific tty.
114096fd7ce5SGreg Kroah-Hartman  * We don't put it into the syslog queue right now maybe in the future if
114196fd7ce5SGreg Kroah-Hartman  * really needed.
114296fd7ce5SGreg Kroah-Hartman  *
114396fd7ce5SGreg Kroah-Hartman  * We must still hold the BTM and test the CLOSING flag for the moment.
114496fd7ce5SGreg Kroah-Hartman  */
114596fd7ce5SGreg Kroah-Hartman 
114696fd7ce5SGreg Kroah-Hartman void tty_write_message(struct tty_struct *tty, char *msg)
114796fd7ce5SGreg Kroah-Hartman {
114896fd7ce5SGreg Kroah-Hartman 	if (tty) {
114996fd7ce5SGreg Kroah-Hartman 		mutex_lock(&tty->atomic_write_lock);
115089c8d91eSAlan Cox 		tty_lock(tty);
115196fd7ce5SGreg Kroah-Hartman 		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
115289c8d91eSAlan Cox 			tty_unlock(tty);
115396fd7ce5SGreg Kroah-Hartman 			tty->ops->write(tty, msg, strlen(msg));
115496fd7ce5SGreg Kroah-Hartman 		} else
115589c8d91eSAlan Cox 			tty_unlock(tty);
115696fd7ce5SGreg Kroah-Hartman 		tty_write_unlock(tty);
115796fd7ce5SGreg Kroah-Hartman 	}
115896fd7ce5SGreg Kroah-Hartman 	return;
115996fd7ce5SGreg Kroah-Hartman }
116096fd7ce5SGreg Kroah-Hartman 
116196fd7ce5SGreg Kroah-Hartman 
116296fd7ce5SGreg Kroah-Hartman /**
116396fd7ce5SGreg Kroah-Hartman  *	tty_write		-	write method for tty device file
116496fd7ce5SGreg Kroah-Hartman  *	@file: tty file pointer
116596fd7ce5SGreg Kroah-Hartman  *	@buf: user data to write
116696fd7ce5SGreg Kroah-Hartman  *	@count: bytes to write
116796fd7ce5SGreg Kroah-Hartman  *	@ppos: unused
116896fd7ce5SGreg Kroah-Hartman  *
116996fd7ce5SGreg Kroah-Hartman  *	Write data to a tty device via the line discipline.
117096fd7ce5SGreg Kroah-Hartman  *
117196fd7ce5SGreg Kroah-Hartman  *	Locking:
117296fd7ce5SGreg Kroah-Hartman  *		Locks the line discipline as required
117396fd7ce5SGreg Kroah-Hartman  *		Writes to the tty driver are serialized by the atomic_write_lock
117496fd7ce5SGreg Kroah-Hartman  *	and are then processed in chunks to the device. The line discipline
117596fd7ce5SGreg Kroah-Hartman  *	write method will not be invoked in parallel for each device.
117696fd7ce5SGreg Kroah-Hartman  */
117796fd7ce5SGreg Kroah-Hartman 
117896fd7ce5SGreg Kroah-Hartman static ssize_t tty_write(struct file *file, const char __user *buf,
117996fd7ce5SGreg Kroah-Hartman 						size_t count, loff_t *ppos)
118096fd7ce5SGreg Kroah-Hartman {
118196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
118296fd7ce5SGreg Kroah-Hartman  	struct tty_ldisc *ld;
118396fd7ce5SGreg Kroah-Hartman 	ssize_t ret;
118496fd7ce5SGreg Kroah-Hartman 
11856131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_write"))
118696fd7ce5SGreg Kroah-Hartman 		return -EIO;
118796fd7ce5SGreg Kroah-Hartman 	if (!tty || !tty->ops->write ||
118896fd7ce5SGreg Kroah-Hartman 		(test_bit(TTY_IO_ERROR, &tty->flags)))
118996fd7ce5SGreg Kroah-Hartman 			return -EIO;
119096fd7ce5SGreg Kroah-Hartman 	/* Short term debug to catch buggy drivers */
119196fd7ce5SGreg Kroah-Hartman 	if (tty->ops->write_room == NULL)
119296fd7ce5SGreg Kroah-Hartman 		printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
119396fd7ce5SGreg Kroah-Hartman 			tty->driver->name);
119496fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
119596fd7ce5SGreg Kroah-Hartman 	if (!ld->ops->write)
119696fd7ce5SGreg Kroah-Hartman 		ret = -EIO;
119796fd7ce5SGreg Kroah-Hartman 	else
119896fd7ce5SGreg Kroah-Hartman 		ret = do_tty_write(ld->ops->write, tty, file, buf, count);
119996fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
120096fd7ce5SGreg Kroah-Hartman 	return ret;
120196fd7ce5SGreg Kroah-Hartman }
120296fd7ce5SGreg Kroah-Hartman 
120396fd7ce5SGreg Kroah-Hartman ssize_t redirected_tty_write(struct file *file, const char __user *buf,
120496fd7ce5SGreg Kroah-Hartman 						size_t count, loff_t *ppos)
120596fd7ce5SGreg Kroah-Hartman {
120696fd7ce5SGreg Kroah-Hartman 	struct file *p = NULL;
120796fd7ce5SGreg Kroah-Hartman 
120896fd7ce5SGreg Kroah-Hartman 	spin_lock(&redirect_lock);
1209cb0942b8SAl Viro 	if (redirect)
1210cb0942b8SAl Viro 		p = get_file(redirect);
121196fd7ce5SGreg Kroah-Hartman 	spin_unlock(&redirect_lock);
121296fd7ce5SGreg Kroah-Hartman 
121396fd7ce5SGreg Kroah-Hartman 	if (p) {
121496fd7ce5SGreg Kroah-Hartman 		ssize_t res;
121596fd7ce5SGreg Kroah-Hartman 		res = vfs_write(p, buf, count, &p->f_pos);
121696fd7ce5SGreg Kroah-Hartman 		fput(p);
121796fd7ce5SGreg Kroah-Hartman 		return res;
121896fd7ce5SGreg Kroah-Hartman 	}
121996fd7ce5SGreg Kroah-Hartman 	return tty_write(file, buf, count, ppos);
122096fd7ce5SGreg Kroah-Hartman }
122196fd7ce5SGreg Kroah-Hartman 
122296fd7ce5SGreg Kroah-Hartman static char ptychar[] = "pqrstuvwxyzabcde";
122396fd7ce5SGreg Kroah-Hartman 
122496fd7ce5SGreg Kroah-Hartman /**
122596fd7ce5SGreg Kroah-Hartman  *	pty_line_name	-	generate name for a pty
122696fd7ce5SGreg Kroah-Hartman  *	@driver: the tty driver in use
122796fd7ce5SGreg Kroah-Hartman  *	@index: the minor number
122896fd7ce5SGreg Kroah-Hartman  *	@p: output buffer of at least 6 bytes
122996fd7ce5SGreg Kroah-Hartman  *
123096fd7ce5SGreg Kroah-Hartman  *	Generate a name from a driver reference and write it to the output
123196fd7ce5SGreg Kroah-Hartman  *	buffer.
123296fd7ce5SGreg Kroah-Hartman  *
123396fd7ce5SGreg Kroah-Hartman  *	Locking: None
123496fd7ce5SGreg Kroah-Hartman  */
123596fd7ce5SGreg Kroah-Hartman static void pty_line_name(struct tty_driver *driver, int index, char *p)
123696fd7ce5SGreg Kroah-Hartman {
123796fd7ce5SGreg Kroah-Hartman 	int i = index + driver->name_base;
123896fd7ce5SGreg Kroah-Hartman 	/* ->name is initialized to "ttyp", but "tty" is expected */
123996fd7ce5SGreg Kroah-Hartman 	sprintf(p, "%s%c%x",
124096fd7ce5SGreg Kroah-Hartman 		driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name,
124196fd7ce5SGreg Kroah-Hartman 		ptychar[i >> 4 & 0xf], i & 0xf);
124296fd7ce5SGreg Kroah-Hartman }
124396fd7ce5SGreg Kroah-Hartman 
124496fd7ce5SGreg Kroah-Hartman /**
124596fd7ce5SGreg Kroah-Hartman  *	tty_line_name	-	generate name for a tty
124696fd7ce5SGreg Kroah-Hartman  *	@driver: the tty driver in use
124796fd7ce5SGreg Kroah-Hartman  *	@index: the minor number
124896fd7ce5SGreg Kroah-Hartman  *	@p: output buffer of at least 7 bytes
124996fd7ce5SGreg Kroah-Hartman  *
125096fd7ce5SGreg Kroah-Hartman  *	Generate a name from a driver reference and write it to the output
125196fd7ce5SGreg Kroah-Hartman  *	buffer.
125296fd7ce5SGreg Kroah-Hartman  *
125396fd7ce5SGreg Kroah-Hartman  *	Locking: None
125496fd7ce5SGreg Kroah-Hartman  */
125596fd7ce5SGreg Kroah-Hartman static void tty_line_name(struct tty_driver *driver, int index, char *p)
125696fd7ce5SGreg Kroah-Hartman {
12570019b408SJiri Slaby 	if (driver->flags & TTY_DRIVER_UNNUMBERED_NODE)
12580019b408SJiri Slaby 		strcpy(p, driver->name);
12590019b408SJiri Slaby 	else
126096fd7ce5SGreg Kroah-Hartman 		sprintf(p, "%s%d", driver->name, index + driver->name_base);
126196fd7ce5SGreg Kroah-Hartman }
126296fd7ce5SGreg Kroah-Hartman 
126396fd7ce5SGreg Kroah-Hartman /**
126496fd7ce5SGreg Kroah-Hartman  *	tty_driver_lookup_tty() - find an existing tty, if any
126596fd7ce5SGreg Kroah-Hartman  *	@driver: the driver for the tty
126696fd7ce5SGreg Kroah-Hartman  *	@idx:	 the minor number
126796fd7ce5SGreg Kroah-Hartman  *
126896fd7ce5SGreg Kroah-Hartman  *	Return the tty, if found or ERR_PTR() otherwise.
126996fd7ce5SGreg Kroah-Hartman  *
127096fd7ce5SGreg Kroah-Hartman  *	Locking: tty_mutex must be held. If tty is found, the mutex must
127196fd7ce5SGreg Kroah-Hartman  *	be held until the 'fast-open' is also done. Will change once we
127296fd7ce5SGreg Kroah-Hartman  *	have refcounting in the driver and per driver locking
127396fd7ce5SGreg Kroah-Hartman  */
127496fd7ce5SGreg Kroah-Hartman static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
127596fd7ce5SGreg Kroah-Hartman 		struct inode *inode, int idx)
127696fd7ce5SGreg Kroah-Hartman {
127796fd7ce5SGreg Kroah-Hartman 	if (driver->ops->lookup)
127896fd7ce5SGreg Kroah-Hartman 		return driver->ops->lookup(driver, inode, idx);
127996fd7ce5SGreg Kroah-Hartman 
1280d4834267SJiri Slaby 	return driver->ttys[idx];
128196fd7ce5SGreg Kroah-Hartman }
128296fd7ce5SGreg Kroah-Hartman 
128396fd7ce5SGreg Kroah-Hartman /**
128496fd7ce5SGreg Kroah-Hartman  *	tty_init_termios	-  helper for termios setup
128596fd7ce5SGreg Kroah-Hartman  *	@tty: the tty to set up
128696fd7ce5SGreg Kroah-Hartman  *
128796fd7ce5SGreg Kroah-Hartman  *	Initialise the termios structures for this tty. Thus runs under
128896fd7ce5SGreg Kroah-Hartman  *	the tty_mutex currently so we can be relaxed about ordering.
128996fd7ce5SGreg Kroah-Hartman  */
129096fd7ce5SGreg Kroah-Hartman 
129196fd7ce5SGreg Kroah-Hartman int tty_init_termios(struct tty_struct *tty)
129296fd7ce5SGreg Kroah-Hartman {
129396fd7ce5SGreg Kroah-Hartman 	struct ktermios *tp;
129496fd7ce5SGreg Kroah-Hartman 	int idx = tty->index;
129596fd7ce5SGreg Kroah-Hartman 
129636b3c070SAlan Cox 	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
129736b3c070SAlan Cox 		tty->termios = tty->driver->init_termios;
129836b3c070SAlan Cox 	else {
129936b3c070SAlan Cox 		/* Check for lazy saved data */
130096fd7ce5SGreg Kroah-Hartman 		tp = tty->driver->termios[idx];
130136b3c070SAlan Cox 		if (tp != NULL)
1302adc8d746SAlan Cox 			tty->termios = *tp;
130336b3c070SAlan Cox 		else
130436b3c070SAlan Cox 			tty->termios = tty->driver->init_termios;
130536b3c070SAlan Cox 	}
130696fd7ce5SGreg Kroah-Hartman 	/* Compatibility until drivers always set this */
1307adc8d746SAlan Cox 	tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
1308adc8d746SAlan Cox 	tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
130996fd7ce5SGreg Kroah-Hartman 	return 0;
131096fd7ce5SGreg Kroah-Hartman }
131196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_init_termios);
131296fd7ce5SGreg Kroah-Hartman 
131366d450e8SJiri Slaby int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
131466d450e8SJiri Slaby {
131566d450e8SJiri Slaby 	int ret = tty_init_termios(tty);
131666d450e8SJiri Slaby 	if (ret)
131766d450e8SJiri Slaby 		return ret;
131866d450e8SJiri Slaby 
131966d450e8SJiri Slaby 	tty_driver_kref_get(driver);
132066d450e8SJiri Slaby 	tty->count++;
132166d450e8SJiri Slaby 	driver->ttys[tty->index] = tty;
132266d450e8SJiri Slaby 	return 0;
132366d450e8SJiri Slaby }
132466d450e8SJiri Slaby EXPORT_SYMBOL_GPL(tty_standard_install);
132566d450e8SJiri Slaby 
132696fd7ce5SGreg Kroah-Hartman /**
132796fd7ce5SGreg Kroah-Hartman  *	tty_driver_install_tty() - install a tty entry in the driver
132896fd7ce5SGreg Kroah-Hartman  *	@driver: the driver for the tty
132996fd7ce5SGreg Kroah-Hartman  *	@tty: the tty
133096fd7ce5SGreg Kroah-Hartman  *
133196fd7ce5SGreg Kroah-Hartman  *	Install a tty object into the driver tables. The tty->index field
133296fd7ce5SGreg Kroah-Hartman  *	will be set by the time this is called. This method is responsible
133396fd7ce5SGreg Kroah-Hartman  *	for ensuring any need additional structures are allocated and
133496fd7ce5SGreg Kroah-Hartman  *	configured.
133596fd7ce5SGreg Kroah-Hartman  *
133696fd7ce5SGreg Kroah-Hartman  *	Locking: tty_mutex for now
133796fd7ce5SGreg Kroah-Hartman  */
133896fd7ce5SGreg Kroah-Hartman static int tty_driver_install_tty(struct tty_driver *driver,
133996fd7ce5SGreg Kroah-Hartman 						struct tty_struct *tty)
134096fd7ce5SGreg Kroah-Hartman {
134166d450e8SJiri Slaby 	return driver->ops->install ? driver->ops->install(driver, tty) :
134266d450e8SJiri Slaby 		tty_standard_install(driver, tty);
134396fd7ce5SGreg Kroah-Hartman }
134496fd7ce5SGreg Kroah-Hartman 
134596fd7ce5SGreg Kroah-Hartman /**
134696fd7ce5SGreg Kroah-Hartman  *	tty_driver_remove_tty() - remove a tty from the driver tables
134796fd7ce5SGreg Kroah-Hartman  *	@driver: the driver for the tty
134896fd7ce5SGreg Kroah-Hartman  *	@idx:	 the minor number
134996fd7ce5SGreg Kroah-Hartman  *
135096fd7ce5SGreg Kroah-Hartman  *	Remvoe a tty object from the driver tables. The tty->index field
135196fd7ce5SGreg Kroah-Hartman  *	will be set by the time this is called.
135296fd7ce5SGreg Kroah-Hartman  *
135396fd7ce5SGreg Kroah-Hartman  *	Locking: tty_mutex for now
135496fd7ce5SGreg Kroah-Hartman  */
135524d406a6SJiri Slaby void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
135696fd7ce5SGreg Kroah-Hartman {
135796fd7ce5SGreg Kroah-Hartman 	if (driver->ops->remove)
135896fd7ce5SGreg Kroah-Hartman 		driver->ops->remove(driver, tty);
135996fd7ce5SGreg Kroah-Hartman 	else
136096fd7ce5SGreg Kroah-Hartman 		driver->ttys[tty->index] = NULL;
136196fd7ce5SGreg Kroah-Hartman }
136296fd7ce5SGreg Kroah-Hartman 
136396fd7ce5SGreg Kroah-Hartman /*
136496fd7ce5SGreg Kroah-Hartman  * 	tty_reopen()	- fast re-open of an open tty
136596fd7ce5SGreg Kroah-Hartman  * 	@tty	- the tty to open
136696fd7ce5SGreg Kroah-Hartman  *
136796fd7ce5SGreg Kroah-Hartman  *	Return 0 on success, -errno on error.
136896fd7ce5SGreg Kroah-Hartman  *
136996fd7ce5SGreg Kroah-Hartman  *	Locking: tty_mutex must be held from the time the tty was found
137096fd7ce5SGreg Kroah-Hartman  *		 till this open completes.
137196fd7ce5SGreg Kroah-Hartman  */
137296fd7ce5SGreg Kroah-Hartman static int tty_reopen(struct tty_struct *tty)
137396fd7ce5SGreg Kroah-Hartman {
137496fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver = tty->driver;
137596fd7ce5SGreg Kroah-Hartman 
1376e2efafbfSJiri Slaby 	if (test_bit(TTY_CLOSING, &tty->flags) ||
1377acfa747bSJiri Slaby 			test_bit(TTY_HUPPING, &tty->flags) ||
1378e2efafbfSJiri Slaby 			test_bit(TTY_LDISC_CHANGING, &tty->flags))
137996fd7ce5SGreg Kroah-Hartman 		return -EIO;
138096fd7ce5SGreg Kroah-Hartman 
138196fd7ce5SGreg Kroah-Hartman 	if (driver->type == TTY_DRIVER_TYPE_PTY &&
138296fd7ce5SGreg Kroah-Hartman 	    driver->subtype == PTY_TYPE_MASTER) {
138396fd7ce5SGreg Kroah-Hartman 		/*
138496fd7ce5SGreg Kroah-Hartman 		 * special case for PTY masters: only one open permitted,
138596fd7ce5SGreg Kroah-Hartman 		 * and the slave side open count is incremented as well.
138696fd7ce5SGreg Kroah-Hartman 		 */
138796fd7ce5SGreg Kroah-Hartman 		if (tty->count)
138896fd7ce5SGreg Kroah-Hartman 			return -EIO;
138996fd7ce5SGreg Kroah-Hartman 
139096fd7ce5SGreg Kroah-Hartman 		tty->link->count++;
139196fd7ce5SGreg Kroah-Hartman 	}
139296fd7ce5SGreg Kroah-Hartman 	tty->count++;
139396fd7ce5SGreg Kroah-Hartman 
139496fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty->ldisc_mutex);
139596fd7ce5SGreg Kroah-Hartman 	WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
139696fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty->ldisc_mutex);
139796fd7ce5SGreg Kroah-Hartman 
139896fd7ce5SGreg Kroah-Hartman 	return 0;
139996fd7ce5SGreg Kroah-Hartman }
140096fd7ce5SGreg Kroah-Hartman 
140196fd7ce5SGreg Kroah-Hartman /**
140296fd7ce5SGreg Kroah-Hartman  *	tty_init_dev		-	initialise a tty device
140396fd7ce5SGreg Kroah-Hartman  *	@driver: tty driver we are opening a device on
140496fd7ce5SGreg Kroah-Hartman  *	@idx: device index
140596fd7ce5SGreg Kroah-Hartman  *	@ret_tty: returned tty structure
140696fd7ce5SGreg Kroah-Hartman  *
140796fd7ce5SGreg Kroah-Hartman  *	Prepare a tty device. This may not be a "new" clean device but
140896fd7ce5SGreg Kroah-Hartman  *	could also be an active device. The pty drivers require special
140996fd7ce5SGreg Kroah-Hartman  *	handling because of this.
141096fd7ce5SGreg Kroah-Hartman  *
141196fd7ce5SGreg Kroah-Hartman  *	Locking:
141296fd7ce5SGreg Kroah-Hartman  *		The function is called under the tty_mutex, which
141396fd7ce5SGreg Kroah-Hartman  *	protects us from the tty struct or driver itself going away.
141496fd7ce5SGreg Kroah-Hartman  *
141596fd7ce5SGreg Kroah-Hartman  *	On exit the tty device has the line discipline attached and
141696fd7ce5SGreg Kroah-Hartman  *	a reference count of 1. If a pair was created for pty/tty use
141796fd7ce5SGreg Kroah-Hartman  *	and the other was a pty master then it too has a reference count of 1.
141896fd7ce5SGreg Kroah-Hartman  *
141996fd7ce5SGreg Kroah-Hartman  * WSH 06/09/97: Rewritten to remove races and properly clean up after a
142096fd7ce5SGreg Kroah-Hartman  * failed open.  The new code protects the open with a mutex, so it's
142196fd7ce5SGreg Kroah-Hartman  * really quite straightforward.  The mutex locking can probably be
142296fd7ce5SGreg Kroah-Hartman  * relaxed for the (most common) case of reopening a tty.
142396fd7ce5SGreg Kroah-Hartman  */
142496fd7ce5SGreg Kroah-Hartman 
1425593a27c4SKonstantin Khlebnikov struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
142696fd7ce5SGreg Kroah-Hartman {
142796fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
142896fd7ce5SGreg Kroah-Hartman 	int retval;
142996fd7ce5SGreg Kroah-Hartman 
143096fd7ce5SGreg Kroah-Hartman 	/*
143196fd7ce5SGreg Kroah-Hartman 	 * First time open is complex, especially for PTY devices.
143296fd7ce5SGreg Kroah-Hartman 	 * This code guarantees that either everything succeeds and the
143396fd7ce5SGreg Kroah-Hartman 	 * TTY is ready for operation, or else the table slots are vacated
143496fd7ce5SGreg Kroah-Hartman 	 * and the allocated memory released.  (Except that the termios
143596fd7ce5SGreg Kroah-Hartman 	 * and locked termios may be retained.)
143696fd7ce5SGreg Kroah-Hartman 	 */
143796fd7ce5SGreg Kroah-Hartman 
143896fd7ce5SGreg Kroah-Hartman 	if (!try_module_get(driver->owner))
143996fd7ce5SGreg Kroah-Hartman 		return ERR_PTR(-ENODEV);
144096fd7ce5SGreg Kroah-Hartman 
144196fd7ce5SGreg Kroah-Hartman 	tty = alloc_tty_struct();
1442d5543503SJiri Slaby 	if (!tty) {
1443d5543503SJiri Slaby 		retval = -ENOMEM;
1444d5543503SJiri Slaby 		goto err_module_put;
1445d5543503SJiri Slaby 	}
144696fd7ce5SGreg Kroah-Hartman 	initialize_tty_struct(tty, driver, idx);
144796fd7ce5SGreg Kroah-Hartman 
144889c8d91eSAlan Cox 	tty_lock(tty);
144996fd7ce5SGreg Kroah-Hartman 	retval = tty_driver_install_tty(driver, tty);
1450d5543503SJiri Slaby 	if (retval < 0)
1451a9dccddbSJiri Slaby 		goto err_deinit_tty;
145296fd7ce5SGreg Kroah-Hartman 
145304831dc1SJiri Slaby 	if (!tty->port)
145404831dc1SJiri Slaby 		tty->port = driver->ports[idx];
145504831dc1SJiri Slaby 
14565d4121c0SJiri Slaby 	WARN_RATELIMIT(!tty->port,
14575d4121c0SJiri Slaby 			"%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
14585d4121c0SJiri Slaby 			__func__, tty->driver->name);
14595d4121c0SJiri Slaby 
1460967fab69SJiri Slaby 	tty->port->itty = tty;
1461967fab69SJiri Slaby 
146296fd7ce5SGreg Kroah-Hartman 	/*
146396fd7ce5SGreg Kroah-Hartman 	 * Structures all installed ... call the ldisc open routines.
146496fd7ce5SGreg Kroah-Hartman 	 * If we fail here just call release_tty to clean up.  No need
146596fd7ce5SGreg Kroah-Hartman 	 * to decrement the use counts, as release_tty doesn't care.
146696fd7ce5SGreg Kroah-Hartman 	 */
146796fd7ce5SGreg Kroah-Hartman 	retval = tty_ldisc_setup(tty, tty->link);
146896fd7ce5SGreg Kroah-Hartman 	if (retval)
1469d5543503SJiri Slaby 		goto err_release_tty;
147089c8d91eSAlan Cox 	/* Return the tty locked so that it cannot vanish under the caller */
147196fd7ce5SGreg Kroah-Hartman 	return tty;
147296fd7ce5SGreg Kroah-Hartman 
1473a9dccddbSJiri Slaby err_deinit_tty:
147489c8d91eSAlan Cox 	tty_unlock(tty);
1475a9dccddbSJiri Slaby 	deinitialize_tty_struct(tty);
1476d5543503SJiri Slaby 	free_tty_struct(tty);
1477d5543503SJiri Slaby err_module_put:
147896fd7ce5SGreg Kroah-Hartman 	module_put(driver->owner);
1479d5543503SJiri Slaby 	return ERR_PTR(retval);
148096fd7ce5SGreg Kroah-Hartman 
148196fd7ce5SGreg Kroah-Hartman 	/* call the tty release_tty routine to clean out this slot */
1482d5543503SJiri Slaby err_release_tty:
148389c8d91eSAlan Cox 	tty_unlock(tty);
14845a3c6b25SManuel Zerpies 	printk_ratelimited(KERN_INFO "tty_init_dev: ldisc open failed, "
148596fd7ce5SGreg Kroah-Hartman 				 "clearing slot %d\n", idx);
148696fd7ce5SGreg Kroah-Hartman 	release_tty(tty, idx);
148796fd7ce5SGreg Kroah-Hartman 	return ERR_PTR(retval);
148896fd7ce5SGreg Kroah-Hartman }
148996fd7ce5SGreg Kroah-Hartman 
149096fd7ce5SGreg Kroah-Hartman void tty_free_termios(struct tty_struct *tty)
149196fd7ce5SGreg Kroah-Hartman {
149296fd7ce5SGreg Kroah-Hartman 	struct ktermios *tp;
149396fd7ce5SGreg Kroah-Hartman 	int idx = tty->index;
149436b3c070SAlan Cox 
149536b3c070SAlan Cox 	/* If the port is going to reset then it has no termios to save */
149636b3c070SAlan Cox 	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
149736b3c070SAlan Cox 		return;
149836b3c070SAlan Cox 
149936b3c070SAlan Cox 	/* Stash the termios data */
1500adc8d746SAlan Cox 	tp = tty->driver->termios[idx];
150136b3c070SAlan Cox 	if (tp == NULL) {
150236b3c070SAlan Cox 		tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);
150336b3c070SAlan Cox 		if (tp == NULL) {
150436b3c070SAlan Cox 			pr_warn("tty: no memory to save termios state.\n");
150536b3c070SAlan Cox 			return;
150696fd7ce5SGreg Kroah-Hartman 		}
15074ac5d705SDan Carpenter 		tty->driver->termios[idx] = tp;
150836b3c070SAlan Cox 	}
150936b3c070SAlan Cox 	*tp = tty->termios;
151096fd7ce5SGreg Kroah-Hartman }
151196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_free_termios);
151296fd7ce5SGreg Kroah-Hartman 
151396fd7ce5SGreg Kroah-Hartman 
151496fd7ce5SGreg Kroah-Hartman /**
151596fd7ce5SGreg Kroah-Hartman  *	release_one_tty		-	release tty structure memory
151696fd7ce5SGreg Kroah-Hartman  *	@kref: kref of tty we are obliterating
151796fd7ce5SGreg Kroah-Hartman  *
151896fd7ce5SGreg Kroah-Hartman  *	Releases memory associated with a tty structure, and clears out the
151996fd7ce5SGreg Kroah-Hartman  *	driver table slots. This function is called when a device is no longer
152096fd7ce5SGreg Kroah-Hartman  *	in use. It also gets called when setup of a device fails.
152196fd7ce5SGreg Kroah-Hartman  *
152296fd7ce5SGreg Kroah-Hartman  *	Locking:
152396fd7ce5SGreg Kroah-Hartman  *		takes the file list lock internally when working on the list
152496fd7ce5SGreg Kroah-Hartman  *	of ttys that the driver keeps.
152596fd7ce5SGreg Kroah-Hartman  *
152696fd7ce5SGreg Kroah-Hartman  *	This method gets called from a work queue so that the driver private
152796fd7ce5SGreg Kroah-Hartman  *	cleanup ops can sleep (needed for USB at least)
152896fd7ce5SGreg Kroah-Hartman  */
152996fd7ce5SGreg Kroah-Hartman static void release_one_tty(struct work_struct *work)
153096fd7ce5SGreg Kroah-Hartman {
153196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty =
153296fd7ce5SGreg Kroah-Hartman 		container_of(work, struct tty_struct, hangup_work);
153396fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver = tty->driver;
153496fd7ce5SGreg Kroah-Hartman 
153596fd7ce5SGreg Kroah-Hartman 	if (tty->ops->cleanup)
153696fd7ce5SGreg Kroah-Hartman 		tty->ops->cleanup(tty);
153796fd7ce5SGreg Kroah-Hartman 
153896fd7ce5SGreg Kroah-Hartman 	tty->magic = 0;
153996fd7ce5SGreg Kroah-Hartman 	tty_driver_kref_put(driver);
154096fd7ce5SGreg Kroah-Hartman 	module_put(driver->owner);
154196fd7ce5SGreg Kroah-Hartman 
154296fd7ce5SGreg Kroah-Hartman 	spin_lock(&tty_files_lock);
154396fd7ce5SGreg Kroah-Hartman 	list_del_init(&tty->tty_files);
154496fd7ce5SGreg Kroah-Hartman 	spin_unlock(&tty_files_lock);
154596fd7ce5SGreg Kroah-Hartman 
154696fd7ce5SGreg Kroah-Hartman 	put_pid(tty->pgrp);
154796fd7ce5SGreg Kroah-Hartman 	put_pid(tty->session);
154896fd7ce5SGreg Kroah-Hartman 	free_tty_struct(tty);
154996fd7ce5SGreg Kroah-Hartman }
155096fd7ce5SGreg Kroah-Hartman 
155196fd7ce5SGreg Kroah-Hartman static void queue_release_one_tty(struct kref *kref)
155296fd7ce5SGreg Kroah-Hartman {
155396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
155496fd7ce5SGreg Kroah-Hartman 
155596fd7ce5SGreg Kroah-Hartman 	/* The hangup queue is now free so we can reuse it rather than
155696fd7ce5SGreg Kroah-Hartman 	   waste a chunk of memory for each port */
155796fd7ce5SGreg Kroah-Hartman 	INIT_WORK(&tty->hangup_work, release_one_tty);
155896fd7ce5SGreg Kroah-Hartman 	schedule_work(&tty->hangup_work);
155996fd7ce5SGreg Kroah-Hartman }
156096fd7ce5SGreg Kroah-Hartman 
156196fd7ce5SGreg Kroah-Hartman /**
156296fd7ce5SGreg Kroah-Hartman  *	tty_kref_put		-	release a tty kref
156396fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
156496fd7ce5SGreg Kroah-Hartman  *
156596fd7ce5SGreg Kroah-Hartman  *	Release a reference to a tty device and if need be let the kref
156696fd7ce5SGreg Kroah-Hartman  *	layer destruct the object for us
156796fd7ce5SGreg Kroah-Hartman  */
156896fd7ce5SGreg Kroah-Hartman 
156996fd7ce5SGreg Kroah-Hartman void tty_kref_put(struct tty_struct *tty)
157096fd7ce5SGreg Kroah-Hartman {
157196fd7ce5SGreg Kroah-Hartman 	if (tty)
157296fd7ce5SGreg Kroah-Hartman 		kref_put(&tty->kref, queue_release_one_tty);
157396fd7ce5SGreg Kroah-Hartman }
157496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_kref_put);
157596fd7ce5SGreg Kroah-Hartman 
157696fd7ce5SGreg Kroah-Hartman /**
157796fd7ce5SGreg Kroah-Hartman  *	release_tty		-	release tty structure memory
157896fd7ce5SGreg Kroah-Hartman  *
157996fd7ce5SGreg Kroah-Hartman  *	Release both @tty and a possible linked partner (think pty pair),
158096fd7ce5SGreg Kroah-Hartman  *	and decrement the refcount of the backing module.
158196fd7ce5SGreg Kroah-Hartman  *
158296fd7ce5SGreg Kroah-Hartman  *	Locking:
1583d155255aSAlan Cox  *		tty_mutex
158496fd7ce5SGreg Kroah-Hartman  *		takes the file list lock internally when working on the list
158596fd7ce5SGreg Kroah-Hartman  *	of ttys that the driver keeps.
158696fd7ce5SGreg Kroah-Hartman  *
158796fd7ce5SGreg Kroah-Hartman  */
158896fd7ce5SGreg Kroah-Hartman static void release_tty(struct tty_struct *tty, int idx)
158996fd7ce5SGreg Kroah-Hartman {
159096fd7ce5SGreg Kroah-Hartman 	/* This should always be true but check for the moment */
159196fd7ce5SGreg Kroah-Hartman 	WARN_ON(tty->index != idx);
1592d155255aSAlan Cox 	WARN_ON(!mutex_is_locked(&tty_mutex));
159336b3c070SAlan Cox 	if (tty->ops->shutdown)
159436b3c070SAlan Cox 		tty->ops->shutdown(tty);
159536b3c070SAlan Cox 	tty_free_termios(tty);
159636b3c070SAlan Cox 	tty_driver_remove_tty(tty->driver, tty);
1597967fab69SJiri Slaby 	tty->port->itty = NULL;
15984f98d467SPeter Hurley 	cancel_work_sync(&tty->port->buf.work);
159936b3c070SAlan Cox 
160096fd7ce5SGreg Kroah-Hartman 	if (tty->link)
160196fd7ce5SGreg Kroah-Hartman 		tty_kref_put(tty->link);
160296fd7ce5SGreg Kroah-Hartman 	tty_kref_put(tty);
160396fd7ce5SGreg Kroah-Hartman }
160496fd7ce5SGreg Kroah-Hartman 
160596fd7ce5SGreg Kroah-Hartman /**
1606955787caSJiri Slaby  *	tty_release_checks - check a tty before real release
1607955787caSJiri Slaby  *	@tty: tty to check
1608955787caSJiri Slaby  *	@o_tty: link of @tty (if any)
1609955787caSJiri Slaby  *	@idx: index of the tty
1610955787caSJiri Slaby  *
1611955787caSJiri Slaby  *	Performs some paranoid checking before true release of the @tty.
1612955787caSJiri Slaby  *	This is a no-op unless TTY_PARANOIA_CHECK is defined.
1613955787caSJiri Slaby  */
1614955787caSJiri Slaby static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty,
1615955787caSJiri Slaby 		int idx)
1616955787caSJiri Slaby {
1617955787caSJiri Slaby #ifdef TTY_PARANOIA_CHECK
1618955787caSJiri Slaby 	if (idx < 0 || idx >= tty->driver->num) {
16199de44bd6SJiri Slaby 		printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n",
16209de44bd6SJiri Slaby 				__func__, tty->name);
1621955787caSJiri Slaby 		return -1;
1622955787caSJiri Slaby 	}
1623955787caSJiri Slaby 
1624955787caSJiri Slaby 	/* not much to check for devpts */
1625955787caSJiri Slaby 	if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)
1626955787caSJiri Slaby 		return 0;
1627955787caSJiri Slaby 
1628955787caSJiri Slaby 	if (tty != tty->driver->ttys[idx]) {
16299de44bd6SJiri Slaby 		printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n",
16309de44bd6SJiri Slaby 				__func__, idx, tty->name);
1631955787caSJiri Slaby 		return -1;
1632955787caSJiri Slaby 	}
1633955787caSJiri Slaby 	if (tty->driver->other) {
1634955787caSJiri Slaby 		if (o_tty != tty->driver->other->ttys[idx]) {
16359de44bd6SJiri Slaby 			printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n",
16369de44bd6SJiri Slaby 					__func__, idx, tty->name);
1637955787caSJiri Slaby 			return -1;
1638955787caSJiri Slaby 		}
1639955787caSJiri Slaby 		if (o_tty->link != tty) {
16409de44bd6SJiri Slaby 			printk(KERN_DEBUG "%s: bad pty pointers\n", __func__);
1641955787caSJiri Slaby 			return -1;
1642955787caSJiri Slaby 		}
1643955787caSJiri Slaby 	}
1644955787caSJiri Slaby #endif
1645955787caSJiri Slaby 	return 0;
1646955787caSJiri Slaby }
1647955787caSJiri Slaby 
1648955787caSJiri Slaby /**
164996fd7ce5SGreg Kroah-Hartman  *	tty_release		-	vfs callback for close
165096fd7ce5SGreg Kroah-Hartman  *	@inode: inode of tty
165196fd7ce5SGreg Kroah-Hartman  *	@filp: file pointer for handle to tty
165296fd7ce5SGreg Kroah-Hartman  *
165396fd7ce5SGreg Kroah-Hartman  *	Called the last time each file handle is closed that references
165496fd7ce5SGreg Kroah-Hartman  *	this tty. There may however be several such references.
165596fd7ce5SGreg Kroah-Hartman  *
165696fd7ce5SGreg Kroah-Hartman  *	Locking:
165796fd7ce5SGreg Kroah-Hartman  *		Takes bkl. See tty_release_dev
165896fd7ce5SGreg Kroah-Hartman  *
165996fd7ce5SGreg Kroah-Hartman  * Even releasing the tty structures is a tricky business.. We have
166096fd7ce5SGreg Kroah-Hartman  * to be very careful that the structures are all released at the
166196fd7ce5SGreg Kroah-Hartman  * same time, as interrupts might otherwise get the wrong pointers.
166296fd7ce5SGreg Kroah-Hartman  *
166396fd7ce5SGreg Kroah-Hartman  * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
166496fd7ce5SGreg Kroah-Hartman  * lead to double frees or releasing memory still in use.
166596fd7ce5SGreg Kroah-Hartman  */
166696fd7ce5SGreg Kroah-Hartman 
166796fd7ce5SGreg Kroah-Hartman int tty_release(struct inode *inode, struct file *filp)
166896fd7ce5SGreg Kroah-Hartman {
166996fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(filp);
167096fd7ce5SGreg Kroah-Hartman 	struct tty_struct *o_tty;
167196fd7ce5SGreg Kroah-Hartman 	int	pty_master, tty_closing, o_tty_closing, do_sleep;
167296fd7ce5SGreg Kroah-Hartman 	int	idx;
167396fd7ce5SGreg Kroah-Hartman 	char	buf[64];
167496fd7ce5SGreg Kroah-Hartman 
16759de44bd6SJiri Slaby 	if (tty_paranoia_check(tty, inode, __func__))
167696fd7ce5SGreg Kroah-Hartman 		return 0;
167796fd7ce5SGreg Kroah-Hartman 
167889c8d91eSAlan Cox 	tty_lock(tty);
16799de44bd6SJiri Slaby 	check_tty_count(tty, __func__);
168096fd7ce5SGreg Kroah-Hartman 
168196fd7ce5SGreg Kroah-Hartman 	__tty_fasync(-1, filp, 0);
168296fd7ce5SGreg Kroah-Hartman 
168396fd7ce5SGreg Kroah-Hartman 	idx = tty->index;
168496fd7ce5SGreg Kroah-Hartman 	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
168596fd7ce5SGreg Kroah-Hartman 		      tty->driver->subtype == PTY_TYPE_MASTER);
168689c8d91eSAlan Cox 	/* Review: parallel close */
168796fd7ce5SGreg Kroah-Hartman 	o_tty = tty->link;
168896fd7ce5SGreg Kroah-Hartman 
1689955787caSJiri Slaby 	if (tty_release_checks(tty, o_tty, idx)) {
169089c8d91eSAlan Cox 		tty_unlock(tty);
169196fd7ce5SGreg Kroah-Hartman 		return 0;
169296fd7ce5SGreg Kroah-Hartman 	}
169396fd7ce5SGreg Kroah-Hartman 
169496fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
16959de44bd6SJiri Slaby 	printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__,
169696fd7ce5SGreg Kroah-Hartman 			tty_name(tty, buf), tty->count);
169796fd7ce5SGreg Kroah-Hartman #endif
169896fd7ce5SGreg Kroah-Hartman 
169996fd7ce5SGreg Kroah-Hartman 	if (tty->ops->close)
170096fd7ce5SGreg Kroah-Hartman 		tty->ops->close(tty, filp);
170196fd7ce5SGreg Kroah-Hartman 
170289c8d91eSAlan Cox 	tty_unlock(tty);
170396fd7ce5SGreg Kroah-Hartman 	/*
170496fd7ce5SGreg Kroah-Hartman 	 * Sanity check: if tty->count is going to zero, there shouldn't be
170596fd7ce5SGreg Kroah-Hartman 	 * any waiters on tty->read_wait or tty->write_wait.  We test the
170696fd7ce5SGreg Kroah-Hartman 	 * wait queues and kick everyone out _before_ actually starting to
170796fd7ce5SGreg Kroah-Hartman 	 * close.  This ensures that we won't block while releasing the tty
170896fd7ce5SGreg Kroah-Hartman 	 * structure.
170996fd7ce5SGreg Kroah-Hartman 	 *
171096fd7ce5SGreg Kroah-Hartman 	 * The test for the o_tty closing is necessary, since the master and
171196fd7ce5SGreg Kroah-Hartman 	 * slave sides may close in any order.  If the slave side closes out
171296fd7ce5SGreg Kroah-Hartman 	 * first, its count will be one, since the master side holds an open.
171396fd7ce5SGreg Kroah-Hartman 	 * Thus this test wouldn't be triggered at the time the slave closes,
171496fd7ce5SGreg Kroah-Hartman 	 * so we do it now.
171596fd7ce5SGreg Kroah-Hartman 	 *
171696fd7ce5SGreg Kroah-Hartman 	 * Note that it's possible for the tty to be opened again while we're
171796fd7ce5SGreg Kroah-Hartman 	 * flushing out waiters.  By recalculating the closing flags before
171896fd7ce5SGreg Kroah-Hartman 	 * each iteration we avoid any problems.
171996fd7ce5SGreg Kroah-Hartman 	 */
172096fd7ce5SGreg Kroah-Hartman 	while (1) {
172196fd7ce5SGreg Kroah-Hartman 		/* Guard against races with tty->count changes elsewhere and
172296fd7ce5SGreg Kroah-Hartman 		   opens on /dev/tty */
172396fd7ce5SGreg Kroah-Hartman 
172496fd7ce5SGreg Kroah-Hartman 		mutex_lock(&tty_mutex);
172589c8d91eSAlan Cox 		tty_lock_pair(tty, o_tty);
172696fd7ce5SGreg Kroah-Hartman 		tty_closing = tty->count <= 1;
172796fd7ce5SGreg Kroah-Hartman 		o_tty_closing = o_tty &&
172896fd7ce5SGreg Kroah-Hartman 			(o_tty->count <= (pty_master ? 1 : 0));
172996fd7ce5SGreg Kroah-Hartman 		do_sleep = 0;
173096fd7ce5SGreg Kroah-Hartman 
173196fd7ce5SGreg Kroah-Hartman 		if (tty_closing) {
173296fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&tty->read_wait)) {
173396fd7ce5SGreg Kroah-Hartman 				wake_up_poll(&tty->read_wait, POLLIN);
173496fd7ce5SGreg Kroah-Hartman 				do_sleep++;
173596fd7ce5SGreg Kroah-Hartman 			}
173696fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&tty->write_wait)) {
173796fd7ce5SGreg Kroah-Hartman 				wake_up_poll(&tty->write_wait, POLLOUT);
173896fd7ce5SGreg Kroah-Hartman 				do_sleep++;
173996fd7ce5SGreg Kroah-Hartman 			}
174096fd7ce5SGreg Kroah-Hartman 		}
174196fd7ce5SGreg Kroah-Hartman 		if (o_tty_closing) {
174296fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&o_tty->read_wait)) {
174396fd7ce5SGreg Kroah-Hartman 				wake_up_poll(&o_tty->read_wait, POLLIN);
174496fd7ce5SGreg Kroah-Hartman 				do_sleep++;
174596fd7ce5SGreg Kroah-Hartman 			}
174696fd7ce5SGreg Kroah-Hartman 			if (waitqueue_active(&o_tty->write_wait)) {
174796fd7ce5SGreg Kroah-Hartman 				wake_up_poll(&o_tty->write_wait, POLLOUT);
174896fd7ce5SGreg Kroah-Hartman 				do_sleep++;
174996fd7ce5SGreg Kroah-Hartman 			}
175096fd7ce5SGreg Kroah-Hartman 		}
175196fd7ce5SGreg Kroah-Hartman 		if (!do_sleep)
175296fd7ce5SGreg Kroah-Hartman 			break;
175396fd7ce5SGreg Kroah-Hartman 
17549de44bd6SJiri Slaby 		printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
17559de44bd6SJiri Slaby 				__func__, tty_name(tty, buf));
175689c8d91eSAlan Cox 		tty_unlock_pair(tty, o_tty);
175796fd7ce5SGreg Kroah-Hartman 		mutex_unlock(&tty_mutex);
175896fd7ce5SGreg Kroah-Hartman 		schedule();
175996fd7ce5SGreg Kroah-Hartman 	}
176096fd7ce5SGreg Kroah-Hartman 
176196fd7ce5SGreg Kroah-Hartman 	/*
176296fd7ce5SGreg Kroah-Hartman 	 * The closing flags are now consistent with the open counts on
176396fd7ce5SGreg Kroah-Hartman 	 * both sides, and we've completed the last operation that could
176496fd7ce5SGreg Kroah-Hartman 	 * block, so it's safe to proceed with closing.
1765d155255aSAlan Cox 	 *
1766d155255aSAlan Cox 	 * We must *not* drop the tty_mutex until we ensure that a further
1767d155255aSAlan Cox 	 * entry into tty_open can not pick up this tty.
176896fd7ce5SGreg Kroah-Hartman 	 */
176996fd7ce5SGreg Kroah-Hartman 	if (pty_master) {
177096fd7ce5SGreg Kroah-Hartman 		if (--o_tty->count < 0) {
17719de44bd6SJiri Slaby 			printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n",
17729de44bd6SJiri Slaby 				__func__, o_tty->count, tty_name(o_tty, buf));
177396fd7ce5SGreg Kroah-Hartman 			o_tty->count = 0;
177496fd7ce5SGreg Kroah-Hartman 		}
177596fd7ce5SGreg Kroah-Hartman 	}
177696fd7ce5SGreg Kroah-Hartman 	if (--tty->count < 0) {
17779de44bd6SJiri Slaby 		printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n",
17789de44bd6SJiri Slaby 				__func__, tty->count, tty_name(tty, buf));
177996fd7ce5SGreg Kroah-Hartman 		tty->count = 0;
178096fd7ce5SGreg Kroah-Hartman 	}
178196fd7ce5SGreg Kroah-Hartman 
178296fd7ce5SGreg Kroah-Hartman 	/*
178396fd7ce5SGreg Kroah-Hartman 	 * We've decremented tty->count, so we need to remove this file
178496fd7ce5SGreg Kroah-Hartman 	 * descriptor off the tty->tty_files list; this serves two
178596fd7ce5SGreg Kroah-Hartman 	 * purposes:
178696fd7ce5SGreg Kroah-Hartman 	 *  - check_tty_count sees the correct number of file descriptors
178796fd7ce5SGreg Kroah-Hartman 	 *    associated with this tty.
178896fd7ce5SGreg Kroah-Hartman 	 *  - do_tty_hangup no longer sees this file descriptor as
178996fd7ce5SGreg Kroah-Hartman 	 *    something that needs to be handled for hangups.
179096fd7ce5SGreg Kroah-Hartman 	 */
179196fd7ce5SGreg Kroah-Hartman 	tty_del_file(filp);
179296fd7ce5SGreg Kroah-Hartman 
179396fd7ce5SGreg Kroah-Hartman 	/*
179496fd7ce5SGreg Kroah-Hartman 	 * Perform some housekeeping before deciding whether to return.
179596fd7ce5SGreg Kroah-Hartman 	 *
179696fd7ce5SGreg Kroah-Hartman 	 * Set the TTY_CLOSING flag if this was the last open.  In the
179796fd7ce5SGreg Kroah-Hartman 	 * case of a pty we may have to wait around for the other side
179896fd7ce5SGreg Kroah-Hartman 	 * to close, and TTY_CLOSING makes sure we can't be reopened.
179996fd7ce5SGreg Kroah-Hartman 	 */
180096fd7ce5SGreg Kroah-Hartman 	if (tty_closing)
180196fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_CLOSING, &tty->flags);
180296fd7ce5SGreg Kroah-Hartman 	if (o_tty_closing)
180396fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_CLOSING, &o_tty->flags);
180496fd7ce5SGreg Kroah-Hartman 
180596fd7ce5SGreg Kroah-Hartman 	/*
180696fd7ce5SGreg Kroah-Hartman 	 * If _either_ side is closing, make sure there aren't any
180796fd7ce5SGreg Kroah-Hartman 	 * processes that still think tty or o_tty is their controlling
180896fd7ce5SGreg Kroah-Hartman 	 * tty.
180996fd7ce5SGreg Kroah-Hartman 	 */
181096fd7ce5SGreg Kroah-Hartman 	if (tty_closing || o_tty_closing) {
181196fd7ce5SGreg Kroah-Hartman 		read_lock(&tasklist_lock);
181296fd7ce5SGreg Kroah-Hartman 		session_clear_tty(tty->session);
181396fd7ce5SGreg Kroah-Hartman 		if (o_tty)
181496fd7ce5SGreg Kroah-Hartman 			session_clear_tty(o_tty->session);
181596fd7ce5SGreg Kroah-Hartman 		read_unlock(&tasklist_lock);
181696fd7ce5SGreg Kroah-Hartman 	}
181796fd7ce5SGreg Kroah-Hartman 
181896fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
181989c8d91eSAlan Cox 	tty_unlock_pair(tty, o_tty);
1820d155255aSAlan Cox 	/* At this point the TTY_CLOSING flag should ensure a dead tty
1821d155255aSAlan Cox 	   cannot be re-opened by a racing opener */
182296fd7ce5SGreg Kroah-Hartman 
182396fd7ce5SGreg Kroah-Hartman 	/* check whether both sides are closing ... */
1824d155255aSAlan Cox 	if (!tty_closing || (o_tty && !o_tty_closing))
182596fd7ce5SGreg Kroah-Hartman 		return 0;
182696fd7ce5SGreg Kroah-Hartman 
182796fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
18289de44bd6SJiri Slaby 	printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__);
182996fd7ce5SGreg Kroah-Hartman #endif
183096fd7ce5SGreg Kroah-Hartman 	/*
183196fd7ce5SGreg Kroah-Hartman 	 * Ask the line discipline code to release its structures
183296fd7ce5SGreg Kroah-Hartman 	 */
183396fd7ce5SGreg Kroah-Hartman 	tty_ldisc_release(tty, o_tty);
183496fd7ce5SGreg Kroah-Hartman 	/*
183596fd7ce5SGreg Kroah-Hartman 	 * The release_tty function takes care of the details of clearing
183689c8d91eSAlan Cox 	 * the slots and preserving the termios structure. The tty_unlock_pair
183789c8d91eSAlan Cox 	 * should be safe as we keep a kref while the tty is locked (so the
183889c8d91eSAlan Cox 	 * unlock never unlocks a freed tty).
183996fd7ce5SGreg Kroah-Hartman 	 */
1840d155255aSAlan Cox 	mutex_lock(&tty_mutex);
184196fd7ce5SGreg Kroah-Hartman 	release_tty(tty, idx);
1842d155255aSAlan Cox 	mutex_unlock(&tty_mutex);
184396fd7ce5SGreg Kroah-Hartman 
184496fd7ce5SGreg Kroah-Hartman 	return 0;
184596fd7ce5SGreg Kroah-Hartman }
184696fd7ce5SGreg Kroah-Hartman 
184796fd7ce5SGreg Kroah-Hartman /**
1848b82154acSJiri Slaby  *	tty_open_current_tty - get tty of current task for open
1849b82154acSJiri Slaby  *	@device: device number
1850b82154acSJiri Slaby  *	@filp: file pointer to tty
1851b82154acSJiri Slaby  *	@return: tty of the current task iff @device is /dev/tty
1852b82154acSJiri Slaby  *
1853b82154acSJiri Slaby  *	We cannot return driver and index like for the other nodes because
1854b82154acSJiri Slaby  *	devpts will not work then. It expects inodes to be from devpts FS.
18553af502b9SAlan Cox  *
18563af502b9SAlan Cox  *	We need to move to returning a refcounted object from all the lookup
18573af502b9SAlan Cox  *	paths including this one.
1858b82154acSJiri Slaby  */
1859b82154acSJiri Slaby static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
1860b82154acSJiri Slaby {
1861b82154acSJiri Slaby 	struct tty_struct *tty;
1862b82154acSJiri Slaby 
1863b82154acSJiri Slaby 	if (device != MKDEV(TTYAUX_MAJOR, 0))
1864b82154acSJiri Slaby 		return NULL;
1865b82154acSJiri Slaby 
1866b82154acSJiri Slaby 	tty = get_current_tty();
1867b82154acSJiri Slaby 	if (!tty)
1868b82154acSJiri Slaby 		return ERR_PTR(-ENXIO);
1869b82154acSJiri Slaby 
1870b82154acSJiri Slaby 	filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
1871b82154acSJiri Slaby 	/* noctty = 1; */
1872b82154acSJiri Slaby 	tty_kref_put(tty);
1873b82154acSJiri Slaby 	/* FIXME: we put a reference and return a TTY! */
18743af502b9SAlan Cox 	/* This is only safe because the caller holds tty_mutex */
1875b82154acSJiri Slaby 	return tty;
1876b82154acSJiri Slaby }
1877b82154acSJiri Slaby 
1878b82154acSJiri Slaby /**
18795b5e7040SJiri Slaby  *	tty_lookup_driver - lookup a tty driver for a given device file
18805b5e7040SJiri Slaby  *	@device: device number
18815b5e7040SJiri Slaby  *	@filp: file pointer to tty
18825b5e7040SJiri Slaby  *	@noctty: set if the device should not become a controlling tty
18835b5e7040SJiri Slaby  *	@index: index for the device in the @return driver
18845b5e7040SJiri Slaby  *	@return: driver for this inode (with increased refcount)
18855b5e7040SJiri Slaby  *
18865b5e7040SJiri Slaby  * 	If @return is not erroneous, the caller is responsible to decrement the
18875b5e7040SJiri Slaby  * 	refcount by tty_driver_kref_put.
18885b5e7040SJiri Slaby  *
18895b5e7040SJiri Slaby  *	Locking: tty_mutex protects get_tty_driver
18905b5e7040SJiri Slaby  */
18915b5e7040SJiri Slaby static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
18925b5e7040SJiri Slaby 		int *noctty, int *index)
18935b5e7040SJiri Slaby {
18945b5e7040SJiri Slaby 	struct tty_driver *driver;
18955b5e7040SJiri Slaby 
18962cd0050cSJiri Slaby 	switch (device) {
18975b5e7040SJiri Slaby #ifdef CONFIG_VT
18982cd0050cSJiri Slaby 	case MKDEV(TTY_MAJOR, 0): {
18995b5e7040SJiri Slaby 		extern struct tty_driver *console_driver;
19005b5e7040SJiri Slaby 		driver = tty_driver_kref_get(console_driver);
19015b5e7040SJiri Slaby 		*index = fg_console;
19025b5e7040SJiri Slaby 		*noctty = 1;
19032cd0050cSJiri Slaby 		break;
19045b5e7040SJiri Slaby 	}
19055b5e7040SJiri Slaby #endif
19062cd0050cSJiri Slaby 	case MKDEV(TTYAUX_MAJOR, 1): {
19075b5e7040SJiri Slaby 		struct tty_driver *console_driver = console_device(index);
19085b5e7040SJiri Slaby 		if (console_driver) {
19095b5e7040SJiri Slaby 			driver = tty_driver_kref_get(console_driver);
19105b5e7040SJiri Slaby 			if (driver) {
19115b5e7040SJiri Slaby 				/* Don't let /dev/console block */
19125b5e7040SJiri Slaby 				filp->f_flags |= O_NONBLOCK;
19135b5e7040SJiri Slaby 				*noctty = 1;
19142cd0050cSJiri Slaby 				break;
19155b5e7040SJiri Slaby 			}
19165b5e7040SJiri Slaby 		}
19175b5e7040SJiri Slaby 		return ERR_PTR(-ENODEV);
19185b5e7040SJiri Slaby 	}
19192cd0050cSJiri Slaby 	default:
19205b5e7040SJiri Slaby 		driver = get_tty_driver(device, index);
19215b5e7040SJiri Slaby 		if (!driver)
19225b5e7040SJiri Slaby 			return ERR_PTR(-ENODEV);
19232cd0050cSJiri Slaby 		break;
19242cd0050cSJiri Slaby 	}
19255b5e7040SJiri Slaby 	return driver;
19265b5e7040SJiri Slaby }
19275b5e7040SJiri Slaby 
19285b5e7040SJiri Slaby /**
192996fd7ce5SGreg Kroah-Hartman  *	tty_open		-	open a tty device
193096fd7ce5SGreg Kroah-Hartman  *	@inode: inode of device file
193196fd7ce5SGreg Kroah-Hartman  *	@filp: file pointer to tty
193296fd7ce5SGreg Kroah-Hartman  *
193396fd7ce5SGreg Kroah-Hartman  *	tty_open and tty_release keep up the tty count that contains the
193496fd7ce5SGreg Kroah-Hartman  *	number of opens done on a tty. We cannot use the inode-count, as
193596fd7ce5SGreg Kroah-Hartman  *	different inodes might point to the same tty.
193696fd7ce5SGreg Kroah-Hartman  *
193796fd7ce5SGreg Kroah-Hartman  *	Open-counting is needed for pty masters, as well as for keeping
193896fd7ce5SGreg Kroah-Hartman  *	track of serial lines: DTR is dropped when the last close happens.
193996fd7ce5SGreg Kroah-Hartman  *	(This is not done solely through tty->count, now.  - Ted 1/27/92)
194096fd7ce5SGreg Kroah-Hartman  *
194196fd7ce5SGreg Kroah-Hartman  *	The termios state of a pty is reset on first open so that
194296fd7ce5SGreg Kroah-Hartman  *	settings don't persist across reuse.
194396fd7ce5SGreg Kroah-Hartman  *
19445b5e7040SJiri Slaby  *	Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.
194596fd7ce5SGreg Kroah-Hartman  *		 tty->count should protect the rest.
194696fd7ce5SGreg Kroah-Hartman  *		 ->siglock protects ->signal/->sighand
194789c8d91eSAlan Cox  *
194889c8d91eSAlan Cox  *	Note: the tty_unlock/lock cases without a ref are only safe due to
194989c8d91eSAlan Cox  *	tty_mutex
195096fd7ce5SGreg Kroah-Hartman  */
195196fd7ce5SGreg Kroah-Hartman 
195296fd7ce5SGreg Kroah-Hartman static int tty_open(struct inode *inode, struct file *filp)
195396fd7ce5SGreg Kroah-Hartman {
1954b82154acSJiri Slaby 	struct tty_struct *tty;
195596fd7ce5SGreg Kroah-Hartman 	int noctty, retval;
1956b82154acSJiri Slaby 	struct tty_driver *driver = NULL;
195796fd7ce5SGreg Kroah-Hartman 	int index;
195896fd7ce5SGreg Kroah-Hartman 	dev_t device = inode->i_rdev;
195996fd7ce5SGreg Kroah-Hartman 	unsigned saved_flags = filp->f_flags;
196096fd7ce5SGreg Kroah-Hartman 
196196fd7ce5SGreg Kroah-Hartman 	nonseekable_open(inode, filp);
196296fd7ce5SGreg Kroah-Hartman 
196396fd7ce5SGreg Kroah-Hartman retry_open:
1964fa90e1c9SJiri Slaby 	retval = tty_alloc_file(filp);
1965fa90e1c9SJiri Slaby 	if (retval)
1966fa90e1c9SJiri Slaby 		return -ENOMEM;
1967fa90e1c9SJiri Slaby 
196896fd7ce5SGreg Kroah-Hartman 	noctty = filp->f_flags & O_NOCTTY;
196996fd7ce5SGreg Kroah-Hartman 	index  = -1;
197096fd7ce5SGreg Kroah-Hartman 	retval = 0;
197196fd7ce5SGreg Kroah-Hartman 
197296fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
197389c8d91eSAlan Cox 	/* This is protected by the tty_mutex */
1974b82154acSJiri Slaby 	tty = tty_open_current_tty(device, filp);
1975b82154acSJiri Slaby 	if (IS_ERR(tty)) {
1976ba5db448SJiri Slaby 		retval = PTR_ERR(tty);
1977ba5db448SJiri Slaby 		goto err_unlock;
19785b5e7040SJiri Slaby 	} else if (!tty) {
19795b5e7040SJiri Slaby 		driver = tty_lookup_driver(device, filp, &noctty, &index);
19805b5e7040SJiri Slaby 		if (IS_ERR(driver)) {
1981ba5db448SJiri Slaby 			retval = PTR_ERR(driver);
1982ba5db448SJiri Slaby 			goto err_unlock;
198396fd7ce5SGreg Kroah-Hartman 		}
198496fd7ce5SGreg Kroah-Hartman 
198596fd7ce5SGreg Kroah-Hartman 		/* check whether we're reopening an existing tty */
198696fd7ce5SGreg Kroah-Hartman 		tty = tty_driver_lookup_tty(driver, inode, index);
198796fd7ce5SGreg Kroah-Hartman 		if (IS_ERR(tty)) {
1988ba5db448SJiri Slaby 			retval = PTR_ERR(tty);
1989ba5db448SJiri Slaby 			goto err_unlock;
199096fd7ce5SGreg Kroah-Hartman 		}
199196fd7ce5SGreg Kroah-Hartman 	}
199296fd7ce5SGreg Kroah-Hartman 
199396fd7ce5SGreg Kroah-Hartman 	if (tty) {
199489c8d91eSAlan Cox 		tty_lock(tty);
199596fd7ce5SGreg Kroah-Hartman 		retval = tty_reopen(tty);
199689c8d91eSAlan Cox 		if (retval < 0) {
199789c8d91eSAlan Cox 			tty_unlock(tty);
199896fd7ce5SGreg Kroah-Hartman 			tty = ERR_PTR(retval);
199989c8d91eSAlan Cox 		}
200089c8d91eSAlan Cox 	} else	/* Returns with the tty_lock held for now */
2001593a27c4SKonstantin Khlebnikov 		tty = tty_init_dev(driver, index);
200296fd7ce5SGreg Kroah-Hartman 
200396fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
2004b82154acSJiri Slaby 	if (driver)
200596fd7ce5SGreg Kroah-Hartman 		tty_driver_kref_put(driver);
200696fd7ce5SGreg Kroah-Hartman 	if (IS_ERR(tty)) {
2007ba5db448SJiri Slaby 		retval = PTR_ERR(tty);
2008ba5db448SJiri Slaby 		goto err_file;
200996fd7ce5SGreg Kroah-Hartman 	}
201096fd7ce5SGreg Kroah-Hartman 
2011fa90e1c9SJiri Slaby 	tty_add_file(tty, filp);
201296fd7ce5SGreg Kroah-Hartman 
20139de44bd6SJiri Slaby 	check_tty_count(tty, __func__);
201496fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
201596fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_MASTER)
201696fd7ce5SGreg Kroah-Hartman 		noctty = 1;
201796fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
20189de44bd6SJiri Slaby 	printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name);
201996fd7ce5SGreg Kroah-Hartman #endif
202096fd7ce5SGreg Kroah-Hartman 	if (tty->ops->open)
202196fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->open(tty, filp);
202296fd7ce5SGreg Kroah-Hartman 	else
202396fd7ce5SGreg Kroah-Hartman 		retval = -ENODEV;
202496fd7ce5SGreg Kroah-Hartman 	filp->f_flags = saved_flags;
202596fd7ce5SGreg Kroah-Hartman 
202696fd7ce5SGreg Kroah-Hartman 	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
202796fd7ce5SGreg Kroah-Hartman 						!capable(CAP_SYS_ADMIN))
202896fd7ce5SGreg Kroah-Hartman 		retval = -EBUSY;
202996fd7ce5SGreg Kroah-Hartman 
203096fd7ce5SGreg Kroah-Hartman 	if (retval) {
203196fd7ce5SGreg Kroah-Hartman #ifdef TTY_DEBUG_HANGUP
20329de44bd6SJiri Slaby 		printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__,
20339de44bd6SJiri Slaby 				retval, tty->name);
203496fd7ce5SGreg Kroah-Hartman #endif
203589c8d91eSAlan Cox 		tty_unlock(tty); /* need to call tty_release without BTM */
203696fd7ce5SGreg Kroah-Hartman 		tty_release(inode, filp);
203796fd7ce5SGreg Kroah-Hartman 		if (retval != -ERESTARTSYS)
203896fd7ce5SGreg Kroah-Hartman 			return retval;
203996fd7ce5SGreg Kroah-Hartman 
204096fd7ce5SGreg Kroah-Hartman 		if (signal_pending(current))
204196fd7ce5SGreg Kroah-Hartman 			return retval;
204296fd7ce5SGreg Kroah-Hartman 
204396fd7ce5SGreg Kroah-Hartman 		schedule();
204496fd7ce5SGreg Kroah-Hartman 		/*
204596fd7ce5SGreg Kroah-Hartman 		 * Need to reset f_op in case a hangup happened.
204696fd7ce5SGreg Kroah-Hartman 		 */
204796fd7ce5SGreg Kroah-Hartman 		if (filp->f_op == &hung_up_tty_fops)
204896fd7ce5SGreg Kroah-Hartman 			filp->f_op = &tty_fops;
204996fd7ce5SGreg Kroah-Hartman 		goto retry_open;
205096fd7ce5SGreg Kroah-Hartman 	}
205189c8d91eSAlan Cox 	tty_unlock(tty);
205296fd7ce5SGreg Kroah-Hartman 
205396fd7ce5SGreg Kroah-Hartman 
205496fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
205589c8d91eSAlan Cox 	tty_lock(tty);
205696fd7ce5SGreg Kroah-Hartman 	spin_lock_irq(&current->sighand->siglock);
205796fd7ce5SGreg Kroah-Hartman 	if (!noctty &&
205896fd7ce5SGreg Kroah-Hartman 	    current->signal->leader &&
205996fd7ce5SGreg Kroah-Hartman 	    !current->signal->tty &&
206096fd7ce5SGreg Kroah-Hartman 	    tty->session == NULL)
206196fd7ce5SGreg Kroah-Hartman 		__proc_set_tty(current, tty);
206296fd7ce5SGreg Kroah-Hartman 	spin_unlock_irq(&current->sighand->siglock);
206389c8d91eSAlan Cox 	tty_unlock(tty);
206496fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
206596fd7ce5SGreg Kroah-Hartman 	return 0;
2066ba5db448SJiri Slaby err_unlock:
2067ba5db448SJiri Slaby 	mutex_unlock(&tty_mutex);
2068ba5db448SJiri Slaby 	/* after locks to avoid deadlock */
2069ba5db448SJiri Slaby 	if (!IS_ERR_OR_NULL(driver))
2070ba5db448SJiri Slaby 		tty_driver_kref_put(driver);
2071ba5db448SJiri Slaby err_file:
2072ba5db448SJiri Slaby 	tty_free_file(filp);
2073ba5db448SJiri Slaby 	return retval;
207496fd7ce5SGreg Kroah-Hartman }
207596fd7ce5SGreg Kroah-Hartman 
207696fd7ce5SGreg Kroah-Hartman 
207796fd7ce5SGreg Kroah-Hartman 
207896fd7ce5SGreg Kroah-Hartman /**
207996fd7ce5SGreg Kroah-Hartman  *	tty_poll	-	check tty status
208096fd7ce5SGreg Kroah-Hartman  *	@filp: file being polled
208196fd7ce5SGreg Kroah-Hartman  *	@wait: poll wait structures to update
208296fd7ce5SGreg Kroah-Hartman  *
208396fd7ce5SGreg Kroah-Hartman  *	Call the line discipline polling method to obtain the poll
208496fd7ce5SGreg Kroah-Hartman  *	status of the device.
208596fd7ce5SGreg Kroah-Hartman  *
208696fd7ce5SGreg Kroah-Hartman  *	Locking: locks called line discipline but ldisc poll method
208796fd7ce5SGreg Kroah-Hartman  *	may be re-entered freely by other callers.
208896fd7ce5SGreg Kroah-Hartman  */
208996fd7ce5SGreg Kroah-Hartman 
209096fd7ce5SGreg Kroah-Hartman static unsigned int tty_poll(struct file *filp, poll_table *wait)
209196fd7ce5SGreg Kroah-Hartman {
209296fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(filp);
209396fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
209496fd7ce5SGreg Kroah-Hartman 	int ret = 0;
209596fd7ce5SGreg Kroah-Hartman 
20966131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(filp), "tty_poll"))
209796fd7ce5SGreg Kroah-Hartman 		return 0;
209896fd7ce5SGreg Kroah-Hartman 
209996fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
210096fd7ce5SGreg Kroah-Hartman 	if (ld->ops->poll)
210196fd7ce5SGreg Kroah-Hartman 		ret = (ld->ops->poll)(tty, filp, wait);
210296fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
210396fd7ce5SGreg Kroah-Hartman 	return ret;
210496fd7ce5SGreg Kroah-Hartman }
210596fd7ce5SGreg Kroah-Hartman 
210696fd7ce5SGreg Kroah-Hartman static int __tty_fasync(int fd, struct file *filp, int on)
210796fd7ce5SGreg Kroah-Hartman {
210896fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(filp);
210996fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
211096fd7ce5SGreg Kroah-Hartman 	int retval = 0;
211196fd7ce5SGreg Kroah-Hartman 
21126131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(filp), "tty_fasync"))
211396fd7ce5SGreg Kroah-Hartman 		goto out;
211496fd7ce5SGreg Kroah-Hartman 
211596fd7ce5SGreg Kroah-Hartman 	retval = fasync_helper(fd, filp, on, &tty->fasync);
211696fd7ce5SGreg Kroah-Hartman 	if (retval <= 0)
211796fd7ce5SGreg Kroah-Hartman 		goto out;
211896fd7ce5SGreg Kroah-Hartman 
211996fd7ce5SGreg Kroah-Hartman 	if (on) {
212096fd7ce5SGreg Kroah-Hartman 		enum pid_type type;
212196fd7ce5SGreg Kroah-Hartman 		struct pid *pid;
212296fd7ce5SGreg Kroah-Hartman 		if (!waitqueue_active(&tty->read_wait))
212396fd7ce5SGreg Kroah-Hartman 			tty->minimum_to_wake = 1;
212496fd7ce5SGreg Kroah-Hartman 		spin_lock_irqsave(&tty->ctrl_lock, flags);
212596fd7ce5SGreg Kroah-Hartman 		if (tty->pgrp) {
212696fd7ce5SGreg Kroah-Hartman 			pid = tty->pgrp;
212796fd7ce5SGreg Kroah-Hartman 			type = PIDTYPE_PGID;
212896fd7ce5SGreg Kroah-Hartman 		} else {
212996fd7ce5SGreg Kroah-Hartman 			pid = task_pid(current);
213096fd7ce5SGreg Kroah-Hartman 			type = PIDTYPE_PID;
213196fd7ce5SGreg Kroah-Hartman 		}
213296fd7ce5SGreg Kroah-Hartman 		get_pid(pid);
213396fd7ce5SGreg Kroah-Hartman 		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
213496fd7ce5SGreg Kroah-Hartman 		retval = __f_setown(filp, pid, type, 0);
213596fd7ce5SGreg Kroah-Hartman 		put_pid(pid);
213696fd7ce5SGreg Kroah-Hartman 		if (retval)
213796fd7ce5SGreg Kroah-Hartman 			goto out;
213896fd7ce5SGreg Kroah-Hartman 	} else {
213996fd7ce5SGreg Kroah-Hartman 		if (!tty->fasync && !waitqueue_active(&tty->read_wait))
214096fd7ce5SGreg Kroah-Hartman 			tty->minimum_to_wake = N_TTY_BUF_SIZE;
214196fd7ce5SGreg Kroah-Hartman 	}
214296fd7ce5SGreg Kroah-Hartman 	retval = 0;
214396fd7ce5SGreg Kroah-Hartman out:
214496fd7ce5SGreg Kroah-Hartman 	return retval;
214596fd7ce5SGreg Kroah-Hartman }
214696fd7ce5SGreg Kroah-Hartman 
214796fd7ce5SGreg Kroah-Hartman static int tty_fasync(int fd, struct file *filp, int on)
214896fd7ce5SGreg Kroah-Hartman {
214989c8d91eSAlan Cox 	struct tty_struct *tty = file_tty(filp);
215096fd7ce5SGreg Kroah-Hartman 	int retval;
215189c8d91eSAlan Cox 
215289c8d91eSAlan Cox 	tty_lock(tty);
215396fd7ce5SGreg Kroah-Hartman 	retval = __tty_fasync(fd, filp, on);
215489c8d91eSAlan Cox 	tty_unlock(tty);
215589c8d91eSAlan Cox 
215696fd7ce5SGreg Kroah-Hartman 	return retval;
215796fd7ce5SGreg Kroah-Hartman }
215896fd7ce5SGreg Kroah-Hartman 
215996fd7ce5SGreg Kroah-Hartman /**
216096fd7ce5SGreg Kroah-Hartman  *	tiocsti			-	fake input character
216196fd7ce5SGreg Kroah-Hartman  *	@tty: tty to fake input into
216296fd7ce5SGreg Kroah-Hartman  *	@p: pointer to character
216396fd7ce5SGreg Kroah-Hartman  *
216496fd7ce5SGreg Kroah-Hartman  *	Fake input to a tty device. Does the necessary locking and
216596fd7ce5SGreg Kroah-Hartman  *	input management.
216696fd7ce5SGreg Kroah-Hartman  *
216796fd7ce5SGreg Kroah-Hartman  *	FIXME: does not honour flow control ??
216896fd7ce5SGreg Kroah-Hartman  *
216996fd7ce5SGreg Kroah-Hartman  *	Locking:
217096fd7ce5SGreg Kroah-Hartman  *		Called functions take tty_ldisc_lock
217196fd7ce5SGreg Kroah-Hartman  *		current->signal->tty check is safe without locks
217296fd7ce5SGreg Kroah-Hartman  *
217396fd7ce5SGreg Kroah-Hartman  *	FIXME: may race normal receive processing
217496fd7ce5SGreg Kroah-Hartman  */
217596fd7ce5SGreg Kroah-Hartman 
217696fd7ce5SGreg Kroah-Hartman static int tiocsti(struct tty_struct *tty, char __user *p)
217796fd7ce5SGreg Kroah-Hartman {
217896fd7ce5SGreg Kroah-Hartman 	char ch, mbz = 0;
217996fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
218096fd7ce5SGreg Kroah-Hartman 
218196fd7ce5SGreg Kroah-Hartman 	if ((current->signal->tty != tty) && !capable(CAP_SYS_ADMIN))
218296fd7ce5SGreg Kroah-Hartman 		return -EPERM;
218396fd7ce5SGreg Kroah-Hartman 	if (get_user(ch, p))
218496fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
218596fd7ce5SGreg Kroah-Hartman 	tty_audit_tiocsti(tty, ch);
218696fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
218796fd7ce5SGreg Kroah-Hartman 	ld->ops->receive_buf(tty, &ch, &mbz, 1);
218896fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
218996fd7ce5SGreg Kroah-Hartman 	return 0;
219096fd7ce5SGreg Kroah-Hartman }
219196fd7ce5SGreg Kroah-Hartman 
219296fd7ce5SGreg Kroah-Hartman /**
219396fd7ce5SGreg Kroah-Hartman  *	tiocgwinsz		-	implement window query ioctl
219496fd7ce5SGreg Kroah-Hartman  *	@tty; tty
219596fd7ce5SGreg Kroah-Hartman  *	@arg: user buffer for result
219696fd7ce5SGreg Kroah-Hartman  *
219796fd7ce5SGreg Kroah-Hartman  *	Copies the kernel idea of the window size into the user buffer.
219896fd7ce5SGreg Kroah-Hartman  *
219996fd7ce5SGreg Kroah-Hartman  *	Locking: tty->termios_mutex is taken to ensure the winsize data
220096fd7ce5SGreg Kroah-Hartman  *		is consistent.
220196fd7ce5SGreg Kroah-Hartman  */
220296fd7ce5SGreg Kroah-Hartman 
220396fd7ce5SGreg Kroah-Hartman static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
220496fd7ce5SGreg Kroah-Hartman {
220596fd7ce5SGreg Kroah-Hartman 	int err;
220696fd7ce5SGreg Kroah-Hartman 
220796fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty->termios_mutex);
220896fd7ce5SGreg Kroah-Hartman 	err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
220996fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty->termios_mutex);
221096fd7ce5SGreg Kroah-Hartman 
221196fd7ce5SGreg Kroah-Hartman 	return err ? -EFAULT: 0;
221296fd7ce5SGreg Kroah-Hartman }
221396fd7ce5SGreg Kroah-Hartman 
221496fd7ce5SGreg Kroah-Hartman /**
221596fd7ce5SGreg Kroah-Hartman  *	tty_do_resize		-	resize event
221696fd7ce5SGreg Kroah-Hartman  *	@tty: tty being resized
221796fd7ce5SGreg Kroah-Hartman  *	@rows: rows (character)
221896fd7ce5SGreg Kroah-Hartman  *	@cols: cols (character)
221996fd7ce5SGreg Kroah-Hartman  *
222096fd7ce5SGreg Kroah-Hartman  *	Update the termios variables and send the necessary signals to
222196fd7ce5SGreg Kroah-Hartman  *	peform a terminal resize correctly
222296fd7ce5SGreg Kroah-Hartman  */
222396fd7ce5SGreg Kroah-Hartman 
222496fd7ce5SGreg Kroah-Hartman int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
222596fd7ce5SGreg Kroah-Hartman {
222696fd7ce5SGreg Kroah-Hartman 	struct pid *pgrp;
222796fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
222896fd7ce5SGreg Kroah-Hartman 
222996fd7ce5SGreg Kroah-Hartman 	/* Lock the tty */
223096fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty->termios_mutex);
223196fd7ce5SGreg Kroah-Hartman 	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
223296fd7ce5SGreg Kroah-Hartman 		goto done;
223396fd7ce5SGreg Kroah-Hartman 	/* Get the PID values and reference them so we can
223496fd7ce5SGreg Kroah-Hartman 	   avoid holding the tty ctrl lock while sending signals */
223596fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&tty->ctrl_lock, flags);
223696fd7ce5SGreg Kroah-Hartman 	pgrp = get_pid(tty->pgrp);
223796fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
223896fd7ce5SGreg Kroah-Hartman 
223996fd7ce5SGreg Kroah-Hartman 	if (pgrp)
224096fd7ce5SGreg Kroah-Hartman 		kill_pgrp(pgrp, SIGWINCH, 1);
224196fd7ce5SGreg Kroah-Hartman 	put_pid(pgrp);
224296fd7ce5SGreg Kroah-Hartman 
224396fd7ce5SGreg Kroah-Hartman 	tty->winsize = *ws;
224496fd7ce5SGreg Kroah-Hartman done:
224596fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty->termios_mutex);
224696fd7ce5SGreg Kroah-Hartman 	return 0;
224796fd7ce5SGreg Kroah-Hartman }
22484d334fd1SMartin Schwidefsky EXPORT_SYMBOL(tty_do_resize);
224996fd7ce5SGreg Kroah-Hartman 
225096fd7ce5SGreg Kroah-Hartman /**
225196fd7ce5SGreg Kroah-Hartman  *	tiocswinsz		-	implement window size set ioctl
225296fd7ce5SGreg Kroah-Hartman  *	@tty; tty side of tty
225396fd7ce5SGreg Kroah-Hartman  *	@arg: user buffer for result
225496fd7ce5SGreg Kroah-Hartman  *
225596fd7ce5SGreg Kroah-Hartman  *	Copies the user idea of the window size to the kernel. Traditionally
225696fd7ce5SGreg Kroah-Hartman  *	this is just advisory information but for the Linux console it
225796fd7ce5SGreg Kroah-Hartman  *	actually has driver level meaning and triggers a VC resize.
225896fd7ce5SGreg Kroah-Hartman  *
225996fd7ce5SGreg Kroah-Hartman  *	Locking:
226025985edcSLucas De Marchi  *		Driver dependent. The default do_resize method takes the
226196fd7ce5SGreg Kroah-Hartman  *	tty termios mutex and ctrl_lock. The console takes its own lock
226296fd7ce5SGreg Kroah-Hartman  *	then calls into the default method.
226396fd7ce5SGreg Kroah-Hartman  */
226496fd7ce5SGreg Kroah-Hartman 
226596fd7ce5SGreg Kroah-Hartman static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
226696fd7ce5SGreg Kroah-Hartman {
226796fd7ce5SGreg Kroah-Hartman 	struct winsize tmp_ws;
226896fd7ce5SGreg Kroah-Hartman 	if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
226996fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
227096fd7ce5SGreg Kroah-Hartman 
227196fd7ce5SGreg Kroah-Hartman 	if (tty->ops->resize)
227296fd7ce5SGreg Kroah-Hartman 		return tty->ops->resize(tty, &tmp_ws);
227396fd7ce5SGreg Kroah-Hartman 	else
227496fd7ce5SGreg Kroah-Hartman 		return tty_do_resize(tty, &tmp_ws);
227596fd7ce5SGreg Kroah-Hartman }
227696fd7ce5SGreg Kroah-Hartman 
227796fd7ce5SGreg Kroah-Hartman /**
227896fd7ce5SGreg Kroah-Hartman  *	tioccons	-	allow admin to move logical console
227996fd7ce5SGreg Kroah-Hartman  *	@file: the file to become console
228096fd7ce5SGreg Kroah-Hartman  *
228125985edcSLucas De Marchi  *	Allow the administrator to move the redirected console device
228296fd7ce5SGreg Kroah-Hartman  *
228396fd7ce5SGreg Kroah-Hartman  *	Locking: uses redirect_lock to guard the redirect information
228496fd7ce5SGreg Kroah-Hartman  */
228596fd7ce5SGreg Kroah-Hartman 
228696fd7ce5SGreg Kroah-Hartman static int tioccons(struct file *file)
228796fd7ce5SGreg Kroah-Hartman {
228896fd7ce5SGreg Kroah-Hartman 	if (!capable(CAP_SYS_ADMIN))
228996fd7ce5SGreg Kroah-Hartman 		return -EPERM;
229096fd7ce5SGreg Kroah-Hartman 	if (file->f_op->write == redirected_tty_write) {
229196fd7ce5SGreg Kroah-Hartman 		struct file *f;
229296fd7ce5SGreg Kroah-Hartman 		spin_lock(&redirect_lock);
229396fd7ce5SGreg Kroah-Hartman 		f = redirect;
229496fd7ce5SGreg Kroah-Hartman 		redirect = NULL;
229596fd7ce5SGreg Kroah-Hartman 		spin_unlock(&redirect_lock);
229696fd7ce5SGreg Kroah-Hartman 		if (f)
229796fd7ce5SGreg Kroah-Hartman 			fput(f);
229896fd7ce5SGreg Kroah-Hartman 		return 0;
229996fd7ce5SGreg Kroah-Hartman 	}
230096fd7ce5SGreg Kroah-Hartman 	spin_lock(&redirect_lock);
230196fd7ce5SGreg Kroah-Hartman 	if (redirect) {
230296fd7ce5SGreg Kroah-Hartman 		spin_unlock(&redirect_lock);
230396fd7ce5SGreg Kroah-Hartman 		return -EBUSY;
230496fd7ce5SGreg Kroah-Hartman 	}
2305cb0942b8SAl Viro 	redirect = get_file(file);
230696fd7ce5SGreg Kroah-Hartman 	spin_unlock(&redirect_lock);
230796fd7ce5SGreg Kroah-Hartman 	return 0;
230896fd7ce5SGreg Kroah-Hartman }
230996fd7ce5SGreg Kroah-Hartman 
231096fd7ce5SGreg Kroah-Hartman /**
231196fd7ce5SGreg Kroah-Hartman  *	fionbio		-	non blocking ioctl
231296fd7ce5SGreg Kroah-Hartman  *	@file: file to set blocking value
231396fd7ce5SGreg Kroah-Hartman  *	@p: user parameter
231496fd7ce5SGreg Kroah-Hartman  *
231596fd7ce5SGreg Kroah-Hartman  *	Historical tty interfaces had a blocking control ioctl before
231696fd7ce5SGreg Kroah-Hartman  *	the generic functionality existed. This piece of history is preserved
231796fd7ce5SGreg Kroah-Hartman  *	in the expected tty API of posix OS's.
231896fd7ce5SGreg Kroah-Hartman  *
231996fd7ce5SGreg Kroah-Hartman  *	Locking: none, the open file handle ensures it won't go away.
232096fd7ce5SGreg Kroah-Hartman  */
232196fd7ce5SGreg Kroah-Hartman 
232296fd7ce5SGreg Kroah-Hartman static int fionbio(struct file *file, int __user *p)
232396fd7ce5SGreg Kroah-Hartman {
232496fd7ce5SGreg Kroah-Hartman 	int nonblock;
232596fd7ce5SGreg Kroah-Hartman 
232696fd7ce5SGreg Kroah-Hartman 	if (get_user(nonblock, p))
232796fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
232896fd7ce5SGreg Kroah-Hartman 
232996fd7ce5SGreg Kroah-Hartman 	spin_lock(&file->f_lock);
233096fd7ce5SGreg Kroah-Hartman 	if (nonblock)
233196fd7ce5SGreg Kroah-Hartman 		file->f_flags |= O_NONBLOCK;
233296fd7ce5SGreg Kroah-Hartman 	else
233396fd7ce5SGreg Kroah-Hartman 		file->f_flags &= ~O_NONBLOCK;
233496fd7ce5SGreg Kroah-Hartman 	spin_unlock(&file->f_lock);
233596fd7ce5SGreg Kroah-Hartman 	return 0;
233696fd7ce5SGreg Kroah-Hartman }
233796fd7ce5SGreg Kroah-Hartman 
233896fd7ce5SGreg Kroah-Hartman /**
233996fd7ce5SGreg Kroah-Hartman  *	tiocsctty	-	set controlling tty
234096fd7ce5SGreg Kroah-Hartman  *	@tty: tty structure
234196fd7ce5SGreg Kroah-Hartman  *	@arg: user argument
234296fd7ce5SGreg Kroah-Hartman  *
234396fd7ce5SGreg Kroah-Hartman  *	This ioctl is used to manage job control. It permits a session
234496fd7ce5SGreg Kroah-Hartman  *	leader to set this tty as the controlling tty for the session.
234596fd7ce5SGreg Kroah-Hartman  *
234696fd7ce5SGreg Kroah-Hartman  *	Locking:
234796fd7ce5SGreg Kroah-Hartman  *		Takes tty_mutex() to protect tty instance
234896fd7ce5SGreg Kroah-Hartman  *		Takes tasklist_lock internally to walk sessions
234996fd7ce5SGreg Kroah-Hartman  *		Takes ->siglock() when updating signal->tty
235096fd7ce5SGreg Kroah-Hartman  */
235196fd7ce5SGreg Kroah-Hartman 
235296fd7ce5SGreg Kroah-Hartman static int tiocsctty(struct tty_struct *tty, int arg)
235396fd7ce5SGreg Kroah-Hartman {
235496fd7ce5SGreg Kroah-Hartman 	int ret = 0;
235596fd7ce5SGreg Kroah-Hartman 	if (current->signal->leader && (task_session(current) == tty->session))
235696fd7ce5SGreg Kroah-Hartman 		return ret;
235796fd7ce5SGreg Kroah-Hartman 
235896fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
235996fd7ce5SGreg Kroah-Hartman 	/*
236096fd7ce5SGreg Kroah-Hartman 	 * The process must be a session leader and
236196fd7ce5SGreg Kroah-Hartman 	 * not have a controlling tty already.
236296fd7ce5SGreg Kroah-Hartman 	 */
236396fd7ce5SGreg Kroah-Hartman 	if (!current->signal->leader || current->signal->tty) {
236496fd7ce5SGreg Kroah-Hartman 		ret = -EPERM;
236596fd7ce5SGreg Kroah-Hartman 		goto unlock;
236696fd7ce5SGreg Kroah-Hartman 	}
236796fd7ce5SGreg Kroah-Hartman 
236896fd7ce5SGreg Kroah-Hartman 	if (tty->session) {
236996fd7ce5SGreg Kroah-Hartman 		/*
237096fd7ce5SGreg Kroah-Hartman 		 * This tty is already the controlling
237196fd7ce5SGreg Kroah-Hartman 		 * tty for another session group!
237296fd7ce5SGreg Kroah-Hartman 		 */
237396fd7ce5SGreg Kroah-Hartman 		if (arg == 1 && capable(CAP_SYS_ADMIN)) {
237496fd7ce5SGreg Kroah-Hartman 			/*
237596fd7ce5SGreg Kroah-Hartman 			 * Steal it away
237696fd7ce5SGreg Kroah-Hartman 			 */
237796fd7ce5SGreg Kroah-Hartman 			read_lock(&tasklist_lock);
237896fd7ce5SGreg Kroah-Hartman 			session_clear_tty(tty->session);
237996fd7ce5SGreg Kroah-Hartman 			read_unlock(&tasklist_lock);
238096fd7ce5SGreg Kroah-Hartman 		} else {
238196fd7ce5SGreg Kroah-Hartman 			ret = -EPERM;
238296fd7ce5SGreg Kroah-Hartman 			goto unlock;
238396fd7ce5SGreg Kroah-Hartman 		}
238496fd7ce5SGreg Kroah-Hartman 	}
238596fd7ce5SGreg Kroah-Hartman 	proc_set_tty(current, tty);
238696fd7ce5SGreg Kroah-Hartman unlock:
238796fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
238896fd7ce5SGreg Kroah-Hartman 	return ret;
238996fd7ce5SGreg Kroah-Hartman }
239096fd7ce5SGreg Kroah-Hartman 
239196fd7ce5SGreg Kroah-Hartman /**
239296fd7ce5SGreg Kroah-Hartman  *	tty_get_pgrp	-	return a ref counted pgrp pid
239396fd7ce5SGreg Kroah-Hartman  *	@tty: tty to read
239496fd7ce5SGreg Kroah-Hartman  *
239596fd7ce5SGreg Kroah-Hartman  *	Returns a refcounted instance of the pid struct for the process
239696fd7ce5SGreg Kroah-Hartman  *	group controlling the tty.
239796fd7ce5SGreg Kroah-Hartman  */
239896fd7ce5SGreg Kroah-Hartman 
239996fd7ce5SGreg Kroah-Hartman struct pid *tty_get_pgrp(struct tty_struct *tty)
240096fd7ce5SGreg Kroah-Hartman {
240196fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
240296fd7ce5SGreg Kroah-Hartman 	struct pid *pgrp;
240396fd7ce5SGreg Kroah-Hartman 
240496fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&tty->ctrl_lock, flags);
240596fd7ce5SGreg Kroah-Hartman 	pgrp = get_pid(tty->pgrp);
240696fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
240796fd7ce5SGreg Kroah-Hartman 
240896fd7ce5SGreg Kroah-Hartman 	return pgrp;
240996fd7ce5SGreg Kroah-Hartman }
241096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_get_pgrp);
241196fd7ce5SGreg Kroah-Hartman 
241296fd7ce5SGreg Kroah-Hartman /**
241396fd7ce5SGreg Kroah-Hartman  *	tiocgpgrp		-	get process group
241496fd7ce5SGreg Kroah-Hartman  *	@tty: tty passed by user
241525985edcSLucas De Marchi  *	@real_tty: tty side of the tty passed by the user if a pty else the tty
241696fd7ce5SGreg Kroah-Hartman  *	@p: returned pid
241796fd7ce5SGreg Kroah-Hartman  *
241896fd7ce5SGreg Kroah-Hartman  *	Obtain the process group of the tty. If there is no process group
241996fd7ce5SGreg Kroah-Hartman  *	return an error.
242096fd7ce5SGreg Kroah-Hartman  *
242196fd7ce5SGreg Kroah-Hartman  *	Locking: none. Reference to current->signal->tty is safe.
242296fd7ce5SGreg Kroah-Hartman  */
242396fd7ce5SGreg Kroah-Hartman 
242496fd7ce5SGreg Kroah-Hartman static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
242596fd7ce5SGreg Kroah-Hartman {
242696fd7ce5SGreg Kroah-Hartman 	struct pid *pid;
242796fd7ce5SGreg Kroah-Hartman 	int ret;
242896fd7ce5SGreg Kroah-Hartman 	/*
242996fd7ce5SGreg Kroah-Hartman 	 * (tty == real_tty) is a cheap way of
243096fd7ce5SGreg Kroah-Hartman 	 * testing if the tty is NOT a master pty.
243196fd7ce5SGreg Kroah-Hartman 	 */
243296fd7ce5SGreg Kroah-Hartman 	if (tty == real_tty && current->signal->tty != real_tty)
243396fd7ce5SGreg Kroah-Hartman 		return -ENOTTY;
243496fd7ce5SGreg Kroah-Hartman 	pid = tty_get_pgrp(real_tty);
243596fd7ce5SGreg Kroah-Hartman 	ret =  put_user(pid_vnr(pid), p);
243696fd7ce5SGreg Kroah-Hartman 	put_pid(pid);
243796fd7ce5SGreg Kroah-Hartman 	return ret;
243896fd7ce5SGreg Kroah-Hartman }
243996fd7ce5SGreg Kroah-Hartman 
244096fd7ce5SGreg Kroah-Hartman /**
244196fd7ce5SGreg Kroah-Hartman  *	tiocspgrp		-	attempt to set process group
244296fd7ce5SGreg Kroah-Hartman  *	@tty: tty passed by user
244396fd7ce5SGreg Kroah-Hartman  *	@real_tty: tty side device matching tty passed by user
244496fd7ce5SGreg Kroah-Hartman  *	@p: pid pointer
244596fd7ce5SGreg Kroah-Hartman  *
244696fd7ce5SGreg Kroah-Hartman  *	Set the process group of the tty to the session passed. Only
244796fd7ce5SGreg Kroah-Hartman  *	permitted where the tty session is our session.
244896fd7ce5SGreg Kroah-Hartman  *
244996fd7ce5SGreg Kroah-Hartman  *	Locking: RCU, ctrl lock
245096fd7ce5SGreg Kroah-Hartman  */
245196fd7ce5SGreg Kroah-Hartman 
245296fd7ce5SGreg Kroah-Hartman static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
245396fd7ce5SGreg Kroah-Hartman {
245496fd7ce5SGreg Kroah-Hartman 	struct pid *pgrp;
245596fd7ce5SGreg Kroah-Hartman 	pid_t pgrp_nr;
245696fd7ce5SGreg Kroah-Hartman 	int retval = tty_check_change(real_tty);
245796fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
245896fd7ce5SGreg Kroah-Hartman 
245996fd7ce5SGreg Kroah-Hartman 	if (retval == -EIO)
246096fd7ce5SGreg Kroah-Hartman 		return -ENOTTY;
246196fd7ce5SGreg Kroah-Hartman 	if (retval)
246296fd7ce5SGreg Kroah-Hartman 		return retval;
246396fd7ce5SGreg Kroah-Hartman 	if (!current->signal->tty ||
246496fd7ce5SGreg Kroah-Hartman 	    (current->signal->tty != real_tty) ||
246596fd7ce5SGreg Kroah-Hartman 	    (real_tty->session != task_session(current)))
246696fd7ce5SGreg Kroah-Hartman 		return -ENOTTY;
246796fd7ce5SGreg Kroah-Hartman 	if (get_user(pgrp_nr, p))
246896fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
246996fd7ce5SGreg Kroah-Hartman 	if (pgrp_nr < 0)
247096fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
247196fd7ce5SGreg Kroah-Hartman 	rcu_read_lock();
247296fd7ce5SGreg Kroah-Hartman 	pgrp = find_vpid(pgrp_nr);
247396fd7ce5SGreg Kroah-Hartman 	retval = -ESRCH;
247496fd7ce5SGreg Kroah-Hartman 	if (!pgrp)
247596fd7ce5SGreg Kroah-Hartman 		goto out_unlock;
247696fd7ce5SGreg Kroah-Hartman 	retval = -EPERM;
247796fd7ce5SGreg Kroah-Hartman 	if (session_of_pgrp(pgrp) != task_session(current))
247896fd7ce5SGreg Kroah-Hartman 		goto out_unlock;
247996fd7ce5SGreg Kroah-Hartman 	retval = 0;
248096fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&tty->ctrl_lock, flags);
248196fd7ce5SGreg Kroah-Hartman 	put_pid(real_tty->pgrp);
248296fd7ce5SGreg Kroah-Hartman 	real_tty->pgrp = get_pid(pgrp);
248396fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&tty->ctrl_lock, flags);
248496fd7ce5SGreg Kroah-Hartman out_unlock:
248596fd7ce5SGreg Kroah-Hartman 	rcu_read_unlock();
248696fd7ce5SGreg Kroah-Hartman 	return retval;
248796fd7ce5SGreg Kroah-Hartman }
248896fd7ce5SGreg Kroah-Hartman 
248996fd7ce5SGreg Kroah-Hartman /**
249096fd7ce5SGreg Kroah-Hartman  *	tiocgsid		-	get session id
249196fd7ce5SGreg Kroah-Hartman  *	@tty: tty passed by user
249225985edcSLucas De Marchi  *	@real_tty: tty side of the tty passed by the user if a pty else the tty
249396fd7ce5SGreg Kroah-Hartman  *	@p: pointer to returned session id
249496fd7ce5SGreg Kroah-Hartman  *
249596fd7ce5SGreg Kroah-Hartman  *	Obtain the session id of the tty. If there is no session
249696fd7ce5SGreg Kroah-Hartman  *	return an error.
249796fd7ce5SGreg Kroah-Hartman  *
249896fd7ce5SGreg Kroah-Hartman  *	Locking: none. Reference to current->signal->tty is safe.
249996fd7ce5SGreg Kroah-Hartman  */
250096fd7ce5SGreg Kroah-Hartman 
250196fd7ce5SGreg Kroah-Hartman static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
250296fd7ce5SGreg Kroah-Hartman {
250396fd7ce5SGreg Kroah-Hartman 	/*
250496fd7ce5SGreg Kroah-Hartman 	 * (tty == real_tty) is a cheap way of
250596fd7ce5SGreg Kroah-Hartman 	 * testing if the tty is NOT a master pty.
250696fd7ce5SGreg Kroah-Hartman 	*/
250796fd7ce5SGreg Kroah-Hartman 	if (tty == real_tty && current->signal->tty != real_tty)
250896fd7ce5SGreg Kroah-Hartman 		return -ENOTTY;
250996fd7ce5SGreg Kroah-Hartman 	if (!real_tty->session)
251096fd7ce5SGreg Kroah-Hartman 		return -ENOTTY;
251196fd7ce5SGreg Kroah-Hartman 	return put_user(pid_vnr(real_tty->session), p);
251296fd7ce5SGreg Kroah-Hartman }
251396fd7ce5SGreg Kroah-Hartman 
251496fd7ce5SGreg Kroah-Hartman /**
251596fd7ce5SGreg Kroah-Hartman  *	tiocsetd	-	set line discipline
251696fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
251796fd7ce5SGreg Kroah-Hartman  *	@p: pointer to user data
251896fd7ce5SGreg Kroah-Hartman  *
251996fd7ce5SGreg Kroah-Hartman  *	Set the line discipline according to user request.
252096fd7ce5SGreg Kroah-Hartman  *
252196fd7ce5SGreg Kroah-Hartman  *	Locking: see tty_set_ldisc, this function is just a helper
252296fd7ce5SGreg Kroah-Hartman  */
252396fd7ce5SGreg Kroah-Hartman 
252496fd7ce5SGreg Kroah-Hartman static int tiocsetd(struct tty_struct *tty, int __user *p)
252596fd7ce5SGreg Kroah-Hartman {
252696fd7ce5SGreg Kroah-Hartman 	int ldisc;
252796fd7ce5SGreg Kroah-Hartman 	int ret;
252896fd7ce5SGreg Kroah-Hartman 
252996fd7ce5SGreg Kroah-Hartman 	if (get_user(ldisc, p))
253096fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
253196fd7ce5SGreg Kroah-Hartman 
253296fd7ce5SGreg Kroah-Hartman 	ret = tty_set_ldisc(tty, ldisc);
253396fd7ce5SGreg Kroah-Hartman 
253496fd7ce5SGreg Kroah-Hartman 	return ret;
253596fd7ce5SGreg Kroah-Hartman }
253696fd7ce5SGreg Kroah-Hartman 
253796fd7ce5SGreg Kroah-Hartman /**
253896fd7ce5SGreg Kroah-Hartman  *	send_break	-	performed time break
253996fd7ce5SGreg Kroah-Hartman  *	@tty: device to break on
254096fd7ce5SGreg Kroah-Hartman  *	@duration: timeout in mS
254196fd7ce5SGreg Kroah-Hartman  *
254296fd7ce5SGreg Kroah-Hartman  *	Perform a timed break on hardware that lacks its own driver level
254396fd7ce5SGreg Kroah-Hartman  *	timed break functionality.
254496fd7ce5SGreg Kroah-Hartman  *
254596fd7ce5SGreg Kroah-Hartman  *	Locking:
254696fd7ce5SGreg Kroah-Hartman  *		atomic_write_lock serializes
254796fd7ce5SGreg Kroah-Hartman  *
254896fd7ce5SGreg Kroah-Hartman  */
254996fd7ce5SGreg Kroah-Hartman 
255096fd7ce5SGreg Kroah-Hartman static int send_break(struct tty_struct *tty, unsigned int duration)
255196fd7ce5SGreg Kroah-Hartman {
255296fd7ce5SGreg Kroah-Hartman 	int retval;
255396fd7ce5SGreg Kroah-Hartman 
255496fd7ce5SGreg Kroah-Hartman 	if (tty->ops->break_ctl == NULL)
255596fd7ce5SGreg Kroah-Hartman 		return 0;
255696fd7ce5SGreg Kroah-Hartman 
255796fd7ce5SGreg Kroah-Hartman 	if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
255896fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->break_ctl(tty, duration);
255996fd7ce5SGreg Kroah-Hartman 	else {
256096fd7ce5SGreg Kroah-Hartman 		/* Do the work ourselves */
256196fd7ce5SGreg Kroah-Hartman 		if (tty_write_lock(tty, 0) < 0)
256296fd7ce5SGreg Kroah-Hartman 			return -EINTR;
256396fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->break_ctl(tty, -1);
256496fd7ce5SGreg Kroah-Hartman 		if (retval)
256596fd7ce5SGreg Kroah-Hartman 			goto out;
256696fd7ce5SGreg Kroah-Hartman 		if (!signal_pending(current))
256796fd7ce5SGreg Kroah-Hartman 			msleep_interruptible(duration);
256896fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->break_ctl(tty, 0);
256996fd7ce5SGreg Kroah-Hartman out:
257096fd7ce5SGreg Kroah-Hartman 		tty_write_unlock(tty);
257196fd7ce5SGreg Kroah-Hartman 		if (signal_pending(current))
257296fd7ce5SGreg Kroah-Hartman 			retval = -EINTR;
257396fd7ce5SGreg Kroah-Hartman 	}
257496fd7ce5SGreg Kroah-Hartman 	return retval;
257596fd7ce5SGreg Kroah-Hartman }
257696fd7ce5SGreg Kroah-Hartman 
257796fd7ce5SGreg Kroah-Hartman /**
257896fd7ce5SGreg Kroah-Hartman  *	tty_tiocmget		-	get modem status
257996fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
258096fd7ce5SGreg Kroah-Hartman  *	@file: user file pointer
258196fd7ce5SGreg Kroah-Hartman  *	@p: pointer to result
258296fd7ce5SGreg Kroah-Hartman  *
258396fd7ce5SGreg Kroah-Hartman  *	Obtain the modem status bits from the tty driver if the feature
258496fd7ce5SGreg Kroah-Hartman  *	is supported. Return -EINVAL if it is not available.
258596fd7ce5SGreg Kroah-Hartman  *
258696fd7ce5SGreg Kroah-Hartman  *	Locking: none (up to the driver)
258796fd7ce5SGreg Kroah-Hartman  */
258896fd7ce5SGreg Kroah-Hartman 
258960b33c13SAlan Cox static int tty_tiocmget(struct tty_struct *tty, int __user *p)
259096fd7ce5SGreg Kroah-Hartman {
259196fd7ce5SGreg Kroah-Hartman 	int retval = -EINVAL;
259296fd7ce5SGreg Kroah-Hartman 
259396fd7ce5SGreg Kroah-Hartman 	if (tty->ops->tiocmget) {
259460b33c13SAlan Cox 		retval = tty->ops->tiocmget(tty);
259596fd7ce5SGreg Kroah-Hartman 
259696fd7ce5SGreg Kroah-Hartman 		if (retval >= 0)
259796fd7ce5SGreg Kroah-Hartman 			retval = put_user(retval, p);
259896fd7ce5SGreg Kroah-Hartman 	}
259996fd7ce5SGreg Kroah-Hartman 	return retval;
260096fd7ce5SGreg Kroah-Hartman }
260196fd7ce5SGreg Kroah-Hartman 
260296fd7ce5SGreg Kroah-Hartman /**
260396fd7ce5SGreg Kroah-Hartman  *	tty_tiocmset		-	set modem status
260496fd7ce5SGreg Kroah-Hartman  *	@tty: tty device
260596fd7ce5SGreg Kroah-Hartman  *	@cmd: command - clear bits, set bits or set all
260696fd7ce5SGreg Kroah-Hartman  *	@p: pointer to desired bits
260796fd7ce5SGreg Kroah-Hartman  *
260896fd7ce5SGreg Kroah-Hartman  *	Set the modem status bits from the tty driver if the feature
260996fd7ce5SGreg Kroah-Hartman  *	is supported. Return -EINVAL if it is not available.
261096fd7ce5SGreg Kroah-Hartman  *
261196fd7ce5SGreg Kroah-Hartman  *	Locking: none (up to the driver)
261296fd7ce5SGreg Kroah-Hartman  */
261396fd7ce5SGreg Kroah-Hartman 
261420b9d177SAlan Cox static int tty_tiocmset(struct tty_struct *tty, unsigned int cmd,
261596fd7ce5SGreg Kroah-Hartman 	     unsigned __user *p)
261696fd7ce5SGreg Kroah-Hartman {
261796fd7ce5SGreg Kroah-Hartman 	int retval;
261896fd7ce5SGreg Kroah-Hartman 	unsigned int set, clear, val;
261996fd7ce5SGreg Kroah-Hartman 
262096fd7ce5SGreg Kroah-Hartman 	if (tty->ops->tiocmset == NULL)
262196fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
262296fd7ce5SGreg Kroah-Hartman 
262396fd7ce5SGreg Kroah-Hartman 	retval = get_user(val, p);
262496fd7ce5SGreg Kroah-Hartman 	if (retval)
262596fd7ce5SGreg Kroah-Hartman 		return retval;
262696fd7ce5SGreg Kroah-Hartman 	set = clear = 0;
262796fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
262896fd7ce5SGreg Kroah-Hartman 	case TIOCMBIS:
262996fd7ce5SGreg Kroah-Hartman 		set = val;
263096fd7ce5SGreg Kroah-Hartman 		break;
263196fd7ce5SGreg Kroah-Hartman 	case TIOCMBIC:
263296fd7ce5SGreg Kroah-Hartman 		clear = val;
263396fd7ce5SGreg Kroah-Hartman 		break;
263496fd7ce5SGreg Kroah-Hartman 	case TIOCMSET:
263596fd7ce5SGreg Kroah-Hartman 		set = val;
263696fd7ce5SGreg Kroah-Hartman 		clear = ~val;
263796fd7ce5SGreg Kroah-Hartman 		break;
263896fd7ce5SGreg Kroah-Hartman 	}
263996fd7ce5SGreg Kroah-Hartman 	set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
264096fd7ce5SGreg Kroah-Hartman 	clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
264120b9d177SAlan Cox 	return tty->ops->tiocmset(tty, set, clear);
264296fd7ce5SGreg Kroah-Hartman }
264396fd7ce5SGreg Kroah-Hartman 
264496fd7ce5SGreg Kroah-Hartman static int tty_tiocgicount(struct tty_struct *tty, void __user *arg)
264596fd7ce5SGreg Kroah-Hartman {
264696fd7ce5SGreg Kroah-Hartman 	int retval = -EINVAL;
264796fd7ce5SGreg Kroah-Hartman 	struct serial_icounter_struct icount;
264896fd7ce5SGreg Kroah-Hartman 	memset(&icount, 0, sizeof(icount));
264996fd7ce5SGreg Kroah-Hartman 	if (tty->ops->get_icount)
265096fd7ce5SGreg Kroah-Hartman 		retval = tty->ops->get_icount(tty, &icount);
265196fd7ce5SGreg Kroah-Hartman 	if (retval != 0)
265296fd7ce5SGreg Kroah-Hartman 		return retval;
265396fd7ce5SGreg Kroah-Hartman 	if (copy_to_user(arg, &icount, sizeof(icount)))
265496fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
265596fd7ce5SGreg Kroah-Hartman 	return 0;
265696fd7ce5SGreg Kroah-Hartman }
265796fd7ce5SGreg Kroah-Hartman 
265896fd7ce5SGreg Kroah-Hartman struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
265996fd7ce5SGreg Kroah-Hartman {
266096fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
266196fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_MASTER)
266296fd7ce5SGreg Kroah-Hartman 		tty = tty->link;
266396fd7ce5SGreg Kroah-Hartman 	return tty;
266496fd7ce5SGreg Kroah-Hartman }
266596fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_pair_get_tty);
266696fd7ce5SGreg Kroah-Hartman 
266796fd7ce5SGreg Kroah-Hartman struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
266896fd7ce5SGreg Kroah-Hartman {
266996fd7ce5SGreg Kroah-Hartman 	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
267096fd7ce5SGreg Kroah-Hartman 	    tty->driver->subtype == PTY_TYPE_MASTER)
267196fd7ce5SGreg Kroah-Hartman 	    return tty;
267296fd7ce5SGreg Kroah-Hartman 	return tty->link;
267396fd7ce5SGreg Kroah-Hartman }
267496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_pair_get_pty);
267596fd7ce5SGreg Kroah-Hartman 
267696fd7ce5SGreg Kroah-Hartman /*
267796fd7ce5SGreg Kroah-Hartman  * Split this up, as gcc can choke on it otherwise..
267896fd7ce5SGreg Kroah-Hartman  */
267996fd7ce5SGreg Kroah-Hartman long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
268096fd7ce5SGreg Kroah-Hartman {
268196fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
268296fd7ce5SGreg Kroah-Hartman 	struct tty_struct *real_tty;
268396fd7ce5SGreg Kroah-Hartman 	void __user *p = (void __user *)arg;
268496fd7ce5SGreg Kroah-Hartman 	int retval;
268596fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
268696fd7ce5SGreg Kroah-Hartman 
26876131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
268896fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
268996fd7ce5SGreg Kroah-Hartman 
269096fd7ce5SGreg Kroah-Hartman 	real_tty = tty_pair_get_tty(tty);
269196fd7ce5SGreg Kroah-Hartman 
269296fd7ce5SGreg Kroah-Hartman 	/*
269396fd7ce5SGreg Kroah-Hartman 	 * Factor out some common prep work
269496fd7ce5SGreg Kroah-Hartman 	 */
269596fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
269696fd7ce5SGreg Kroah-Hartman 	case TIOCSETD:
269796fd7ce5SGreg Kroah-Hartman 	case TIOCSBRK:
269896fd7ce5SGreg Kroah-Hartman 	case TIOCCBRK:
269996fd7ce5SGreg Kroah-Hartman 	case TCSBRK:
270096fd7ce5SGreg Kroah-Hartman 	case TCSBRKP:
270196fd7ce5SGreg Kroah-Hartman 		retval = tty_check_change(tty);
270296fd7ce5SGreg Kroah-Hartman 		if (retval)
270396fd7ce5SGreg Kroah-Hartman 			return retval;
270496fd7ce5SGreg Kroah-Hartman 		if (cmd != TIOCCBRK) {
270596fd7ce5SGreg Kroah-Hartman 			tty_wait_until_sent(tty, 0);
270696fd7ce5SGreg Kroah-Hartman 			if (signal_pending(current))
270796fd7ce5SGreg Kroah-Hartman 				return -EINTR;
270896fd7ce5SGreg Kroah-Hartman 		}
270996fd7ce5SGreg Kroah-Hartman 		break;
271096fd7ce5SGreg Kroah-Hartman 	}
271196fd7ce5SGreg Kroah-Hartman 
271296fd7ce5SGreg Kroah-Hartman 	/*
271396fd7ce5SGreg Kroah-Hartman 	 *	Now do the stuff.
271496fd7ce5SGreg Kroah-Hartman 	 */
271596fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
271696fd7ce5SGreg Kroah-Hartman 	case TIOCSTI:
271796fd7ce5SGreg Kroah-Hartman 		return tiocsti(tty, p);
271896fd7ce5SGreg Kroah-Hartman 	case TIOCGWINSZ:
271996fd7ce5SGreg Kroah-Hartman 		return tiocgwinsz(real_tty, p);
272096fd7ce5SGreg Kroah-Hartman 	case TIOCSWINSZ:
272196fd7ce5SGreg Kroah-Hartman 		return tiocswinsz(real_tty, p);
272296fd7ce5SGreg Kroah-Hartman 	case TIOCCONS:
272396fd7ce5SGreg Kroah-Hartman 		return real_tty != tty ? -EINVAL : tioccons(file);
272496fd7ce5SGreg Kroah-Hartman 	case FIONBIO:
272596fd7ce5SGreg Kroah-Hartman 		return fionbio(file, p);
272696fd7ce5SGreg Kroah-Hartman 	case TIOCEXCL:
272796fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_EXCLUSIVE, &tty->flags);
272896fd7ce5SGreg Kroah-Hartman 		return 0;
272996fd7ce5SGreg Kroah-Hartman 	case TIOCNXCL:
273096fd7ce5SGreg Kroah-Hartman 		clear_bit(TTY_EXCLUSIVE, &tty->flags);
273196fd7ce5SGreg Kroah-Hartman 		return 0;
273284fd7bdfSCyrill Gorcunov 	case TIOCGEXCL:
273384fd7bdfSCyrill Gorcunov 	{
273484fd7bdfSCyrill Gorcunov 		int excl = test_bit(TTY_EXCLUSIVE, &tty->flags);
273584fd7bdfSCyrill Gorcunov 		return put_user(excl, (int __user *)p);
273684fd7bdfSCyrill Gorcunov 	}
273796fd7ce5SGreg Kroah-Hartman 	case TIOCNOTTY:
273896fd7ce5SGreg Kroah-Hartman 		if (current->signal->tty != tty)
273996fd7ce5SGreg Kroah-Hartman 			return -ENOTTY;
274096fd7ce5SGreg Kroah-Hartman 		no_tty();
274196fd7ce5SGreg Kroah-Hartman 		return 0;
274296fd7ce5SGreg Kroah-Hartman 	case TIOCSCTTY:
274396fd7ce5SGreg Kroah-Hartman 		return tiocsctty(tty, arg);
274496fd7ce5SGreg Kroah-Hartman 	case TIOCGPGRP:
274596fd7ce5SGreg Kroah-Hartman 		return tiocgpgrp(tty, real_tty, p);
274696fd7ce5SGreg Kroah-Hartman 	case TIOCSPGRP:
274796fd7ce5SGreg Kroah-Hartman 		return tiocspgrp(tty, real_tty, p);
274896fd7ce5SGreg Kroah-Hartman 	case TIOCGSID:
274996fd7ce5SGreg Kroah-Hartman 		return tiocgsid(tty, real_tty, p);
275096fd7ce5SGreg Kroah-Hartman 	case TIOCGETD:
275196fd7ce5SGreg Kroah-Hartman 		return put_user(tty->ldisc->ops->num, (int __user *)p);
275296fd7ce5SGreg Kroah-Hartman 	case TIOCSETD:
275396fd7ce5SGreg Kroah-Hartman 		return tiocsetd(tty, p);
27543c95c985SKay Sievers 	case TIOCVHANGUP:
27553c95c985SKay Sievers 		if (!capable(CAP_SYS_ADMIN))
27563c95c985SKay Sievers 			return -EPERM;
27573c95c985SKay Sievers 		tty_vhangup(tty);
27583c95c985SKay Sievers 		return 0;
2759b7b8de08SWerner Fink 	case TIOCGDEV:
2760b7b8de08SWerner Fink 	{
2761b7b8de08SWerner Fink 		unsigned int ret = new_encode_dev(tty_devnum(real_tty));
2762b7b8de08SWerner Fink 		return put_user(ret, (unsigned int __user *)p);
2763b7b8de08SWerner Fink 	}
276496fd7ce5SGreg Kroah-Hartman 	/*
276596fd7ce5SGreg Kroah-Hartman 	 * Break handling
276696fd7ce5SGreg Kroah-Hartman 	 */
276796fd7ce5SGreg Kroah-Hartman 	case TIOCSBRK:	/* Turn break on, unconditionally */
276896fd7ce5SGreg Kroah-Hartman 		if (tty->ops->break_ctl)
276996fd7ce5SGreg Kroah-Hartman 			return tty->ops->break_ctl(tty, -1);
277096fd7ce5SGreg Kroah-Hartman 		return 0;
277196fd7ce5SGreg Kroah-Hartman 	case TIOCCBRK:	/* Turn break off, unconditionally */
277296fd7ce5SGreg Kroah-Hartman 		if (tty->ops->break_ctl)
277396fd7ce5SGreg Kroah-Hartman 			return tty->ops->break_ctl(tty, 0);
277496fd7ce5SGreg Kroah-Hartman 		return 0;
277596fd7ce5SGreg Kroah-Hartman 	case TCSBRK:   /* SVID version: non-zero arg --> no break */
277696fd7ce5SGreg Kroah-Hartman 		/* non-zero arg means wait for all output data
277796fd7ce5SGreg Kroah-Hartman 		 * to be sent (performed above) but don't send break.
277896fd7ce5SGreg Kroah-Hartman 		 * This is used by the tcdrain() termios function.
277996fd7ce5SGreg Kroah-Hartman 		 */
278096fd7ce5SGreg Kroah-Hartman 		if (!arg)
278196fd7ce5SGreg Kroah-Hartman 			return send_break(tty, 250);
278296fd7ce5SGreg Kroah-Hartman 		return 0;
278396fd7ce5SGreg Kroah-Hartman 	case TCSBRKP:	/* support for POSIX tcsendbreak() */
278496fd7ce5SGreg Kroah-Hartman 		return send_break(tty, arg ? arg*100 : 250);
278596fd7ce5SGreg Kroah-Hartman 
278696fd7ce5SGreg Kroah-Hartman 	case TIOCMGET:
278760b33c13SAlan Cox 		return tty_tiocmget(tty, p);
278896fd7ce5SGreg Kroah-Hartman 	case TIOCMSET:
278996fd7ce5SGreg Kroah-Hartman 	case TIOCMBIC:
279096fd7ce5SGreg Kroah-Hartman 	case TIOCMBIS:
279120b9d177SAlan Cox 		return tty_tiocmset(tty, cmd, p);
279296fd7ce5SGreg Kroah-Hartman 	case TIOCGICOUNT:
279396fd7ce5SGreg Kroah-Hartman 		retval = tty_tiocgicount(tty, p);
279496fd7ce5SGreg Kroah-Hartman 		/* For the moment allow fall through to the old method */
279596fd7ce5SGreg Kroah-Hartman         	if (retval != -EINVAL)
279696fd7ce5SGreg Kroah-Hartman 			return retval;
279796fd7ce5SGreg Kroah-Hartman 		break;
279896fd7ce5SGreg Kroah-Hartman 	case TCFLSH:
279996fd7ce5SGreg Kroah-Hartman 		switch (arg) {
280096fd7ce5SGreg Kroah-Hartman 		case TCIFLUSH:
280196fd7ce5SGreg Kroah-Hartman 		case TCIOFLUSH:
280296fd7ce5SGreg Kroah-Hartman 		/* flush tty buffer and allow ldisc to process ioctl */
280396fd7ce5SGreg Kroah-Hartman 			tty_buffer_flush(tty);
280496fd7ce5SGreg Kroah-Hartman 			break;
280596fd7ce5SGreg Kroah-Hartman 		}
280696fd7ce5SGreg Kroah-Hartman 		break;
280796fd7ce5SGreg Kroah-Hartman 	}
280896fd7ce5SGreg Kroah-Hartman 	if (tty->ops->ioctl) {
28096caa76b7SAlan Cox 		retval = (tty->ops->ioctl)(tty, cmd, arg);
281096fd7ce5SGreg Kroah-Hartman 		if (retval != -ENOIOCTLCMD)
281196fd7ce5SGreg Kroah-Hartman 			return retval;
281296fd7ce5SGreg Kroah-Hartman 	}
281396fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
281496fd7ce5SGreg Kroah-Hartman 	retval = -EINVAL;
281596fd7ce5SGreg Kroah-Hartman 	if (ld->ops->ioctl) {
281696fd7ce5SGreg Kroah-Hartman 		retval = ld->ops->ioctl(tty, file, cmd, arg);
281796fd7ce5SGreg Kroah-Hartman 		if (retval == -ENOIOCTLCMD)
2818bbb63c51SWanlong Gao 			retval = -ENOTTY;
281996fd7ce5SGreg Kroah-Hartman 	}
282096fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
282196fd7ce5SGreg Kroah-Hartman 	return retval;
282296fd7ce5SGreg Kroah-Hartman }
282396fd7ce5SGreg Kroah-Hartman 
282496fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_COMPAT
282596fd7ce5SGreg Kroah-Hartman static long tty_compat_ioctl(struct file *file, unsigned int cmd,
282696fd7ce5SGreg Kroah-Hartman 				unsigned long arg)
282796fd7ce5SGreg Kroah-Hartman {
282896fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty = file_tty(file);
282996fd7ce5SGreg Kroah-Hartman 	struct tty_ldisc *ld;
283096fd7ce5SGreg Kroah-Hartman 	int retval = -ENOIOCTLCMD;
283196fd7ce5SGreg Kroah-Hartman 
28326131ffaaSAl Viro 	if (tty_paranoia_check(tty, file_inode(file), "tty_ioctl"))
283396fd7ce5SGreg Kroah-Hartman 		return -EINVAL;
283496fd7ce5SGreg Kroah-Hartman 
283596fd7ce5SGreg Kroah-Hartman 	if (tty->ops->compat_ioctl) {
28366caa76b7SAlan Cox 		retval = (tty->ops->compat_ioctl)(tty, cmd, arg);
283796fd7ce5SGreg Kroah-Hartman 		if (retval != -ENOIOCTLCMD)
283896fd7ce5SGreg Kroah-Hartman 			return retval;
283996fd7ce5SGreg Kroah-Hartman 	}
284096fd7ce5SGreg Kroah-Hartman 
284196fd7ce5SGreg Kroah-Hartman 	ld = tty_ldisc_ref_wait(tty);
284296fd7ce5SGreg Kroah-Hartman 	if (ld->ops->compat_ioctl)
284396fd7ce5SGreg Kroah-Hartman 		retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
28448193c429SThomas Meyer 	else
28458193c429SThomas Meyer 		retval = n_tty_compat_ioctl_helper(tty, file, cmd, arg);
284696fd7ce5SGreg Kroah-Hartman 	tty_ldisc_deref(ld);
284796fd7ce5SGreg Kroah-Hartman 
284896fd7ce5SGreg Kroah-Hartman 	return retval;
284996fd7ce5SGreg Kroah-Hartman }
285096fd7ce5SGreg Kroah-Hartman #endif
285196fd7ce5SGreg Kroah-Hartman 
2852c3c073f8SAl Viro static int this_tty(const void *t, struct file *file, unsigned fd)
2853c3c073f8SAl Viro {
2854c3c073f8SAl Viro 	if (likely(file->f_op->read != tty_read))
2855c3c073f8SAl Viro 		return 0;
2856c3c073f8SAl Viro 	return file_tty(file) != t ? 0 : fd + 1;
2857c3c073f8SAl Viro }
2858c3c073f8SAl Viro 
285996fd7ce5SGreg Kroah-Hartman /*
286096fd7ce5SGreg Kroah-Hartman  * This implements the "Secure Attention Key" ---  the idea is to
286196fd7ce5SGreg Kroah-Hartman  * prevent trojan horses by killing all processes associated with this
286296fd7ce5SGreg Kroah-Hartman  * tty when the user hits the "Secure Attention Key".  Required for
286396fd7ce5SGreg Kroah-Hartman  * super-paranoid applications --- see the Orange Book for more details.
286496fd7ce5SGreg Kroah-Hartman  *
286596fd7ce5SGreg Kroah-Hartman  * This code could be nicer; ideally it should send a HUP, wait a few
286696fd7ce5SGreg Kroah-Hartman  * seconds, then send a INT, and then a KILL signal.  But you then
286796fd7ce5SGreg Kroah-Hartman  * have to coordinate with the init process, since all processes associated
286896fd7ce5SGreg Kroah-Hartman  * with the current tty must be dead before the new getty is allowed
286996fd7ce5SGreg Kroah-Hartman  * to spawn.
287096fd7ce5SGreg Kroah-Hartman  *
287196fd7ce5SGreg Kroah-Hartman  * Now, if it would be correct ;-/ The current code has a nasty hole -
287296fd7ce5SGreg Kroah-Hartman  * it doesn't catch files in flight. We may send the descriptor to ourselves
287396fd7ce5SGreg Kroah-Hartman  * via AF_UNIX socket, close it and later fetch from socket. FIXME.
287496fd7ce5SGreg Kroah-Hartman  *
287596fd7ce5SGreg Kroah-Hartman  * Nasty bug: do_SAK is being called in interrupt context.  This can
287696fd7ce5SGreg Kroah-Hartman  * deadlock.  We punt it up to process context.  AKPM - 16Mar2001
287796fd7ce5SGreg Kroah-Hartman  */
287896fd7ce5SGreg Kroah-Hartman void __do_SAK(struct tty_struct *tty)
287996fd7ce5SGreg Kroah-Hartman {
288096fd7ce5SGreg Kroah-Hartman #ifdef TTY_SOFT_SAK
288196fd7ce5SGreg Kroah-Hartman 	tty_hangup(tty);
288296fd7ce5SGreg Kroah-Hartman #else
288396fd7ce5SGreg Kroah-Hartman 	struct task_struct *g, *p;
288496fd7ce5SGreg Kroah-Hartman 	struct pid *session;
288596fd7ce5SGreg Kroah-Hartman 	int		i;
288696fd7ce5SGreg Kroah-Hartman 
288796fd7ce5SGreg Kroah-Hartman 	if (!tty)
288896fd7ce5SGreg Kroah-Hartman 		return;
288996fd7ce5SGreg Kroah-Hartman 	session = tty->session;
289096fd7ce5SGreg Kroah-Hartman 
289196fd7ce5SGreg Kroah-Hartman 	tty_ldisc_flush(tty);
289296fd7ce5SGreg Kroah-Hartman 
289396fd7ce5SGreg Kroah-Hartman 	tty_driver_flush_buffer(tty);
289496fd7ce5SGreg Kroah-Hartman 
289596fd7ce5SGreg Kroah-Hartman 	read_lock(&tasklist_lock);
289696fd7ce5SGreg Kroah-Hartman 	/* Kill the entire session */
289796fd7ce5SGreg Kroah-Hartman 	do_each_pid_task(session, PIDTYPE_SID, p) {
289896fd7ce5SGreg Kroah-Hartman 		printk(KERN_NOTICE "SAK: killed process %d"
289996fd7ce5SGreg Kroah-Hartman 			" (%s): task_session(p)==tty->session\n",
290096fd7ce5SGreg Kroah-Hartman 			task_pid_nr(p), p->comm);
290196fd7ce5SGreg Kroah-Hartman 		send_sig(SIGKILL, p, 1);
290296fd7ce5SGreg Kroah-Hartman 	} while_each_pid_task(session, PIDTYPE_SID, p);
290396fd7ce5SGreg Kroah-Hartman 	/* Now kill any processes that happen to have the
290496fd7ce5SGreg Kroah-Hartman 	 * tty open.
290596fd7ce5SGreg Kroah-Hartman 	 */
290696fd7ce5SGreg Kroah-Hartman 	do_each_thread(g, p) {
290796fd7ce5SGreg Kroah-Hartman 		if (p->signal->tty == tty) {
290896fd7ce5SGreg Kroah-Hartman 			printk(KERN_NOTICE "SAK: killed process %d"
290996fd7ce5SGreg Kroah-Hartman 			    " (%s): task_session(p)==tty->session\n",
291096fd7ce5SGreg Kroah-Hartman 			    task_pid_nr(p), p->comm);
291196fd7ce5SGreg Kroah-Hartman 			send_sig(SIGKILL, p, 1);
291296fd7ce5SGreg Kroah-Hartman 			continue;
291396fd7ce5SGreg Kroah-Hartman 		}
291496fd7ce5SGreg Kroah-Hartman 		task_lock(p);
2915c3c073f8SAl Viro 		i = iterate_fd(p->files, 0, this_tty, tty);
2916c3c073f8SAl Viro 		if (i != 0) {
291796fd7ce5SGreg Kroah-Hartman 			printk(KERN_NOTICE "SAK: killed process %d"
291896fd7ce5SGreg Kroah-Hartman 			    " (%s): fd#%d opened to the tty\n",
2919c3c073f8SAl Viro 				    task_pid_nr(p), p->comm, i - 1);
292096fd7ce5SGreg Kroah-Hartman 			force_sig(SIGKILL, p);
292196fd7ce5SGreg Kroah-Hartman 		}
292296fd7ce5SGreg Kroah-Hartman 		task_unlock(p);
292396fd7ce5SGreg Kroah-Hartman 	} while_each_thread(g, p);
292496fd7ce5SGreg Kroah-Hartman 	read_unlock(&tasklist_lock);
292596fd7ce5SGreg Kroah-Hartman #endif
292696fd7ce5SGreg Kroah-Hartman }
292796fd7ce5SGreg Kroah-Hartman 
292896fd7ce5SGreg Kroah-Hartman static void do_SAK_work(struct work_struct *work)
292996fd7ce5SGreg Kroah-Hartman {
293096fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty =
293196fd7ce5SGreg Kroah-Hartman 		container_of(work, struct tty_struct, SAK_work);
293296fd7ce5SGreg Kroah-Hartman 	__do_SAK(tty);
293396fd7ce5SGreg Kroah-Hartman }
293496fd7ce5SGreg Kroah-Hartman 
293596fd7ce5SGreg Kroah-Hartman /*
293696fd7ce5SGreg Kroah-Hartman  * The tq handling here is a little racy - tty->SAK_work may already be queued.
293796fd7ce5SGreg Kroah-Hartman  * Fortunately we don't need to worry, because if ->SAK_work is already queued,
293896fd7ce5SGreg Kroah-Hartman  * the values which we write to it will be identical to the values which it
293996fd7ce5SGreg Kroah-Hartman  * already has. --akpm
294096fd7ce5SGreg Kroah-Hartman  */
294196fd7ce5SGreg Kroah-Hartman void do_SAK(struct tty_struct *tty)
294296fd7ce5SGreg Kroah-Hartman {
294396fd7ce5SGreg Kroah-Hartman 	if (!tty)
294496fd7ce5SGreg Kroah-Hartman 		return;
294596fd7ce5SGreg Kroah-Hartman 	schedule_work(&tty->SAK_work);
294696fd7ce5SGreg Kroah-Hartman }
294796fd7ce5SGreg Kroah-Hartman 
294896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(do_SAK);
294996fd7ce5SGreg Kroah-Hartman 
29506e9430acSGreg Kroah-Hartman static int dev_match_devt(struct device *dev, const void *data)
295196fd7ce5SGreg Kroah-Hartman {
29526e9430acSGreg Kroah-Hartman 	const dev_t *devt = data;
295396fd7ce5SGreg Kroah-Hartman 	return dev->devt == *devt;
295496fd7ce5SGreg Kroah-Hartman }
295596fd7ce5SGreg Kroah-Hartman 
295696fd7ce5SGreg Kroah-Hartman /* Must put_device() after it's unused! */
295796fd7ce5SGreg Kroah-Hartman static struct device *tty_get_device(struct tty_struct *tty)
295896fd7ce5SGreg Kroah-Hartman {
295996fd7ce5SGreg Kroah-Hartman 	dev_t devt = tty_devnum(tty);
296096fd7ce5SGreg Kroah-Hartman 	return class_find_device(tty_class, NULL, &devt, dev_match_devt);
296196fd7ce5SGreg Kroah-Hartman }
296296fd7ce5SGreg Kroah-Hartman 
296396fd7ce5SGreg Kroah-Hartman 
296496fd7ce5SGreg Kroah-Hartman /**
296596fd7ce5SGreg Kroah-Hartman  *	initialize_tty_struct
296696fd7ce5SGreg Kroah-Hartman  *	@tty: tty to initialize
296796fd7ce5SGreg Kroah-Hartman  *
296896fd7ce5SGreg Kroah-Hartman  *	This subroutine initializes a tty structure that has been newly
296996fd7ce5SGreg Kroah-Hartman  *	allocated.
297096fd7ce5SGreg Kroah-Hartman  *
297196fd7ce5SGreg Kroah-Hartman  *	Locking: none - tty in question must not be exposed at this point
297296fd7ce5SGreg Kroah-Hartman  */
297396fd7ce5SGreg Kroah-Hartman 
297496fd7ce5SGreg Kroah-Hartman void initialize_tty_struct(struct tty_struct *tty,
297596fd7ce5SGreg Kroah-Hartman 		struct tty_driver *driver, int idx)
297696fd7ce5SGreg Kroah-Hartman {
297796fd7ce5SGreg Kroah-Hartman 	memset(tty, 0, sizeof(struct tty_struct));
297896fd7ce5SGreg Kroah-Hartman 	kref_init(&tty->kref);
297996fd7ce5SGreg Kroah-Hartman 	tty->magic = TTY_MAGIC;
298096fd7ce5SGreg Kroah-Hartman 	tty_ldisc_init(tty);
298196fd7ce5SGreg Kroah-Hartman 	tty->session = NULL;
298296fd7ce5SGreg Kroah-Hartman 	tty->pgrp = NULL;
298389c8d91eSAlan Cox 	mutex_init(&tty->legacy_mutex);
298496fd7ce5SGreg Kroah-Hartman 	mutex_init(&tty->termios_mutex);
298596fd7ce5SGreg Kroah-Hartman 	mutex_init(&tty->ldisc_mutex);
298696fd7ce5SGreg Kroah-Hartman 	init_waitqueue_head(&tty->write_wait);
298796fd7ce5SGreg Kroah-Hartman 	init_waitqueue_head(&tty->read_wait);
298896fd7ce5SGreg Kroah-Hartman 	INIT_WORK(&tty->hangup_work, do_tty_hangup);
298996fd7ce5SGreg Kroah-Hartman 	mutex_init(&tty->atomic_write_lock);
299096fd7ce5SGreg Kroah-Hartman 	spin_lock_init(&tty->ctrl_lock);
299196fd7ce5SGreg Kroah-Hartman 	INIT_LIST_HEAD(&tty->tty_files);
299296fd7ce5SGreg Kroah-Hartman 	INIT_WORK(&tty->SAK_work, do_SAK_work);
299396fd7ce5SGreg Kroah-Hartman 
299496fd7ce5SGreg Kroah-Hartman 	tty->driver = driver;
299596fd7ce5SGreg Kroah-Hartman 	tty->ops = driver->ops;
299696fd7ce5SGreg Kroah-Hartman 	tty->index = idx;
299796fd7ce5SGreg Kroah-Hartman 	tty_line_name(driver, idx, tty->name);
299896fd7ce5SGreg Kroah-Hartman 	tty->dev = tty_get_device(tty);
299996fd7ce5SGreg Kroah-Hartman }
300096fd7ce5SGreg Kroah-Hartman 
300196fd7ce5SGreg Kroah-Hartman /**
30026716671dSJiri Slaby  *	deinitialize_tty_struct
30036716671dSJiri Slaby  *	@tty: tty to deinitialize
30046716671dSJiri Slaby  *
30056716671dSJiri Slaby  *	This subroutine deinitializes a tty structure that has been newly
30066716671dSJiri Slaby  *	allocated but tty_release cannot be called on that yet.
30076716671dSJiri Slaby  *
30086716671dSJiri Slaby  *	Locking: none - tty in question must not be exposed at this point
30096716671dSJiri Slaby  */
30106716671dSJiri Slaby void deinitialize_tty_struct(struct tty_struct *tty)
30116716671dSJiri Slaby {
30126716671dSJiri Slaby 	tty_ldisc_deinit(tty);
30136716671dSJiri Slaby }
30146716671dSJiri Slaby 
30156716671dSJiri Slaby /**
301696fd7ce5SGreg Kroah-Hartman  *	tty_put_char	-	write one character to a tty
301796fd7ce5SGreg Kroah-Hartman  *	@tty: tty
301896fd7ce5SGreg Kroah-Hartman  *	@ch: character
301996fd7ce5SGreg Kroah-Hartman  *
302096fd7ce5SGreg Kroah-Hartman  *	Write one byte to the tty using the provided put_char method
302196fd7ce5SGreg Kroah-Hartman  *	if present. Returns the number of characters successfully output.
302296fd7ce5SGreg Kroah-Hartman  *
302396fd7ce5SGreg Kroah-Hartman  *	Note: the specific put_char operation in the driver layer may go
302496fd7ce5SGreg Kroah-Hartman  *	away soon. Don't call it directly, use this method
302596fd7ce5SGreg Kroah-Hartman  */
302696fd7ce5SGreg Kroah-Hartman 
302796fd7ce5SGreg Kroah-Hartman int tty_put_char(struct tty_struct *tty, unsigned char ch)
302896fd7ce5SGreg Kroah-Hartman {
302996fd7ce5SGreg Kroah-Hartman 	if (tty->ops->put_char)
303096fd7ce5SGreg Kroah-Hartman 		return tty->ops->put_char(tty, ch);
303196fd7ce5SGreg Kroah-Hartman 	return tty->ops->write(tty, &ch, 1);
303296fd7ce5SGreg Kroah-Hartman }
303396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(tty_put_char);
303496fd7ce5SGreg Kroah-Hartman 
303596fd7ce5SGreg Kroah-Hartman struct class *tty_class;
303696fd7ce5SGreg Kroah-Hartman 
30377e73eca6SJiri Slaby static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
30387e73eca6SJiri Slaby 		unsigned int index, unsigned int count)
30397e73eca6SJiri Slaby {
30407e73eca6SJiri Slaby 	/* init here, since reused cdevs cause crashes */
30417e73eca6SJiri Slaby 	cdev_init(&driver->cdevs[index], &tty_fops);
30427e73eca6SJiri Slaby 	driver->cdevs[index].owner = driver->owner;
30437e73eca6SJiri Slaby 	return cdev_add(&driver->cdevs[index], dev, count);
30447e73eca6SJiri Slaby }
30457e73eca6SJiri Slaby 
304696fd7ce5SGreg Kroah-Hartman /**
304796fd7ce5SGreg Kroah-Hartman  *	tty_register_device - register a tty device
304896fd7ce5SGreg Kroah-Hartman  *	@driver: the tty driver that describes the tty device
304996fd7ce5SGreg Kroah-Hartman  *	@index: the index in the tty driver for this tty device
305096fd7ce5SGreg Kroah-Hartman  *	@device: a struct device that is associated with this tty device.
305196fd7ce5SGreg Kroah-Hartman  *		This field is optional, if there is no known struct device
305296fd7ce5SGreg Kroah-Hartman  *		for this tty device it can be set to NULL safely.
305396fd7ce5SGreg Kroah-Hartman  *
305496fd7ce5SGreg Kroah-Hartman  *	Returns a pointer to the struct device for this tty device
305596fd7ce5SGreg Kroah-Hartman  *	(or ERR_PTR(-EFOO) on error).
305696fd7ce5SGreg Kroah-Hartman  *
305796fd7ce5SGreg Kroah-Hartman  *	This call is required to be made to register an individual tty device
305896fd7ce5SGreg Kroah-Hartman  *	if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If
305996fd7ce5SGreg Kroah-Hartman  *	that bit is not set, this function should not be called by a tty
306096fd7ce5SGreg Kroah-Hartman  *	driver.
306196fd7ce5SGreg Kroah-Hartman  *
306296fd7ce5SGreg Kroah-Hartman  *	Locking: ??
306396fd7ce5SGreg Kroah-Hartman  */
306496fd7ce5SGreg Kroah-Hartman 
306596fd7ce5SGreg Kroah-Hartman struct device *tty_register_device(struct tty_driver *driver, unsigned index,
306696fd7ce5SGreg Kroah-Hartman 				   struct device *device)
306796fd7ce5SGreg Kroah-Hartman {
30686915c0e4STomas Hlavacek 	return tty_register_device_attr(driver, index, device, NULL, NULL);
30696915c0e4STomas Hlavacek }
30706915c0e4STomas Hlavacek EXPORT_SYMBOL(tty_register_device);
30716915c0e4STomas Hlavacek 
3072b1b79916STomas Hlavacek static void tty_device_create_release(struct device *dev)
3073b1b79916STomas Hlavacek {
3074b1b79916STomas Hlavacek 	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
3075b1b79916STomas Hlavacek 	kfree(dev);
3076b1b79916STomas Hlavacek }
3077b1b79916STomas Hlavacek 
30786915c0e4STomas Hlavacek /**
30796915c0e4STomas Hlavacek  *	tty_register_device_attr - register a tty device
30806915c0e4STomas Hlavacek  *	@driver: the tty driver that describes the tty device
30816915c0e4STomas Hlavacek  *	@index: the index in the tty driver for this tty device
30826915c0e4STomas Hlavacek  *	@device: a struct device that is associated with this tty device.
30836915c0e4STomas Hlavacek  *		This field is optional, if there is no known struct device
30846915c0e4STomas Hlavacek  *		for this tty device it can be set to NULL safely.
30856915c0e4STomas Hlavacek  *	@drvdata: Driver data to be set to device.
30866915c0e4STomas Hlavacek  *	@attr_grp: Attribute group to be set on device.
30876915c0e4STomas Hlavacek  *
30886915c0e4STomas Hlavacek  *	Returns a pointer to the struct device for this tty device
30896915c0e4STomas Hlavacek  *	(or ERR_PTR(-EFOO) on error).
30906915c0e4STomas Hlavacek  *
30916915c0e4STomas Hlavacek  *	This call is required to be made to register an individual tty device
30926915c0e4STomas Hlavacek  *	if the tty driver's flags have the TTY_DRIVER_DYNAMIC_DEV bit set.  If
30936915c0e4STomas Hlavacek  *	that bit is not set, this function should not be called by a tty
30946915c0e4STomas Hlavacek  *	driver.
30956915c0e4STomas Hlavacek  *
30966915c0e4STomas Hlavacek  *	Locking: ??
30976915c0e4STomas Hlavacek  */
30986915c0e4STomas Hlavacek struct device *tty_register_device_attr(struct tty_driver *driver,
30996915c0e4STomas Hlavacek 				   unsigned index, struct device *device,
31006915c0e4STomas Hlavacek 				   void *drvdata,
31016915c0e4STomas Hlavacek 				   const struct attribute_group **attr_grp)
31026915c0e4STomas Hlavacek {
310396fd7ce5SGreg Kroah-Hartman 	char name[64];
31046915c0e4STomas Hlavacek 	dev_t devt = MKDEV(driver->major, driver->minor_start) + index;
31056915c0e4STomas Hlavacek 	struct device *dev = NULL;
31066915c0e4STomas Hlavacek 	int retval = -ENODEV;
31077e73eca6SJiri Slaby 	bool cdev = false;
310896fd7ce5SGreg Kroah-Hartman 
310996fd7ce5SGreg Kroah-Hartman 	if (index >= driver->num) {
311096fd7ce5SGreg Kroah-Hartman 		printk(KERN_ERR "Attempt to register invalid tty line number "
311196fd7ce5SGreg Kroah-Hartman 		       " (%d).\n", index);
311296fd7ce5SGreg Kroah-Hartman 		return ERR_PTR(-EINVAL);
311396fd7ce5SGreg Kroah-Hartman 	}
311496fd7ce5SGreg Kroah-Hartman 
311596fd7ce5SGreg Kroah-Hartman 	if (driver->type == TTY_DRIVER_TYPE_PTY)
311696fd7ce5SGreg Kroah-Hartman 		pty_line_name(driver, index, name);
311796fd7ce5SGreg Kroah-Hartman 	else
311896fd7ce5SGreg Kroah-Hartman 		tty_line_name(driver, index, name);
311996fd7ce5SGreg Kroah-Hartman 
31207e73eca6SJiri Slaby 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
31216915c0e4STomas Hlavacek 		retval = tty_cdev_add(driver, devt, index, 1);
31226915c0e4STomas Hlavacek 		if (retval)
31236915c0e4STomas Hlavacek 			goto error;
31247e73eca6SJiri Slaby 		cdev = true;
31257e73eca6SJiri Slaby 	}
31267e73eca6SJiri Slaby 
31276915c0e4STomas Hlavacek 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
31286915c0e4STomas Hlavacek 	if (!dev) {
31296915c0e4STomas Hlavacek 		retval = -ENOMEM;
31306915c0e4STomas Hlavacek 		goto error;
313196fd7ce5SGreg Kroah-Hartman 	}
31326915c0e4STomas Hlavacek 
31336915c0e4STomas Hlavacek 	dev->devt = devt;
31346915c0e4STomas Hlavacek 	dev->class = tty_class;
31356915c0e4STomas Hlavacek 	dev->parent = device;
3136b1b79916STomas Hlavacek 	dev->release = tty_device_create_release;
31376915c0e4STomas Hlavacek 	dev_set_name(dev, "%s", name);
31386915c0e4STomas Hlavacek 	dev->groups = attr_grp;
31396915c0e4STomas Hlavacek 	dev_set_drvdata(dev, drvdata);
31406915c0e4STomas Hlavacek 
31416915c0e4STomas Hlavacek 	retval = device_register(dev);
31426915c0e4STomas Hlavacek 	if (retval)
31436915c0e4STomas Hlavacek 		goto error;
31446915c0e4STomas Hlavacek 
31456915c0e4STomas Hlavacek 	return dev;
31466915c0e4STomas Hlavacek 
31476915c0e4STomas Hlavacek error:
31486915c0e4STomas Hlavacek 	put_device(dev);
31496915c0e4STomas Hlavacek 	if (cdev)
31506915c0e4STomas Hlavacek 		cdev_del(&driver->cdevs[index]);
31516915c0e4STomas Hlavacek 	return ERR_PTR(retval);
31526915c0e4STomas Hlavacek }
31536915c0e4STomas Hlavacek EXPORT_SYMBOL_GPL(tty_register_device_attr);
315496fd7ce5SGreg Kroah-Hartman 
315596fd7ce5SGreg Kroah-Hartman /**
315696fd7ce5SGreg Kroah-Hartman  * 	tty_unregister_device - unregister a tty device
315796fd7ce5SGreg Kroah-Hartman  * 	@driver: the tty driver that describes the tty device
315896fd7ce5SGreg Kroah-Hartman  * 	@index: the index in the tty driver for this tty device
315996fd7ce5SGreg Kroah-Hartman  *
316096fd7ce5SGreg Kroah-Hartman  * 	If a tty device is registered with a call to tty_register_device() then
316196fd7ce5SGreg Kroah-Hartman  *	this function must be called when the tty device is gone.
316296fd7ce5SGreg Kroah-Hartman  *
316396fd7ce5SGreg Kroah-Hartman  *	Locking: ??
316496fd7ce5SGreg Kroah-Hartman  */
316596fd7ce5SGreg Kroah-Hartman 
316696fd7ce5SGreg Kroah-Hartman void tty_unregister_device(struct tty_driver *driver, unsigned index)
316796fd7ce5SGreg Kroah-Hartman {
316896fd7ce5SGreg Kroah-Hartman 	device_destroy(tty_class,
316996fd7ce5SGreg Kroah-Hartman 		MKDEV(driver->major, driver->minor_start) + index);
31707e73eca6SJiri Slaby 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_ALLOC))
31717e73eca6SJiri Slaby 		cdev_del(&driver->cdevs[index]);
317296fd7ce5SGreg Kroah-Hartman }
317396fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_unregister_device);
317496fd7ce5SGreg Kroah-Hartman 
31757f0bc6a6SJiri Slaby /**
31767f0bc6a6SJiri Slaby  * __tty_alloc_driver -- allocate tty driver
31777f0bc6a6SJiri Slaby  * @lines: count of lines this driver can handle at most
31787f0bc6a6SJiri Slaby  * @owner: module which is repsonsible for this driver
31797f0bc6a6SJiri Slaby  * @flags: some of TTY_DRIVER_* flags, will be set in driver->flags
31807f0bc6a6SJiri Slaby  *
31817f0bc6a6SJiri Slaby  * This should not be called directly, some of the provided macros should be
31827f0bc6a6SJiri Slaby  * used instead. Use IS_ERR and friends on @retval.
31837f0bc6a6SJiri Slaby  */
31847f0bc6a6SJiri Slaby struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
31857f0bc6a6SJiri Slaby 		unsigned long flags)
318696fd7ce5SGreg Kroah-Hartman {
318796fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver;
31887e73eca6SJiri Slaby 	unsigned int cdevs = 1;
318916a02081SJiri Slaby 	int err;
319096fd7ce5SGreg Kroah-Hartman 
31910019b408SJiri Slaby 	if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
31927f0bc6a6SJiri Slaby 		return ERR_PTR(-EINVAL);
31937f0bc6a6SJiri Slaby 
319496fd7ce5SGreg Kroah-Hartman 	driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
31957f0bc6a6SJiri Slaby 	if (!driver)
31967f0bc6a6SJiri Slaby 		return ERR_PTR(-ENOMEM);
31977f0bc6a6SJiri Slaby 
319896fd7ce5SGreg Kroah-Hartman 	kref_init(&driver->kref);
319996fd7ce5SGreg Kroah-Hartman 	driver->magic = TTY_DRIVER_MAGIC;
320096fd7ce5SGreg Kroah-Hartman 	driver->num = lines;
32011a54a76dSJiri Slaby 	driver->owner = owner;
32027f0bc6a6SJiri Slaby 	driver->flags = flags;
320316a02081SJiri Slaby 
320416a02081SJiri Slaby 	if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
320516a02081SJiri Slaby 		driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
320616a02081SJiri Slaby 				GFP_KERNEL);
320716a02081SJiri Slaby 		driver->termios = kcalloc(lines, sizeof(*driver->termios),
320816a02081SJiri Slaby 				GFP_KERNEL);
320916a02081SJiri Slaby 		if (!driver->ttys || !driver->termios) {
321016a02081SJiri Slaby 			err = -ENOMEM;
321116a02081SJiri Slaby 			goto err_free_all;
321216a02081SJiri Slaby 		}
321316a02081SJiri Slaby 	}
321416a02081SJiri Slaby 
321516a02081SJiri Slaby 	if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
321616a02081SJiri Slaby 		driver->ports = kcalloc(lines, sizeof(*driver->ports),
321716a02081SJiri Slaby 				GFP_KERNEL);
321816a02081SJiri Slaby 		if (!driver->ports) {
321916a02081SJiri Slaby 			err = -ENOMEM;
322016a02081SJiri Slaby 			goto err_free_all;
322116a02081SJiri Slaby 		}
32227e73eca6SJiri Slaby 		cdevs = lines;
32237e73eca6SJiri Slaby 	}
32247e73eca6SJiri Slaby 
32257e73eca6SJiri Slaby 	driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);
32267e73eca6SJiri Slaby 	if (!driver->cdevs) {
32277e73eca6SJiri Slaby 		err = -ENOMEM;
32287e73eca6SJiri Slaby 		goto err_free_all;
322916a02081SJiri Slaby 	}
32307f0bc6a6SJiri Slaby 
323196fd7ce5SGreg Kroah-Hartman 	return driver;
323216a02081SJiri Slaby err_free_all:
323316a02081SJiri Slaby 	kfree(driver->ports);
323416a02081SJiri Slaby 	kfree(driver->ttys);
323516a02081SJiri Slaby 	kfree(driver->termios);
323616a02081SJiri Slaby 	kfree(driver);
323716a02081SJiri Slaby 	return ERR_PTR(err);
323896fd7ce5SGreg Kroah-Hartman }
32397f0bc6a6SJiri Slaby EXPORT_SYMBOL(__tty_alloc_driver);
324096fd7ce5SGreg Kroah-Hartman 
324196fd7ce5SGreg Kroah-Hartman static void destruct_tty_driver(struct kref *kref)
324296fd7ce5SGreg Kroah-Hartman {
324396fd7ce5SGreg Kroah-Hartman 	struct tty_driver *driver = container_of(kref, struct tty_driver, kref);
324496fd7ce5SGreg Kroah-Hartman 	int i;
324596fd7ce5SGreg Kroah-Hartman 	struct ktermios *tp;
324696fd7ce5SGreg Kroah-Hartman 
324796fd7ce5SGreg Kroah-Hartman 	if (driver->flags & TTY_DRIVER_INSTALLED) {
324896fd7ce5SGreg Kroah-Hartman 		/*
324996fd7ce5SGreg Kroah-Hartman 		 * Free the termios and termios_locked structures because
325096fd7ce5SGreg Kroah-Hartman 		 * we don't want to get memory leaks when modular tty
325196fd7ce5SGreg Kroah-Hartman 		 * drivers are removed from the kernel.
325296fd7ce5SGreg Kroah-Hartman 		 */
325396fd7ce5SGreg Kroah-Hartman 		for (i = 0; i < driver->num; i++) {
325496fd7ce5SGreg Kroah-Hartman 			tp = driver->termios[i];
325596fd7ce5SGreg Kroah-Hartman 			if (tp) {
325696fd7ce5SGreg Kroah-Hartman 				driver->termios[i] = NULL;
325796fd7ce5SGreg Kroah-Hartman 				kfree(tp);
325896fd7ce5SGreg Kroah-Hartman 			}
325996fd7ce5SGreg Kroah-Hartman 			if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV))
326096fd7ce5SGreg Kroah-Hartman 				tty_unregister_device(driver, i);
326196fd7ce5SGreg Kroah-Hartman 		}
326296fd7ce5SGreg Kroah-Hartman 		proc_tty_unregister_driver(driver);
32637e73eca6SJiri Slaby 		if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC)
32647e73eca6SJiri Slaby 			cdev_del(&driver->cdevs[0]);
326596fd7ce5SGreg Kroah-Hartman 	}
32667e73eca6SJiri Slaby 	kfree(driver->cdevs);
326704831dc1SJiri Slaby 	kfree(driver->ports);
326816a02081SJiri Slaby 	kfree(driver->termios);
326916a02081SJiri Slaby 	kfree(driver->ttys);
327096fd7ce5SGreg Kroah-Hartman 	kfree(driver);
327196fd7ce5SGreg Kroah-Hartman }
327296fd7ce5SGreg Kroah-Hartman 
327396fd7ce5SGreg Kroah-Hartman void tty_driver_kref_put(struct tty_driver *driver)
327496fd7ce5SGreg Kroah-Hartman {
327596fd7ce5SGreg Kroah-Hartman 	kref_put(&driver->kref, destruct_tty_driver);
327696fd7ce5SGreg Kroah-Hartman }
327796fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_driver_kref_put);
327896fd7ce5SGreg Kroah-Hartman 
327996fd7ce5SGreg Kroah-Hartman void tty_set_operations(struct tty_driver *driver,
328096fd7ce5SGreg Kroah-Hartman 			const struct tty_operations *op)
328196fd7ce5SGreg Kroah-Hartman {
328296fd7ce5SGreg Kroah-Hartman 	driver->ops = op;
328396fd7ce5SGreg Kroah-Hartman };
328496fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_set_operations);
328596fd7ce5SGreg Kroah-Hartman 
328696fd7ce5SGreg Kroah-Hartman void put_tty_driver(struct tty_driver *d)
328796fd7ce5SGreg Kroah-Hartman {
328896fd7ce5SGreg Kroah-Hartman 	tty_driver_kref_put(d);
328996fd7ce5SGreg Kroah-Hartman }
329096fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(put_tty_driver);
329196fd7ce5SGreg Kroah-Hartman 
329296fd7ce5SGreg Kroah-Hartman /*
329396fd7ce5SGreg Kroah-Hartman  * Called by a tty driver to register itself.
329496fd7ce5SGreg Kroah-Hartman  */
329596fd7ce5SGreg Kroah-Hartman int tty_register_driver(struct tty_driver *driver)
329696fd7ce5SGreg Kroah-Hartman {
329796fd7ce5SGreg Kroah-Hartman 	int error;
329896fd7ce5SGreg Kroah-Hartman 	int i;
329996fd7ce5SGreg Kroah-Hartman 	dev_t dev;
330096fd7ce5SGreg Kroah-Hartman 	struct device *d;
330196fd7ce5SGreg Kroah-Hartman 
330296fd7ce5SGreg Kroah-Hartman 	if (!driver->major) {
330396fd7ce5SGreg Kroah-Hartman 		error = alloc_chrdev_region(&dev, driver->minor_start,
330496fd7ce5SGreg Kroah-Hartman 						driver->num, driver->name);
330596fd7ce5SGreg Kroah-Hartman 		if (!error) {
330696fd7ce5SGreg Kroah-Hartman 			driver->major = MAJOR(dev);
330796fd7ce5SGreg Kroah-Hartman 			driver->minor_start = MINOR(dev);
330896fd7ce5SGreg Kroah-Hartman 		}
330996fd7ce5SGreg Kroah-Hartman 	} else {
331096fd7ce5SGreg Kroah-Hartman 		dev = MKDEV(driver->major, driver->minor_start);
331196fd7ce5SGreg Kroah-Hartman 		error = register_chrdev_region(dev, driver->num, driver->name);
331296fd7ce5SGreg Kroah-Hartman 	}
33139bb8a3d4SJiri Slaby 	if (error < 0)
331416a02081SJiri Slaby 		goto err;
331596fd7ce5SGreg Kroah-Hartman 
33167e73eca6SJiri Slaby 	if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
33177e73eca6SJiri Slaby 		error = tty_cdev_add(driver, dev, 0, driver->num);
33189bb8a3d4SJiri Slaby 		if (error)
33199bb8a3d4SJiri Slaby 			goto err_unreg_char;
33207e73eca6SJiri Slaby 	}
332196fd7ce5SGreg Kroah-Hartman 
332296fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
332396fd7ce5SGreg Kroah-Hartman 	list_add(&driver->tty_drivers, &tty_drivers);
332496fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
332596fd7ce5SGreg Kroah-Hartman 
332696fd7ce5SGreg Kroah-Hartman 	if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
332796fd7ce5SGreg Kroah-Hartman 		for (i = 0; i < driver->num; i++) {
332896fd7ce5SGreg Kroah-Hartman 			d = tty_register_device(driver, i, NULL);
332996fd7ce5SGreg Kroah-Hartman 			if (IS_ERR(d)) {
333096fd7ce5SGreg Kroah-Hartman 				error = PTR_ERR(d);
333116a02081SJiri Slaby 				goto err_unreg_devs;
333296fd7ce5SGreg Kroah-Hartman 			}
333396fd7ce5SGreg Kroah-Hartman 		}
333496fd7ce5SGreg Kroah-Hartman 	}
333596fd7ce5SGreg Kroah-Hartman 	proc_tty_register_driver(driver);
333696fd7ce5SGreg Kroah-Hartman 	driver->flags |= TTY_DRIVER_INSTALLED;
333796fd7ce5SGreg Kroah-Hartman 	return 0;
333896fd7ce5SGreg Kroah-Hartman 
333916a02081SJiri Slaby err_unreg_devs:
334096fd7ce5SGreg Kroah-Hartman 	for (i--; i >= 0; i--)
334196fd7ce5SGreg Kroah-Hartman 		tty_unregister_device(driver, i);
334296fd7ce5SGreg Kroah-Hartman 
334396fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
334496fd7ce5SGreg Kroah-Hartman 	list_del(&driver->tty_drivers);
334596fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
334696fd7ce5SGreg Kroah-Hartman 
33479bb8a3d4SJiri Slaby err_unreg_char:
334896fd7ce5SGreg Kroah-Hartman 	unregister_chrdev_region(dev, driver->num);
334916a02081SJiri Slaby err:
335096fd7ce5SGreg Kroah-Hartman 	return error;
335196fd7ce5SGreg Kroah-Hartman }
335296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_register_driver);
335396fd7ce5SGreg Kroah-Hartman 
335496fd7ce5SGreg Kroah-Hartman /*
335596fd7ce5SGreg Kroah-Hartman  * Called by a tty driver to unregister itself.
335696fd7ce5SGreg Kroah-Hartman  */
335796fd7ce5SGreg Kroah-Hartman int tty_unregister_driver(struct tty_driver *driver)
335896fd7ce5SGreg Kroah-Hartman {
335996fd7ce5SGreg Kroah-Hartman #if 0
336096fd7ce5SGreg Kroah-Hartman 	/* FIXME */
336196fd7ce5SGreg Kroah-Hartman 	if (driver->refcount)
336296fd7ce5SGreg Kroah-Hartman 		return -EBUSY;
336396fd7ce5SGreg Kroah-Hartman #endif
336496fd7ce5SGreg Kroah-Hartman 	unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
336596fd7ce5SGreg Kroah-Hartman 				driver->num);
336696fd7ce5SGreg Kroah-Hartman 	mutex_lock(&tty_mutex);
336796fd7ce5SGreg Kroah-Hartman 	list_del(&driver->tty_drivers);
336896fd7ce5SGreg Kroah-Hartman 	mutex_unlock(&tty_mutex);
336996fd7ce5SGreg Kroah-Hartman 	return 0;
337096fd7ce5SGreg Kroah-Hartman }
337196fd7ce5SGreg Kroah-Hartman 
337296fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_unregister_driver);
337396fd7ce5SGreg Kroah-Hartman 
337496fd7ce5SGreg Kroah-Hartman dev_t tty_devnum(struct tty_struct *tty)
337596fd7ce5SGreg Kroah-Hartman {
337696fd7ce5SGreg Kroah-Hartman 	return MKDEV(tty->driver->major, tty->driver->minor_start) + tty->index;
337796fd7ce5SGreg Kroah-Hartman }
337896fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL(tty_devnum);
337996fd7ce5SGreg Kroah-Hartman 
338096fd7ce5SGreg Kroah-Hartman void proc_clear_tty(struct task_struct *p)
338196fd7ce5SGreg Kroah-Hartman {
338296fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
338396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
338496fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&p->sighand->siglock, flags);
338596fd7ce5SGreg Kroah-Hartman 	tty = p->signal->tty;
338696fd7ce5SGreg Kroah-Hartman 	p->signal->tty = NULL;
338796fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&p->sighand->siglock, flags);
338896fd7ce5SGreg Kroah-Hartman 	tty_kref_put(tty);
338996fd7ce5SGreg Kroah-Hartman }
339096fd7ce5SGreg Kroah-Hartman 
339196fd7ce5SGreg Kroah-Hartman /* Called under the sighand lock */
339296fd7ce5SGreg Kroah-Hartman 
339396fd7ce5SGreg Kroah-Hartman static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
339496fd7ce5SGreg Kroah-Hartman {
339596fd7ce5SGreg Kroah-Hartman 	if (tty) {
339696fd7ce5SGreg Kroah-Hartman 		unsigned long flags;
339796fd7ce5SGreg Kroah-Hartman 		/* We should not have a session or pgrp to put here but.... */
339896fd7ce5SGreg Kroah-Hartman 		spin_lock_irqsave(&tty->ctrl_lock, flags);
339996fd7ce5SGreg Kroah-Hartman 		put_pid(tty->session);
340096fd7ce5SGreg Kroah-Hartman 		put_pid(tty->pgrp);
340196fd7ce5SGreg Kroah-Hartman 		tty->pgrp = get_pid(task_pgrp(tsk));
340296fd7ce5SGreg Kroah-Hartman 		spin_unlock_irqrestore(&tty->ctrl_lock, flags);
340396fd7ce5SGreg Kroah-Hartman 		tty->session = get_pid(task_session(tsk));
340496fd7ce5SGreg Kroah-Hartman 		if (tsk->signal->tty) {
340596fd7ce5SGreg Kroah-Hartman 			printk(KERN_DEBUG "tty not NULL!!\n");
340696fd7ce5SGreg Kroah-Hartman 			tty_kref_put(tsk->signal->tty);
340796fd7ce5SGreg Kroah-Hartman 		}
340896fd7ce5SGreg Kroah-Hartman 	}
340996fd7ce5SGreg Kroah-Hartman 	put_pid(tsk->signal->tty_old_pgrp);
341096fd7ce5SGreg Kroah-Hartman 	tsk->signal->tty = tty_kref_get(tty);
341196fd7ce5SGreg Kroah-Hartman 	tsk->signal->tty_old_pgrp = NULL;
341296fd7ce5SGreg Kroah-Hartman }
341396fd7ce5SGreg Kroah-Hartman 
341496fd7ce5SGreg Kroah-Hartman static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
341596fd7ce5SGreg Kroah-Hartman {
341696fd7ce5SGreg Kroah-Hartman 	spin_lock_irq(&tsk->sighand->siglock);
341796fd7ce5SGreg Kroah-Hartman 	__proc_set_tty(tsk, tty);
341896fd7ce5SGreg Kroah-Hartman 	spin_unlock_irq(&tsk->sighand->siglock);
341996fd7ce5SGreg Kroah-Hartman }
342096fd7ce5SGreg Kroah-Hartman 
342196fd7ce5SGreg Kroah-Hartman struct tty_struct *get_current_tty(void)
342296fd7ce5SGreg Kroah-Hartman {
342396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
342496fd7ce5SGreg Kroah-Hartman 	unsigned long flags;
342596fd7ce5SGreg Kroah-Hartman 
342696fd7ce5SGreg Kroah-Hartman 	spin_lock_irqsave(&current->sighand->siglock, flags);
342796fd7ce5SGreg Kroah-Hartman 	tty = tty_kref_get(current->signal->tty);
342896fd7ce5SGreg Kroah-Hartman 	spin_unlock_irqrestore(&current->sighand->siglock, flags);
342996fd7ce5SGreg Kroah-Hartman 	return tty;
343096fd7ce5SGreg Kroah-Hartman }
343196fd7ce5SGreg Kroah-Hartman EXPORT_SYMBOL_GPL(get_current_tty);
343296fd7ce5SGreg Kroah-Hartman 
343396fd7ce5SGreg Kroah-Hartman void tty_default_fops(struct file_operations *fops)
343496fd7ce5SGreg Kroah-Hartman {
343596fd7ce5SGreg Kroah-Hartman 	*fops = tty_fops;
343696fd7ce5SGreg Kroah-Hartman }
343796fd7ce5SGreg Kroah-Hartman 
343896fd7ce5SGreg Kroah-Hartman /*
343996fd7ce5SGreg Kroah-Hartman  * Initialize the console device. This is called *early*, so
344096fd7ce5SGreg Kroah-Hartman  * we can't necessarily depend on lots of kernel help here.
344196fd7ce5SGreg Kroah-Hartman  * Just do some early initializations, and do the complex setup
344296fd7ce5SGreg Kroah-Hartman  * later.
344396fd7ce5SGreg Kroah-Hartman  */
344496fd7ce5SGreg Kroah-Hartman void __init console_init(void)
344596fd7ce5SGreg Kroah-Hartman {
344696fd7ce5SGreg Kroah-Hartman 	initcall_t *call;
344796fd7ce5SGreg Kroah-Hartman 
344896fd7ce5SGreg Kroah-Hartman 	/* Setup the default TTY line discipline. */
344996fd7ce5SGreg Kroah-Hartman 	tty_ldisc_begin();
345096fd7ce5SGreg Kroah-Hartman 
345196fd7ce5SGreg Kroah-Hartman 	/*
345296fd7ce5SGreg Kroah-Hartman 	 * set up the console device so that later boot sequences can
345396fd7ce5SGreg Kroah-Hartman 	 * inform about problems etc..
345496fd7ce5SGreg Kroah-Hartman 	 */
345596fd7ce5SGreg Kroah-Hartman 	call = __con_initcall_start;
345696fd7ce5SGreg Kroah-Hartman 	while (call < __con_initcall_end) {
345796fd7ce5SGreg Kroah-Hartman 		(*call)();
345896fd7ce5SGreg Kroah-Hartman 		call++;
345996fd7ce5SGreg Kroah-Hartman 	}
346096fd7ce5SGreg Kroah-Hartman }
346196fd7ce5SGreg Kroah-Hartman 
34622c9ede55SAl Viro static char *tty_devnode(struct device *dev, umode_t *mode)
346396fd7ce5SGreg Kroah-Hartman {
346496fd7ce5SGreg Kroah-Hartman 	if (!mode)
346596fd7ce5SGreg Kroah-Hartman 		return NULL;
346696fd7ce5SGreg Kroah-Hartman 	if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
346796fd7ce5SGreg Kroah-Hartman 	    dev->devt == MKDEV(TTYAUX_MAJOR, 2))
346896fd7ce5SGreg Kroah-Hartman 		*mode = 0666;
346996fd7ce5SGreg Kroah-Hartman 	return NULL;
347096fd7ce5SGreg Kroah-Hartman }
347196fd7ce5SGreg Kroah-Hartman 
347296fd7ce5SGreg Kroah-Hartman static int __init tty_class_init(void)
347396fd7ce5SGreg Kroah-Hartman {
347496fd7ce5SGreg Kroah-Hartman 	tty_class = class_create(THIS_MODULE, "tty");
347596fd7ce5SGreg Kroah-Hartman 	if (IS_ERR(tty_class))
347696fd7ce5SGreg Kroah-Hartman 		return PTR_ERR(tty_class);
347796fd7ce5SGreg Kroah-Hartman 	tty_class->devnode = tty_devnode;
347896fd7ce5SGreg Kroah-Hartman 	return 0;
347996fd7ce5SGreg Kroah-Hartman }
348096fd7ce5SGreg Kroah-Hartman 
348196fd7ce5SGreg Kroah-Hartman postcore_initcall(tty_class_init);
348296fd7ce5SGreg Kroah-Hartman 
348396fd7ce5SGreg Kroah-Hartman /* 3/2004 jmc: why do these devices exist? */
348496fd7ce5SGreg Kroah-Hartman static struct cdev tty_cdev, console_cdev;
348596fd7ce5SGreg Kroah-Hartman 
3486fbc92a34SKay Sievers static ssize_t show_cons_active(struct device *dev,
3487fbc92a34SKay Sievers 				struct device_attribute *attr, char *buf)
3488fbc92a34SKay Sievers {
3489fbc92a34SKay Sievers 	struct console *cs[16];
3490fbc92a34SKay Sievers 	int i = 0;
3491fbc92a34SKay Sievers 	struct console *c;
3492fbc92a34SKay Sievers 	ssize_t count = 0;
3493fbc92a34SKay Sievers 
3494ac751efaSTorben Hohn 	console_lock();
3495a2a6a822SKay Sievers 	for_each_console(c) {
3496fbc92a34SKay Sievers 		if (!c->device)
3497fbc92a34SKay Sievers 			continue;
3498fbc92a34SKay Sievers 		if (!c->write)
3499fbc92a34SKay Sievers 			continue;
3500fbc92a34SKay Sievers 		if ((c->flags & CON_ENABLED) == 0)
3501fbc92a34SKay Sievers 			continue;
3502fbc92a34SKay Sievers 		cs[i++] = c;
3503fbc92a34SKay Sievers 		if (i >= ARRAY_SIZE(cs))
3504fbc92a34SKay Sievers 			break;
3505fbc92a34SKay Sievers 	}
3506fbc92a34SKay Sievers 	while (i--)
3507fbc92a34SKay Sievers 		count += sprintf(buf + count, "%s%d%c",
3508fbc92a34SKay Sievers 				 cs[i]->name, cs[i]->index, i ? ' ':'\n');
3509ac751efaSTorben Hohn 	console_unlock();
3510fbc92a34SKay Sievers 
3511fbc92a34SKay Sievers 	return count;
3512fbc92a34SKay Sievers }
3513fbc92a34SKay Sievers static DEVICE_ATTR(active, S_IRUGO, show_cons_active, NULL);
3514fbc92a34SKay Sievers 
3515fbc92a34SKay Sievers static struct device *consdev;
3516fbc92a34SKay Sievers 
3517fbc92a34SKay Sievers void console_sysfs_notify(void)
3518fbc92a34SKay Sievers {
3519fbc92a34SKay Sievers 	if (consdev)
3520fbc92a34SKay Sievers 		sysfs_notify(&consdev->kobj, NULL, "active");
3521fbc92a34SKay Sievers }
3522fbc92a34SKay Sievers 
352396fd7ce5SGreg Kroah-Hartman /*
352496fd7ce5SGreg Kroah-Hartman  * Ok, now we can initialize the rest of the tty devices and can count
352596fd7ce5SGreg Kroah-Hartman  * on memory allocations, interrupts etc..
352696fd7ce5SGreg Kroah-Hartman  */
352796fd7ce5SGreg Kroah-Hartman int __init tty_init(void)
352896fd7ce5SGreg Kroah-Hartman {
352996fd7ce5SGreg Kroah-Hartman 	cdev_init(&tty_cdev, &tty_fops);
353096fd7ce5SGreg Kroah-Hartman 	if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
353196fd7ce5SGreg Kroah-Hartman 	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
353296fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register /dev/tty driver\n");
3533fbc92a34SKay Sievers 	device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");
353496fd7ce5SGreg Kroah-Hartman 
353596fd7ce5SGreg Kroah-Hartman 	cdev_init(&console_cdev, &console_fops);
353696fd7ce5SGreg Kroah-Hartman 	if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
353796fd7ce5SGreg Kroah-Hartman 	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
353896fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register /dev/console driver\n");
3539fbc92a34SKay Sievers 	consdev = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
354096fd7ce5SGreg Kroah-Hartman 			      "console");
3541fbc92a34SKay Sievers 	if (IS_ERR(consdev))
3542fbc92a34SKay Sievers 		consdev = NULL;
3543fbc92a34SKay Sievers 	else
3544a2a6a822SKay Sievers 		WARN_ON(device_create_file(consdev, &dev_attr_active) < 0);
354596fd7ce5SGreg Kroah-Hartman 
354696fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_VT
354796fd7ce5SGreg Kroah-Hartman 	vty_init(&console_fops);
354896fd7ce5SGreg Kroah-Hartman #endif
354996fd7ce5SGreg Kroah-Hartman 	return 0;
355096fd7ce5SGreg Kroah-Hartman }
355196fd7ce5SGreg Kroah-Hartman 
3552