| /* |
| * 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; |
| } |