⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tty_io.c

📁 gives an orthogonal feeling to tty s, be they consoles or rs-channels. It also implements echoing, c
💻 C
📖 第 1 页 / 共 4 页
字号:
#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 <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>#ifdef CONFIG_VTextern void con_init_devfs (void);#endif#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)#define SYSCONS_DEV MKDEV(TTYAUX_MAJOR,1)#define PTMX_DEV MKDEV(TTYAUX_MAJOR,2)#undef TTY_DEBUG_HANGUP#define TTY_PARANOIA_CHECK 1#define CHECK_TTY_COUNT 1struct termios tty_std_termios;		/* for the benefit of tty drivers  */struct tty_driver *tty_drivers;		/* linked list of tty drivers */struct tty_ldisc ldiscs[NR_LDISCS];	/* line disc dispatch table	*/#ifdef CONFIG_UNIX98_PTYSextern struct tty_driver ptm_driver[];	/* Unix98 pty masters; for /dev/ptmx */extern struct tty_driver pts_driver[];	/* Unix98 pty slaves;  for /dev/ptmx */#endif/* * redirect is the pseudo-tty that console output * is redirected to if asked by TIOCCONS. */struct tty_struct * redirect;static void initialize_tty_struct(struct tty_struct *tty);static ssize_t tty_read(struct file *, char *, size_t, loff_t *);static ssize_t tty_write(struct file *, const char *, size_t, loff_t *);static unsigned int tty_poll(struct file *, poll_table *);static int tty_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 int vme_scc_init (void);extern long vme_scc_console_init(void);extern int serial167_init(void);extern long serial167_console_init(void);extern void console_8xx_init(void);extern int rs_8xx_init(void);extern void mac_scc_console_init(void);extern void hwc_console_init(void);extern void hwc_tty_init(void);extern void con3215_init(void);extern void tty3215_init(void);extern void tub3270_con_init(void);extern void tub3270_init(void);extern void uart_console_init(void);extern void sgi_serial_console_init(void);extern void sci_console_init(void);extern void tx3912_console_init(void);extern void tx3912_rs_init(void);extern void txx927_console_init(void);extern void sb1250_serial_console_init(void);#ifndef MIN#define MIN(a,b)	((a) < (b) ? (a) : (b))#endif#ifndef MAX#define MAX(a,b)	((a) < (b) ? (b) : (a))#endifstatic 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);}/* * This routine returns the name of tty. */static char *_tty_make_name(struct tty_struct *tty, const char *name, char *buf){	int idx = (tty)?MINOR(tty->device) - tty->driver.minor_start:0;	if (!tty) /* Hmm.  NULL pointer.  That's fun. */		strcpy(buf, "NULL tty");	else		sprintf(buf, name,			idx + tty->driver.name_base);			return buf;}#define TTY_NUMBER(tty) (MINOR((tty)->device) - (tty)->driver.minor_start + \			 (tty)->driver.name_base)char *tty_name(struct tty_struct *tty, char *buf){	return _tty_make_name(tty, (tty)?tty->driver.name:NULL, buf);}inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,			      const char *routine){#ifdef TTY_PARANOIA_CHECK	static const char badmagic[] = KERN_WARNING		"Warning: bad magic number for tty struct (%s) in %s\n";	static const char badtty[] = KERN_WARNING		"Warning: null TTY for (%s) in %s\n";	if (!tty) {		printk(badtty, kdevname(device), routine);		return 1;	}	if (tty->magic != TTY_MAGIC) {		printk(badmagic, kdevname(device), 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();	for(p = tty->tty_files.next; p != &tty->tty_files; p = p->next) {		if(list_entry(p, struct file, f_list)->private_data == tty)			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",		       kdevname(tty->device), tty->count, count, routine);		return count;       }	#endif	return 0;}int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc){	if (disc < N_TTY || disc >= NR_LDISCS)		return -EINVAL;		if (new_ldisc) {		ldiscs[disc] = *new_ldisc;		ldiscs[disc].flags |= LDISC_FLAG_DEFINED;		ldiscs[disc].num = disc;	} else		memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));		return 0;}EXPORT_SYMBOL(tty_register_ldisc);/* Set the discipline of a tty line. */static int tty_set_ldisc(struct tty_struct *tty, int ldisc){	int	retval = 0;	struct	tty_ldisc o_ldisc;	char buf[64];	if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))		return -EINVAL;	/* Eduardo Blanco <ejbs@cs.cs.com.uy> */	/* Cyrus Durgin <cider@speakeasy.org> */	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {		char modname [20];		sprintf(modname, "tty-ldisc-%d", ldisc);		request_module (modname);	}	if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))		return -EINVAL;	if (tty->ldisc.num == ldisc)		return 0;	/* We are already in the desired discipline */	o_ldisc = tty->ldisc;	tty_wait_until_sent(tty, 0);		/* Shutdown the current discipline. */	if (tty->ldisc.close)		(tty->ldisc.close)(tty);	/* Now set up the new line discipline. */	tty->ldisc = ldiscs[ldisc];	tty->termios->c_line = ldisc;	if (tty->ldisc.open)		retval = (tty->ldisc.open)(tty);	if (retval < 0) {		tty->ldisc = o_ldisc;		tty->termios->c_line = tty->ldisc.num;		if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {			tty->ldisc = ldiscs[N_TTY];			tty->termios->c_line = 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);			}		}	}	if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)		tty->driver.set_ldisc(tty);	return retval;}/* * This routine returns a tty driver structure, given a device number */struct tty_driver *get_tty_driver(kdev_t device){	int	major, minor;	struct tty_driver *p;		minor = MINOR(device);	major = MAJOR(device);	for (p = tty_drivers; p; p = p->next) {		if (p->major != major)			continue;		if (minor < p->minor_start)			continue;		if (minor >= p->minor_start + p->num)			continue;		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->tty != tty)		return 0;	if (tty->pgrp <= 0) {		printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n");		return 0;	}	if (current->pgrp == tty->pgrp)		return 0;	if (is_ignored(SIGTTOU))		return 0;	if (is_orphaned_pgrp(current->pgrp))		return -EIO;	(void) kill_pg(current->pgrp,SIGTTOU,1);	return -ERESTARTSYS;}static ssize_t hung_up_tty_read(struct file * file, char * buf,				size_t count, loff_t *ppos){	/* Can't seek (pread) on ttys.  */	if (ppos != &file->f_pos)		return -ESPIPE;	return 0;}static ssize_t hung_up_tty_write(struct file * file, const char * buf,				 size_t count, loff_t *ppos){	/* Can't seek (pwrite) on ttys.  */	if (ppos != &file->f_pos)		return -ESPIPE;	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,};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,};/* * 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 task_struct *p;	struct list_head *l;	int    closecount = 0, n;	if (!tty)		return;	/* inuse_filps is protected by the single kernel lock */	lock_kernel();		check_tty_count(tty, "do_tty_hangup");	file_list_lock();	for (l = tty->tty_files.next; l != &tty->tty_files; l = l->next) {		struct file * filp = list_entry(l, struct file, f_list);		if (filp->f_dentry->d_inode->i_rdev == CONSOLE_DEV ||		    filp->f_dentry->d_inode->i_rdev == SYSCONS_DEV) {			cons_filp = filp;			continue;		}		if (filp->f_op != &tty_fops)			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.. */	{		unsigned long flags;		save_flags(flags); cli();		if (tty->ldisc.flush_buffer)			tty->ldisc.flush_buffer(tty);		if (tty->driver.flush_buffer)			tty->driver.flush_buffer(tty);		if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&		    tty->ldisc.write_wakeup)			(tty->ldisc.write_wakeup)(tty);		restore_flags(flags);	}	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->termios = tty->driver.init_termios;	if (tty->ldisc.num != ldiscs[N_TTY].num) {		if (tty->ldisc.close)			(tty->ldisc.close)(tty);		tty->ldisc = ldiscs[N_TTY];		tty->termios->c_line = N_TTY;		if (tty->ldisc.open) {			int i = (tty->ldisc.open)(tty);			if (i < 0)				printk(KERN_ERR "do_tty_hangup: N_TTY open: "						"error %d\n", -i);		}	}		read_lock(&tasklist_lock); 	for_each_task(p) {		if ((tty->session > 0) && (p->session == tty->session) &&		    p->leader) {			send_sig(SIGHUP,p,1);			send_sig(SIGCONT,p,1);			if (tty->pgrp > 0)				p->tty_old_pgrp = tty->pgrp;		}		if (p->tty == tty)			p->tty = NULL;	}	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);	unlock_kernel();}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_task(&tty->tq_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);}int tty_hung_up_p(struct file * filp){	return (filp->f_op == &hung_up_tty_fops);}/* * 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 = current->tty;	struct task_struct *p;	int tty_pgrp = -1;	if (tty) {		tty_pgrp = tty->pgrp;		if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)			tty_vhangup(tty);	} else {		if (current->tty_old_pgrp) {			kill_pg(current->tty_old_pgrp, SIGHUP, on_exit);			kill_pg(current->tty_old_pgrp, SIGCONT, on_exit);		}		return;	}	if (tty_pgrp > 0) {		kill_pg(tty_pgrp, SIGHUP, on_exit);		if (!on_exit)			kill_pg(tty_pgrp, SIGCONT, on_exit);	}	current->tty_old_pgrp = 0;	tty->session = 0;	tty->pgrp = -1;	read_lock(&tasklist_lock);	for_each_task(p)	  	if (p->session == current->session)			p->tty = NULL;	read_unlock(&tasklist_lock);}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);}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 ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&	    tty->ldisc.write_wakeup)		(tty->ldisc.write_wakeup)(tty);	wake_up_interruptible(&tty->write_wait);}static ssize_t tty_read(struct file * file, char * buf, size_t count, 			loff_t *ppos){	int i;	struct tty_struct * tty;	struct inode *inode;	/* Can't seek (pread) on ttys.  */	if (ppos != &file->f_pos)		return -ESPIPE;

⌨️ 快捷键说明

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