码迷,mamicode.com
首页 > 其他好文 > 详细

tty

时间:2018-06-24 22:31:44      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:i/o   add   date   fine   nis   run   immediate   atime   with   

/**
 *    tty_chars_in_buffer    -    characters pending
 *    @tty: terminal
 *
 *    Return the number of bytes of data in the device private
 *    output queue. If no private method is supplied there is assumed
 *    to be no queue on the device.
 */

int tty_chars_in_buffer(struct tty_struct *tty)
{
    if (tty->ops->chars_in_buffer)
        return tty->ops->chars_in_buffer(tty);
    else
        return 0;
}
EXPORT_SYMBOL(tty_chars_in_buffer);

//
static ssize_t chars_in_buffer(struct tty_struct *tty)
{
    struct n_tty_data *ldata = tty->disc_data;
    ssize_t n = 0;

    if (!ldata->icanon)
        n = ldata->commit_head - ldata->read_tail;
    else
        n = ldata->canon_head - ldata->read_tail;
    return n;
}



/**
 *    tty_read    -    read method for tty device files
 *    @file: pointer to tty file
 *    @buf: user buffer
 *    @count: size of user buffer
 *    @ppos: unused
 *
 *    Perform the read system call function on this terminal device. Checks
 *    for hung up devices before calling the line discipline method.
 *
 *    Locking:
 *        Locks the line discipline internally while needed. Multiple
 *    read calls may be outstanding in parallel.
 */

static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
            loff_t *ppos)
{
    int i;
    struct inode *inode = file_inode(file);
    struct tty_struct *tty = file_tty(file);
    struct tty_ldisc *ld;

    if (tty_paranoia_check(tty, inode, "tty_read"))
        return -EIO;
    if (!tty || tty_io_error(tty))
        return -EIO;

    /* We want to wait for the line discipline to sort out in this
       situation */
    ld = tty_ldisc_ref_wait(tty);
    if (!ld)
        return hung_up_tty_read(file, buf, count, ppos);
    if (ld->ops->read)
        i = ld->ops->read(tty, file, buf, count);
    else
        i = -EIO;
    tty_ldisc_deref(ld);

    if (i > 0)
        tty_update_time(&inode->i_atime);

    return i;
}


/**
 *    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.
 *
 *    Returns: NULL if the tty has been hungup and not re-opened with
 *         a new file descriptor, otherwise valid ldisc reference
 *
 *    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)
 *
 *    Note: a file_operations routine (read/poll/write) should use this
 *    function to wait for any ldisc lifetime events to finish.
 */

struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{
    struct tty_ldisc *ld;

    ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
    ld = tty->ldisc;
    if (!ld)
        ldsem_up_read(&tty->ldisc_sem);
    return ld;
}
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);



static inline int input_available_p(struct tty_struct *tty, int poll)
{
    struct n_tty_data *ldata = tty->disc_data;
    int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1;

    if (ldata->icanon && !L_EXTPROC(tty))
        return ldata->canon_head != ldata->read_tail;
    else
        return ldata->commit_head - ldata->read_tail >= amt;
}


/**
 *    n_tty_read        -    read function for tty
 *    @tty: tty device
 *    @file: file object
 *    @buf: userspace buffer pointer
 *    @nr: size of I/O
 *
 *    Perform reads for the line discipline. We are guaranteed that the
 *    line discipline will not be closed under us but we may get multiple
 *    parallel readers and must handle this ourselves. We may also get
 *    a hangup. Always called in user context, may sleep.
 *
 *    This code must be sure never to sleep through a hangup.
 *
 *    n_tty_read()/consumer path:
 *        claims non-exclusive termios_rwsem
 *        publishes read_tail
 */

