tty_io.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,610 行 · 第 1/5 页
C
2,610 行
work = cancel_delayed_work(&tty->flip.work); /* * Wait for ->hangup_work and ->flip.work handlers to terminate */ flush_scheduled_work(); /* Shutdown the current discipline. */ if (tty->ldisc.close) (tty->ldisc.close)(tty); /* Now set up the new line discipline. */ tty_ldisc_assign(tty, ld); tty_set_termios_ldisc(tty, ldisc); if (tty->ldisc.open) retval = (tty->ldisc.open)(tty); if (retval < 0) { tty_ldisc_put(ldisc); /* There is an outstanding reference here so this is safe */ tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num)); tty_set_termios_ldisc(tty, tty->ldisc.num); if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) { tty_ldisc_put(o_ldisc.num); /* This driver is always present */ tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); tty_set_termios_ldisc(tty, 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); } } } /* At this point we hold a reference to the new ldisc and a a reference to the old ldisc. If we ended up flipping back to the existing ldisc we have two references to it */ if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc) tty->driver->set_ldisc(tty); tty_ldisc_put(o_ldisc.num); /* * Allow ldisc referencing to occur as soon as the driver * ldisc callback completes. */ tty_ldisc_enable(tty); /* Restart it in case no characters kick it off. Safe if already running */ if(work) schedule_delayed_work(&tty->flip.work, 1); return retval;}/* * This routine returns a tty driver structure, given a device number */struct tty_driver *get_tty_driver(dev_t device, int *index){ struct tty_driver *p; list_for_each_entry(p, &tty_drivers, tty_drivers) { dev_t base = MKDEV(p->major, p->minor_start); if (device < base || device >= base + p->num) continue; *index = device - base; 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->signal->tty != tty) return 0; if (tty->pgrp <= 0) { printk(KERN_WARNING "tty_check_change: tty->pgrp <= 0!\n"); return 0; } if (process_group(current) == tty->pgrp) return 0; if (is_ignored(SIGTTOU)) return 0; if (is_orphaned_pgrp(process_group(current))) return -EIO; (void) kill_pg(process_group(current), SIGTTOU, 1); return -ERESTARTSYS;}EXPORT_SYMBOL(tty_check_change);static ssize_t hung_up_tty_read(struct file * file, char __user * buf, size_t count, loff_t *ppos){ return 0;}static ssize_t hung_up_tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ 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,};#ifdef CONFIG_UNIX98_PTYSstatic struct file_operations ptmx_fops = { .llseek = no_llseek, .read = tty_read, .write = tty_write, .poll = tty_poll, .ioctl = tty_ioctl, .open = ptmx_open, .release = tty_release, .fasync = tty_fasync,};#endifstatic struct file_operations console_fops = { .llseek = no_llseek, .read = tty_read, .write = redirected_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,};static spinlock_t redirect_lock = SPIN_LOCK_UNLOCKED;static struct file *redirect;/** * tty_wakeup - request more data * @tty: terminal * * Internal and external helper for wakeups of tty. This function * informs the line discipline if present that the driver is ready * to receive more output data. */ void tty_wakeup(struct tty_struct *tty){ struct tty_ldisc *ld; if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) { ld = tty_ldisc_ref(tty); if(ld) { if(ld->write_wakeup) ld->write_wakeup(tty); tty_ldisc_deref(ld); } } wake_up_interruptible(&tty->write_wait);}EXPORT_SYMBOL_GPL(tty_wakeup);/** * tty_ldisc_flush - flush line discipline queue * @tty: tty * * Flush the line discipline queue (if any) for this tty. If there * is no line discipline active this is a no-op. */ void tty_ldisc_flush(struct tty_struct *tty){ struct tty_ldisc *ld = tty_ldisc_ref(tty); if(ld) { if(ld->flush_buffer) ld->flush_buffer(tty); tty_ldisc_deref(ld); }}EXPORT_SYMBOL_GPL(tty_ldisc_flush); /* * 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 file *filp, *f = NULL; struct task_struct *p; struct tty_ldisc *ld; int closecount = 0, n; if (!tty) return; /* inuse_filps is protected by the single kernel lock */ lock_kernel(); spin_lock(&redirect_lock); if (redirect && redirect->private_data == tty) { f = redirect; redirect = NULL; } spin_unlock(&redirect_lock); check_tty_count(tty, "do_tty_hangup"); file_list_lock(); /* This breaks for file handles being sent over AF_UNIX sockets ? */ list_for_each_entry(filp, &tty->tty_files, f_list) { if (filp->f_op->write == redirected_tty_write) cons_filp = filp; if (filp->f_op->write != tty_write) 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.. * this question is especially important now that we've removed the irqlock. */ ld = tty_ldisc_ref(tty); if(ld != NULL) /* We may have no line discipline at this point */ { if (ld->flush_buffer) ld->flush_buffer(tty); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) && ld->write_wakeup) ld->write_wakeup(tty); if (ld->hangup) ld->hangup(tty); } /* FIXME: Once we trust the LDISC code better we can wait here for ldisc completion and fix the driver call race */ 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) { down(&tty->termios_sem); *tty->termios = tty->driver->init_termios; up(&tty->termios_sem); } /* Defer ldisc switch */ /* tty_deferred_ldisc_switch(N_TTY); This should get done automatically when the port closes and tty_release is called */ read_lock(&tasklist_lock); if (tty->session > 0) { do_each_task_pid(tty->session, PIDTYPE_SID, p) { if (p->signal->tty == tty) p->signal->tty = NULL; if (!p->signal->leader) continue; send_group_sig_info(SIGHUP, SEND_SIG_PRIV, p); send_group_sig_info(SIGCONT, SEND_SIG_PRIV, p); if (tty->pgrp > 0) p->signal->tty_old_pgrp = tty->pgrp; } while_each_task_pid(tty->session, PIDTYPE_SID, p); } 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); /* We don't want to have driver/ldisc interactions beyond the ones we did here. The driver layer expects no calls after ->hangup() from the ldisc side. However we can't yet guarantee all that */ set_bit(TTY_HUPPED, &tty->flags); if (ld) { tty_ldisc_enable(tty); tty_ldisc_deref(ld); } unlock_kernel(); if (f) fput(f);}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_work(&tty->hangup_work);}EXPORT_SYMBOL(tty_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);}EXPORT_SYMBOL(tty_vhangup);int tty_hung_up_p(struct file * filp){ return (filp->f_op == &hung_up_tty_fops);}EXPORT_SYMBOL(tty_hung_up_p);/* * 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; struct task_struct *p; int tty_pgrp = -1; lock_kernel(); tty = current->signal->tty; if (tty) { tty_pgrp = tty->pgrp; if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) tty_vhangup(tty); } else { if (current->signal->tty_old_pgrp) { kill_pg(current->signal->tty_old_pgrp, SIGHUP, on_exit); kill_pg(current->signal->tty_old_pgrp, SIGCONT, on_exit); } unlock_kernel(); return; } if (tty_pgrp > 0) { kill_pg(tty_pgrp, SIGHUP, on_exit); if (!on_exit) kill_pg(tty_pgrp, SIGCONT, on_exit); } current->signal->tty_old_pgrp = 0; tty->session = 0; tty->pgrp = -1; read_lock(&tasklist_lock); do_each_task_pid(current->signal->session, PIDTYPE_SID, p) { p->signal->tty = NULL; } while_each_task_pid(current->signal->session, PIDTYPE_SID, p); read_unlock(&tasklist_lock); unlock_kernel();}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);}EXPORT_SYMBOL(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 we have a running line discipline it may need kicking */ tty_wakeup(tty); wake_up_interruptible(&tty->write_wait);}EXPORT_SYMBOL(start_tty);static ssize_t tty_read(struct file * file, char __user * buf, size_t count, loff_t *ppos){ int i; struct tty_struct * tty; struct inode *inode; struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; inode = file->f_dentry->d_inode; if (tty_paranoia_check(tty, inode, "tty_read")) return -EIO; if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; /* We want to wait for the line discipline to sort out in this situation */ ld = tty_ldisc_ref_wait(tty); lock_kernel(); if (ld->read) i = (ld->read)(tty,file,buf,count); else i = -EIO; tty_ldisc_deref(ld); unlock_kernel(); if (i > 0) inode->i_atime = CURRENT_TIME; return i;}/* * Split writes up in sane blocksizes to avoid * denial-of-service type attacks */static inline ssize_t do_tty_write( ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char __user *, size_t), struct tty_struct *tty, struct file *file, const unsigned char __user *buf, size_t count){ ssize_t ret = 0, written = 0; if (down_interruptible(&tty->atomic_write)) { return -ERESTARTSYS; } if ( test_bit(TTY_NO_WRITE_SPLIT, &tty->flags) ) { lock_kernel(); written = write(tty, file, buf, count); unlock_kernel(); } else { for (;;) { unsigned long size = max((unsigned long)PAGE_SIZE*2, 16384UL); if (size > count) size = count; lock_kernel(); ret = write(tty, file, buf, size); unlock_kernel();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?