tty_io.c

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

C
2,610
字号
/* *  linux/drivers/char/tty_io.c * *  Copyright (C) 1991, 1992  Linus Torvalds *//* * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles * or rs-channels. It also implements echoing, cooked mode etc. * * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0. * * Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the * tty_struct and tty_queue structures.  Previously there was an array * of 256 tty_struct's which was statically allocated, and the * tty_queue structures were allocated at boot time.  Both are now * dynamically allocated only when the tty is open. * * Also restructured routines so that there is more of a separation * between the high-level tty routines (tty_io.c and tty_ioctl.c) and * the low-level tty routines (serial.c, pty.c, console.c).  This * makes for cleaner and more compact code.  -TYT, 9/17/92  * * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines * which can be dynamically activated and de-activated by the line * discipline handling modules (like SLIP). * * NOTE: pay no attention to the line discipline code (yet); its * interface is still subject to change in this version... * -- TYT, 1/31/92 * * Added functionality to the OPOST tty handling.  No delays, but all * other bits should be there. *	-- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993. * * Rewrote canonical mode and added more termios flags. * 	-- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94 * * Reorganized FASYNC support so mouse code can share it. *	-- ctm@ardi.com, 9Sep95 * * New TIOCLINUX variants added. *	-- mj@k332.feld.cvut.cz, 19-Nov-95 *  * Restrict vt switching via ioctl() *      -- grif@cs.ucr.edu, 5-Dec-95 * * Move console and virtual terminal code to more appropriate files, * implement CONFIG_VT and generalize console device interface. *	-- Marko Kohtala <Marko.Kohtala@hut.fi>, March 97 * * Rewrote init_dev and release_dev to eliminate races. *	-- Bill Hawes <whawes@star.net>, June 97 * * Added devfs support. *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 13-Jan-1998 * * Added support for a Unix98-style ptmx device. *      -- C. Scott Ananian <cananian@alumni.princeton.edu>, 14-Jan-1998 * * Reduced memory usage for older ARM systems *      -- Russell King <rmk@arm.linux.org.uk> * * Move do_SAK() into process context.  Less stack use in devfs functions. * alloc_tty_struct() always uses kmalloc() -- Andrew Morton <andrewm@uow.edu.eu> 17Mar01 */#include <linux/config.h>#include <linux/types.h>#include <linux/major.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/fcntl.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_driver.h>#include <linux/tty_flip.h>#include <linux/devpts_fs.h>#include <linux/file.h>#include <linux/console.h>#include <linux/timer.h>#include <linux/ctype.h>#include <linux/kd.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/module.h>#include <linux/smp_lock.h>#include <linux/device.h>#include <linux/idr.h>#include <linux/wait.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/bitops.h>#include <linux/kbd_kern.h>#include <linux/vt_kern.h>#include <linux/selection.h>#include <linux/devfs_fs_kernel.h>#include <linux/kmod.h>#undef TTY_DEBUG_HANGUP#define TTY_PARANOIA_CHECK 1#define CHECK_TTY_COUNT 1struct termios tty_std_termios = {	/* for the benefit of tty drivers  */	.c_iflag = ICRNL | IXON,	.c_oflag = OPOST | ONLCR,	.c_cflag = B38400 | CS8 | CREAD | HUPCL,	.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |		   ECHOCTL | ECHOKE | IEXTEN,	.c_cc = INIT_C_CC};EXPORT_SYMBOL(tty_std_termios);/* This list gets poked at by procfs and various bits of boot up code. This   could do with some rationalisation such as pulling the tty proc function   into this file */   LIST_HEAD(tty_drivers);			/* linked list of tty drivers *//* Semaphore to protect creating and releasing a tty. This is shared with   vt.c for deeply disgusting hack reasons */DECLARE_MUTEX(tty_sem);#ifdef CONFIG_UNIX98_PTYSextern struct tty_driver *ptm_driver;	/* Unix98 pty masters; for /dev/ptmx */extern int pty_limit;		/* Config limit on Unix98 ptys */static DEFINE_IDR(allocated_ptys);static DECLARE_MUTEX(allocated_ptys_lock);#endifextern void disable_early_printk(void);static void initialize_tty_struct(struct tty_struct *tty);static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);ssize_t redirected_tty_write(struct file *, const char __user *, size_t, loff_t *);static unsigned int tty_poll(struct file *, poll_table *);static int tty_open(struct inode *, struct file *);static int ptmx_open(struct inode *, struct file *);static int tty_release(struct inode *, struct file *);int tty_ioctl(struct inode * inode, struct file * file,	      unsigned int cmd, unsigned long arg);static int tty_fasync(int fd, struct file * filp, int on);extern void rs_360_init(void);static void release_mem(struct tty_struct *tty, int idx);static struct tty_struct *alloc_tty_struct(void){	struct tty_struct *tty;	tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);	if (tty)		memset(tty, 0, sizeof(struct tty_struct));	return tty;}static inline void free_tty_struct(struct tty_struct *tty){	kfree(tty);}#define TTY_NUMBER(tty) ((tty)->index + (tty)->driver->name_base)char *tty_name(struct tty_struct *tty, char *buf){	if (!tty) /* Hmm.  NULL pointer.  That's fun. */		strcpy(buf, "NULL tty");	else		strcpy(buf, tty->name);	return buf;}EXPORT_SYMBOL(tty_name);inline int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,			      const char *routine){#ifdef TTY_PARANOIA_CHECK	if (!tty) {		printk(KERN_WARNING			"null TTY for (%d:%d) in %s\n",			imajor(inode), iminor(inode), routine);		return 1;	}	if (tty->magic != TTY_MAGIC) {		printk(KERN_WARNING			"bad magic number for tty struct (%d:%d) in %s\n",			imajor(inode), iminor(inode), routine);		return 1;	}#endif	return 0;}static int check_tty_count(struct tty_struct *tty, const char *routine){#ifdef CHECK_TTY_COUNT	struct list_head *p;	int count = 0;		file_list_lock();	list_for_each(p, &tty->tty_files) {		count++;	}	file_list_unlock();	if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&	    tty->driver->subtype == PTY_TYPE_SLAVE &&	    tty->link && tty->link->count)		count++;	if (tty->count != count) {		printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "				    "!= #fd's(%d) in %s\n",		       tty->name, tty->count, count, routine);		return count;       }	#endif	return 0;}/* *	This is probably overkill for real world processors but *	they are not on hot paths so a little discipline won't do  *	any harm. */ static void tty_set_termios_ldisc(struct tty_struct *tty, int num){	down(&tty->termios_sem);	tty->termios->c_line = num;	up(&tty->termios_sem);}/* *	This guards the refcounted line discipline lists. The lock *	must be taken with irqs off because there are hangup path *	callers who will do ldisc lookups and cannot sleep. */ static spinlock_t tty_ldisc_lock = SPIN_LOCK_UNLOCKED;static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);static struct tty_ldisc tty_ldiscs[NR_LDISCS];	/* line disc dispatch table	*/int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc){	unsigned long flags;	int ret = 0;		if (disc < N_TTY || disc >= NR_LDISCS)		return -EINVAL;		spin_lock_irqsave(&tty_ldisc_lock, flags);	if (new_ldisc) {		tty_ldiscs[disc] = *new_ldisc;		tty_ldiscs[disc].num = disc;		tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;		tty_ldiscs[disc].refcount = 0;	} else {		if(tty_ldiscs[disc].refcount)			ret = -EBUSY;		else			tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;	}	spin_unlock_irqrestore(&tty_ldisc_lock, flags);		return ret;}EXPORT_SYMBOL(tty_register_ldisc);struct tty_ldisc *tty_ldisc_get(int disc){	unsigned long flags;	struct tty_ldisc *ld;	if (disc < N_TTY || disc >= NR_LDISCS)		return NULL;		spin_lock_irqsave(&tty_ldisc_lock, flags);	ld = &tty_ldiscs[disc];	/* Check the entry is defined */	if(ld->flags & LDISC_FLAG_DEFINED)	{		/* If the module is being unloaded we can't use it */		if (!try_module_get(ld->owner))		       	ld = NULL;		else /* lock it */			ld->refcount++;	}	else		ld = NULL;	spin_unlock_irqrestore(&tty_ldisc_lock, flags);	return ld;}EXPORT_SYMBOL_GPL(tty_ldisc_get);void tty_ldisc_put(int disc){	struct tty_ldisc *ld;	unsigned long flags;		if (disc < N_TTY || disc >= NR_LDISCS)		BUG();			spin_lock_irqsave(&tty_ldisc_lock, flags);	ld = &tty_ldiscs[disc];	if(ld->refcount == 0)		BUG();	ld->refcount --;	module_put(ld->owner);	spin_unlock_irqrestore(&tty_ldisc_lock, flags);}	EXPORT_SYMBOL_GPL(tty_ldisc_put);void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld){	tty->ldisc = *ld;	tty->ldisc.refcount = 0;}/** *	tty_ldisc_try		-	internal helper *	@tty: the tty * *	Make a single attempt to grab and bump the refcount on *	the tty ldisc. Return 0 on failure or 1 on success. This is *	used to implement both the waiting and non waiting versions *	of tty_ldisc_ref */static int tty_ldisc_try(struct tty_struct *tty){	unsigned long flags;	struct tty_ldisc *ld;	int ret = 0;		spin_lock_irqsave(&tty_ldisc_lock, flags);	ld = &tty->ldisc;	if(test_bit(TTY_LDISC, &tty->flags))	{		ld->refcount++;		ret = 1;	}	spin_unlock_irqrestore(&tty_ldisc_lock, flags);	return ret;}/** *	tty_ldisc_ref_wait	-	wait for the tty ldisc *	@tty: tty device * *	Dereference the line discipline for the terminal and take a  *	reference to it. If the line discipline is in flux then  *	wait patiently until it changes. * *	Note: Must not be called from an IRQ/timer context. The caller *	must also be careful not to hold other locks that will deadlock *	against a discipline change, such as an existing ldisc reference *	(which we check for) */ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty){	/* wait_event is a macro */	wait_event(tty_ldisc_wait, tty_ldisc_try(tty));	if(tty->ldisc.refcount == 0)		printk(KERN_ERR "tty_ldisc_ref_wait\n");	return &tty->ldisc;}EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);/** *	tty_ldisc_ref		-	get the tty ldisc *	@tty: tty device * *	Dereference the line discipline for the terminal and take a  *	reference to it. If the line discipline is in flux then  *	return NULL. Can be called from IRQ and timer functions. */ struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty){	if(tty_ldisc_try(tty))		return &tty->ldisc;	return NULL;}EXPORT_SYMBOL_GPL(tty_ldisc_ref);/** *	tty_ldisc_deref		-	free a tty ldisc reference *	@ld: reference to free up * *	Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May *	be called in IRQ context. */ void tty_ldisc_deref(struct tty_ldisc *ld){	unsigned long flags;	if(ld == NULL)		BUG();			spin_lock_irqsave(&tty_ldisc_lock, flags);	if(ld->refcount == 0)		printk(KERN_ERR "tty_ldisc_deref: no references.\n");	else		ld->refcount--;	if(ld->refcount == 0)		wake_up(&tty_ldisc_wait);	spin_unlock_irqrestore(&tty_ldisc_lock, flags);}EXPORT_SYMBOL_GPL(tty_ldisc_deref);/** *	tty_ldisc_enable	-	allow ldisc use *	@tty: terminal to activate ldisc on * *	Set the TTY_LDISC flag when the line discipline can be called *	again. Do neccessary wakeups for existing sleepers. * *	Note: nobody should set this bit except via this function. Clearing *	directly is allowed. */static void tty_ldisc_enable(struct tty_struct *tty){	set_bit(TTY_LDISC, &tty->flags);	wake_up(&tty_ldisc_wait);}	/** *	tty_set_ldisc		-	set line discipline *	@tty: the terminal to set *	@ldisc: the line discipline * *	Set the discipline of a tty line. Must be called from a process *	context. */ static int tty_set_ldisc(struct tty_struct *tty, int ldisc){	int	retval = 0;	struct	tty_ldisc o_ldisc;	char buf[64];	int work;	unsigned long flags;	struct tty_ldisc *ld;	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))		return -EINVAL;restart:	if (tty->ldisc.num == ldisc)		return 0;	/* We are already in the desired discipline */		ld = tty_ldisc_get(ldisc);	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */	/* Cyrus Durgin <cider@speakeasy.org> */	if (ld == NULL) {		request_module("tty-ldisc-%d", ldisc);		ld = tty_ldisc_get(ldisc);	}	if (ld == NULL)		return -EINVAL;	o_ldisc = tty->ldisc;	tty_wait_until_sent(tty, 0);	/*	 *	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)	{		/* 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;	}	clear_bit(TTY_LDISC, &tty->flags);		clear_bit(TTY_DONT_FLIP, &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.	 */

⌨️ 快捷键说明

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