tty_io.c

来自「linux 内核源代码」· C语言 代码 · 共 2,498 行 · 第 1/5 页

C
2,498
字号
	if (tty->ldisc.num == ldisc) {		tty_ldisc_put(ldisc);		return 0;	}	/*	 *	No more input please, we are switching. The new ldisc	 *	will update this value in the ldisc open function	 */	tty->receive_room = 0;	o_ldisc = tty->ldisc;	o_tty = tty->link;	/*	 *	Make sure we don't change while someone holds a	 *	reference to the line discipline. The TTY_LDISC bit	 *	prevents anyone taking a reference once it is clear.	 *	We need the lock to avoid racing reference takers.	 */	spin_lock_irqsave(&tty_ldisc_lock, flags);	if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {		if(tty->ldisc.refcount) {			/* Free the new ldisc we grabbed. Must drop the lock			   first. */			spin_unlock_irqrestore(&tty_ldisc_lock, flags);			tty_ldisc_put(ldisc);			/*			 * There are several reasons we may be busy, including			 * random momentary I/O traffic. We must therefore			 * retry. We could distinguish between blocking ops			 * and retries if we made tty_ldisc_wait() smarter. That			 * is up for discussion.			 */			if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)				return -ERESTARTSYS;			goto restart;		}		if(o_tty && o_tty->ldisc.refcount) {			spin_unlock_irqrestore(&tty_ldisc_lock, flags);			tty_ldisc_put(ldisc);			if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)				return -ERESTARTSYS;			goto restart;		}	}	/* if the TTY_LDISC bit is set, then we are racing against another ldisc change */	if (!test_bit(TTY_LDISC, &tty->flags)) {		spin_unlock_irqrestore(&tty_ldisc_lock, flags);		tty_ldisc_put(ldisc);		ld = tty_ldisc_ref_wait(tty);		tty_ldisc_deref(ld);		goto restart;	}	clear_bit(TTY_LDISC, &tty->flags);	if (o_tty)		clear_bit(TTY_LDISC, &o_tty->flags);	spin_unlock_irqrestore(&tty_ldisc_lock, flags);	/*	 *	From this point on we know nobody has an ldisc	 *	usage reference, nor can they obtain one until	 *	we say so later on.	 */	work = cancel_delayed_work(&tty->buf.work);	/*	 * Wait for ->hangup_work and ->buf.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);	if (o_tty)		tty_ldisc_enable(o_tty);		/* Restart it in case no characters kick it off. Safe if	   already running */	if (work)		schedule_delayed_work(&tty->buf.work, 1);	return retval;}/** *	get_tty_driver		-	find device of a tty *	@dev_t: device identifier *	@index: returns the index of the tty * *	This routine returns a tty driver structure, given a device number *	and also passes back the index number. * *	Locking: caller must hold tty_mutex */static 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;}/** *	tty_check_change	-	check for POSIX terminal changes *	@tty: tty to check * *	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) * *	Locking: none */int tty_check_change(struct tty_struct * tty){	if (current->signal->tty != tty)		return 0;	if (!tty->pgrp) {		printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");		return 0;	}	if (task_pgrp(current) == tty->pgrp)		return 0;	if (is_ignored(SIGTTOU))		return 0;	if (is_current_pgrp_orphaned())		return -EIO;	kill_pgrp(task_pgrp(current), SIGTTOU, 1);	set_thread_flag(TIF_SIGPENDING);	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 long hung_up_tty_compat_ioctl(struct file * file,				     unsigned int cmd, unsigned long arg){	return cmd == TIOCSPGRP ? -ENOTTY : -EIO;}static const struct file_operations tty_fops = {	.llseek		= no_llseek,	.read		= tty_read,	.write		= tty_write,	.poll		= tty_poll,	.ioctl		= tty_ioctl,	.compat_ioctl	= tty_compat_ioctl,	.open		= tty_open,	.release	= tty_release,	.fasync		= tty_fasync,};#ifdef CONFIG_UNIX98_PTYSstatic const struct file_operations ptmx_fops = {	.llseek		= no_llseek,	.read		= tty_read,	.write		= tty_write,	.poll		= tty_poll,	.ioctl		= tty_ioctl,	.compat_ioctl	= tty_compat_ioctl,	.open		= ptmx_open,	.release	= tty_release,	.fasync		= tty_fasync,};#endifstatic const struct file_operations console_fops = {	.llseek		= no_llseek,	.read		= tty_read,	.write		= redirected_tty_write,	.poll		= tty_poll,	.ioctl		= tty_ioctl,	.compat_ioctl	= tty_compat_ioctl,	.open		= tty_open,	.release	= tty_release,	.fasync		= tty_fasync,};static const 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,	.compat_ioctl	= hung_up_tty_compat_ioctl,	.release	= tty_release,};static DEFINE_SPINLOCK(redirect_lock);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);	}	tty_buffer_flush(tty);}EXPORT_SYMBOL_GPL(tty_ldisc_flush);/** *	tty_reset_termios	-	reset terminal state *	@tty: tty to reset * *	Restore a terminal to the driver default state */static void tty_reset_termios(struct tty_struct *tty){	mutex_lock(&tty->termios_mutex);	*tty->termios = tty->driver->init_termios;	tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);	tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);	mutex_unlock(&tty->termios_mutex);}	/** *	do_tty_hangup		-	actual handler for hangup events *	@work: tty device * *	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. * *	The hangup event clears any pending redirections onto the hung up *	device. It ensures future writes will error and it does the needed *	line discipline hangup and signal delivery. The tty object itself *	remains intact. * *	Locking: *		BKL *		  redirect lock for undoing redirection *		  file list lock for manipulating list of ttys *		  tty_ldisc_lock from called functions *		  termios_mutex resetting termios data *		  tasklist_lock to walk task list for hangup event *		    ->siglock to protect ->signal/->sighand */static void do_tty_hangup(struct work_struct *work){	struct tty_struct *tty =		container_of(work, struct tty_struct, hangup_work);	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_u.fu_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)		tty_reset_termios(tty);		/* 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) {		do_each_pid_task(tty->session, PIDTYPE_SID, p) {			spin_lock_irq(&p->sighand->siglock);			if (p->signal->tty == tty)				p->signal->tty = NULL;			if (!p->signal->leader) {				spin_unlock_irq(&p->sighand->siglock);				continue;			}			__group_send_sig_info(SIGHUP, SEND_SIG_PRIV, p);			__group_send_sig_info(SIGCONT, SEND_SIG_PRIV, p);			put_pid(p->signal->tty_old_pgrp);  /* A noop */			if (tty->pgrp)				p->signal->tty_old_pgrp = get_pid(tty->pgrp);			spin_unlock_irq(&p->sighand->siglock);		} while_each_pid_task(tty->session, PIDTYPE_SID, p);	}	read_unlock(&tasklist_lock);	tty->flags = 0;	put_pid(tty->session);	put_pid(tty->pgrp);	tty->session = NULL;	tty->pgrp = NULL;	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);}/** *	tty_hangup		-	trigger a hangup event *	@tty: tty to hangup * *	A carrier loss (virtual or otherwise) has occurred on this like *	schedule a hangup sequence to run after this event. */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);

⌨️ 快捷键说明

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