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 + -
显示快捷键?