📄 tty_io.c
字号:
kfree_s(o_tp, sizeof(struct termios)); if (o_tty) free_page((unsigned long) o_tty); if (ltp) kfree_s(ltp, sizeof(struct termios)); if (tp) kfree_s(tp, sizeof(struct termios)); free_page((unsigned long) tty);fail_no_mem: retval = -ENOMEM; goto end_init; /* call the tty release_mem routine to clean out this slot */release_mem_out: printk("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; if ((o_tty = tty->link) != NULL) { o_tty->driver.table[idx] = NULL; if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { tp = o_tty->driver.termios[idx]; o_tty->driver.termios[idx] = NULL; kfree_s(tp, sizeof(struct termios)); } o_tty->magic = 0; (*o_tty->driver.refcount)--; free_page((unsigned long) o_tty); } tty->driver.table[idx] = NULL; if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) { tp = tty->driver.termios[idx]; tty->driver.termios[idx] = NULL; kfree_s(tp, sizeof(struct termios)); } tty->magic = 0; (*tty->driver.refcount)--; free_page((unsigned long) 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. */static void release_dev(struct file * filp){ struct tty_struct *tty, *o_tty; int pty_master, tty_closing, o_tty_closing, do_sleep; int idx; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev")) return; check_tty_count(tty, "release_dev"); tty_fasync(filp->f_inode, filp, 0); idx = MINOR(tty->device) - tty->driver.minor_start; pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER); o_tty = tty->link;#ifdef TTY_PARANOIA_CHECK if (idx < 0 || idx >= tty->driver.num) { printk("release_dev: bad idx when trying to free (%s)\n", kdevname(tty->device)); return; } if (tty != tty->driver.table[idx]) { printk("release_dev: driver.table[%d] not tty for (%s)\n", idx, kdevname(tty->device)); return; } if (tty->termios != tty->driver.termios[idx]) { printk("release_dev: driver.termios[%d] not termios " "for (%s)\n", idx, kdevname(tty->device)); return; } if (tty->termios_locked != tty->driver.termios_locked[idx]) { printk("release_dev: driver.termios_locked[%d] not " "termios_locked for (%s)\n", idx, kdevname(tty->device)); return; }#endif#ifdef TTY_DEBUG_HANGUP printk("release_dev of %s (tty count=%d)...", tty_name(tty), tty->count);#endif#ifdef TTY_PARANOIA_CHECK if (tty->driver.other) { if (o_tty != tty->driver.other->table[idx]) { printk("release_dev: other->table[%d] not o_tty for (" "%s)\n", idx, kdevname(tty->device)); return; } if (o_tty->termios != tty->driver.other->termios[idx]) { printk("release_dev: other->termios[%d] not o_termios " "for (%s)\n", idx, kdevname(tty->device)); return; } if (o_tty->termios_locked != tty->driver.other->termios_locked[idx]) { printk("release_dev: other->termios_locked[%d] not " "o_termios_locked for (%s)\n", idx, kdevname(tty->device)); return; } if (o_tty->link != tty) { printk("release_dev: bad pty pointers\n"); return; } }#endif /* * 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("release_dev: %s: read/write wait queue active!\n", tty_name(tty)); schedule(); } /* * The closing flags are now consistent with the open counts on * both sides, and we've completed the last operation that could * block, so it's safe to proceed with closing. */ if (tty->driver.close) tty->driver.close(tty, filp); if (pty_master) { if (--o_tty->count < 0) { printk("release_dev: bad pty slave count (%d) for %s\n", o_tty->count, tty_name(o_tty)); o_tty->count = 0; } } if (--tty->count < 0) { printk("release_dev: bad tty->count (%d) for %s\n", tty->count, tty_name(tty)); tty->count = 0; } /* * Perform some housekeeping before deciding whether to return. * * Set the TTY_CLOSING flag if this was the last open. In the * case of a pty we may have to wait around for the other side * to close, and TTY_CLOSING makes sure we can't be reopened. */ if(tty_closing) set_bit(TTY_CLOSING, &tty->flags); if(o_tty_closing) set_bit(TTY_CLOSING, &o_tty->flags); /* * If _either_ side is closing, make sure there aren't any * processes that still think tty or o_tty is their controlling * tty. Also, clear redirect if it points to either tty. */ if (tty_closing || o_tty_closing) { struct task_struct *p; for_each_task(p) { if (p->tty == tty || (o_tty && p->tty == o_tty)) p->tty = NULL; } if (redirect == tty || (o_tty && redirect == o_tty)) redirect = NULL; } /* check whether both sides are closing ... */ if (!tty_closing || (o_tty && !o_tty_closing)) return; filp->private_data = 0; #ifdef TTY_DEBUG_HANGUP printk("freeing tty structure...");#endif /* * Shutdown the current line discipline, and reset it to N_TTY. * N.B. why reset ldisc when we're releasing the memory?? */ if (tty->ldisc.close) (tty->ldisc.close)(tty); tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (o_tty) { if (o_tty->ldisc.close) (o_tty->ldisc.close)(o_tty); o_tty->ldisc = ldiscs[N_TTY]; } /* * Make sure that the tty's task queue isn't activated. If it * is, take it out of the linked list. The tqueue isn't used by * pty's, so skip the test for them. */ if (tty->driver.type != TTY_DRIVER_TYPE_PTY) { cli(); if (tty->flip.tqueue.sync) { struct tq_struct *tq, *prev; for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) { if (tq == &tty->flip.tqueue) { if (prev) prev->next = tq->next; else tq_timer = tq->next; break; } } } sti(); } /* * The release_mem function takes care of the details of clearing * the slots and preserving the termios structure. */ release_mem(tty, idx);}/* * tty_open and tty_release keep up the tty count that contains the * number of opens done on a tty. We cannot use the inode-count, as * different inodes might point to the same tty. * * Open-counting is needed for pty masters, as well as for keeping * track of serial lines: DTR is dropped when the last close happens. * (This is not done solely through tty->count, now. - Ted 1/27/92) * * The termios state of a pty is reset on first open so that * settings don't persist across reuse. */static int tty_open(struct inode * inode, struct file * filp){ struct tty_struct *tty; int minor; int noctty, retval; kdev_t device;retry_open: noctty = filp->f_flags & O_NOCTTY; device = inode->i_rdev; if (device == TTY_DEV) { if (!current->tty) return -ENXIO; device = current->tty->device; /* noctty = 1; */ } if (device == CONSOLE_DEV) { device = MKDEV(TTY_MAJOR, fg_console+1); noctty = 1; } minor = MINOR(device); retval = init_dev(device, &tty); if (retval) return retval; filp->private_data = tty; check_tty_count(tty, "tty_open"); if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER) noctty = 1;#ifdef TTY_DEBUG_HANGUP printk("opening %s...", tty_name(tty));#endif if (tty->driver.open) retval = tty->driver.open(tty, filp); else retval = -ENODEV; if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser()) retval = -EBUSY; if (retval) {#ifdef TTY_DEBUG_HANGUP printk("error %d in opening %s...", retval, tty_name(tty));#endif release_dev(filp); if (retval != -ERESTARTSYS) return retval; if (current->signal & ~current->blocked) return retval; schedule(); /* * Need to reset f_op in case a hangup happened. */ filp->f_op = &tty_fops; goto retry_open; } if (!noctty && current->leader && !current->tty && tty->session == 0) { current->tty = tty; current->tty_old_pgrp = 0; tty->session = current->session; tty->pgrp = current->pgrp; } return 0;}static void tty_release(struct inode * inode, struct file * filp){ release_dev(filp);}static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait){ struct tty_struct * tty; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_select")) return 0; if (tty->ldisc.select) return (tty->ldisc.select)(tty, inode, filp, sel_type, wait); return 0;}/* * fasync_helper() is used by some character device drivers (mainly mice) * to set up the fasync queue. It returns negative on error, 0 if it did * no changes and positive if it added/deleted the entry. */int fasync_helper(struct inode * inode, struct file * filp, int on, struct fasync_struct **fapp){ struct fasync_struct *fa, **fp; unsigned long flags; for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) { if (fa->fa_file == filp) break; } if (on) { if (fa) return 0; fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL); if (!fa) return -ENOMEM; fa->magic = FASYNC_MAGIC; fa->fa_file = filp; save_flags(flags); cli(); fa->fa_next = *fapp; *fapp = fa; restore_flags(flags); return 1; } if (!fa) return 0; save_flags(flags); cli(); *fp = fa->fa_next; restore_flags(flags); kfree(fa); return 1;}static int tty_fasync(struct inode * inode, struct file * filp, int on){ struct tty_struct * tty; int retval; tty = (struct tty_struct *)filp->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync")) return 0; retval = fasync_helper(inode, filp, on, &tty->fasync); if (retval <= 0) return retval; if (on) { if (!waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = 1; if (filp->f_owner.pid == 0) { filp->f_owner.pid = (-tty->pgrp) ? : current->pid; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid; } } else { if (!tty->fasync && !waitqueue_active(&tty->read_wait)) tty->minimum_to_wake = N_TTY_BUF_SIZE; } return 0;}#if 0/* * XXX does anyone use this anymore?!? */static int do_get_ps_info(unsigned long arg){ struct tstruct { int flag; int present[NR_TASKS]; struct task_struct tasks[NR_TASKS]; }; struct tstruct *ts = (struct tstruct *)arg; struct task_struct **p; char *c, *d; int i, n = 0; i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct)); if (i) return i; for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++) if (*p) { c = (char *)(*p); d = (char *)(ts->tasks+n); for (i=0 ; i<sizeof(struct task_struct) ; i++) put_user(*c++, d++); put_user(1, ts->present+n); } else put_user(0, ts->present+n); return(0); }#endifstatic int tty_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg){ int retval; struct tty_struct * tty; struct tty_struct * real_tty; struct winsize tmp_ws; pid_t pgrp; unsigned char ch; char mbz = 0; tty = (struct tty_struct *)file->private_data; if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl")) return -EINVAL; if (tty->driver.type == TTY_DRIVER_TYPE_PTY && tty->driver.subtype == PTY_TYPE_MASTER) real_tty = tty->link; else real_tty = tty; switch (cmd) { case TIOCSTI: if ((current->tty != tty) && !suser()) return -EPERM; retval = verify_area(VERIFY_READ, (void *) arg, 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -