tty_io.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,610 行 · 第 1/5 页

C
2,610
字号
	 	work = cancel_delayed_work(&tty->flip.work);	/*	 * Wait for ->hangup_work and ->flip.work handlers to terminate	 */	 	flush_scheduled_work();	/* Shutdown the current discipline. */	if (tty->ldisc.close)		(tty->ldisc.close)(tty);	/* Now set up the new line discipline. */	tty_ldisc_assign(tty, ld);	tty_set_termios_ldisc(tty, ldisc);	if (tty->ldisc.open)		retval = (tty->ldisc.open)(tty);	if (retval < 0) {		tty_ldisc_put(ldisc);		/* There is an outstanding reference here so this is safe */		tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));		tty_set_termios_ldisc(tty, tty->ldisc.num);		if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {			tty_ldisc_put(o_ldisc.num);			/* This driver is always present */			tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));			tty_set_termios_ldisc(tty, N_TTY);			if (tty->ldisc.open) {				int r = tty->ldisc.open(tty);				if (r < 0)					panic("Couldn't open N_TTY ldisc for "					      "%s --- error %d.",					      tty_name(tty, buf), r);			}		}	}	/* At this point we hold a reference to the new ldisc and a	   a reference to the old ldisc. If we ended up flipping back	   to the existing ldisc we have two references to it */		if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)		tty->driver->set_ldisc(tty);			tty_ldisc_put(o_ldisc.num);		/*	 *	Allow ldisc referencing to occur as soon as the driver	 *	ldisc callback completes.	 */	 	tty_ldisc_enable(tty);		/* Restart it in case no characters kick it off. Safe if	   already running */	if(work)		schedule_delayed_work(&tty->flip.work, 1);	return retval;}/* * This routine returns a tty driver structure, given a device number */struct tty_driver *get_tty_driver(dev_t device, int *index){	struct tty_driver *p;	list_for_each_entry(p, &tty_drivers, tty_drivers) {		dev_t base = MKDEV(p->major, p->minor_start);		if (device < base || device >= base + p->num)			continue;		*index = device - base;		return p;	}	return NULL;}/* * If we try to write to, or set the state of, a terminal and we're * not in the foreground, send a SIGTTOU.  If the signal is blocked or * ignored, go ahead and perform the operation.  (POSIX 7.2) */int tty_check_change(struct tty_struct * tty){	if (current->signal->tty != tty)		return 0;	if (tty->pgrp <= 0) {		printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");		return 0;	}	if (process_group(current) == tty->pgrp)		return 0;	if (is_ignored(SIGTTOU))		return 0;	if (is_orphaned_pgrp(process_group(current)))		return -EIO;	(void) kill_pg(process_group(current), SIGTTOU, 1);	return -ERESTARTSYS;}EXPORT_SYMBOL(tty_check_change);static ssize_t hung_up_tty_read(struct file * file, char __user * buf,				size_t count, loff_t *ppos){	return 0;}static ssize_t hung_up_tty_write(struct file * file, const char __user * buf,				 size_t count, loff_t *ppos){	return -EIO;}/* No kernel lock held - none needed ;) */static unsigned int hung_up_tty_poll(struct file * filp, poll_table * wait){	return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;}static int hung_up_tty_ioctl(struct inode * inode, struct file * file,			     unsigned int cmd, unsigned long arg){	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;}static struct file_operations tty_fops = {	.llseek		= no_llseek,	.read		= tty_read,	.write		= tty_write,	.poll		= tty_poll,	.ioctl		= tty_ioctl,	.open		= tty_open,	.release	= tty_release,	.fasync		= tty_fasync,};#ifdef CONFIG_UNIX98_PTYSstatic struct file_operations ptmx_fops = {	.llseek		= no_llseek,	.read		= tty_read,	.write		= tty_write,	.poll		= tty_poll,	.ioctl		= tty_ioctl,	.open		= ptmx_open,	.release	= tty_release,	.fasync		= tty_fasync,};#endifstatic struct file_operations console_fops = {	.llseek		= no_llseek,	.read		= tty_read,	.write		= redirected_tty_write,	.poll		= tty_poll,	.ioctl		= tty_ioctl,	.open		= tty_open,	.release	= tty_release,	.fasync		= tty_fasync,};static struct file_operations hung_up_tty_fops = {	.llseek		= no_llseek,	.read		= hung_up_tty_read,	.write		= hung_up_tty_write,	.poll		= hung_up_tty_poll,	.ioctl		= hung_up_tty_ioctl,	.release	= tty_release,};static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;static struct file *redirect;/** *	tty_wakeup	-	request more data *	@tty: terminal * *	Internal and external helper for wakeups of tty. This function *	informs the line discipline if present that the driver is ready *	to receive more output data. */ void tty_wakeup(struct tty_struct *tty){	struct tty_ldisc *ld;		if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {		ld = tty_ldisc_ref(tty);		if(ld) {			if(ld->write_wakeup)				ld->write_wakeup(tty);			tty_ldisc_deref(ld);		}	}	wake_up_interruptible(&tty->write_wait);}EXPORT_SYMBOL_GPL(tty_wakeup);/** *	tty_ldisc_flush	-	flush line discipline queue *	@tty: tty * *	Flush the line discipline queue (if any) for this tty. If there *	is no line discipline active this is a no-op. */ void tty_ldisc_flush(struct tty_struct *tty){	struct tty_ldisc *ld = tty_ldisc_ref(tty);	if(ld) {		if(ld->flush_buffer)			ld->flush_buffer(tty);		tty_ldisc_deref(ld);	}}EXPORT_SYMBOL_GPL(tty_ldisc_flush);	/* * This can be called by the "eventd" kernel thread.  That is process synchronous, * but doesn't hold any locks, so we need to make sure we have the appropriate * locks for what we're doing.. */void do_tty_hangup(void *data){	struct tty_struct *tty = (struct tty_struct *) data;	struct file * cons_filp = NULL;	struct file *filp, *f = NULL;	struct task_struct *p;	struct tty_ldisc *ld;	int    closecount = 0, n;	if (!tty)		return;	/* inuse_filps is protected by the single kernel lock */	lock_kernel();	spin_lock(&redirect_lock);	if (redirect && redirect->private_data == tty) {		f = redirect;		redirect = NULL;	}	spin_unlock(&redirect_lock);		check_tty_count(tty, "do_tty_hangup");	file_list_lock();	/* This breaks for file handles being sent over AF_UNIX sockets ? */	list_for_each_entry(filp, &tty->tty_files, f_list) {		if (filp->f_op->write == redirected_tty_write)			cons_filp = filp;		if (filp->f_op->write != tty_write)			continue;		closecount++;		tty_fasync(-1, filp, 0);	/* can't block */		filp->f_op = &hung_up_tty_fops;	}	file_list_unlock();		/* FIXME! What are the locking issues here? This may me overdoing things..	 * this question is especially important now that we've removed the irqlock. */	ld = tty_ldisc_ref(tty);	if(ld != NULL)	/* We may have no line discipline at this point */	{		if (ld->flush_buffer)			ld->flush_buffer(tty);		if (tty->driver->flush_buffer)			tty->driver->flush_buffer(tty);		if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&		    ld->write_wakeup)			ld->write_wakeup(tty);		if (ld->hangup)			ld->hangup(tty);	}	/* FIXME: Once we trust the LDISC code better we can wait here for	   ldisc completion and fix the driver call race */	   	wake_up_interruptible(&tty->write_wait);	wake_up_interruptible(&tty->read_wait);	/*	 * Shutdown the current line discipline, and reset it to	 * N_TTY.	 */	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)	{		down(&tty->termios_sem);		*tty->termios = tty->driver->init_termios;		up(&tty->termios_sem);	}		/* Defer ldisc switch */	/* tty_deferred_ldisc_switch(N_TTY);		  This should get done automatically when the port closes and	  tty_release is called */		read_lock(&tasklist_lock);	if (tty->session > 0) {		do_each_task_pid(tty->session, PIDTYPE_SID, p) {			if (p->signal->tty == tty)				p->signal->tty = NULL;			if (!p->signal->leader)				continue;			send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p);			send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p);			if (tty->pgrp > 0)				p->signal->tty_old_pgrp = tty->pgrp;		} while_each_task_pid(tty->session, PIDTYPE_SID, p);	}	read_unlock(&tasklist_lock);	tty->flags = 0;	tty->session = 0;	tty->pgrp = -1;	tty->ctrl_status = 0;	/*	 *	If one of the devices matches a console pointer, we	 *	cannot just call hangup() because that will cause	 *	tty->count and state->count to go out of sync.	 *	So we just call close() the right number of times.	 */	if (cons_filp) {		if (tty->driver->close)			for (n = 0; n < closecount; n++)				tty->driver->close(tty, cons_filp);	} else if (tty->driver->hangup)		(tty->driver->hangup)(tty);			/* We don't want to have driver/ldisc interactions beyond	   the ones we did here. The driver layer expects no	   calls after ->hangup() from the ldisc side. However we	   can't yet guarantee all that */	set_bit(TTY_HUPPED, &tty->flags);	if (ld) {		tty_ldisc_enable(tty);		tty_ldisc_deref(ld);	}	unlock_kernel();	if (f)		fput(f);}void tty_hangup(struct tty_struct * tty){#ifdef TTY_DEBUG_HANGUP	char	buf[64];		printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));#endif	schedule_work(&tty->hangup_work);}EXPORT_SYMBOL(tty_hangup);void tty_vhangup(struct tty_struct * tty){#ifdef TTY_DEBUG_HANGUP	char	buf[64];	printk(KERN_DEBUG "%s vhangup...\n", tty_name(tty, buf));#endif	do_tty_hangup((void *) tty);}EXPORT_SYMBOL(tty_vhangup);int tty_hung_up_p(struct file * filp){	return (filp->f_op == &hung_up_tty_fops);}EXPORT_SYMBOL(tty_hung_up_p);/* * This function is typically called only by the session leader, when * it wants to disassociate itself from its controlling tty. * * It performs the following functions: * 	(1)  Sends a SIGHUP and SIGCONT to the foreground process group * 	(2)  Clears the tty from being controlling the session * 	(3)  Clears the controlling tty for all processes in the * 		session group. * * The argument on_exit is set to 1 if called when a process is * exiting; it is 0 if called by the ioctl TIOCNOTTY. */void disassociate_ctty(int on_exit){	struct tty_struct *tty;	struct task_struct *p;	int tty_pgrp = -1;	lock_kernel();	tty = current->signal->tty;	if (tty) {		tty_pgrp = tty->pgrp;		if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)			tty_vhangup(tty);	} else {		if (current->signal->tty_old_pgrp) {			kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit);			kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit);		}		unlock_kernel();			return;	}	if (tty_pgrp > 0) {		kill_pg(tty_pgrp, SIGHUP, on_exit);		if (!on_exit)			kill_pg(tty_pgrp, SIGCONT, on_exit);	}	current->signal->tty_old_pgrp = 0;	tty->session = 0;	tty->pgrp = -1;	read_lock(&tasklist_lock);	do_each_task_pid(current->signal->session, PIDTYPE_SID, p) {		p->signal->tty = NULL;	} while_each_task_pid(current->signal->session, PIDTYPE_SID, p);	read_unlock(&tasklist_lock);	unlock_kernel();}void stop_tty(struct tty_struct *tty){	if (tty->stopped)		return;	tty->stopped = 1;	if (tty->link && tty->link->packet) {		tty->ctrl_status &= ~TIOCPKT_START;		tty->ctrl_status |= TIOCPKT_STOP;		wake_up_interruptible(&tty->link->read_wait);	}	if (tty->driver->stop)		(tty->driver->stop)(tty);}EXPORT_SYMBOL(stop_tty);void start_tty(struct tty_struct *tty){	if (!tty->stopped || tty->flow_stopped)		return;	tty->stopped = 0;	if (tty->link && tty->link->packet) {		tty->ctrl_status &= ~TIOCPKT_STOP;		tty->ctrl_status |= TIOCPKT_START;		wake_up_interruptible(&tty->link->read_wait);	}	if (tty->driver->start)		(tty->driver->start)(tty);	/* If we have a running line discipline it may need kicking */	tty_wakeup(tty);	wake_up_interruptible(&tty->write_wait);}EXPORT_SYMBOL(start_tty);static ssize_t tty_read(struct file * file, char __user * buf, size_t count, 			loff_t *ppos){	int i;	struct tty_struct * tty;	struct inode *inode;	struct tty_ldisc *ld;	tty = (struct tty_struct *)file->private_data;	inode = file->f_dentry->d_inode;	if (tty_paranoia_check(tty, inode, "tty_read"))		return -EIO;	if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))		return -EIO;	/* We want to wait for the line discipline to sort out in this	   situation */	ld = tty_ldisc_ref_wait(tty);	lock_kernel();	if (ld->read)		i = (ld->read)(tty,file,buf,count);	else		i = -EIO;	tty_ldisc_deref(ld);	unlock_kernel();	if (i > 0)		inode->i_atime = CURRENT_TIME;	return i;}/* * Split writes up in sane blocksizes to avoid * denial-of-service type attacks */static inline ssize_t do_tty_write(	ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char __user *, size_t),	struct tty_struct *tty,	struct file *file,	const unsigned char __user *buf,	size_t count){	ssize_t ret = 0, written = 0;		if (down_interruptible(&tty->atomic_write)) {		return -ERESTARTSYS;	}	if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) {		lock_kernel();		written = write(tty, file, buf, count);		unlock_kernel();	} else {		for (;;) {			unsigned long size = max((unsigned long)PAGE_SIZE*2, 16384UL);			if (size > count)				size = count;			lock_kernel();			ret = write(tty, file, buf, size);			unlock_kernel();

⌨️ 快捷键说明

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