xref: /openbmc/linux/drivers/tty/pty.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
296fd7ce5SGreg Kroah-Hartman /*
396fd7ce5SGreg Kroah-Hartman  *  Copyright (C) 1991, 1992  Linus Torvalds
496fd7ce5SGreg Kroah-Hartman  *
596fd7ce5SGreg Kroah-Hartman  *  Added support for a Unix98-style ptmx device.
696fd7ce5SGreg Kroah-Hartman  *    -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998
796fd7ce5SGreg Kroah-Hartman  *
896fd7ce5SGreg Kroah-Hartman  */
996fd7ce5SGreg Kroah-Hartman 
1096fd7ce5SGreg Kroah-Hartman #include <linux/module.h>
1196fd7ce5SGreg Kroah-Hartman #include <linux/errno.h>
1296fd7ce5SGreg Kroah-Hartman #include <linux/interrupt.h>
1396fd7ce5SGreg Kroah-Hartman #include <linux/tty.h>
1496fd7ce5SGreg Kroah-Hartman #include <linux/tty_flip.h>
1596fd7ce5SGreg Kroah-Hartman #include <linux/fcntl.h>
163f07c014SIngo Molnar #include <linux/sched/signal.h>
1796fd7ce5SGreg Kroah-Hartman #include <linux/string.h>
1896fd7ce5SGreg Kroah-Hartman #include <linux/major.h>
1996fd7ce5SGreg Kroah-Hartman #include <linux/mm.h>
2096fd7ce5SGreg Kroah-Hartman #include <linux/init.h>
2196fd7ce5SGreg Kroah-Hartman #include <linux/device.h>
2296fd7ce5SGreg Kroah-Hartman #include <linux/uaccess.h>
2396fd7ce5SGreg Kroah-Hartman #include <linux/bitops.h>
2496fd7ce5SGreg Kroah-Hartman #include <linux/devpts_fs.h>
2596fd7ce5SGreg Kroah-Hartman #include <linux/slab.h>
26d739e65bSAlan Cox #include <linux/mutex.h>
2701adc807SPeter Hurley #include <linux/poll.h>
2854ebbfb1SAleksa Sarai #include <linux/mount.h>
2954ebbfb1SAleksa Sarai #include <linux/file.h>
3054ebbfb1SAleksa Sarai #include <linux/ioctl.h>
3150f45326SAl Viro #include <linux/compat.h>
3298602c01SGreg Kroah-Hartman #include "tty.h"
3396fd7ce5SGreg Kroah-Hartman 
34d6847793SPeter Hurley #undef TTY_DEBUG_HANGUP
35d6847793SPeter Hurley #ifdef TTY_DEBUG_HANGUP
36d6847793SPeter Hurley # define tty_debug_hangup(tty, f, args...)	tty_debug(tty, f, ##args)
37d6847793SPeter Hurley #else
38d6847793SPeter Hurley # define tty_debug_hangup(tty, f, args...)	do {} while (0)
39d6847793SPeter Hurley #endif
4096fd7ce5SGreg Kroah-Hartman 
4196fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_UNIX98_PTYS
4296fd7ce5SGreg Kroah-Hartman static struct tty_driver *ptm_driver;
4396fd7ce5SGreg Kroah-Hartman static struct tty_driver *pts_driver;
44d739e65bSAlan Cox static DEFINE_MUTEX(devpts_mutex);
4596fd7ce5SGreg Kroah-Hartman #endif
4696fd7ce5SGreg Kroah-Hartman 
pty_close(struct tty_struct * tty,struct file * filp)4796fd7ce5SGreg Kroah-Hartman static void pty_close(struct tty_struct *tty, struct file *filp)
4896fd7ce5SGreg Kroah-Hartman {
4996fd7ce5SGreg Kroah-Hartman 	if (tty->driver->subtype == PTY_TYPE_MASTER)
5096fd7ce5SGreg Kroah-Hartman 		WARN_ON(tty->count > 1);
5196fd7ce5SGreg Kroah-Hartman 	else {
5218900ca6SPeter Hurley 		if (tty_io_error(tty))
5369939035SPeter Hurley 			return;
5496fd7ce5SGreg Kroah-Hartman 		if (tty->count > 2)
5596fd7ce5SGreg Kroah-Hartman 			return;
5696fd7ce5SGreg Kroah-Hartman 	}
5769939035SPeter Hurley 	set_bit(TTY_IO_ERROR, &tty->flags);
5896fd7ce5SGreg Kroah-Hartman 	wake_up_interruptible(&tty->read_wait);
5996fd7ce5SGreg Kroah-Hartman 	wake_up_interruptible(&tty->write_wait);
6064d608dbSJiri Slaby 	spin_lock_irq(&tty->ctrl.lock);
6164d608dbSJiri Slaby 	tty->ctrl.packet = false;
6264d608dbSJiri Slaby 	spin_unlock_irq(&tty->ctrl.lock);
6389c8d91eSAlan Cox 	/* Review - krefs on tty_link ?? */
6496fd7ce5SGreg Kroah-Hartman 	if (!tty->link)
6596fd7ce5SGreg Kroah-Hartman 		return;
6696fd7ce5SGreg Kroah-Hartman 	set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
670f40fbbcSBrian Bloniarz 	wake_up_interruptible(&tty->link->read_wait);
6896fd7ce5SGreg Kroah-Hartman 	wake_up_interruptible(&tty->link->write_wait);
6996fd7ce5SGreg Kroah-Hartman 	if (tty->driver->subtype == PTY_TYPE_MASTER) {
70c776b77aSGreg Kroah-Hartman 		set_bit(TTY_OTHER_CLOSED, &tty->flags);
71ce1000ddSGreg Kroah-Hartman #ifdef CONFIG_UNIX98_PTYS
72d739e65bSAlan Cox 		if (tty->driver == ptm_driver) {
73d739e65bSAlan Cox 			mutex_lock(&devpts_mutex);
74311fc65cSEric W. Biederman 			if (tty->link->driver_data)
75311fc65cSEric W. Biederman 				devpts_pty_kill(tty->link->driver_data);
76d739e65bSAlan Cox 			mutex_unlock(&devpts_mutex);
77d739e65bSAlan Cox 		}
78ce1000ddSGreg Kroah-Hartman #endif
79c776b77aSGreg Kroah-Hartman 		tty_vhangup(tty->link);
8096fd7ce5SGreg Kroah-Hartman 	}
8196fd7ce5SGreg Kroah-Hartman }
8296fd7ce5SGreg Kroah-Hartman 
8396fd7ce5SGreg Kroah-Hartman /*
8496fd7ce5SGreg Kroah-Hartman  * The unthrottle routine is called by the line discipline to signal
8596fd7ce5SGreg Kroah-Hartman  * that it can receive more characters.  For PTY's, the TTY_THROTTLED
8696fd7ce5SGreg Kroah-Hartman  * flag is always set, to force the line discipline to always call the
8796fd7ce5SGreg Kroah-Hartman  * unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
8896fd7ce5SGreg Kroah-Hartman  * characters in the queue.  This is necessary since each time this
8996fd7ce5SGreg Kroah-Hartman  * happens, we need to wake up any sleeping processes that could be
9096fd7ce5SGreg Kroah-Hartman  * (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
9196fd7ce5SGreg Kroah-Hartman  * for the pty buffer to be drained.
9296fd7ce5SGreg Kroah-Hartman  */
pty_unthrottle(struct tty_struct * tty)9396fd7ce5SGreg Kroah-Hartman static void pty_unthrottle(struct tty_struct *tty)
9496fd7ce5SGreg Kroah-Hartman {
9596fd7ce5SGreg Kroah-Hartman 	tty_wakeup(tty->link);
9696fd7ce5SGreg Kroah-Hartman 	set_bit(TTY_THROTTLED, &tty->flags);
9796fd7ce5SGreg Kroah-Hartman }
9896fd7ce5SGreg Kroah-Hartman 
9996fd7ce5SGreg Kroah-Hartman /**
10096fd7ce5SGreg Kroah-Hartman  *	pty_write		-	write to a pty
10196fd7ce5SGreg Kroah-Hartman  *	@tty: the tty we write from
10296fd7ce5SGreg Kroah-Hartman  *	@buf: kernel buffer of data
103fa441954SJiri Slaby  *	@c: bytes to write
10496fd7ce5SGreg Kroah-Hartman  *
10596fd7ce5SGreg Kroah-Hartman  *	Our "hardware" write method. Data is coming from the ldisc which
10696fd7ce5SGreg Kroah-Hartman  *	may be in a non sleeping state. We simply throw this at the other
10796fd7ce5SGreg Kroah-Hartman  *	end of the link as if we were an IRQ handler receiving stuff for
10896fd7ce5SGreg Kroah-Hartman  *	the other side of the pty/tty pair.
10996fd7ce5SGreg Kroah-Hartman  */
11096fd7ce5SGreg Kroah-Hartman 
pty_write(struct tty_struct * tty,const u8 * buf,size_t c)111*95713967SJiri Slaby (SUSE) static ssize_t pty_write(struct tty_struct *tty, const u8 *buf, size_t c)
11296fd7ce5SGreg Kroah-Hartman {
11396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *to = tty->link;
11496fd7ce5SGreg Kroah-Hartman 
115a501ab75SJiri Slaby 	if (tty->flow.stopped || !c)
11696fd7ce5SGreg Kroah-Hartman 		return 0;
11796fd7ce5SGreg Kroah-Hartman 
118a501ab75SJiri Slaby 	return tty_insert_flip_string_and_push_buffer(to->port, buf, c);
11996fd7ce5SGreg Kroah-Hartman }
12096fd7ce5SGreg Kroah-Hartman 
12196fd7ce5SGreg Kroah-Hartman /**
12296fd7ce5SGreg Kroah-Hartman  *	pty_write_room	-	write space
12396fd7ce5SGreg Kroah-Hartman  *	@tty: tty we are writing from
12496fd7ce5SGreg Kroah-Hartman  *
12596fd7ce5SGreg Kroah-Hartman  *	Report how many bytes the ldisc can send into the queue for
12696fd7ce5SGreg Kroah-Hartman  *	the other device.
12796fd7ce5SGreg Kroah-Hartman  */
12896fd7ce5SGreg Kroah-Hartman 
pty_write_room(struct tty_struct * tty)12903b3b1a2SJiri Slaby static unsigned int pty_write_room(struct tty_struct *tty)
13096fd7ce5SGreg Kroah-Hartman {
1316e94dbc7SJiri Slaby 	if (tty->flow.stopped)
13296fd7ce5SGreg Kroah-Hartman 		return 0;
133c81622acSPeter Hurley 	return tty_buffer_space_avail(tty->link->port);
13496fd7ce5SGreg Kroah-Hartman }
13596fd7ce5SGreg Kroah-Hartman 
13696fd7ce5SGreg Kroah-Hartman /* Set the lock flag on a pty */
pty_set_lock(struct tty_struct * tty,int __user * arg)13796fd7ce5SGreg Kroah-Hartman static int pty_set_lock(struct tty_struct *tty, int __user *arg)
13896fd7ce5SGreg Kroah-Hartman {
13996fd7ce5SGreg Kroah-Hartman 	int val;
140202680c7SXiaofei Tan 
14196fd7ce5SGreg Kroah-Hartman 	if (get_user(val, arg))
14296fd7ce5SGreg Kroah-Hartman 		return -EFAULT;
14396fd7ce5SGreg Kroah-Hartman 	if (val)
14496fd7ce5SGreg Kroah-Hartman 		set_bit(TTY_PTY_LOCK, &tty->flags);
14596fd7ce5SGreg Kroah-Hartman 	else
14696fd7ce5SGreg Kroah-Hartman 		clear_bit(TTY_PTY_LOCK, &tty->flags);
14796fd7ce5SGreg Kroah-Hartman 	return 0;
14896fd7ce5SGreg Kroah-Hartman }
14996fd7ce5SGreg Kroah-Hartman 
pty_get_lock(struct tty_struct * tty,int __user * arg)15084fd7bdfSCyrill Gorcunov static int pty_get_lock(struct tty_struct *tty, int __user *arg)
15184fd7bdfSCyrill Gorcunov {
15284fd7bdfSCyrill Gorcunov 	int locked = test_bit(TTY_PTY_LOCK, &tty->flags);
153202680c7SXiaofei Tan 
15484fd7bdfSCyrill Gorcunov 	return put_user(locked, arg);
15584fd7bdfSCyrill Gorcunov }
15684fd7bdfSCyrill Gorcunov 
15706026d91SCyrill Gorcunov /* Set the packet mode on a pty */
pty_set_pktmode(struct tty_struct * tty,int __user * arg)15806026d91SCyrill Gorcunov static int pty_set_pktmode(struct tty_struct *tty, int __user *arg)
15906026d91SCyrill Gorcunov {
16006026d91SCyrill Gorcunov 	int pktmode;
16106026d91SCyrill Gorcunov 
16206026d91SCyrill Gorcunov 	if (get_user(pktmode, arg))
16306026d91SCyrill Gorcunov 		return -EFAULT;
16406026d91SCyrill Gorcunov 
16564d608dbSJiri Slaby 	spin_lock_irq(&tty->ctrl.lock);
16606026d91SCyrill Gorcunov 	if (pktmode) {
16764d608dbSJiri Slaby 		if (!tty->ctrl.packet) {
16864d608dbSJiri Slaby 			tty->link->ctrl.pktstatus = 0;
1692622d73eSPeter Hurley 			smp_mb();
17064d608dbSJiri Slaby 			tty->ctrl.packet = true;
17106026d91SCyrill Gorcunov 		}
17206026d91SCyrill Gorcunov 	} else
17364d608dbSJiri Slaby 		tty->ctrl.packet = false;
17464d608dbSJiri Slaby 	spin_unlock_irq(&tty->ctrl.lock);
17506026d91SCyrill Gorcunov 
17606026d91SCyrill Gorcunov 	return 0;
17706026d91SCyrill Gorcunov }
17806026d91SCyrill Gorcunov 
17984fd7bdfSCyrill Gorcunov /* Get the packet mode of a pty */
pty_get_pktmode(struct tty_struct * tty,int __user * arg)18084fd7bdfSCyrill Gorcunov static int pty_get_pktmode(struct tty_struct *tty, int __user *arg)
18184fd7bdfSCyrill Gorcunov {
18264d608dbSJiri Slaby 	int pktmode = tty->ctrl.packet;
183202680c7SXiaofei Tan 
18484fd7bdfSCyrill Gorcunov 	return put_user(pktmode, arg);
18584fd7bdfSCyrill Gorcunov }
18684fd7bdfSCyrill Gorcunov 
18796fd7ce5SGreg Kroah-Hartman /* Send a signal to the slave */
pty_signal(struct tty_struct * tty,int sig)18896fd7ce5SGreg Kroah-Hartman static int pty_signal(struct tty_struct *tty, int sig)
18996fd7ce5SGreg Kroah-Hartman {
19096fd7ce5SGreg Kroah-Hartman 	struct pid *pgrp;
19196fd7ce5SGreg Kroah-Hartman 
19237480a05SPeter Hurley 	if (sig != SIGINT && sig != SIGQUIT && sig != SIGTSTP)
19337480a05SPeter Hurley 		return -EINVAL;
19437480a05SPeter Hurley 
19596fd7ce5SGreg Kroah-Hartman 	if (tty->link) {
1965b239542SPeter Hurley 		pgrp = tty_get_pgrp(tty->link);
1975b239542SPeter Hurley 		if (pgrp)
19896fd7ce5SGreg Kroah-Hartman 			kill_pgrp(pgrp, sig, 1);
19996fd7ce5SGreg Kroah-Hartman 		put_pid(pgrp);
20096fd7ce5SGreg Kroah-Hartman 	}
20196fd7ce5SGreg Kroah-Hartman 	return 0;
20296fd7ce5SGreg Kroah-Hartman }
20396fd7ce5SGreg Kroah-Hartman 
pty_flush_buffer(struct tty_struct * tty)20496fd7ce5SGreg Kroah-Hartman static void pty_flush_buffer(struct tty_struct *tty)
20596fd7ce5SGreg Kroah-Hartman {
20696fd7ce5SGreg Kroah-Hartman 	struct tty_struct *to = tty->link;
20796fd7ce5SGreg Kroah-Hartman 
20896fd7ce5SGreg Kroah-Hartman 	if (!to)
20996fd7ce5SGreg Kroah-Hartman 		return;
2101d1d14daSPeter Hurley 
21177dae613SWang YanQing 	tty_buffer_flush(to, NULL);
21264d608dbSJiri Slaby 	if (to->ctrl.packet) {
21364d608dbSJiri Slaby 		spin_lock_irq(&tty->ctrl.lock);
21464d608dbSJiri Slaby 		tty->ctrl.pktstatus |= TIOCPKT_FLUSHWRITE;
21596fd7ce5SGreg Kroah-Hartman 		wake_up_interruptible(&to->read_wait);
21664d608dbSJiri Slaby 		spin_unlock_irq(&tty->ctrl.lock);
21796fd7ce5SGreg Kroah-Hartman 	}
21896fd7ce5SGreg Kroah-Hartman }
21996fd7ce5SGreg Kroah-Hartman 
pty_open(struct tty_struct * tty,struct file * filp)22096fd7ce5SGreg Kroah-Hartman static int pty_open(struct tty_struct *tty, struct file *filp)
22196fd7ce5SGreg Kroah-Hartman {
22296fd7ce5SGreg Kroah-Hartman 	if (!tty || !tty->link)
2237c61c3d8SPeter Hurley 		return -ENODEV;
22496fd7ce5SGreg Kroah-Hartman 
22596fd7ce5SGreg Kroah-Hartman 	if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
22696fd7ce5SGreg Kroah-Hartman 		goto out;
22796fd7ce5SGreg Kroah-Hartman 	if (test_bit(TTY_PTY_LOCK, &tty->link->flags))
22896fd7ce5SGreg Kroah-Hartman 		goto out;
22980cace72SPeter Hurley 	if (tty->driver->subtype == PTY_TYPE_SLAVE && tty->link->count != 1)
23096fd7ce5SGreg Kroah-Hartman 		goto out;
23196fd7ce5SGreg Kroah-Hartman 
23269939035SPeter Hurley 	clear_bit(TTY_IO_ERROR, &tty->flags);
23396fd7ce5SGreg Kroah-Hartman 	clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
23496fd7ce5SGreg Kroah-Hartman 	set_bit(TTY_THROTTLED, &tty->flags);
2357c61c3d8SPeter Hurley 	return 0;
2367c61c3d8SPeter Hurley 
23796fd7ce5SGreg Kroah-Hartman out:
2387c61c3d8SPeter Hurley 	set_bit(TTY_IO_ERROR, &tty->flags);
2397c61c3d8SPeter Hurley 	return -EIO;
24096fd7ce5SGreg Kroah-Hartman }
24196fd7ce5SGreg Kroah-Hartman 
pty_set_termios(struct tty_struct * tty,const struct ktermios * old_termios)24296fd7ce5SGreg Kroah-Hartman static void pty_set_termios(struct tty_struct *tty,
243a8c11c15SIlpo Järvinen 			    const struct ktermios *old_termios)
24496fd7ce5SGreg Kroah-Hartman {
245dbfcd851SPeter Hurley 	/* See if packet mode change of state. */
24664d608dbSJiri Slaby 	if (tty->link && tty->link->ctrl.packet) {
2479db276f8SPeter Hurley 		int extproc = (old_termios->c_lflag & EXTPROC) | L_EXTPROC(tty);
248dbfcd851SPeter Hurley 		int old_flow = ((old_termios->c_iflag & IXON) &&
249dbfcd851SPeter Hurley 				(old_termios->c_cc[VSTOP] == '\023') &&
250dbfcd851SPeter Hurley 				(old_termios->c_cc[VSTART] == '\021'));
251dbfcd851SPeter Hurley 		int new_flow = (I_IXON(tty) &&
252dbfcd851SPeter Hurley 				STOP_CHAR(tty) == '\023' &&
253dbfcd851SPeter Hurley 				START_CHAR(tty) == '\021');
254dbfcd851SPeter Hurley 		if ((old_flow != new_flow) || extproc) {
25564d608dbSJiri Slaby 			spin_lock_irq(&tty->ctrl.lock);
256dbfcd851SPeter Hurley 			if (old_flow != new_flow) {
25764d608dbSJiri Slaby 				tty->ctrl.pktstatus &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
258dbfcd851SPeter Hurley 				if (new_flow)
25964d608dbSJiri Slaby 					tty->ctrl.pktstatus |= TIOCPKT_DOSTOP;
260dbfcd851SPeter Hurley 				else
26164d608dbSJiri Slaby 					tty->ctrl.pktstatus |= TIOCPKT_NOSTOP;
262dbfcd851SPeter Hurley 			}
263dbfcd851SPeter Hurley 			if (extproc)
26464d608dbSJiri Slaby 				tty->ctrl.pktstatus |= TIOCPKT_IOCTL;
26564d608dbSJiri Slaby 			spin_unlock_irq(&tty->ctrl.lock);
266dbfcd851SPeter Hurley 			wake_up_interruptible(&tty->link->read_wait);
267dbfcd851SPeter Hurley 		}
268dbfcd851SPeter Hurley 	}
269dbfcd851SPeter Hurley 
270adc8d746SAlan Cox 	tty->termios.c_cflag &= ~(CSIZE | PARENB);
271adc8d746SAlan Cox 	tty->termios.c_cflag |= (CS8 | CREAD);
27296fd7ce5SGreg Kroah-Hartman }
27396fd7ce5SGreg Kroah-Hartman 
27496fd7ce5SGreg Kroah-Hartman /**
275f6038cf4SYang Yingliang  *	pty_resize		-	resize event
27696fd7ce5SGreg Kroah-Hartman  *	@tty: tty being resized
27796fd7ce5SGreg Kroah-Hartman  *	@ws: window size being set.
27896fd7ce5SGreg Kroah-Hartman  *
27996fd7ce5SGreg Kroah-Hartman  *	Update the termios variables and send the necessary signals to
28096fd7ce5SGreg Kroah-Hartman  *	peform a terminal resize correctly
28196fd7ce5SGreg Kroah-Hartman  */
28296fd7ce5SGreg Kroah-Hartman 
pty_resize(struct tty_struct * tty,struct winsize * ws)283159a8e92SJosh Triplett static int pty_resize(struct tty_struct *tty,  struct winsize *ws)
28496fd7ce5SGreg Kroah-Hartman {
28596fd7ce5SGreg Kroah-Hartman 	struct pid *pgrp, *rpgrp;
28696fd7ce5SGreg Kroah-Hartman 	struct tty_struct *pty = tty->link;
28796fd7ce5SGreg Kroah-Hartman 
28896fd7ce5SGreg Kroah-Hartman 	/* For a PTY we need to lock the tty side */
289dee4a0beSPeter Hurley 	mutex_lock(&tty->winsize_mutex);
29096fd7ce5SGreg Kroah-Hartman 	if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
29196fd7ce5SGreg Kroah-Hartman 		goto done;
29296fd7ce5SGreg Kroah-Hartman 
2935b239542SPeter Hurley 	/* Signal the foreground process group of both ptys */
2945b239542SPeter Hurley 	pgrp = tty_get_pgrp(tty);
2955b239542SPeter Hurley 	rpgrp = tty_get_pgrp(pty);
29696fd7ce5SGreg Kroah-Hartman 
29796fd7ce5SGreg Kroah-Hartman 	if (pgrp)
29896fd7ce5SGreg Kroah-Hartman 		kill_pgrp(pgrp, SIGWINCH, 1);
29996fd7ce5SGreg Kroah-Hartman 	if (rpgrp != pgrp && rpgrp)
30096fd7ce5SGreg Kroah-Hartman 		kill_pgrp(rpgrp, SIGWINCH, 1);
30196fd7ce5SGreg Kroah-Hartman 
30296fd7ce5SGreg Kroah-Hartman 	put_pid(pgrp);
30396fd7ce5SGreg Kroah-Hartman 	put_pid(rpgrp);
30496fd7ce5SGreg Kroah-Hartman 
30596fd7ce5SGreg Kroah-Hartman 	tty->winsize = *ws;
30696fd7ce5SGreg Kroah-Hartman 	pty->winsize = *ws;	/* Never used so will go away soon */
30796fd7ce5SGreg Kroah-Hartman done:
308dee4a0beSPeter Hurley 	mutex_unlock(&tty->winsize_mutex);
30996fd7ce5SGreg Kroah-Hartman 	return 0;
31096fd7ce5SGreg Kroah-Hartman }
31196fd7ce5SGreg Kroah-Hartman 
312d155255aSAlan Cox /**
31301adc807SPeter Hurley  *	pty_start - start() handler
31401adc807SPeter Hurley  *	pty_stop  - stop() handler
31501adc807SPeter Hurley  *	@tty: tty being flow-controlled
31601adc807SPeter Hurley  *
31701adc807SPeter Hurley  *	Propagates the TIOCPKT status to the master pty.
31801adc807SPeter Hurley  *
31901adc807SPeter Hurley  *	NB: only the master pty can be in packet mode so only the slave
32001adc807SPeter Hurley  *	    needs start()/stop() handlers
32101adc807SPeter Hurley  */
pty_start(struct tty_struct * tty)32201adc807SPeter Hurley static void pty_start(struct tty_struct *tty)
32301adc807SPeter Hurley {
32401adc807SPeter Hurley 	unsigned long flags;
32501adc807SPeter Hurley 
32664d608dbSJiri Slaby 	if (tty->link && tty->link->ctrl.packet) {
32764d608dbSJiri Slaby 		spin_lock_irqsave(&tty->ctrl.lock, flags);
32864d608dbSJiri Slaby 		tty->ctrl.pktstatus &= ~TIOCPKT_STOP;
32964d608dbSJiri Slaby 		tty->ctrl.pktstatus |= TIOCPKT_START;
33064d608dbSJiri Slaby 		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
331a9a08845SLinus Torvalds 		wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
33201adc807SPeter Hurley 	}
33301adc807SPeter Hurley }
33401adc807SPeter Hurley 
pty_stop(struct tty_struct * tty)33501adc807SPeter Hurley static void pty_stop(struct tty_struct *tty)
33601adc807SPeter Hurley {
33701adc807SPeter Hurley 	unsigned long flags;
33801adc807SPeter Hurley 
33964d608dbSJiri Slaby 	if (tty->link && tty->link->ctrl.packet) {
34064d608dbSJiri Slaby 		spin_lock_irqsave(&tty->ctrl.lock, flags);
34164d608dbSJiri Slaby 		tty->ctrl.pktstatus &= ~TIOCPKT_START;
34264d608dbSJiri Slaby 		tty->ctrl.pktstatus |= TIOCPKT_STOP;
34364d608dbSJiri Slaby 		spin_unlock_irqrestore(&tty->ctrl.lock, flags);
344a9a08845SLinus Torvalds 		wake_up_interruptible_poll(&tty->link->read_wait, EPOLLIN);
34501adc807SPeter Hurley 	}
34601adc807SPeter Hurley }
34701adc807SPeter Hurley 
34801adc807SPeter Hurley /**
349d155255aSAlan Cox  *	pty_common_install		-	set up the pty pair
350d155255aSAlan Cox  *	@driver: the pty driver
351d155255aSAlan Cox  *	@tty: the tty being instantiated
3522c964a2fSRasmus Villemoes  *	@legacy: true if this is BSD style
353d155255aSAlan Cox  *
354d155255aSAlan Cox  *	Perform the initial set up for the tty/pty pair. Called from the
355d155255aSAlan Cox  *	tty layer when the port is first opened.
356d155255aSAlan Cox  *
357d155255aSAlan Cox  *	Locking: the caller must hold the tty_mutex
358d155255aSAlan Cox  */
pty_common_install(struct tty_driver * driver,struct tty_struct * tty,bool legacy)3595d249bc6SJiri Slaby static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty,
3605d249bc6SJiri Slaby 		bool legacy)
36196fd7ce5SGreg Kroah-Hartman {
36296fd7ce5SGreg Kroah-Hartman 	struct tty_struct *o_tty;
363d03702a2SJiri Slaby 	struct tty_port *ports[2];
36496fd7ce5SGreg Kroah-Hartman 	int idx = tty->index;
3655d249bc6SJiri Slaby 	int retval = -ENOMEM;
36696fd7ce5SGreg Kroah-Hartman 
36755199ea3SPeter Hurley 	/* Opening the slave first has always returned -EIO */
36855199ea3SPeter Hurley 	if (driver->subtype != PTY_TYPE_MASTER)
36955199ea3SPeter Hurley 		return -EIO;
37055199ea3SPeter Hurley 
371d03702a2SJiri Slaby 	ports[0] = kmalloc(sizeof **ports, GFP_KERNEL);
372d03702a2SJiri Slaby 	ports[1] = kmalloc(sizeof **ports, GFP_KERNEL);
3736f9ea7adSJiri Slaby 	if (!ports[0] || !ports[1])
3742c964a2fSRasmus Villemoes 		goto err;
37596fd7ce5SGreg Kroah-Hartman 	if (!try_module_get(driver->other->owner)) {
37696fd7ce5SGreg Kroah-Hartman 		/* This cannot in fact currently happen */
3772c964a2fSRasmus Villemoes 		goto err;
37896fd7ce5SGreg Kroah-Hartman 	}
3792c964a2fSRasmus Villemoes 	o_tty = alloc_tty_struct(driver->other, idx);
3802c964a2fSRasmus Villemoes 	if (!o_tty)
3812c964a2fSRasmus Villemoes 		goto err_put_module;
38296fd7ce5SGreg Kroah-Hartman 
3832febdb63SPeter Hurley 	tty_set_lock_subclass(o_tty);
384d2b6f447SPeter Hurley 	lockdep_set_subclass(&o_tty->termios_rwsem, TTY_LOCK_SLAVE);
3852febdb63SPeter Hurley 
3865d249bc6SJiri Slaby 	if (legacy) {
38796fd7ce5SGreg Kroah-Hartman 		/* We always use new tty termios data so we can do this
38896fd7ce5SGreg Kroah-Hartman 		   the easy way .. */
389a3123fd0SPeter Hurley 		tty_init_termios(tty);
390a3123fd0SPeter Hurley 		tty_init_termios(o_tty);
39196fd7ce5SGreg Kroah-Hartman 
3925d249bc6SJiri Slaby 		driver->other->ttys[idx] = o_tty;
3935d249bc6SJiri Slaby 		driver->ttys[idx] = tty;
3945d249bc6SJiri Slaby 	} else {
395adc8d746SAlan Cox 		memset(&tty->termios_locked, 0, sizeof(tty->termios_locked));
396adc8d746SAlan Cox 		tty->termios = driver->init_termios;
397adc8d746SAlan Cox 		memset(&o_tty->termios_locked, 0, sizeof(tty->termios_locked));
398adc8d746SAlan Cox 		o_tty->termios = driver->other->init_termios;
3995d249bc6SJiri Slaby 	}
4005d249bc6SJiri Slaby 
40196fd7ce5SGreg Kroah-Hartman 	/*
40296fd7ce5SGreg Kroah-Hartman 	 * Everything allocated ... set up the o_tty structure.
40396fd7ce5SGreg Kroah-Hartman 	 */
40496fd7ce5SGreg Kroah-Hartman 	tty_driver_kref_get(driver->other);
40596fd7ce5SGreg Kroah-Hartman 	/* Establish the links in both directions */
40696fd7ce5SGreg Kroah-Hartman 	tty->link   = o_tty;
40796fd7ce5SGreg Kroah-Hartman 	o_tty->link = tty;
408d03702a2SJiri Slaby 	tty_port_init(ports[0]);
409d03702a2SJiri Slaby 	tty_port_init(ports[1]);
410c81622acSPeter Hurley 	tty_buffer_set_limit(ports[0], 8192);
411c81622acSPeter Hurley 	tty_buffer_set_limit(ports[1], 8192);
412d03702a2SJiri Slaby 	o_tty->port = ports[0];
413d03702a2SJiri Slaby 	tty->port = ports[1];
414967fab69SJiri Slaby 	o_tty->port->itty = o_tty;
41596fd7ce5SGreg Kroah-Hartman 
4161d1d14daSPeter Hurley 	tty_buffer_set_lock_subclass(o_tty->port);
4171d1d14daSPeter Hurley 
41896fd7ce5SGreg Kroah-Hartman 	tty_driver_kref_get(driver);
41996fd7ce5SGreg Kroah-Hartman 	tty->count++;
42055199ea3SPeter Hurley 	o_tty->count++;
42196fd7ce5SGreg Kroah-Hartman 	return 0;
422a3123fd0SPeter Hurley 
4232c964a2fSRasmus Villemoes err_put_module:
42407584d4aSRasmus Villemoes 	module_put(driver->other->owner);
4252c964a2fSRasmus Villemoes err:
426d03702a2SJiri Slaby 	kfree(ports[0]);
427d03702a2SJiri Slaby 	kfree(ports[1]);
4288a1b8d70SJiri Slaby 	return retval;
42996fd7ce5SGreg Kroah-Hartman }
43096fd7ce5SGreg Kroah-Hartman 
pty_cleanup(struct tty_struct * tty)431d03702a2SJiri Slaby static void pty_cleanup(struct tty_struct *tty)
432d03702a2SJiri Slaby {
43381c79838SJiri Slaby 	tty_port_put(tty->port);
434d03702a2SJiri Slaby }
435d03702a2SJiri Slaby 
4365d249bc6SJiri Slaby /* Traditional BSD devices */
4375d249bc6SJiri Slaby #ifdef CONFIG_LEGACY_PTYS
4385d249bc6SJiri Slaby 
pty_install(struct tty_driver * driver,struct tty_struct * tty)4395d249bc6SJiri Slaby static int pty_install(struct tty_driver *driver, struct tty_struct *tty)
4405d249bc6SJiri Slaby {
4415d249bc6SJiri Slaby 	return pty_common_install(driver, tty, true);
4425d249bc6SJiri Slaby }
4435d249bc6SJiri Slaby 
pty_remove(struct tty_driver * driver,struct tty_struct * tty)444d155255aSAlan Cox static void pty_remove(struct tty_driver *driver, struct tty_struct *tty)
445d155255aSAlan Cox {
446d155255aSAlan Cox 	struct tty_struct *pair = tty->link;
447202680c7SXiaofei Tan 
448d155255aSAlan Cox 	driver->ttys[tty->index] = NULL;
449d155255aSAlan Cox 	if (pair)
450d155255aSAlan Cox 		pair->driver->ttys[pair->index] = NULL;
451d155255aSAlan Cox }
452d155255aSAlan Cox 
pty_bsd_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)4536caa76b7SAlan Cox static int pty_bsd_ioctl(struct tty_struct *tty,
45496fd7ce5SGreg Kroah-Hartman 			 unsigned int cmd, unsigned long arg)
45596fd7ce5SGreg Kroah-Hartman {
45696fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
45796fd7ce5SGreg Kroah-Hartman 	case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
45896fd7ce5SGreg Kroah-Hartman 		return pty_set_lock(tty, (int __user *) arg);
45984fd7bdfSCyrill Gorcunov 	case TIOCGPTLCK: /* Get PT Lock status */
46084fd7bdfSCyrill Gorcunov 		return pty_get_lock(tty, (int __user *)arg);
46106026d91SCyrill Gorcunov 	case TIOCPKT: /* Set PT packet mode */
46206026d91SCyrill Gorcunov 		return pty_set_pktmode(tty, (int __user *)arg);
46384fd7bdfSCyrill Gorcunov 	case TIOCGPKT: /* Get PT packet mode */
46484fd7bdfSCyrill Gorcunov 		return pty_get_pktmode(tty, (int __user *)arg);
46596fd7ce5SGreg Kroah-Hartman 	case TIOCSIG:    /* Send signal to other side of pty */
46696fd7ce5SGreg Kroah-Hartman 		return pty_signal(tty, (int) arg);
467ded2f295SJiri Slaby 	case TIOCGPTN: /* TTY returns ENOTTY, but glibc expects EINVAL here */
468ded2f295SJiri Slaby 		return -EINVAL;
46996fd7ce5SGreg Kroah-Hartman 	}
47096fd7ce5SGreg Kroah-Hartman 	return -ENOIOCTLCMD;
47196fd7ce5SGreg Kroah-Hartman }
47296fd7ce5SGreg Kroah-Hartman 
47350f45326SAl Viro #ifdef CONFIG_COMPAT
pty_bsd_compat_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)4745f0f187fSAleksa Sarai static long pty_bsd_compat_ioctl(struct tty_struct *tty,
4755f0f187fSAleksa Sarai 				 unsigned int cmd, unsigned long arg)
4765f0f187fSAleksa Sarai {
4775f0f187fSAleksa Sarai 	/*
4785f0f187fSAleksa Sarai 	 * PTY ioctls don't require any special translation between 32-bit and
4795f0f187fSAleksa Sarai 	 * 64-bit userspace, they are already compatible.
4805f0f187fSAleksa Sarai 	 */
48150f45326SAl Viro 	return pty_bsd_ioctl(tty, cmd, (unsigned long)compat_ptr(arg));
4825f0f187fSAleksa Sarai }
48350f45326SAl Viro #else
48450f45326SAl Viro #define pty_bsd_compat_ioctl NULL
48550f45326SAl Viro #endif
4865f0f187fSAleksa Sarai 
48796fd7ce5SGreg Kroah-Hartman static int legacy_count = CONFIG_LEGACY_PTY_COUNT;
4887154988fSPaul Gortmaker /*
4897154988fSPaul Gortmaker  * not really modular, but the easiest way to keep compat with existing
4907154988fSPaul Gortmaker  * bootargs behaviour is to continue using module_param here.
4917154988fSPaul Gortmaker  */
49296fd7ce5SGreg Kroah-Hartman module_param(legacy_count, int, 0);
49396fd7ce5SGreg Kroah-Hartman 
49496fd7ce5SGreg Kroah-Hartman /*
49596fd7ce5SGreg Kroah-Hartman  * The master side of a pty can do TIOCSPTLCK and thus
49696fd7ce5SGreg Kroah-Hartman  * has pty_bsd_ioctl.
49796fd7ce5SGreg Kroah-Hartman  */
49896fd7ce5SGreg Kroah-Hartman static const struct tty_operations master_pty_ops_bsd = {
49996fd7ce5SGreg Kroah-Hartman 	.install = pty_install,
50096fd7ce5SGreg Kroah-Hartman 	.open = pty_open,
50196fd7ce5SGreg Kroah-Hartman 	.close = pty_close,
50296fd7ce5SGreg Kroah-Hartman 	.write = pty_write,
50396fd7ce5SGreg Kroah-Hartman 	.write_room = pty_write_room,
50496fd7ce5SGreg Kroah-Hartman 	.flush_buffer = pty_flush_buffer,
50596fd7ce5SGreg Kroah-Hartman 	.unthrottle = pty_unthrottle,
50696fd7ce5SGreg Kroah-Hartman 	.ioctl = pty_bsd_ioctl,
5075f0f187fSAleksa Sarai 	.compat_ioctl = pty_bsd_compat_ioctl,
508d03702a2SJiri Slaby 	.cleanup = pty_cleanup,
509d155255aSAlan Cox 	.resize = pty_resize,
510d155255aSAlan Cox 	.remove = pty_remove
51196fd7ce5SGreg Kroah-Hartman };
51296fd7ce5SGreg Kroah-Hartman 
51396fd7ce5SGreg Kroah-Hartman static const struct tty_operations slave_pty_ops_bsd = {
51496fd7ce5SGreg Kroah-Hartman 	.install = pty_install,
51596fd7ce5SGreg Kroah-Hartman 	.open = pty_open,
51696fd7ce5SGreg Kroah-Hartman 	.close = pty_close,
51796fd7ce5SGreg Kroah-Hartman 	.write = pty_write,
51896fd7ce5SGreg Kroah-Hartman 	.write_room = pty_write_room,
51996fd7ce5SGreg Kroah-Hartman 	.flush_buffer = pty_flush_buffer,
52096fd7ce5SGreg Kroah-Hartman 	.unthrottle = pty_unthrottle,
52196fd7ce5SGreg Kroah-Hartman 	.set_termios = pty_set_termios,
522d03702a2SJiri Slaby 	.cleanup = pty_cleanup,
523d155255aSAlan Cox 	.resize = pty_resize,
52401adc807SPeter Hurley 	.start = pty_start,
52501adc807SPeter Hurley 	.stop = pty_stop,
526d155255aSAlan Cox 	.remove = pty_remove
52796fd7ce5SGreg Kroah-Hartman };
52896fd7ce5SGreg Kroah-Hartman 
legacy_pty_init(void)52996fd7ce5SGreg Kroah-Hartman static void __init legacy_pty_init(void)
53096fd7ce5SGreg Kroah-Hartman {
53196fd7ce5SGreg Kroah-Hartman 	struct tty_driver *pty_driver, *pty_slave_driver;
53296fd7ce5SGreg Kroah-Hartman 
53396fd7ce5SGreg Kroah-Hartman 	if (legacy_count <= 0)
53496fd7ce5SGreg Kroah-Hartman 		return;
53596fd7ce5SGreg Kroah-Hartman 
53621aca2faSJiri Slaby 	pty_driver = tty_alloc_driver(legacy_count,
53721aca2faSJiri Slaby 			TTY_DRIVER_RESET_TERMIOS |
53821aca2faSJiri Slaby 			TTY_DRIVER_REAL_RAW |
53921aca2faSJiri Slaby 			TTY_DRIVER_DYNAMIC_ALLOC);
540c3a6344aSDan Carpenter 	if (IS_ERR(pty_driver))
54196fd7ce5SGreg Kroah-Hartman 		panic("Couldn't allocate pty driver");
54296fd7ce5SGreg Kroah-Hartman 
54321aca2faSJiri Slaby 	pty_slave_driver = tty_alloc_driver(legacy_count,
54421aca2faSJiri Slaby 			TTY_DRIVER_RESET_TERMIOS |
54521aca2faSJiri Slaby 			TTY_DRIVER_REAL_RAW |
54621aca2faSJiri Slaby 			TTY_DRIVER_DYNAMIC_ALLOC);
547c3a6344aSDan Carpenter 	if (IS_ERR(pty_slave_driver))
54896fd7ce5SGreg Kroah-Hartman 		panic("Couldn't allocate pty slave driver");
54996fd7ce5SGreg Kroah-Hartman 
55096fd7ce5SGreg Kroah-Hartman 	pty_driver->driver_name = "pty_master";
55196fd7ce5SGreg Kroah-Hartman 	pty_driver->name = "pty";
55296fd7ce5SGreg Kroah-Hartman 	pty_driver->major = PTY_MASTER_MAJOR;
55396fd7ce5SGreg Kroah-Hartman 	pty_driver->minor_start = 0;
55496fd7ce5SGreg Kroah-Hartman 	pty_driver->type = TTY_DRIVER_TYPE_PTY;
55596fd7ce5SGreg Kroah-Hartman 	pty_driver->subtype = PTY_TYPE_MASTER;
55696fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios = tty_std_termios;
55796fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios.c_iflag = 0;
55896fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios.c_oflag = 0;
55996fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
56096fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios.c_lflag = 0;
56196fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios.c_ispeed = 38400;
56296fd7ce5SGreg Kroah-Hartman 	pty_driver->init_termios.c_ospeed = 38400;
56396fd7ce5SGreg Kroah-Hartman 	pty_driver->other = pty_slave_driver;
56496fd7ce5SGreg Kroah-Hartman 	tty_set_operations(pty_driver, &master_pty_ops_bsd);
56596fd7ce5SGreg Kroah-Hartman 
56696fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->driver_name = "pty_slave";
56796fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->name = "ttyp";
56896fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->major = PTY_SLAVE_MAJOR;
56996fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->minor_start = 0;
57096fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->type = TTY_DRIVER_TYPE_PTY;
57196fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->subtype = PTY_TYPE_SLAVE;
57296fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->init_termios = tty_std_termios;
57396fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
57496fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->init_termios.c_ispeed = 38400;
57596fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->init_termios.c_ospeed = 38400;
57696fd7ce5SGreg Kroah-Hartman 	pty_slave_driver->other = pty_driver;
57796fd7ce5SGreg Kroah-Hartman 	tty_set_operations(pty_slave_driver, &slave_pty_ops_bsd);
57896fd7ce5SGreg Kroah-Hartman 
57996fd7ce5SGreg Kroah-Hartman 	if (tty_register_driver(pty_driver))
58096fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register pty driver");
58196fd7ce5SGreg Kroah-Hartman 	if (tty_register_driver(pty_slave_driver))
58296fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register pty slave driver");
58396fd7ce5SGreg Kroah-Hartman }
58496fd7ce5SGreg Kroah-Hartman #else
legacy_pty_init(void)58596fd7ce5SGreg Kroah-Hartman static inline void legacy_pty_init(void) { }
58696fd7ce5SGreg Kroah-Hartman #endif
58796fd7ce5SGreg Kroah-Hartman 
58896fd7ce5SGreg Kroah-Hartman /* Unix98 devices */
58996fd7ce5SGreg Kroah-Hartman #ifdef CONFIG_UNIX98_PTYS
59096fd7ce5SGreg Kroah-Hartman static struct cdev ptmx_cdev;
59196fd7ce5SGreg Kroah-Hartman 
5926509f309SArnd Bergmann /**
593311fc65cSEric W. Biederman  *	ptm_open_peer - open the peer of a pty
594311fc65cSEric W. Biederman  *	@master: the open struct file of the ptmx device node
595311fc65cSEric W. Biederman  *	@tty: the master of the pty being opened
596311fc65cSEric W. Biederman  *	@flags: the flags for open
5976509f309SArnd Bergmann  *
598311fc65cSEric W. Biederman  *	Provide a race free way for userspace to open the slave end of a pty
599311fc65cSEric W. Biederman  *	(where they have the master fd and cannot access or trust the mount
600311fc65cSEric W. Biederman  *	namespace /dev/pts was mounted inside).
6016509f309SArnd Bergmann  */
ptm_open_peer(struct file * master,struct tty_struct * tty,int flags)602311fc65cSEric W. Biederman int ptm_open_peer(struct file *master, struct tty_struct *tty, int flags)
6036509f309SArnd Bergmann {
6044503b1c2SColin Ian King 	int fd;
605311fc65cSEric W. Biederman 	struct file *filp;
6066509f309SArnd Bergmann 	int retval = -EINVAL;
607311fc65cSEric W. Biederman 	struct path path;
608311fc65cSEric W. Biederman 
609311fc65cSEric W. Biederman 	if (tty->driver != ptm_driver)
610311fc65cSEric W. Biederman 		return -EIO;
6116509f309SArnd Bergmann 
61236ecc148SMatthijs van Duin 	fd = get_unused_fd_flags(flags);
6136509f309SArnd Bergmann 	if (fd < 0) {
6146509f309SArnd Bergmann 		retval = fd;
6156509f309SArnd Bergmann 		goto err;
6166509f309SArnd Bergmann 	}
6176509f309SArnd Bergmann 
618311fc65cSEric W. Biederman 	/* Compute the slave's path */
619311fc65cSEric W. Biederman 	path.mnt = devpts_mntget(master, tty->driver_data);
620311fc65cSEric W. Biederman 	if (IS_ERR(path.mnt)) {
621311fc65cSEric W. Biederman 		retval = PTR_ERR(path.mnt);
622311fc65cSEric W. Biederman 		goto err_put;
623311fc65cSEric W. Biederman 	}
624311fc65cSEric W. Biederman 	path.dentry = tty->link->driver_data;
625311fc65cSEric W. Biederman 
626311fc65cSEric W. Biederman 	filp = dentry_open(&path, flags, current_cred());
627311fc65cSEric W. Biederman 	mntput(path.mnt);
6286509f309SArnd Bergmann 	if (IS_ERR(filp)) {
6296509f309SArnd Bergmann 		retval = PTR_ERR(filp);
6306509f309SArnd Bergmann 		goto err_put;
6316509f309SArnd Bergmann 	}
6326509f309SArnd Bergmann 
6336509f309SArnd Bergmann 	fd_install(fd, filp);
6346509f309SArnd Bergmann 	return fd;
6356509f309SArnd Bergmann 
6366509f309SArnd Bergmann err_put:
6376509f309SArnd Bergmann 	put_unused_fd(fd);
6386509f309SArnd Bergmann err:
6396509f309SArnd Bergmann 	return retval;
6406509f309SArnd Bergmann }
6416509f309SArnd Bergmann 
pty_unix98_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)6426caa76b7SAlan Cox static int pty_unix98_ioctl(struct tty_struct *tty,
64396fd7ce5SGreg Kroah-Hartman 			    unsigned int cmd, unsigned long arg)
64496fd7ce5SGreg Kroah-Hartman {
64596fd7ce5SGreg Kroah-Hartman 	switch (cmd) {
64696fd7ce5SGreg Kroah-Hartman 	case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */
64796fd7ce5SGreg Kroah-Hartman 		return pty_set_lock(tty, (int __user *)arg);
64884fd7bdfSCyrill Gorcunov 	case TIOCGPTLCK: /* Get PT Lock status */
64984fd7bdfSCyrill Gorcunov 		return pty_get_lock(tty, (int __user *)arg);
65006026d91SCyrill Gorcunov 	case TIOCPKT: /* Set PT packet mode */
65106026d91SCyrill Gorcunov 		return pty_set_pktmode(tty, (int __user *)arg);
65284fd7bdfSCyrill Gorcunov 	case TIOCGPKT: /* Get PT packet mode */
65384fd7bdfSCyrill Gorcunov 		return pty_get_pktmode(tty, (int __user *)arg);
65496fd7ce5SGreg Kroah-Hartman 	case TIOCGPTN: /* Get PT Number */
65596fd7ce5SGreg Kroah-Hartman 		return put_user(tty->index, (unsigned int __user *)arg);
65696fd7ce5SGreg Kroah-Hartman 	case TIOCSIG:    /* Send signal to other side of pty */
65796fd7ce5SGreg Kroah-Hartman 		return pty_signal(tty, (int) arg);
65896fd7ce5SGreg Kroah-Hartman 	}
65996fd7ce5SGreg Kroah-Hartman 
66096fd7ce5SGreg Kroah-Hartman 	return -ENOIOCTLCMD;
66196fd7ce5SGreg Kroah-Hartman }
66296fd7ce5SGreg Kroah-Hartman 
66350f45326SAl Viro #ifdef CONFIG_COMPAT
pty_unix98_compat_ioctl(struct tty_struct * tty,unsigned int cmd,unsigned long arg)6645f0f187fSAleksa Sarai static long pty_unix98_compat_ioctl(struct tty_struct *tty,
6655f0f187fSAleksa Sarai 				 unsigned int cmd, unsigned long arg)
6665f0f187fSAleksa Sarai {
6675f0f187fSAleksa Sarai 	/*
6685f0f187fSAleksa Sarai 	 * PTY ioctls don't require any special translation between 32-bit and
6695f0f187fSAleksa Sarai 	 * 64-bit userspace, they are already compatible.
6705f0f187fSAleksa Sarai 	 */
67150f45326SAl Viro 	return pty_unix98_ioctl(tty, cmd,
67250f45326SAl Viro 		cmd == TIOCSIG ? arg : (unsigned long)compat_ptr(arg));
6735f0f187fSAleksa Sarai }
67450f45326SAl Viro #else
67550f45326SAl Viro #define pty_unix98_compat_ioctl NULL
67650f45326SAl Viro #endif
6775f0f187fSAleksa Sarai 
67896fd7ce5SGreg Kroah-Hartman /**
67996fd7ce5SGreg Kroah-Hartman  *	ptm_unix98_lookup	-	find a pty master
68096fd7ce5SGreg Kroah-Hartman  *	@driver: ptm driver
6810b0a66a5SLee Jones  *	@file: unused
68296fd7ce5SGreg Kroah-Hartman  *	@idx: tty index
68396fd7ce5SGreg Kroah-Hartman  *
68496fd7ce5SGreg Kroah-Hartman  *	Look up a pty master device. Called under the tty_mutex for now.
68596fd7ce5SGreg Kroah-Hartman  *	This provides our locking.
68696fd7ce5SGreg Kroah-Hartman  */
68796fd7ce5SGreg Kroah-Hartman 
ptm_unix98_lookup(struct tty_driver * driver,struct file * file,int idx)68896fd7ce5SGreg Kroah-Hartman static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
6898ead9dd5SLinus Torvalds 		struct file *file, int idx)
69096fd7ce5SGreg Kroah-Hartman {
691593a27c4SKonstantin Khlebnikov 	/* Master must be open via /dev/ptmx */
692593a27c4SKonstantin Khlebnikov 	return ERR_PTR(-EIO);
69396fd7ce5SGreg Kroah-Hartman }
69496fd7ce5SGreg Kroah-Hartman 
69596fd7ce5SGreg Kroah-Hartman /**
69696fd7ce5SGreg Kroah-Hartman  *	pts_unix98_lookup	-	find a pty slave
69796fd7ce5SGreg Kroah-Hartman  *	@driver: pts driver
6980b0a66a5SLee Jones  *	@file: file pointer to tty
69996fd7ce5SGreg Kroah-Hartman  *	@idx: tty index
70096fd7ce5SGreg Kroah-Hartman  *
70196fd7ce5SGreg Kroah-Hartman  *	Look up a pty master device. Called under the tty_mutex for now.
702d739e65bSAlan Cox  *	This provides our locking for the tty pointer.
70396fd7ce5SGreg Kroah-Hartman  */
70496fd7ce5SGreg Kroah-Hartman 
pts_unix98_lookup(struct tty_driver * driver,struct file * file,int idx)70596fd7ce5SGreg Kroah-Hartman static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
7068ead9dd5SLinus Torvalds 		struct file *file, int idx)
70796fd7ce5SGreg Kroah-Hartman {
708d739e65bSAlan Cox 	struct tty_struct *tty;
709d739e65bSAlan Cox 
710d739e65bSAlan Cox 	mutex_lock(&devpts_mutex);
7118ead9dd5SLinus Torvalds 	tty = devpts_get_priv(file->f_path.dentry);
712d739e65bSAlan Cox 	mutex_unlock(&devpts_mutex);
71396fd7ce5SGreg Kroah-Hartman 	/* Master must be open before slave */
71496fd7ce5SGreg Kroah-Hartman 	if (!tty)
71596fd7ce5SGreg Kroah-Hartman 		return ERR_PTR(-EIO);
71696fd7ce5SGreg Kroah-Hartman 	return tty;
71796fd7ce5SGreg Kroah-Hartman }
71896fd7ce5SGreg Kroah-Hartman 
pty_unix98_install(struct tty_driver * driver,struct tty_struct * tty)71996fd7ce5SGreg Kroah-Hartman static int pty_unix98_install(struct tty_driver *driver, struct tty_struct *tty)
72096fd7ce5SGreg Kroah-Hartman {
7215d249bc6SJiri Slaby 	return pty_common_install(driver, tty, false);
72296fd7ce5SGreg Kroah-Hartman }
72396fd7ce5SGreg Kroah-Hartman 
724f4b208ebSJiri Slaby /* this is called once with whichever end is closed last */
pty_unix98_remove(struct tty_driver * driver,struct tty_struct * tty)725c1e33af1SPeter Hurley static void pty_unix98_remove(struct tty_driver *driver, struct tty_struct *tty)
726f4b208ebSJiri Slaby {
72767245ff3SLinus Torvalds 	struct pts_fs_info *fsi;
7282831c89fSHerton R. Krzesinski 
7292831c89fSHerton R. Krzesinski 	if (tty->driver->subtype == PTY_TYPE_MASTER)
73067245ff3SLinus Torvalds 		fsi = tty->driver_data;
7312831c89fSHerton R. Krzesinski 	else
73267245ff3SLinus Torvalds 		fsi = tty->link->driver_data;
7335353ed8dSColin Ian King 
7345353ed8dSColin Ian King 	if (fsi) {
73567245ff3SLinus Torvalds 		devpts_kill_index(fsi, tty->index);
736eedf265aSEric W. Biederman 		devpts_release(fsi);
737f4b208ebSJiri Slaby 	}
7385353ed8dSColin Ian King }
739f4b208ebSJiri Slaby 
pty_show_fdinfo(struct tty_struct * tty,struct seq_file * m)740d01c3289SMasatake YAMATO static void pty_show_fdinfo(struct tty_struct *tty, struct seq_file *m)
741d01c3289SMasatake YAMATO {
742d01c3289SMasatake YAMATO 	seq_printf(m, "tty-index:\t%d\n", tty->index);
743d01c3289SMasatake YAMATO }
744d01c3289SMasatake YAMATO 
74596fd7ce5SGreg Kroah-Hartman static const struct tty_operations ptm_unix98_ops = {
74696fd7ce5SGreg Kroah-Hartman 	.lookup = ptm_unix98_lookup,
74796fd7ce5SGreg Kroah-Hartman 	.install = pty_unix98_install,
7487171604aSJiri Slaby 	.remove = pty_unix98_remove,
74996fd7ce5SGreg Kroah-Hartman 	.open = pty_open,
75096fd7ce5SGreg Kroah-Hartman 	.close = pty_close,
75196fd7ce5SGreg Kroah-Hartman 	.write = pty_write,
75296fd7ce5SGreg Kroah-Hartman 	.write_room = pty_write_room,
75396fd7ce5SGreg Kroah-Hartman 	.flush_buffer = pty_flush_buffer,
75496fd7ce5SGreg Kroah-Hartman 	.unthrottle = pty_unthrottle,
75596fd7ce5SGreg Kroah-Hartman 	.ioctl = pty_unix98_ioctl,
7565f0f187fSAleksa Sarai 	.compat_ioctl = pty_unix98_compat_ioctl,
75736b3c070SAlan Cox 	.resize = pty_resize,
758d01c3289SMasatake YAMATO 	.cleanup = pty_cleanup,
759d01c3289SMasatake YAMATO 	.show_fdinfo = pty_show_fdinfo,
76096fd7ce5SGreg Kroah-Hartman };
76196fd7ce5SGreg Kroah-Hartman 
76296fd7ce5SGreg Kroah-Hartman static const struct tty_operations pty_unix98_ops = {
76396fd7ce5SGreg Kroah-Hartman 	.lookup = pts_unix98_lookup,
76496fd7ce5SGreg Kroah-Hartman 	.install = pty_unix98_install,
7657171604aSJiri Slaby 	.remove = pty_unix98_remove,
76696fd7ce5SGreg Kroah-Hartman 	.open = pty_open,
76796fd7ce5SGreg Kroah-Hartman 	.close = pty_close,
76896fd7ce5SGreg Kroah-Hartman 	.write = pty_write,
76996fd7ce5SGreg Kroah-Hartman 	.write_room = pty_write_room,
77096fd7ce5SGreg Kroah-Hartman 	.flush_buffer = pty_flush_buffer,
77196fd7ce5SGreg Kroah-Hartman 	.unthrottle = pty_unthrottle,
77296fd7ce5SGreg Kroah-Hartman 	.set_termios = pty_set_termios,
77301adc807SPeter Hurley 	.start = pty_start,
77401adc807SPeter Hurley 	.stop = pty_stop,
775d03702a2SJiri Slaby 	.cleanup = pty_cleanup,
77696fd7ce5SGreg Kroah-Hartman };
77796fd7ce5SGreg Kroah-Hartman 
77896fd7ce5SGreg Kroah-Hartman /**
77996fd7ce5SGreg Kroah-Hartman  *	ptmx_open		-	open a unix 98 pty master
78096fd7ce5SGreg Kroah-Hartman  *	@inode: inode of device file
78196fd7ce5SGreg Kroah-Hartman  *	@filp: file pointer to tty
78296fd7ce5SGreg Kroah-Hartman  *
78396fd7ce5SGreg Kroah-Hartman  *	Allocate a unix98 pty master device from the ptmx driver.
78496fd7ce5SGreg Kroah-Hartman  *
78596fd7ce5SGreg Kroah-Hartman  *	Locking: tty_mutex protects the init_dev work. tty->count should
78696fd7ce5SGreg Kroah-Hartman  *		protect the rest.
78796fd7ce5SGreg Kroah-Hartman  *		allocated_ptys_lock handles the list of free pty numbers
78896fd7ce5SGreg Kroah-Hartman  */
78996fd7ce5SGreg Kroah-Hartman 
ptmx_open(struct inode * inode,struct file * filp)79096fd7ce5SGreg Kroah-Hartman static int ptmx_open(struct inode *inode, struct file *filp)
79196fd7ce5SGreg Kroah-Hartman {
79267245ff3SLinus Torvalds 	struct pts_fs_info *fsi;
79396fd7ce5SGreg Kroah-Hartman 	struct tty_struct *tty;
7948ead9dd5SLinus Torvalds 	struct dentry *dentry;
79596fd7ce5SGreg Kroah-Hartman 	int retval;
79696fd7ce5SGreg Kroah-Hartman 	int index;
79796fd7ce5SGreg Kroah-Hartman 
79896fd7ce5SGreg Kroah-Hartman 	nonseekable_open(inode, filp);
79996fd7ce5SGreg Kroah-Hartman 
800b0b88565SLinus Torvalds 	/* We refuse fsnotify events on ptmx, since it's a shared resource */
801b0b88565SLinus Torvalds 	filp->f_mode |= FMODE_NONOTIFY;
802b0b88565SLinus Torvalds 
803fa90e1c9SJiri Slaby 	retval = tty_alloc_file(filp);
804fa90e1c9SJiri Slaby 	if (retval)
805fa90e1c9SJiri Slaby 		return retval;
806fa90e1c9SJiri Slaby 
807143c97ccSLinus Torvalds 	fsi = devpts_acquire(filp);
808eedf265aSEric W. Biederman 	if (IS_ERR(fsi)) {
809eedf265aSEric W. Biederman 		retval = PTR_ERR(fsi);
81067245ff3SLinus Torvalds 		goto out_free_file;
811eedf265aSEric W. Biederman 	}
81267245ff3SLinus Torvalds 
81396fd7ce5SGreg Kroah-Hartman 	/* find a device that is not in use. */
81489c8d91eSAlan Cox 	mutex_lock(&devpts_mutex);
81567245ff3SLinus Torvalds 	index = devpts_new_index(fsi);
81605fb79e4SAlan Cox 	mutex_unlock(&devpts_mutex);
81796fd7ce5SGreg Kroah-Hartman 
81867245ff3SLinus Torvalds 	retval = index;
81967245ff3SLinus Torvalds 	if (index < 0)
820eedf265aSEric W. Biederman 		goto out_put_fsi;
82167245ff3SLinus Torvalds 
82289c8d91eSAlan Cox 
82389c8d91eSAlan Cox 	mutex_lock(&tty_mutex);
82489c8d91eSAlan Cox 	tty = tty_init_dev(ptm_driver, index);
82589c8d91eSAlan Cox 	/* The tty returned here is locked so we can safely
82689c8d91eSAlan Cox 	   drop the mutex */
82789c8d91eSAlan Cox 	mutex_unlock(&tty_mutex);
82889c8d91eSAlan Cox 
82967245ff3SLinus Torvalds 	retval = PTR_ERR(tty);
83067245ff3SLinus Torvalds 	if (IS_ERR(tty))
83167245ff3SLinus Torvalds 		goto out;
83296fd7ce5SGreg Kroah-Hartman 
8332831c89fSHerton R. Krzesinski 	/*
83467245ff3SLinus Torvalds 	 * From here on out, the tty is "live", and the index and
83567245ff3SLinus Torvalds 	 * fsi will be killed/put by the tty_release()
8362831c89fSHerton R. Krzesinski 	 */
83767245ff3SLinus Torvalds 	set_bit(TTY_PTY_LOCK, &tty->flags); /* LOCK THE SLAVE */
83867245ff3SLinus Torvalds 	tty->driver_data = fsi;
8392831c89fSHerton R. Krzesinski 
840fa90e1c9SJiri Slaby 	tty_add_file(tty, filp);
84196fd7ce5SGreg Kroah-Hartman 
8428ead9dd5SLinus Torvalds 	dentry = devpts_pty_new(fsi, index, tty->link);
8438ead9dd5SLinus Torvalds 	if (IS_ERR(dentry)) {
8448ead9dd5SLinus Torvalds 		retval = PTR_ERR(dentry);
8451177c0efSJiri Slaby 		goto err_release;
846162b97cfSJiri Slaby 	}
847311fc65cSEric W. Biederman 	tty->link->driver_data = dentry;
84896fd7ce5SGreg Kroah-Hartman 
84996fd7ce5SGreg Kroah-Hartman 	retval = ptm_driver->ops->open(tty, filp);
85096fd7ce5SGreg Kroah-Hartman 	if (retval)
851311fc65cSEric W. Biederman 		goto err_release;
8521177c0efSJiri Slaby 
853d435cefeSPeter Hurley 	tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
854d6847793SPeter Hurley 
85589c8d91eSAlan Cox 	tty_unlock(tty);
8561177c0efSJiri Slaby 	return 0;
8571177c0efSJiri Slaby err_release:
85889c8d91eSAlan Cox 	tty_unlock(tty);
85967245ff3SLinus Torvalds 	// This will also put-ref the fsi
86096fd7ce5SGreg Kroah-Hartman 	tty_release(inode, filp);
86196fd7ce5SGreg Kroah-Hartman 	return retval;
86296fd7ce5SGreg Kroah-Hartman out:
86367245ff3SLinus Torvalds 	devpts_kill_index(fsi, index);
864eedf265aSEric W. Biederman out_put_fsi:
865eedf265aSEric W. Biederman 	devpts_release(fsi);
86667245ff3SLinus Torvalds out_free_file:
867fa90e1c9SJiri Slaby 	tty_free_file(filp);
86896fd7ce5SGreg Kroah-Hartman 	return retval;
86996fd7ce5SGreg Kroah-Hartman }
87096fd7ce5SGreg Kroah-Hartman 
871d2ec3f77SKees Cook static struct file_operations ptmx_fops __ro_after_init;
87296fd7ce5SGreg Kroah-Hartman 
unix98_pty_init(void)87396fd7ce5SGreg Kroah-Hartman static void __init unix98_pty_init(void)
87496fd7ce5SGreg Kroah-Hartman {
87521aca2faSJiri Slaby 	ptm_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
87621aca2faSJiri Slaby 			TTY_DRIVER_RESET_TERMIOS |
87721aca2faSJiri Slaby 			TTY_DRIVER_REAL_RAW |
87821aca2faSJiri Slaby 			TTY_DRIVER_DYNAMIC_DEV |
87921aca2faSJiri Slaby 			TTY_DRIVER_DEVPTS_MEM |
88021aca2faSJiri Slaby 			TTY_DRIVER_DYNAMIC_ALLOC);
881c3a6344aSDan Carpenter 	if (IS_ERR(ptm_driver))
88296fd7ce5SGreg Kroah-Hartman 		panic("Couldn't allocate Unix98 ptm driver");
88321aca2faSJiri Slaby 	pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX,
88421aca2faSJiri Slaby 			TTY_DRIVER_RESET_TERMIOS |
88521aca2faSJiri Slaby 			TTY_DRIVER_REAL_RAW |
88621aca2faSJiri Slaby 			TTY_DRIVER_DYNAMIC_DEV |
88721aca2faSJiri Slaby 			TTY_DRIVER_DEVPTS_MEM |
88821aca2faSJiri Slaby 			TTY_DRIVER_DYNAMIC_ALLOC);
889c3a6344aSDan Carpenter 	if (IS_ERR(pts_driver))
89096fd7ce5SGreg Kroah-Hartman 		panic("Couldn't allocate Unix98 pts driver");
89196fd7ce5SGreg Kroah-Hartman 
89296fd7ce5SGreg Kroah-Hartman 	ptm_driver->driver_name = "pty_master";
89396fd7ce5SGreg Kroah-Hartman 	ptm_driver->name = "ptm";
89496fd7ce5SGreg Kroah-Hartman 	ptm_driver->major = UNIX98_PTY_MASTER_MAJOR;
89596fd7ce5SGreg Kroah-Hartman 	ptm_driver->minor_start = 0;
89696fd7ce5SGreg Kroah-Hartman 	ptm_driver->type = TTY_DRIVER_TYPE_PTY;
89796fd7ce5SGreg Kroah-Hartman 	ptm_driver->subtype = PTY_TYPE_MASTER;
89896fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios = tty_std_termios;
89996fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios.c_iflag = 0;
90096fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios.c_oflag = 0;
90196fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
90296fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios.c_lflag = 0;
90396fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios.c_ispeed = 38400;
90496fd7ce5SGreg Kroah-Hartman 	ptm_driver->init_termios.c_ospeed = 38400;
90596fd7ce5SGreg Kroah-Hartman 	ptm_driver->other = pts_driver;
90696fd7ce5SGreg Kroah-Hartman 	tty_set_operations(ptm_driver, &ptm_unix98_ops);
90796fd7ce5SGreg Kroah-Hartman 
90896fd7ce5SGreg Kroah-Hartman 	pts_driver->driver_name = "pty_slave";
90996fd7ce5SGreg Kroah-Hartman 	pts_driver->name = "pts";
91096fd7ce5SGreg Kroah-Hartman 	pts_driver->major = UNIX98_PTY_SLAVE_MAJOR;
91196fd7ce5SGreg Kroah-Hartman 	pts_driver->minor_start = 0;
91296fd7ce5SGreg Kroah-Hartman 	pts_driver->type = TTY_DRIVER_TYPE_PTY;
91396fd7ce5SGreg Kroah-Hartman 	pts_driver->subtype = PTY_TYPE_SLAVE;
91496fd7ce5SGreg Kroah-Hartman 	pts_driver->init_termios = tty_std_termios;
91596fd7ce5SGreg Kroah-Hartman 	pts_driver->init_termios.c_cflag = B38400 | CS8 | CREAD;
91696fd7ce5SGreg Kroah-Hartman 	pts_driver->init_termios.c_ispeed = 38400;
91796fd7ce5SGreg Kroah-Hartman 	pts_driver->init_termios.c_ospeed = 38400;
91896fd7ce5SGreg Kroah-Hartman 	pts_driver->other = ptm_driver;
91996fd7ce5SGreg Kroah-Hartman 	tty_set_operations(pts_driver, &pty_unix98_ops);
92096fd7ce5SGreg Kroah-Hartman 
92196fd7ce5SGreg Kroah-Hartman 	if (tty_register_driver(ptm_driver))
92296fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register Unix98 ptm driver");
92396fd7ce5SGreg Kroah-Hartman 	if (tty_register_driver(pts_driver))
92496fd7ce5SGreg Kroah-Hartman 		panic("Couldn't register Unix98 pts driver");
92596fd7ce5SGreg Kroah-Hartman 
92696fd7ce5SGreg Kroah-Hartman 	/* Now create the /dev/ptmx special device */
92796fd7ce5SGreg Kroah-Hartman 	tty_default_fops(&ptmx_fops);
92896fd7ce5SGreg Kroah-Hartman 	ptmx_fops.open = ptmx_open;
92996fd7ce5SGreg Kroah-Hartman 
93096fd7ce5SGreg Kroah-Hartman 	cdev_init(&ptmx_cdev, &ptmx_fops);
93196fd7ce5SGreg Kroah-Hartman 	if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
93296fd7ce5SGreg Kroah-Hartman 	    register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
93382f8c35fSCong Ding 		panic("Couldn't register /dev/ptmx driver");
934862d8312SGreg Kroah-Hartman 	device_create(&tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
93596fd7ce5SGreg Kroah-Hartman }
93696fd7ce5SGreg Kroah-Hartman 
93796fd7ce5SGreg Kroah-Hartman #else
unix98_pty_init(void)93896fd7ce5SGreg Kroah-Hartman static inline void unix98_pty_init(void) { }
93996fd7ce5SGreg Kroah-Hartman #endif
94096fd7ce5SGreg Kroah-Hartman 
pty_init(void)94196fd7ce5SGreg Kroah-Hartman static int __init pty_init(void)
94296fd7ce5SGreg Kroah-Hartman {
94396fd7ce5SGreg Kroah-Hartman 	legacy_pty_init();
94496fd7ce5SGreg Kroah-Hartman 	unix98_pty_init();
94596fd7ce5SGreg Kroah-Hartman 	return 0;
94696fd7ce5SGreg Kroah-Hartman }
9477154988fSPaul Gortmaker device_initcall(pty_init);
948