tty driver(4)

与tty_write相关的数据成员

struct tty_struct {/*在tty_io层维护*/
    unsigned char *write_buf;
    int write_cnt;
}

1]数据从用户空间copy到内核空间

tty_write -> do_tty_write(ld->ops->write, tty, file, buf, count)
{
    tty_write_lock(tty, file->f_flags & O_NDELAY);
    /*
     * We chunk up writes into a temporary buffer. This
     * simplifies low-level drivers immensely, since they
     * don't have locking issues and user mode accesses.
     *
     * But if TTY_NO_WRITE_SPLIT is set, we should use a
     * big chunk-size..
     *
     * The default chunk-size is 2kB, because the NTTY
     * layer has problems with bigger chunks. It will
     * claim to be able to handle more characters than
     * it actually does.
     *
     * FIXME: This can probably go away now except that 64K chunks
     * are too likely to fail unless switched to vmalloc...
     */

    chunk = 2048;
    if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
        chunk = 65536;
    if (count < chunk)
        chunk = count;
    /*申请kernel memory for writing operation*/
    /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
    if (tty->write_cnt < chunk) {
        unsigned char *buf_chunk;

        if (chunk < 1024)
            chunk = 1024;

        buf_chunk = kmalloc(chunk, GFP_KERNEL);
        if (!buf_chunk) {
            ret = -ENOMEM;
            goto out;
        }
        kfree(tty->write_buf);
        tty->write_cnt = chunk;
        tty->write_buf = buf_chunk;
    }
    /*把来自用户空间的数据copy到内核空间*/
    copy_from_user(tty->write_buf, buf, size);
    /*调用行规write operation*/
    write(tty, file, tty->write_buf, size);

    tty_write_unlock(tty);
}

2]数据从内核write_buffer到行规/tty_driver

n_tty_write(struct tty_struct *tty, struct file *file,
       const unsigned char *buf, size_t nr)
    -> tty->ops->write(tty, b, nr);
        -> {
            smd_stream_write_avail(tty_info->ch);
            smd_stream_write(tty_info->ch, buf, len);
           }

tty 写操作流程

1. tty_io 层

1.1 tty_write

/**

 *    tty_write        -    write method for tty device file
 *    @file: tty file pointer
 *    @buf: user data to write
 *    @count: bytes to write
 *    @ppos: unused
 *
 *    Write data to a tty device via the line discipline.
 *
 *    Locking:
 *        Locks the line discipline as required
 *        Writes to the tty driver are serialized by the atomic_write_lock
 *    and are then processed in chunks to the device. The line discipline
 *    write method will not be invoked in parallel for each device.
 */

static ssize_t tty_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
    struct inode *inode = file->f_path.dentry->d_inode;
    struct tty_struct *tty = file_tty(file);
     struct tty_ldisc *ld;
    ssize_t ret;

/*1. check the input arguments */

    if (tty_paranoia_check(tty, inode, "tty_write"))
        return -EIO;
    if (!tty || !tty->ops->write ||
        (test_bit(TTY_IO_ERROR, &tty->flags)))
        return -EIO;
    /* Short term debug to catch buggy drivers */
    if (tty->ops->write_room == NULL)
        printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
            tty->driver->name);

/*2. get the line discipline by tty */

    ld = tty_ldisc_ref_wait(tty);

/*3. call do_tty_write with ldops write operation */

    if (!ld->ops->write)
        ret = -EIO;
    else
        ret = do_tty_write(ld->ops->write, tty, file, buf, count);

/*4. free a tty ldisc reference */

    tty_ldisc_deref(ld);
    return ret;
}

1.2 do_tty_write

/*
 * 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 *, size_t),
    struct tty_struct *tty,
    struct file *file,
    const char __user *buf,
    size_t count)
{
    ssize_t ret, written = 0;
    unsigned int chunk;

/*1. get the atomic_write_lock */

    ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
    if (ret < 0)
        return ret;

    /*
     * We chunk up writes into a temporary buffer. This
     * simplifies low-level drivers immensely, since they
     * don't have locking issues and user mode accesses.
     *
     * But if TTY_NO_WRITE_SPLIT is set, we should use a
     * big chunk-size..
     *
     * The default chunk-size is 2kB, because the NTTY
     * layer has problems with bigger chunks. It will
     * claim to be able to handle more characters than
     * it actually does.
     *
     * FIXME: This can probably go away now except that 64K chunks
     * are too likely to fail unless switched to vmalloc...
     */

