tty_io.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,610 行 · 第 1/5 页
C
2,610 行
if (ret <= 0) break; written += ret; buf += ret; count -= ret; if (!count) break; ret = -ERESTARTSYS; if (signal_pending(current)) break; cond_resched(); } } if (written) { file->f_dentry->d_inode->i_mtime = CURRENT_TIME; ret = written; } up(&tty->atomic_write); return ret;}static ssize_t tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ struct tty_struct * tty; struct inode *inode = file->f_dentry->d_inode; ssize_t ret; struct tty_ldisc *ld; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode, "tty_write")) return -EIO; if (!tty || !tty->driver->write || (test_bit(TTY_IO_ERROR, &tty->flags))) return -EIO; ld = tty_ldisc_ref_wait(tty); if (!ld->write) ret = -EIO; else ret = do_tty_write(ld->write, tty, file, (const unsigned char __user *)buf, count); tty_ldisc_deref(ld); return ret;}ssize_t redirected_tty_write(struct file * file, const char __user * buf, size_t count, loff_t *ppos){ struct file *p = NULL; spin_lock(&redirect_lock); if (redirect) { get_file(redirect); p = redirect; } spin_unlock(&redirect_lock); if (p) { ssize_t res; res = vfs_write(p, buf, count, &p->f_pos); fput(p); return res; } return tty_write(file, buf, count, ppos);}static char ptychar[] = "pqrstuvwxyzabcde";static inline void pty_line_name(struct tty_driver *driver, int index, char *p){ int i = index + driver->name_base; /* ->name is initialized to "ttyp", but "tty" is expected */ sprintf(p, "%s%c%x", driver->subtype == PTY_TYPE_SLAVE ? "tty" : driver->name, ptychar[i >> 4 & 0xf], i & 0xf);}static inline void tty_line_name(struct tty_driver *driver, int index, char *p){ sprintf(p, "%s%d", driver->name, index + driver->name_base);}/* * WSH 06/09/97: Rewritten to remove races and properly clean up after a * failed open. The new code protects the open with a semaphore, so it's * really quite straightforward. The semaphore locking can probably be * relaxed for the (most common) case of reopening a tty. */static int init_dev(struct tty_driver *driver, int idx, struct tty_struct **ret_tty){ struct tty_struct *tty, *o_tty; struct termios *tp, **tp_loc, *o_tp, **o_tp_loc; struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc; int retval=0; /* * Check whether we need to acquire the tty semaphore to avoid * race conditions. For now, play it safe. */ down(&tty_sem); /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { tty = devpts_get_tty(idx); if (tty && driver->subtype == PTY_TYPE_MASTER) tty = tty->link; } else { tty = driver->ttys[idx]; } if (tty) goto fast_track; /* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released. (Except that the termios * and locked termios may be retained.) */ if (!try_module_get(driver->owner)) { retval = -ENODEV; goto end_init; } o_tty = NULL; tp = o_tp = NULL; ltp = o_ltp = NULL; tty = alloc_tty_struct(); if(!tty) goto fail_no_mem; initialize_tty_struct(tty); tty->driver = driver; tty->index = idx; tty_line_name(driver, idx, tty->name); if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { tp_loc = &tty->termios; ltp_loc = &tty->termios_locked; } else { tp_loc = &driver->termios[idx]; ltp_loc = &driver->termios_locked[idx]; } if (!*tp_loc) { tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!tp) goto free_mem_out; *tp = driver->init_termios; } if (!*ltp_loc) { ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!ltp) goto free_mem_out; memset(ltp, 0, sizeof(struct termios)); } if (driver->type == TTY_DRIVER_TYPE_PTY) { o_tty = alloc_tty_struct(); if (!o_tty) goto free_mem_out; initialize_tty_struct(o_tty); o_tty->driver = driver->other; o_tty->index = idx; tty_line_name(driver->other, idx, o_tty->name); if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { o_tp_loc = &o_tty->termios; o_ltp_loc = &o_tty->termios_locked; } else { o_tp_loc = &driver->other->termios[idx]; o_ltp_loc = &driver->other->termios_locked[idx]; } if (!*o_tp_loc) { o_tp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_tp) goto free_mem_out; *o_tp = driver->other->init_termios; } if (!*o_ltp_loc) { o_ltp = (struct termios *) kmalloc(sizeof(struct termios), GFP_KERNEL); if (!o_ltp) goto free_mem_out; memset(o_ltp, 0, sizeof(struct termios)); } /* * Everything allocated ... set up the o_tty structure. */ if (!(driver->other->flags & TTY_DRIVER_DEVPTS_MEM)) { driver->other->ttys[idx] = o_tty; } if (!*o_tp_loc) *o_tp_loc = o_tp; if (!*o_ltp_loc) *o_ltp_loc = o_ltp; o_tty->termios = *o_tp_loc; o_tty->termios_locked = *o_ltp_loc; driver->other->refcount++; if (driver->subtype == PTY_TYPE_MASTER) o_tty->count++; /* Establish the links in both directions */ tty->link = o_tty; o_tty->link = tty; } /* * All structures have been allocated, so now we install them. * Failures after this point use release_mem to clean up, so * there's no need to null out the local pointers. */ if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM)) { driver->ttys[idx] = tty; } if (!*tp_loc) *tp_loc = tp; if (!*ltp_loc) *ltp_loc = ltp; tty->termios = *tp_loc; tty->termios_locked = *ltp_loc; driver->refcount++; tty->count++; /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_mem to clean up. No need * to decrement the use counts, as release_mem doesn't care. */ if (tty->ldisc.open) { retval = (tty->ldisc.open)(tty); if (retval) goto release_mem_out; } if (o_tty && o_tty->ldisc.open) { retval = (o_tty->ldisc.open)(o_tty); if (retval) { if (tty->ldisc.close) (tty->ldisc.close)(tty); goto release_mem_out; } tty_ldisc_enable(o_tty); } tty_ldisc_enable(tty); goto success; /* * This fast open can be used if the tty is already open. * No memory is allocated, and the only failures are from * attempting to open a closing tty or attempting multiple * opens on a pty master. */fast_track: if (test_bit(TTY_CLOSING, &tty->flags)) { retval = -EIO; goto end_init; } if (driver->type == TTY_DRIVER_TYPE_PTY && driver->subtype == PTY_TYPE_MASTER) { /* * special case for PTY masters: only one open permitted, * and the slave side open count is incremented as well. */ if (tty->count) { retval = -EIO; goto end_init; } tty->link->count++; } tty->count++; tty->driver = driver; /* N.B. why do this every time?? */ /* FIXME */ if(!test_bit(TTY_LDISC, &tty->flags)) printk(KERN_ERR "init_dev but no ldisc\n");success: *ret_tty = tty; /* All paths come through here to release the semaphore */end_init: up(&tty_sem); return retval; /* Release locally allocated memory ... nothing placed in slots */free_mem_out: if (o_tp) kfree(o_tp); if (o_tty) free_tty_struct(o_tty); if (ltp) kfree(ltp); if (tp) kfree(tp); free_tty_struct(tty);fail_no_mem: module_put(driver->owner); retval = -ENOMEM; goto end_init; /* call the tty release_mem routine to clean out this slot */release_mem_out: printk(KERN_INFO "init_dev: ldisc open failed, " "clearing slot %d\n", idx); release_mem(tty, idx); goto end_init;}/* * Releases memory associated with a tty structure, and clears out the * driver table slots. */static void release_mem(struct tty_struct *tty, int idx){ struct tty_struct *o_tty; struct termios *tp; int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM; if ((o_tty = tty->link) != NULL) { if (!devpts) o_tty->driver->ttys[idx] = NULL; if (o_tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { tp = o_tty->termios; if (!devpts) o_tty->driver->termios[idx] = NULL; kfree(tp); tp = o_tty->termios_locked; if (!devpts) o_tty->driver->termios_locked[idx] = NULL; kfree(tp); } o_tty->magic = 0; o_tty->driver->refcount--; file_list_lock(); list_del_init(&o_tty->tty_files); file_list_unlock(); free_tty_struct(o_tty); } if (!devpts) tty->driver->ttys[idx] = NULL; if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { tp = tty->termios; if (!devpts) tty->driver->termios[idx] = NULL; kfree(tp); tp = tty->termios_locked; if (!devpts) tty->driver->termios_locked[idx] = NULL; kfree(tp); } tty->magic = 0; tty->driver->refcount--; file_list_lock(); list_del_init(&tty->tty_files); file_list_unlock(); module_put(tty->driver->owner); free_tty_struct(tty);}/* * Even releasing the tty structures is a tricky business.. We have * to be very careful that the structures are all released at the * same time, as interrupts might otherwise get the wrong pointers. * * WSH 09/09/97: rewritten to avoid some nasty race conditions that could * lead to double frees or releasing memory still in use. */static void release_dev(struct file * filp){ struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int devpts_master, devpts; int idx; char buf[64]; unsigned long flags; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_dentry->d_inode, "release_dev")) return; check_tty_count(tty, "release_dev"); tty_fasync(-1, filp, 0); idx = tty->index; pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY && tty->driver->subtype == PTY_TYPE_MASTER); devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0; devpts_master = pty_master && devpts; o_tty = tty->link;#ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver->num) { printk(KERN_DEBUG "release_dev: bad idx when trying to " "free (%s)\n", tty->name); return; } if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (tty != tty->driver->ttys[idx]) { printk(KERN_DEBUG "release_dev: driver.table[%d] not tty " "for (%s)\n", idx, tty->name); return; } if (tty->termios != tty->driver->termios[idx]) { printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios " "for (%s)\n", idx, tty->name); return; } if (tty->termios_locked != tty->driver->termios_locked[idx]) { printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not " "termios_locked for (%s)\n", idx, tty->name); return; } }#endif#ifdef TTY_DEBUG_HANGUP printk(KERN_DEBUG "release_dev of %s (tty count=%d)...", tty_name(tty, buf), tty->count);#endif#ifdef TTY_PARANOIA_CHECK if (tty->driver->other && !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) { if (o_tty != tty->driver->other->ttys[idx]) { printk(KERN_DEBUG "release_dev: other->table[%d] " "not o_tty for (%s)\n", idx, tty->name); return; } if (o_tty->termios != tty->driver->other->termios[idx]) { printk(KERN_DEBUG "release_dev: other->termios[%d] " "not o_termios for (%s)\n", idx, tty->name); return; } if (o_tty->termios_locked != tty->driver->other->termios_locked[idx]) { printk(KERN_DEBUG "release_dev: other->termios_locked[" "%d] not o_termios_locked for (%s)\n", idx, tty->name); return; } if (o_tty->link != tty) { printk(KERN_DEBUG "release_dev: bad pty pointers\n"); return; } }#endif if (tty->driver->close) tty->driver->close(tty, filp); /* * Sanity check: if tty->count is going to zero, there shouldn't be * any waiters on tty->read_wait or tty->write_wait. We test the * wait queues and kick everyone out _before_ actually starting to * close. This ensures that we won't block while releasing the tty * structure. * * The test for the o_tty closing is necessary, since the master and * slave sides may close in any order. If the slave side closes out * first, its count will be one, since the master side holds an open. * Thus this test wouldn't be triggered at the time the slave closes, * so we do it now. * * Note that it's possible for the tty to be opened again while we're * flushing out waiters. By recalculating the closing flags before * each iteration we avoid any problems. */ while (1) { tty_closing = tty->count <= 1; o_tty_closing = o_tty && (o_tty->count <= (pty_master ? 1 : 0)); do_sleep = 0; if (tty_closing) { if (waitqueue_active(&tty->read_wait)) { wake_up(&tty->read_wait); do_sleep++; } if (waitqueue_active(&tty->write_wait)) { wake_up(&tty->write_wait); do_sleep++; } } if (o_tty_closing) { if (waitqueue_active(&o_tty->read_wait)) { wake_up(&o_tty->read_wait); do_sleep++; } if (waitqueue_active(&o_tty->write_wait)) { wake_up(&o_tty->write_wait); do_sleep++; } } if (!do_sleep) break; printk(KERN_WARNING "release_dev: %s: read/write wait queue " "active!\n", tty_name(tty, buf)); schedule(); } /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?