blob: 482afe6b2132f1064f07631fbdf42ae1b0008957 [file] [log] [blame]
/*
* 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 <linux/string.h>
#include <asm/system.h>
#include <asm/bitops.h>
#define MIN(a,b) ((a) < (b) ? (a) : (b))
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)
printk("master pty_close: count = %d!!\n", tty->count);
} 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);
else {
start_tty(tty);
set_bit(TTY_SLAVE_CLOSED, &tty->link->flags);
}
}
static inline void pty_copy(struct tty_struct * from, struct tty_struct * to)
{
unsigned long count, n;
struct tty_queue *fq, *tq;
int skip_readq;
if (from->stopped || EMPTY(&from->write_q))
return;
fq = &from->write_q;
/* Bypass the read_q if this is a pty master. */
skip_readq = IS_A_PTY_MASTER(to->line) && to->disc == N_TTY;
tq = skip_readq ? &to->secondary : &to->read_q;
count = MIN(CHARS(fq), LEFT(tq));
while (count) {
n = MIN(MIN(TTY_BUF_SIZE - fq->tail, TTY_BUF_SIZE - tq->head),
count);
memcpy(&tq->buf[tq->head], &fq->buf[fq->tail], n);
count -= n;
fq->tail = (fq->tail + n) & (TTY_BUF_SIZE - 1);
tq->head = (tq->head + n) & (TTY_BUF_SIZE - 1);
}
if (skip_readq)
wake_up_interruptible(&to->secondary.proc_list);
else
TTY_READ_FLUSH(to);
if (LEFT(fq) > WAKEUP_CHARS)
wake_up_interruptible(&fq->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_SLAVE(tty->line))
clear_bit(TTY_SLAVE_CLOSED, &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;
}