⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tty_io.c

📁 Linux 1.0 内核C源代码 Linux最早版本代码 由Linus Torvalds亲自书写的
💻 C
📖 第 1 页 / 共 4 页
字号:
		}
	}
	tty->write_data_cnt = 0; /* Clear out pending trash */
	if (tty->close)
		tty->close(tty, filp);
	if (IS_A_PTY_MASTER(dev)) {
		if (--tty->link->count < 0) {
			printk("release_dev: bad tty slave count (dev = %d): %d\n",
			       dev, tty->count);
			tty->link->count = 0;
		}
	}
	if (--tty->count < 0) {
		printk("release_dev: bad tty_table[%d]->count: %d\n",
		       dev, tty->count);
		tty->count = 0;
	}
	if (tty->count)
		return;
	
#ifdef TTY_DEBUG_HANGUP
	printk("freeing tty structure...");
#endif

	/*
	 * Make sure there aren't any processes that still think this
	 * tty is their controlling tty.
	 */
	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
		if ((*p) && (*p)->tty == tty->line)
		(*p)->tty = -1;
	}

	/*
	 * Shutdown the current line discipline, and reset it to
	 * N_TTY.
	 */
	if (ldiscs[tty->disc].close != NULL)
		ldiscs[tty->disc].close(tty);
	tty->disc = N_TTY;
	tty->termios->c_line = N_TTY;
	
	if (o_tty) {
		if (o_tty->count)
			return;
		else {
			tty_table[PTY_OTHER(dev)] = NULL;
			tty_termios[PTY_OTHER(dev)] = NULL;
		}
	}
	tty_table[dev] = NULL;
	if (IS_A_PTY(dev)) {
		tty_termios[dev] = NULL;
		kfree_s(tp, sizeof(struct termios));
	}
	if (tty == redirect || o_tty == redirect)
		redirect = NULL;
	free_page((unsigned long) tty);
	if (o_tty)
		free_page((unsigned long) o_tty);
	if (o_tp)
		kfree_s(o_tp, sizeof(struct termios));
}

/*
 * tty_open and tty_release keep up the tty count that contains the
 * number of opens done on a tty. We cannot use the inode-count, as
 * different inodes might point to the same tty.
 *
 * Open-counting is needed for pty masters, as well as for keeping
 * track of serial lines: DTR is dropped when the last close happens.
 * (This is not done solely through tty->count, now.  - Ted 1/27/92)
 *
 * The termios state of a pty is reset on first open so that
 * settings don't persist across reuse.
 */
static int tty_open(struct inode * inode, struct file * filp)
{
	struct tty_struct *tty;
	int major, minor;
	int noctty, retval;

retry_open:
	minor = MINOR(inode->i_rdev);
	major = MAJOR(inode->i_rdev);
	noctty = filp->f_flags & O_NOCTTY;
	if (major == TTYAUX_MAJOR) {
		if (!minor) {
			major = TTY_MAJOR;
			minor = current->tty;
		}
		/* noctty = 1; */
	} else if (major == TTY_MAJOR) {
		if (!minor) {
			minor = fg_console + 1;
			noctty = 1;
		}
	} else {
		printk("Bad major #%d in tty_open\n", MAJOR(inode->i_rdev));
		return -ENODEV;
	}
	if (minor <= 0)
		return -ENXIO;
	if (IS_A_PTY_MASTER(minor))
		noctty = 1;
	filp->f_rdev = (major << 8) | minor;
	retval = init_dev(minor);
	if (retval)
		return retval;
	tty = tty_table[minor];
#ifdef TTY_DEBUG_HANGUP
	printk("opening tty%d...", tty->line);
#endif
	if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
		return -EBUSY;

#if 0
	/* clean up the packet stuff. */
	/*
	 *  Why is this not done in init_dev?  Right here, if another 
	 * process opens up a tty in packet mode, all the packet 
	 * variables get cleared.  Come to think of it, is anything 
	 * using the packet mode at all???  - Ted, 1/27/93
	 *
	 * Not to worry, a pty master can only be opened once.
	 * And rlogind and telnetd both use packet mode.  -- jrs
	 *
	 * Not needed.  These are cleared in initialize_tty_struct. -- jlc
	 */
	tty->ctrl_status = 0;
	tty->packet = 0;
#endif

	if (tty->open) {
		retval = tty->open(tty, filp);
	} else {
		retval = -ENODEV;
	}
	if (retval) {
#ifdef TTY_DEBUG_HANGUP
		printk("error %d in opening tty%d...", retval, tty->line);
#endif

		release_dev(minor, filp);
		if (retval != -ERESTARTSYS)
			return retval;
		if (current->signal & ~current->blocked)
			return retval;
		schedule();
		goto retry_open;
	}
	if (!noctty &&
	    current->leader &&
	    current->tty<0 &&
	    tty->session==0) {
		current->tty = minor;
		tty->session = current->session;
		tty->pgrp = current->pgrp;
	}
	filp->f_rdev = MKDEV(TTY_MAJOR,minor); /* Set it to something normal */
	return 0;
}

