欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

n_tty.c

linux 内核源代码
C
第 1 页 / 共 3 页
字号:
/* * n_tty.c --- implements the N_TTY line discipline. *  * This code used to be in tty_io.c, but things are getting hairy * enough that it made sense to split things off.  (The N_TTY * processing has changed so much that it's hardly recognizable, * anyway...) * * Note that the open routine for N_TTY is guaranteed never to return * an error.  This is because Linux will fall back to setting a line * to N_TTY if it can not switch to any other line discipline.   * * Written by Theodore Ts'o, Copyright 1994. *  * This file also contains code originally written by Linus Torvalds, * Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994. *  * This file may be redistributed under the terms of the GNU General Public * License. * * Reduced memory usage for older ARM systems  - Russell King. * * 2000/01/20   Fixed SMP locking on put_tty_queue using bits of  *		the patch by Andrew J. Kroll <ag784@freenet.buffalo.edu> *		who actually finally proved there really was a race. * * 2002/03/18   Implemented n_tty_wakeup to send SIGIO POLL_OUTs to *		waiting writing processes-Sapan Bhatia <sapan@corewars.org>. *		Also fixed a bug in BLOCKING mode where write_chan returns *		EAGAIN */#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/timer.h>#include <linux/ctype.h>#include <linux/mm.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/bitops.h>#include <linux/audit.h>#include <linux/file.h>#include <asm/uaccess.h>#include <asm/system.h>/* number of characters left in xmit buffer before select has we have room */#define WAKEUP_CHARS 256/* * This defines the low- and high-watermarks for throttling and * unthrottling the TTY driver.  These watermarks are used for * controlling the space in the read buffer. */#define TTY_THRESHOLD_THROTTLE		128 /* now based on remaining room */#define TTY_THRESHOLD_UNTHROTTLE 	128static inline unsigned char *alloc_buf(void){	gfp_t prio = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;	if (PAGE_SIZE != N_TTY_BUF_SIZE)		return kmalloc(N_TTY_BUF_SIZE, prio);	else		return (unsigned char *)__get_free_page(prio);}static inline void free_buf(unsigned char *buf){	if (PAGE_SIZE != N_TTY_BUF_SIZE)		kfree(buf);	else		free_page((unsigned long) buf);}static inline int tty_put_user(struct tty_struct *tty, unsigned char x,			       unsigned char __user *ptr){	tty_audit_add_data(tty, &x, 1);	return put_user(x, ptr);}/** *	n_tty_set__room	-	receive space *	@tty: terminal * *	Called by the driver to find out how much data it is *	permitted to feed to the line discipline without any being lost *	and thus to manage flow control. Not serialized. Answers for the *	"instant". */static void n_tty_set_room(struct tty_struct *tty){	int	left = N_TTY_BUF_SIZE - tty->read_cnt - 1;	/*	 * If we are doing input canonicalization, and there are no	 * pending newlines, let characters through without limit, so	 * that erase characters will be handled.  Other excess	 * characters will be beeped.	 */	if (left <= 0)		left = tty->icanon && !tty->canon_data;	tty->receive_room = left;}static void put_tty_queue_nolock(unsigned char c, struct tty_struct *tty){	if (tty->read_cnt < N_TTY_BUF_SIZE) {		tty->read_buf[tty->read_head] = c;		tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);		tty->read_cnt++;	}}static void put_tty_queue(unsigned char c, struct tty_struct *tty){	unsigned long flags;	/*	 *	The problem of stomping on the buffers ends here.	 *	Why didn't anyone see this one coming? --AJK	*/	spin_lock_irqsave(&tty->read_lock, flags);	put_tty_queue_nolock(c, tty);	spin_unlock_irqrestore(&tty->read_lock, flags);}/** *	check_unthrottle	-	allow new receive data *	@tty; tty device * *	Check whether to call the driver.unthrottle function. *	We test the TTY_THROTTLED bit first so that it always *	indicates the current state. The decision about whether *	it is worth allowing more input has been taken by the caller. *	Can sleep, may be called under the atomic_read_lock mutex but *	this is not guaranteed. */ static void check_unthrottle(struct tty_struct * tty){	if (tty->count &&	    test_and_clear_bit(TTY_THROTTLED, &tty->flags) && 	    tty->driver->unthrottle)		tty->driver->unthrottle(tty);}/** *	reset_buffer_flags	-	reset buffer state *	@tty: terminal to reset * *	Reset the read buffer counters, clear the flags,  *	and make sure the driver is unthrottled. Called *	from n_tty_open() and n_tty_flush_buffer(). */static void reset_buffer_flags(struct tty_struct *tty){	unsigned long flags;	spin_lock_irqsave(&tty->read_lock, flags);	tty->read_head = tty->read_tail = tty->read_cnt = 0;	spin_unlock_irqrestore(&tty->read_lock, flags);	tty->canon_head = tty->canon_data = tty->erasing = 0;	memset(&tty->read_flags, 0, sizeof tty->read_flags);	n_tty_set_room(tty);	check_unthrottle(tty);}/** *	n_tty_flush_buffer	-	clean input queue *	@tty:	terminal device * *	Flush the input buffer. Called when the line discipline is *	being closed, when the tty layer wants the buffer flushed (eg *	at hangup) or when the N_TTY line discipline internally has to *	clean the pending queue (for example some signals). * *	FIXME: tty->ctrl_status is not spinlocked and relies on *	lock_kernel() still. */ static void n_tty_flush_buffer(struct tty_struct * tty){	/* clear everything and unthrottle the driver */	reset_buffer_flags(tty);		if (!tty->link)		return;	if (tty->link->packet) {		tty->ctrl_status |= TIOCPKT_FLUSHREAD;		wake_up_interruptible(&tty->link->read_wait);	}}/** *	n_tty_chars_in_buffer	-	report available bytes *	@tty: tty device * *	Report the number of characters buffered to be delivered to user *	at this instant in time.  */ static ssize_t n_tty_chars_in_buffer(struct tty_struct *tty){	unsigned long flags;	ssize_t n = 0;	spin_lock_irqsave(&tty->read_lock, flags);	if (!tty->icanon) {		n = tty->read_cnt;	} else if (tty->canon_data) {		n = (tty->canon_head > tty->read_tail) ?			tty->canon_head - tty->read_tail :			tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);	}	spin_unlock_irqrestore(&tty->read_lock, flags);	return n;}/** *	is_utf8_continuation	-	utf8 multibyte check *	@c: byte to check * *	Returns true if the utf8 character 'c' is a multibyte continuation *	character. We use this to correctly compute the on screen size *	of the character when printing */ static inline int is_utf8_continuation(unsigned char c){	return (c & 0xc0) == 0x80;}/** *	is_continuation		-	multibyte check *	@c: byte to check * *	Returns true if the utf8 character 'c' is a multibyte continuation *	character and the terminal is in unicode mode. */ static inline int is_continuation(unsigned char c, struct tty_struct *tty){	return I_IUTF8(tty) && is_utf8_continuation(c);}/** *	opost			-	output post processor *	@c: character (or partial unicode symbol) *	@tty: terminal device * *	Perform OPOST processing.  Returns -1 when the output device is *	full and the character must be retried. Note that Linux currently *	ignores TABDLY, CRDLY, VTDLY, FFDLY and NLDLY. They simply aren't *	relevant in the world today. If you ever need them, add them here. * *	Called from both the receive and transmit sides and can be called *	re-entrantly. Relies on lock_kernel() still. */ static int opost(unsigned char c, struct tty_struct *tty){	int	space, spaces;	space = tty->driver->write_room(tty);	if (!space)		return -1;	if (O_OPOST(tty)) {		switch (c) {		case '\n':			if (O_ONLRET(tty))				tty->column = 0;			if (O_ONLCR(tty)) {				if (space < 2)					return -1;				tty->driver->put_char(tty, '\r');				tty->column = 0;			}			tty->canon_column = tty->column;			break;		case '\r':			if (O_ONOCR(tty) && tty->column == 0)				return 0;			if (O_OCRNL(tty)) {				c = '\n';				if (O_ONLRET(tty))					tty->canon_column = tty->column = 0;				break;			}			tty->canon_column = tty->column = 0;			break;		case '\t':			spaces = 8 - (tty->column & 7);			if (O_TABDLY(tty) == XTABS) {				if (space < spaces)					return -1;				tty->column += spaces;				tty->driver->write(tty, "        ", spaces);				return 0;			}			tty->column += spaces;			break;		case '\b':			if (tty->column > 0)				tty->column--;			break;		default:			if (O_OLCUC(tty))				c = toupper(c);			if (!iscntrl(c) && !is_continuation(c, tty))				tty->column++;			break;		}	}	tty->driver->put_char(tty, c);	return 0;}/** *	opost_block		-	block postprocess *	@tty: terminal device *	@inbuf: user buffer *	@nr: number of bytes * *	This path is used to speed up block console writes, among other *	things when processing blocks of output data. It handles only *	the simple cases normally found and helps to generate blocks of *	symbols for the console driver and thus improve performance. * *	Called from write_chan under the tty layer write lock. */ static ssize_t opost_block(struct tty_struct * tty,		       const unsigned char * buf, unsigned int nr){	int	space;	int 	i;	const unsigned char *cp;	space = tty->driver->write_room(tty);	if (!space)		return 0;	if (nr > space)		nr = space;	for (i = 0, cp = buf; i < nr; i++, cp++) {		switch (*cp) {		case '\n':			if (O_ONLRET(tty))				tty->column = 0;			if (O_ONLCR(tty))				goto break_out;			tty->canon_column = tty->column;			break;		case '\r':			if (O_ONOCR(tty) && tty->column == 0)				goto break_out;			if (O_OCRNL(tty))				goto break_out;			tty->canon_column = tty->column = 0;			break;		case '\t':			goto break_out;		case '\b':			if (tty->column > 0)				tty->column--;			break;		default:			if (O_OLCUC(tty))				goto break_out;			if (!iscntrl(*cp))				tty->column++;			break;		}	}break_out:	if (tty->driver->flush_chars)		tty->driver->flush_chars(tty);	i = tty->driver->write(tty, buf, i);		return i;}/** *	put_char	-	write character to driver *	@c: character (or part of unicode symbol) *	@tty: terminal device * *	Queue a byte to the driver layer for output */ static inline void put_char(unsigned char c, struct tty_struct *tty){	tty->driver->put_char(tty, c);}/** *	echo_char	-	echo characters *	@c: unicode byte to echo *	@tty: terminal device * *	Echo user input back onto the screen. This must be called only when  *	L_ECHO(tty) is true. Called from the driver receive_buf path. */static void echo_char(unsigned char c, struct tty_struct *tty){	if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {		put_char('^', tty);		put_char(c ^ 0100, tty);		tty->column += 2;	} else		opost(c, tty);}static inline void finish_erasing(struct tty_struct *tty){	if (tty->erasing) {		put_char('/', tty);		tty->column++;		tty->erasing = 0;	}}/** *	eraser		-	handle erase function *	@c: character input *	@tty: terminal device * *	Perform erase and necessary output when an erase character is *	present in the stream from the driver layer. Handles the complexities *	of UTF-8 multibyte symbols. */ static void eraser(unsigned char c, struct tty_struct *tty){	enum { ERASE, WERASE, KILL } kill_type;	int head, seen_alnums, cnt;	unsigned long flags;	if (tty->read_head == tty->canon_head) {		/* opost('\a', tty); */		/* what do you think? */		return;	}	if (c == ERASE_CHAR(tty))		kill_type = ERASE;	else if (c == WERASE_CHAR(tty))		kill_type = WERASE;	else {		if (!L_ECHO(tty)) {			spin_lock_irqsave(&tty->read_lock, flags);			tty->read_cnt -= ((tty->read_head - tty->canon_head) &					  (N_TTY_BUF_SIZE - 1));			tty->read_head = tty->canon_head;			spin_unlock_irqrestore(&tty->read_lock, flags);			return;		}		if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {			spin_lock_irqsave(&tty->read_lock, flags);			tty->read_cnt -= ((tty->read_head - tty->canon_head) &					  (N_TTY_BUF_SIZE - 1));			tty->read_head = tty->canon_head;			spin_unlock_irqrestore(&tty->read_lock, flags);			finish_erasing(tty);			echo_char(KILL_CHAR(tty), tty);			/* Add a newline if ECHOK is on and ECHOKE is off. */			if (L_ECHOK(tty))				opost('\n', tty);			return;		}		kill_type = KILL;	}	seen_alnums = 0;	while (tty->read_head != tty->canon_head) {		head = tty->read_head;		/* erase a single possibly multibyte character */		do {			head = (head - 1) & (N_TTY_BUF_SIZE-1);			c = tty->read_buf[head];		} while (is_continuation(c, tty) && head != tty->canon_head);		/* do not partially erase */		if (is_continuation(c, tty))			break;		if (kill_type == WERASE) {			/* Equivalent to BSD's ALTWERASE. */			if (isalnum(c) || c == '_')				seen_alnums++;			else if (seen_alnums)				break;		}		cnt = (tty->read_head - head) & (N_TTY_BUF_SIZE-1);		spin_lock_irqsave(&tty->read_lock, flags);		tty->read_head = head;		tty->read_cnt -= cnt;		spin_unlock_irqrestore(&tty->read_lock, flags);		if (L_ECHO(tty)) {			if (L_ECHOPRT(tty)) {				if (!tty->erasing) {					put_char('\\', tty);					tty->column++;					tty->erasing = 1;				}				/* if cnt > 1, output a multi-byte character */				echo_char(c, tty);				while (--cnt > 0) {					head = (head+1) & (N_TTY_BUF_SIZE-1);					put_char(tty->read_buf[head], tty);				}			} else if (kill_type == ERASE && !L_ECHOE(tty)) {				echo_char(ERASE_CHAR(tty), tty);			} else if (c == '\t') {				unsigned int col = tty->canon_column;

⌨️ 快捷键说明

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