tty_io.c

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

C
2,498
字号
static int init_dev(struct tty_driver *driver, int idx,	struct tty_struct **ret_tty){	struct tty_struct *tty, *o_tty;	struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;	struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;	int retval = 0;	/* check whether we're reopening an existing tty */	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {		tty = devpts_get_tty(idx);		/*		 * If we don't have a tty here on a slave open, it's because		 * the master already started the close process and there's		 * no relation between devpts file and tty anymore.		 */		if (!tty && driver->subtype == PTY_TYPE_SLAVE) {			retval = -EIO;			goto end_init;		}		/*		 * It's safe from now on because init_dev() is called with		 * tty_mutex held and release_dev() won't change tty->count		 * or tty->flags without having to grab tty_mutex		 */		if (tty && driver->subtype == PTY_TYPE_MASTER)			tty = tty->link;	} else {		tty = driver->ttys[idx];	}	if (tty) goto fast_track;	/*	 * First time open is complex, especially for PTY devices.	 * This code guarantees that either everything succeeds and the	 * TTY is ready for operation, or else the table slots are vacated	 * and the allocated memory released.  (Except that the termios 	 * and locked termios may be retained.)	 */	if (!try_module_get(driver->owner)) {		retval = -ENODEV;		goto end_init;	}	o_tty = NULL;	tp = o_tp = NULL;	ltp = o_ltp = NULL;	tty = alloc_tty_struct();	if(!tty)		goto fail_no_mem;	initialize_tty_struct(tty);	tty->driver = driver;	tty->index = idx;	tty_line_name(driver, idx, tty->name);	if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {		tp_loc = &tty->termios;		ltp_loc = &tty->termios_locked;	} else {		tp_loc = &driver->termios[idx];		ltp_loc = &driver->termios_locked[idx];	}	if (!*tp_loc) {		tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);		if (!tp)			goto free_mem_out;		*tp = driver->init_termios;	}	if (!*ltp_loc) {		ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);		if (!ltp)			goto free_mem_out;	}	if (driver->type == TTY_DRIVER_TYPE_PTY) {		o_tty = alloc_tty_struct();		if (!o_tty)			goto free_mem_out;		initialize_tty_struct(o_tty);		o_tty->driver = driver->other;		o_tty->index = idx;		tty_line_name(driver->other, idx, o_tty->name);		if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {			o_tp_loc = &o_tty->termios;			o_ltp_loc = &o_tty->termios_locked;		} else {			o_tp_loc = &driver->other->termios[idx];			o_ltp_loc = &driver->other->termios_locked[idx];		}		if (!*o_tp_loc) {			o_tp = kmalloc(sizeof(struct ktermios), GFP_KERNEL);			if (!o_tp)				goto free_mem_out;			*o_tp = driver->other->init_termios;		}		if (!*o_ltp_loc) {			o_ltp = kzalloc(sizeof(struct ktermios), GFP_KERNEL);			if (!o_ltp)				goto free_mem_out;		}		/*		 * Everything allocated ... set up the o_tty structure.		 */		if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) {			driver->other->ttys[idx] = o_tty;		}		if (!*o_tp_loc)			*o_tp_loc = o_tp;		if (!*o_ltp_loc)			*o_ltp_loc = o_ltp;		o_tty->termios = *o_tp_loc;		o_tty->termios_locked = *o_ltp_loc;		driver->other->refcount++;		if (driver->subtype == PTY_TYPE_MASTER)			o_tty->count++;		/* Establish the links in both directions */		tty->link   = o_tty;		o_tty->link = tty;	}	/* 	 * All structures have been allocated, so now we install them.	 * Failures after this point use release_tty to clean up, so	 * there's no need to null out the local pointers.	 */	if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) {		driver->ttys[idx] = tty;	}		if (!*tp_loc)		*tp_loc = tp;	if (!*ltp_loc)		*ltp_loc = ltp;	tty->termios = *tp_loc;	tty->termios_locked = *ltp_loc;	/* Compatibility until drivers always set this */	tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);	tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);	driver->refcount++;	tty->count++;	/* 	 * Structures all installed ... call the ldisc open routines.	 * If we fail here just call release_tty to clean up.  No need	 * to decrement the use counts, as release_tty doesn't care.	 */	if (tty->ldisc.open) {		retval = (tty->ldisc.open)(tty);		if (retval)			goto release_mem_out;	}	if (o_tty && o_tty->ldisc.open) {		retval = (o_tty->ldisc.open)(o_tty);		if (retval) {			if (tty->ldisc.close)				(tty->ldisc.close)(tty);			goto release_mem_out;		}		tty_ldisc_enable(o_tty);	}	tty_ldisc_enable(tty);	goto success;	/*	 * This fast open can be used if the tty is already open.	 * No memory is allocated, and the only failures are from	 * attempting to open a closing tty or attempting multiple	 * opens on a pty master.	 */fast_track:	if (test_bit(TTY_CLOSING, &tty->flags)) {		retval = -EIO;		goto end_init;	}	if (driver->type == TTY_DRIVER_TYPE_PTY &&	    driver->subtype == PTY_TYPE_MASTER) {		/*		 * special case for PTY masters: only one open permitted, 		 * and the slave side open count is incremented as well.		 */		if (tty->count) {			retval = -EIO;			goto end_init;		}		tty->link->count++;	}	tty->count++;	tty->driver = driver; /* N.B. why do this every time?? */	/* FIXME */	if(!test_bit(TTY_LDISC, &tty->flags))		printk(KERN_ERR "init_dev but no ldisc\n");success:	*ret_tty = tty;		/* All paths come through here to release the mutex */end_init:	return retval;	/* Release locally allocated memory ... nothing placed in slots */free_mem_out:	kfree(o_tp);	if (o_tty)		free_tty_struct(o_tty);	kfree(ltp);	kfree(tp);	free_tty_struct(tty);fail_no_mem:	module_put(driver->owner);	retval = -ENOMEM;	goto end_init;	/* call the tty release_tty routine to clean out this slot */release_mem_out:	if (printk_ratelimit())		printk(KERN_INFO "init_dev: ldisc open failed, "				 "clearing slot %d\n", idx);	release_tty(tty, idx);	goto end_init;}/** *	release_one_tty		-	release tty structure memory * *	Releases memory associated with a tty structure, and clears out the *	driver table slots. This function is called when a device is no longer *	in use. It also gets called when setup of a device fails. * *	Locking: *		tty_mutex - sometimes only *		takes the file list lock internally when working on the list *	of ttys that the driver keeps. *		FIXME: should we require tty_mutex is held here ?? */static void release_one_tty(struct tty_struct *tty, int idx){	int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;	struct ktermios *tp;	if (!devpts)		tty->driver->ttys[idx] = NULL;	if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {		tp = tty->termios;		if (!devpts)			tty->driver->termios[idx] = NULL;		kfree(tp);		tp = tty->termios_locked;		if (!devpts)			tty->driver->termios_locked[idx] = NULL;		kfree(tp);	}	tty->magic = 0;	tty->driver->refcount--;	file_list_lock();	list_del_init(&tty->tty_files);	file_list_unlock();	free_tty_struct(tty);}/** *	release_tty		-	release tty structure memory * *	Release both @tty and a possible linked partner (think pty pair), *	and decrement the refcount of the backing module. * *	Locking: *		tty_mutex - sometimes only *		takes the file list lock internally when working on the list *	of ttys that the driver keeps. *		FIXME: should we require tty_mutex is held here ?? */static void release_tty(struct tty_struct *tty, int idx){	struct tty_driver *driver = tty->driver;	if (tty->link)		release_one_tty(tty->link, idx);	release_one_tty(tty, idx);	module_put(driver->owner);}/* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. * * WSH 09/09/97: rewritten to avoid some nasty race conditions that could * lead to double frees or releasing memory still in use. */static void release_dev(struct file * filp){	struct tty_struct *tty, *o_tty;	int	pty_master, tty_closing, o_tty_closing, do_sleep;	int	devpts;	int	idx;	char	buf[64];	unsigned long flags;		tty = (struct tty_struct *)filp->private_data;	if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode, "release_dev"))		return;	check_tty_count(tty, "release_dev");	tty_fasync(-1, filp, 0);	idx = tty->index;	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&		      tty->driver->subtype == PTY_TYPE_MASTER);	devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;	o_tty = tty->link;#ifdef TTY_PARANOIA_CHECK	if (idx < 0 || idx >= tty->driver->num) {		printk(KERN_DEBUG "release_dev: bad idx when trying to "				  "free (%s)\n", tty->name);		return;	}	if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {		if (tty != tty->driver->ttys[idx]) {			printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "			       "for (%s)\n", idx, tty->name);			return;		}		if (tty->termios != tty->driver->termios[idx]) {			printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "			       "for (%s)\n",			       idx, tty->name);			return;		}		if (tty->termios_locked != tty->driver->termios_locked[idx]) {			printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "			       "termios_locked for (%s)\n",			       idx, tty->name);			return;		}	}#endif#ifdef TTY_DEBUG_HANGUP	printk(KERN_DEBUG "release_dev of %s (tty count=%d)...",	       tty_name(tty, buf), tty->count);#endif#ifdef TTY_PARANOIA_CHECK	if (tty->driver->other &&	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {		if (o_tty != tty->driver->other->ttys[idx]) {			printk(KERN_DEBUG "release_dev: other->table[%d] "					  "not o_tty for (%s)\n",			       idx, tty->name);			return;		}		if (o_tty->termios != tty->driver->other->termios[idx]) {			printk(KERN_DEBUG "release_dev: other->termios[%d] "					  "not o_termios for (%s)\n",			       idx, tty->name);			return;		}		if (o_tty->termios_locked != 		      tty->driver->other->termios_locked[idx]) {			printk(KERN_DEBUG "release_dev: other->termios_locked["					  "%d] not o_termios_locked for (%s)\n",			       idx, tty->name);			return;		}		if (o_tty->link != tty) {			printk(KERN_DEBUG "release_dev: bad pty pointers\n");			return;		}	}#endif	if (tty->driver->close)		tty->driver->close(tty, filp);	/*	 * Sanity check: if tty->count is going to zero, there shouldn't be	 * any waiters on tty->read_wait or tty->write_wait.  We test the	 * wait queues and kick everyone out _before_ actually starting to	 * close.  This ensures that we won't block while releasing the tty	 * structure.	 *	 * The test for the o_tty closing is necessary, since the master and	 * slave sides may close in any order.  If the slave side closes out	 * first, its count will be one, since the master side holds an open.	 * Thus this test wouldn't be triggered at the time the slave closes,	 * so we do it now.	 *	 * Note that it's possible for the tty to be opened again while we're	 * flushing out waiters.  By recalculating the closing flags before	 * each iteration we avoid any problems.	 */	while (1) {		/* Guard against races with tty->count changes elsewhere and		   opens on /dev/tty */		   		mutex_lock(&tty_mutex);		tty_closing = tty->count <= 1;		o_tty_closing = o_tty &&			(o_tty->count <= (pty_master ? 1 : 0));		do_sleep = 0;		if (tty_closing) {			if (waitqueue_active(&tty->read_wait)) {				wake_up(&tty->read_wait);				do_sleep++;			}			if (waitqueue_active(&tty->write_wait)) {				wake_up(&tty->write_wait);				do_sleep++;			}		}		if (o_tty_closing) {			if (waitqueue_active(&o_tty->read_wait)) {				wake_up(&o_tty->read_wait);				do_sleep++;			}			if (waitqueue_active(&o_tty->write_wait)) {				wake_up(&o_tty->write_wait);				do_sleep++;			}		}		if (!do_sleep)			break;		printk(KERN_WARNING "release_dev: %s: read/write wait queue "				    "active!\n", tty_name(tty, buf));		mutex_unlock(&tty_mutex);		schedule();	}		/*	 * The closing flags are now consistent with the open counts on 	 * both sides, and we've completed the last operation that could 	 * block, so it's safe to proceed with closing.	 */	if (pty_master) {		if (--o_tty->count < 0) {			printk(KERN_WARNING "release_dev: bad pty slave count "					    "(%d) for %s\n",			       o_tty->count, tty_name(o_tty, buf));			o_tty->count = 0;		}	}	if (--tty->count < 0) {		printk(KERN_WARNING "release_dev: bad tty->count (%d) for %s\n",		       tty->count, tty_name(tty, buf));		tty->count = 0;	}		/*	 * We've decremented tty->count, so we need to remove this file	 * descriptor off the tty->tty_files list; this serves two	 * purposes:	 *  - check_tty_count sees the correct number of file descriptors	 *    associated with this tty.	 *  - do_tty_hangup no longer sees this file descriptor as	 *    something that needs to be handled for hangups.	 */	file_kill(filp);	filp->private_data = NULL;	/*	 * Perform some housekeeping before deciding whether to return.	 *	 * Set the TTY_CLOSING flag if this was the last open.  In the	 * case of a pty we may have to wait around for the other side	 * to close, and TTY_CLOSING makes sure we can't be reopened.	 */	if(tty_closing)		set_bit(TTY_CLOSING, &tty->flags);	if(o_tty_closing)		set_bit(TTY_CLOSING, &o_tty->flags);	/*	 * If _either_ side is closing, make sure there aren't any	 * processes that still think tty or o_tty is their controlling	 * tty.	 */	if (tty_closing || o_tty_closing) {		rea

⌨️ 快捷键说明

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