/*
 * Note that releasing a pty master also releases the child, so
 * we have to make the redirection checks after that and on both
 * sides of a pty.
 */
static void tty_release(struct inode * inode, struct file * filp)
{
	int dev;

	dev = filp->f_rdev;
	if (MAJOR(dev) != TTY_MAJOR) {
		printk("tty_release: tty pseudo-major != TTY_MAJOR\n");
		return;
	}
	dev = MINOR(filp->f_rdev);
	if (!dev) {
		printk("tty_release: bad f_rdev\n");
		return;
	}
	release_dev(dev, filp);
}

static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
{
	int dev;
	struct tty_struct * tty;

	dev = filp->f_rdev;
	if (MAJOR(dev) != TTY_MAJOR) {
		printk("tty_select: tty pseudo-major != TTY_MAJOR\n");
		return 0;
	}
	dev = MINOR(filp->f_rdev);
	tty = TTY_TABLE(dev);
	if (!tty) {
		printk("tty_select: tty struct for dev %d was NULL\n", dev);
		return 0;
	}
	if (ldiscs[tty->disc].select)
		return (ldiscs[tty->disc].select)(tty, inode, filp,
						  sel_type, wait);
	return 0;
}

static int normal_select(struct tty_struct * tty, struct inode * inode,
			 struct file * file, int sel_type, select_table *wait)
{
	switch (sel_type) {
		case SEL_IN:
			if (input_available_p(tty))
				return 1;
			/* fall through */
		case SEL_EX:
			if (tty->packet && tty->link->ctrl_status)
				return 1;
			if (tty->flags & (1 << TTY_SLAVE_CLOSED))
				return 1;
			if (tty_hung_up_p(file))
				return 1;
			select_wait(&tty->secondary.proc_list, wait);
			return 0;
		case SEL_OUT:
			if (LEFT(&tty->write_q) > WAKEUP_CHARS)
				return 1;
			select_wait(&tty->write_q.proc_list, wait);
			return 0;
	}
	return 0;
}

/*
 * This implements the "Secure Attention Key" ---  the idea is to
 * prevent trojan horses by killing all processes associated with this
 * tty when the user hits the "Secure Attention Key".  Required for
 * super-paranoid applications --- see the Orange Book for more details.
 * 
 * This code could be nicer; ideally it should send a HUP, wait a few
 * seconds, then send a INT, and then a KILL signal.  But you then
 * have to coordinate with the init process, since all processes associated
 * with the current tty must be dead before the new getty is allowed
 * to spawn.
 */
void do_SAK( struct tty_struct *tty)
{
#ifdef TTY_SOFT_SAK
	tty_hangup(tty);
#else
	struct task_struct **p;
	int line = tty->line;
	int session = tty->session;
	int		i;
	struct file	*filp;
	
	flush_input(tty);
	flush_output(tty);
 	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
		if (!(*p))
			continue;
		if (((*p)->tty == line) ||
		    ((session > 0) && ((*p)->session == session)))
			send_sig(SIGKILL, *p, 1);
		else {
			for (i=0; i < NR_OPEN; i++) {
				filp = (*p)->filp[i];
				if (filp && (filp->f_op == &tty_fops) &&
				    (MINOR(filp->f_rdev) == line)) {
					send_sig(SIGKILL, *p, 1);
					break;
				}
			}
		}
	}
#endif
}

/*
 * This routine allows a kernel routine to send a large chunk of data
 * to a particular tty; if all of the data can be queued up for ouput
 * immediately, tty_write_data() will return 0.  If, however, not all
 * of the data can be immediately queued for delivery, the number of
 * bytes left to be queued up will be returned, and the rest of the
 * data will be queued up when there is room.  The callback function
 * will be called (with the argument callarg) when the last of the
 * data is finally in the queue.
 *
 * Note that the callback routine will _not_ be called if all of the
 * data could be queued immediately.  This is to avoid a problem with
 * the kernel stack getting too deep, which might happen if the
 * callback routine calls tty_write_data with itself as an argument.
 */