static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
             unsigned char __user *buf, size_t nr)
{
    struct n_tty_data *ldata = tty->disc_data;
    unsigned char __user *b = buf;
    DEFINE_WAIT_FUNC(wait, woken_wake_function);
    int c;
    int minimum, time;
    ssize_t retval = 0;
    long timeout;
    int packet;
    size_t tail;

    c = job_control(tty, file);
    if (c < 0)
        return c;

    /*
     *    Internal serialization of reads.
     */
    if (file->f_flags & O_NONBLOCK) {
        if (!mutex_trylock(&ldata->atomic_read_lock))
            return -EAGAIN;
    } else {
        if (mutex_lock_interruptible(&ldata->atomic_read_lock))
            return -ERESTARTSYS;
    }

    down_read(&tty->termios_rwsem);

    minimum = time = 0;
    timeout = MAX_SCHEDULE_TIMEOUT;
    if (!ldata->icanon) {
        minimum = MIN_CHAR(tty);
        if (minimum) {
            time = (HZ / 10) * TIME_CHAR(tty);
        } else {
            timeout = (HZ / 10) * TIME_CHAR(tty);
            minimum = 1;
        }
    }

    packet = tty->packet;
    tail = ldata->read_tail;

    add_wait_queue(&tty->read_wait, &wait);
    while (nr) {
        /* First test for status change. */
        if (packet && tty->link->ctrl_status) {
            unsigned char cs;
            if (b != buf)
                break;
            spin_lock_irq(&tty->link->ctrl_lock);
            cs = tty->link->ctrl_status;
            tty->link->ctrl_status = 0;
            spin_unlock_irq(&tty->link->ctrl_lock);
            if (put_user(cs, b)) {
                retval = -EFAULT;
                break;
            }
            b++;
            nr--;
            break;
        }

        if (!input_available_p(tty, 0)) {
            up_read(&tty->termios_rwsem);
            tty_buffer_flush_work(tty->port);
            down_read(&tty->termios_rwsem);
            if (!input_available_p(tty, 0)) {
                if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
                    retval = -EIO;
                    break;
                }
                if (tty_hung_up_p(file))
                    break;
                /*
                 * Abort readers for ttys which never actually
                 * get hung up.  See __tty_hangup().
                 */
                if (test_bit(TTY_HUPPING, &tty->flags))
                    break;
                if (!timeout)
                    break;
                if (file->f_flags & O_NONBLOCK) {
                    retval = -EAGAIN;
                    break;
                }
                if (signal_pending(current)) {
                    retval = -ERESTARTSYS;
                    break;
                }
                up_read(&tty->termios_rwsem);

                timeout = wait_woken(&wait, TASK_INTERRUPTIBLE,
                        timeout);

                down_read(&tty->termios_rwsem);
                continue;
            }
        }

        if (ldata->icanon && !L_EXTPROC(tty)) {
            retval = canon_copy_from_read_buf(tty, &b, &nr);
            if (retval)
                break;
        } else {
            int uncopied;

            /* Deal with packet mode. */
            if (packet && b == buf) {
                if (put_user(TIOCPKT_DATA, b)) {
                    retval = -EFAULT;
                    break;
                }
                b++;
                nr--;
            }

            uncopied = copy_from_read_buf(tty, &b, &nr);
            uncopied += copy_from_read_buf(tty, &b, &nr);
            if (uncopied) {
                retval = -EFAULT;
                break;
            }
        }

        n_tty_check_unthrottle(tty);

        if (b - buf >= minimum)
            break;
        if (time)
            timeout = time;
    }
    if (tail != ldata->read_tail)
        n_tty_kick_worker(tty);
    up_read(&tty->termios_rwsem);

    remove_wait_queue(&tty->read_wait, &wait);
    mutex_unlock(&ldata->atomic_read_lock);

    if (b - buf)
        retval = b - buf;

    return retval;
}


/**
 *    canon_copy_from_read_buf    -    copy read data in canonical mode
 *    @tty: terminal device
 *    @b: user data
 *    @nr: size of data
 *
 *    Helper function for n_tty_read.  It is only called when ICANON is on;
 *    it copies one line of input up to and including the line-delimiting
 *    character into the user-space buffer.
 *
 *    NB: When termios is changed from non-canonical to canonical mode and
 *    the read buffer contains data, n_tty_set_termios() simulates an EOF
 *    push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer.
 *    This causes data already processed as input to be immediately available
 *    as input although a newline has not been received.
 *
 *    Called under the atomic_read_lock mutex
 *
 *    n_tty_read()/consumer path:
 *        caller holds non-exclusive termios_rwsem
 *        read_tail published
 */

static int canon_copy_from_read_buf(struct tty_struct *tty,
                    unsigned char __user **b,
                    size_t *nr)
{
    struct n_tty_data *ldata = tty->disc_data;
    size_t n, size, more, c;
    size_t eol;
    size_t tail;
    int ret, found = 0;

    /* N.B. avoid overrun if nr == 0 */
    if (!*nr)
        return 0;

    n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);

    tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
    size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);

    n_tty_trace("%s: nr:%zu tail:%zu n:%zu size:%zu\n",
            __func__, *nr, tail, n, size);

    eol = find_next_bit(ldata->read_flags, size, tail);
    more = n - (size - tail);
    if (eol == N_TTY_BUF_SIZE && more) {
        /* scan wrapped without finding set bit */
        eol = find_next_bit(ldata->read_flags, more, 0);
        found = eol != more;
    } else
        found = eol != size;

    n = eol - tail;
    if (n > N_TTY_BUF_SIZE)
        n += N_TTY_BUF_SIZE;
    c = n + found;

    if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) {
        c = min(*nr, c);
        n = c;
    }

    n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n",
            __func__, eol, found, n, c, tail, more);

    ret = tty_copy_to_user(tty, *b, tail, n);
    if (ret)
        return -EFAULT;
    *b += n;
    *nr -= n;

    if (found)
        clear_bit(eol, ldata->read_flags);
    smp_store_release(&ldata->read_tail, ldata->read_tail + c);

    if (found) {
        if (!ldata->push)
            ldata->line_start = ldata->read_tail;
        else
            ldata->push = 0;
        tty_audit_push();
    }
    return 0;
}


static int tty_copy_to_user(struct tty_struct *tty, void __user *to,
                size_t tail, size_t n)
{
    struct n_tty_data *ldata = tty->disc_data;
    size_t size = N_TTY_BUF_SIZE - tail;
    const void *from = read_buf_addr(ldata, tail);
    int uncopied;

    if (n > size) {
        tty_audit_add_data(tty, from, size);
        uncopied = copy_to_user(to, from, size);
        if (uncopied)
            return uncopied;
        to += size;
        n -= size;
        from = ldata->read_buf;
    }

    tty_audit_add_data(tty, from, n);
    return copy_to_user(to, from, n);
}

 

tty

标签:i/o   add   date   fine   nis   run   immediate   atime   with   

原文地址:https://www.cnblogs.com/fluray/p/9221821.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!