/*
 *  linux/kernel/chr_drv/pty.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/*
 *	pty.c
 *
 * This module exports the following pty function:
 * 
 * 	int  pty_open(struct tty_struct * tty, struct file * filp);
 */

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>

#include <asm/system.h>
#include <asm/bitops.h>

static void pty_close(struct tty_struct * tty, struct file * filp)
{
	if (!tty)
		return;
	if (IS_A_PTY_MASTER(tty->line)) {
		if (tty->count > 1)
			return;
	} else {
		if (tty->count > 2)
			return;
	}
	wake_up_interruptible(&tty->secondary.proc_list);
	wake_up_interruptible(&tty->read_q.proc_list);
	wake_up_interruptible(&tty->write_q.proc_list);
	if (!tty->link)
		return;
	wake_up_interruptible(&tty->link->secondary.proc_list);
	wake_up_interruptible(&tty->link->read_q.proc_list);
	wake_up_interruptible(&tty->link->write_q.proc_list);
	if (IS_A_PTY_MASTER(tty->line)) {
		tty_hangup(tty->link);
		flush_input(tty);
		flush_output(tty);
	}
}

static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
{
	int c;

	while (!from->stopped && !EMPTY(&from->write_q)) {
		if (FULL(&to->read_q)) {
			TTY_READ_FLUSH(to);
			if (FULL(&to->read_q))
				break;
			continue;
		}
		c = get_tty_queue(&from->write_q);
		put_tty_queue(c, &to->read_q);
		if (current->signal & ~current->blocked)
			break;
	}
	TTY_READ_FLUSH(to);
	if (!FULL(&from->write_q))
		wake_up_interruptible(&from->write_q.proc_list);
	if (from->write_data_cnt) {
		set_bit(from->line, &tty_check_write);
		mark_bh(TTY_BH);
	}
}

/*
 * This routine gets called when tty_write has put something into
 * the write_queue. It copies the input to the output-queue of its
 * slave.
 */
static void pty_write(struct tty_struct * tty)
{
	if (tty->link)
		pty_copy(tty,tty->link);
}

int pty_open(struct tty_struct *tty, struct file * filp)
{
	if (!tty || !tty->link)
		return -ENODEV;
	if (IS_A_PTY_MASTER(tty->line))
		clear_bit(TTY_SLAVE_OPENED, &tty->flags);
	else
		set_bit(TTY_SLAVE_OPENED, &tty->link->flags);
	tty->write = tty->link->write = pty_write;
	tty->close = tty->link->close = pty_close;
	wake_up_interruptible(&tty->read_q.proc_list);
	if (filp->f_flags & O_NDELAY)
		return 0;
	while (!tty->link->count && !(current->signal & ~current->blocked))
		interruptible_sleep_on(&tty->link->read_q.proc_list);
	if (!tty->link->count)
		return -ERESTARTSYS;
	return 0;
}