/*2. 分配 write_buf and update the member of write_cnt, write_buf*/

    chunk = 2048;
    if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
        chunk = 65536;
    if (count < chunk)
        chunk = count;

    /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
    if (tty->write_cnt < chunk) {
        unsigned char *buf_chunk;

        if (chunk < 1024)
            chunk = 1024;

        buf_chunk = kmalloc(chunk, GFP_KERNEL);
        if (!buf_chunk) {
            ret = -ENOMEM;
            goto out;
        }
        kfree(tty->write_buf);
        tty->write_cnt = chunk;
        tty->write_buf = buf_chunk;
    }

/*3. Do the write .. */

    for (;;) {
        size_t size = count;
        if (size > chunk)
            size = chunk;
        ret = -EFAULT;
    /*3.1 把来自用户空间的数据保存到分配的 write_buf内*/
        if (copy_from_user(tty->write_buf, buf, size))
            break;
    /*3.2 通过函数指针调用 n_tty_write*/
        ret = write(tty, file, tty->write_buf, size);
        if (ret <= 0)
            break;
        written += ret;
        buf += ret;
        count -= ret;
        if (!count)
            break;
        ret = -ERESTARTSYS;
        /*signal pending,外界发送命令停止传送等情况*/
        if (signal_pending(current))
            break;
        cond_resched();
    }

/*4. 有数据写入,更新 inode的时间信息,估计就是最后一次写入时间*/

    if (written) {
        struct inode *inode = file->f_path.dentry->d_inode;
        inode->i_mtime = current_fs_time(inode->i_sb);
        ret = written;
    }
out:

/*5. 释放atomic_write_lock*/

    tty_write_unlock(tty);
    return ret;
}

2. n_tty层


/**
 *    n_tty_write        -    write function for tty
 *    @tty: tty device
 *    @file: file object
 *    @buf: userspace buffer pointer
 *    @nr: size of I/O
 *
 *    Write function of the terminal device.  This is serialized with
 *    respect to other write callers but not to termios changes, reads
 *    and other such events.  Since the receive code will echo characters,
 *    thus calling driver write methods, the output_lock is used in
 *    the output processing functions called here as well as in the
 *    echo processing function to protect the column state and space
 *    left in the buffer.
 *
 *    This code must be sure never to sleep through a hangup.
 *
 *    Locking: output_lock to protect column state and space left
 *         (note that the process_output*() functions take this
 *          lock themselves)
 */

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
               const unsigned char *buf, size_t nr)
{
    const unsigned char *b = buf;
    DECLARE_WAITQUEUE(wait, current);
    int c;
    ssize_t retval = 0;
/*不知这里的 waitqueue什么个意思?*/
    add_wait_queue(&tty->write_wait, &wait);
    while (1) {
        set_current_state(TASK_INTERRUPTIBLE);
            while (nr > 0) {
/*这里是多次写入的,如果期望写入的大小和返回值【实际写入的】不一致,继续写剩下的
 *这有点流控的意思,不知道写操作的流控是怎样实现的?
 */
                c = tty->ops->write(tty, b, nr);
                if (c < 0) {
                    retval = c;
                    goto break_out;
                }
                if (!c)
                    break;
                b += c;
                nr -= c;
            }

        if (!nr)
            break;
        if (file->f_flags & O_NONBLOCK) {
            retval = -EAGAIN;
            break;
        }
        schedule();
    }
break_out:
    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&tty->write_wait, &wait);
    if (b - buf != nr && tty->fasync)
        set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
    return (b - buf) ? b - buf : retval;
}

3. 硬件具体的层

static int smd_tty_write(struct tty_struct *tty,
             const unsigned char *buf, int len)
{
    struct smd_tty_info *info = tty->driver_data;
    int avail;

    /* if we're writing to a packet channel we will
    ** never be able to write more data than there
    ** is currently space for
    */
    avail = smd_write_avail(info->ch);
    if (len > avail)
        len = avail;
    return smd_write(info->ch, buf, len);
}


4 总结

tty write操作分问3个层次 tty_io, n_tty,  设备具体的tty_driver.从中个层次间的调用关系也
看出,设备具体的要实现那些函数,或者那些函数是必须的。
    int  (*write_room)(struct tty_struct *tty);    /*有多少写空间*/
    int  (*write)(struct tty_struct * tty,        /*具体硬件相关的写操作*/    
              const unsigned char *buf, int count);



static ssize_t tty_write(struct file *file, const char __user *buf,
                        size_t count, loff_t *ppos)
{
    struct inode *inode = file->f_path.dentry->d_inode;
    struct tty_struct *tty = file_tty(file);
     struct tty_ldisc *ld;
    ssize_t ret;

    if (tty_paranoia_check(tty, inode, "tty_write"))
        return -EIO;
    if (!tty || !tty->ops->write ||
        (test_bit(TTY_IO_ERROR, &tty->flags)))
            return -EIO;
    /* Short term debug to catch buggy drivers */
    if (tty->ops->write_room == NULL)
        printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
            tty->driver->name);
    ld = tty_ldisc_ref_wait(tty);
    if (!ld->ops->write)
        ret = -EIO;
    else
        ret = do_tty_write(ld->ops->write, tty, file, buf, count);
    tty_ldisc_deref(ld);
    return ret;
}

/*
 * 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 *, size_t),
    struct tty_struct *tty,
    struct file *file,
    const char __user *buf,
    size_t count)
{
    ssize_t ret, written = 0;
    unsigned int chunk;

    ret = tty_write_lock(tty, file->f_flags & O_NDELAY);
    if (ret < 0)
        return ret;

    /*
     * We chunk up writes into a temporary buffer. This
     * simplifies low-level drivers immensely, since they
     * don't have locking issues and user mode accesses.
     *
     * But if TTY_NO_WRITE_SPLIT is set, we should use a
     * big chunk-size..
     *
     * The default chunk-size is 2kB, because the NTTY
     * layer has problems with bigger chunks. It will
     * claim to be able to handle more characters than
     * it actually does.
     *
     * FIXME: This can probably go away now except that 64K chunks
     * are too likely to fail unless switched to vmalloc...
     */

    chunk = 2048;
    if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
        chunk = 65536;
    if (count < chunk)
        chunk = count;

    /* write_buf/write_cnt is protected by the atomic_write_lock mutex */
    if (tty->write_cnt < chunk) {
        unsigned char *buf_chunk;

        if (chunk < 1024)
            chunk = 1024;

        buf_chunk = kmalloc(chunk, GFP_KERNEL);
        if (!buf_chunk) {
            ret = -ENOMEM;
            goto out;
        }
        kfree(tty->write_buf);
        tty->write_cnt = chunk;
        tty->write_buf = buf_chunk;
    }

/* Do the write ..
 * 这个过程会被阻塞吗?
 **/

    for (;;) {
        size_t size = count;
        if (size > chunk)
            size = chunk;
        ret = -EFAULT;
        if (copy_from_user(tty->write_buf, buf, size))
            break;
        ret = write(tty, file, tty->write_buf, size);
        if (ret <= 0)
            break;
        written += ret;
        buf += ret;
        count -= ret;
        if (!count)
            break;
        ret = -ERESTARTSYS;
        if (signal_pending(current))
            break;
        cond_resched();
    }
    if (written) {
        struct inode *inode = file->f_path.dentry->d_inode;
        inode->i_mtime = current_fs_time(inode->i_sb);
        ret = written;
    }
out:
    tty_write_unlock(tty);
    return ret;
}


/**
 *    n_tty_write        -    write function for tty
 *    @tty: tty device
 *    @file: file object
 *    @buf: userspace buffer pointer
 *    @nr: size of I/O
 **/

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
               const unsigned char *buf, size_t nr)
{
    const unsigned char *b = buf;
    DECLARE_WAITQUEUE(wait, current);
    int c;
    ssize_t retval = 0;

    add_wait_queue(&tty->write_wait, &wait);

    while (1) {
/* 设置状态TASK_INTERRUPTIBLE,应该并不会把进程设置为睡眠状态,直到schedule()
 *
 **/

        set_current_state(TASK_INTERRUPTIBLE);
        if (signal_pending(current)) {
            retval = -ERESTARTSYS;
            break;
        }

        while (nr > 0) {
            c = tty->ops->write(tty, b, nr);
            if (c < 0) {
                retval = c;
                goto break_out;
            }
            /* 如果这里写入数据为0,当前进程将会sleep,什么时候醒啊?
             * 这里看是存在隐患的。
             **/

            if (!c)
                break;
            b += c;
            nr -= c;
        }


        if (!nr)
            break;
        if (file->f_flags & O_NONBLOCK) {
            retval = -EAGAIN;
            break;
        }
        schedule();
    }
break_out:
    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&tty->write_wait, &wait);
    if (b - buf != nr && tty->fasync)
        set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
    return (b - buf) ? b - buf : retval;
}

static int smd_tty_write(struct tty_struct *tty,
             const unsigned char *buf, int len)
{
    struct smd_tty_info *tty_info = tty->driver_data;
    int avail;
    dbg("enter %s\n",__func__);
    /* if we're writing to a packet channel we will
    ** never be able to write more data than there
    ** is currently space for
    */
    avail = smd_stream_write_avail(tty_info->ch);
    dbg("avail=%d\n", avail);
    if (len > avail)
        len = avail;

    if (len) {
        len = smd_stream_write(tty_info->ch, buf, len);
        smd_send_intr(tty_info->a2b_int_tx);
    }

    return len;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值