int tty_write_data(struct tty_struct *tty, char *bufp, int buflen,
		    void (*callback)(void * data), void * callarg)
{
	int head, tail, count;
	unsigned long flags;
	char *p;

#define VLEFT ((tail-head-1)&(TTY_BUF_SIZE-1))

	save_flags(flags);
	cli();
	if (tty->write_data_cnt) {
		restore_flags(flags);
		return -EBUSY;
	}

	head = tty->write_q.head;
	tail = tty->write_q.tail;
	count = buflen;
	p = bufp;

	while (count && VLEFT > 0) {
		tty->write_q.buf[head++] = *p++;
		head &= TTY_BUF_SIZE-1;
		count--;
	}
	tty->write_q.head = head;
	if (count) {
		tty->write_data_cnt = count;
		tty->write_data_ptr = (unsigned char *) p;
		tty->write_data_callback = callback;
		tty->write_data_arg = callarg;
	}
	restore_flags(flags);
	tty->write(tty);
	return count;
}

/*
 * This routine routine is called after an interrupt has drained a
 * tty's write queue, so that there is more space for data waiting to
 * be sent in tty->write_data_ptr.
 *
 * tty_check_write[8] is a bitstring which indicates which ttys
 * needs to be processed.
 */
void tty_bh_routine(void * unused)
{
	int	i, j, line, mask;
	int	head, tail, count;
	unsigned char * p;
	struct tty_struct * tty;

	for (i = 0, line = 0; i < MAX_TTYS / 32; i++) {
		if (!tty_check_write[i]) {
			line += 32;
			continue;
		}
		for (j=0, mask=0; j < 32; j++, line++, mask <<= 1) {
			if (clear_bit(j, &tty_check_write[i])) {
				tty = tty_table[line];
				if (!tty || !tty->write_data_cnt)
					continue;
				cli();
				head = tty->write_q.head;
				tail = tty->write_q.tail;
				count = tty->write_data_cnt;
				p = tty->write_data_ptr;

				while (count && VLEFT > 0) {
					tty->write_q.buf[head++] = *p++;
					head &= TTY_BUF_SIZE-1;
					count--;
				}
				tty->write_q.head = head;
				tty->write_data_ptr = p;
				tty->write_data_cnt = count;
				sti();
				if (!count)
					(tty->write_data_callback)
						(tty->write_data_arg);
			}
		}
	}
	
}

/*
 * This subroutine initializes a tty structure.  We have to set up
 * things correctly for each different type of tty.
 */
static void initialize_tty_struct(int line, struct tty_struct *tty)
{
	memset(tty, 0, sizeof(struct tty_struct));
	tty->line = line;
	tty->disc = N_TTY;
	tty->pgrp = -1;
	if (IS_A_CONSOLE(line)) {
		tty->open = con_open;
		tty->winsize.ws_row = video_num_lines;
		tty->winsize.ws_col = video_num_columns;
	} else if IS_A_SERIAL(line) {
		tty->open = rs_open;
	} else if IS_A_PTY(line) {
		tty->open = pty_open;
	}
}

static void initialize_termios(int line, struct termios * tp)
{
	memset(tp, 0, sizeof(struct termios));
	memcpy(tp->c_cc, INIT_C_CC, NCCS);
	if (IS_A_CONSOLE(line) || IS_A_PTY_SLAVE(line)) {
		tp->c_iflag = ICRNL | IXON;
		tp->c_oflag = OPOST | ONLCR;
		tp->c_cflag = B38400 | CS8 | CREAD;
		tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
			ECHOCTL | ECHOKE | IEXTEN;
	} else if (IS_A_SERIAL(line)) {
		tp->c_iflag = ICRNL | IXON;
		tp->c_oflag = OPOST | ONLCR | XTABS;
		tp->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
		tp->c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
			ECHOCTL | ECHOKE | IEXTEN;
	} else if (IS_A_PTY_MASTER(line))
		tp->c_cflag = B9600 | CS8 | CREAD;
}

static struct tty_ldisc tty_ldisc_N_TTY = {
	0,			/* flags */
	NULL,			/* open */
	NULL,			/* close */
	read_chan,		/* read */
	write_chan,		/* write */
	NULL,			/* ioctl */
	normal_select,		/* select */
	copy_to_cooked		/* handler */
};

	
long tty_init(long kmem_start)
{
	int i;

	if (sizeof(struct tty_struct) > PAGE_SIZE)
		panic("size of tty structure > PAGE_SIZE!");
	if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
		panic("unable to get major %d for tty device", TTY_MAJOR);
	if (register_chrdev(TTYAUX_MAJOR,"tty",&tty_fops))
		panic("unable to get major %d for tty device", TTYAUX_MAJOR);
	for (i=0 ; i< MAX_TTYS ; i++) {
		tty_table[i] =  0;
		tty_termios[i] = 0;
	}
	memset(tty_check_write, 0, sizeof(tty_check_write));
	bh_base[TTY_BH].routine = tty_bh_routine;

	/* Setup the default TTY line discipline. */
	memset(ldiscs, 0, sizeof(ldiscs));
	(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);

	kmem_start = kbd_init(kmem_start);
	kmem_start = con_init(kmem_start);
	kmem_start = rs_init(kmem_start);
	return kmem_start;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -