Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1880452
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: LINUX

2012-05-10 14:56:50

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_path.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->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);//ld->ops->write实质是n_tty_write
    tty_ldisc_deref(ld);
    return ret;
}

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);//调用n_tty_write输出
        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;
}

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;

    /* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
    if (L_TOSTOP(tty) && file->f_op->write != redirected_tty_write) {
        retval = tty_check_change(tty);
        if (retval)
            return retval;
    }

    /* Write out any echoed characters that are still pending */
    process_echoes(tty);

    add_wait_queue(&tty->write_wait, &wait);
    while (1) {
        set_current_state(TASK_INTERRUPTIBLE);
        if (signal_pending(current)) {
            retval = -ERESTARTSYS;
            break;
        }
        if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
            retval = -EIO;
            break;
        }
       //下面条件为真
        if (O_OPOST(tty) && !(test_bit(TTY_HW_COOK_OUT, &tty->flags))) {
            while (nr > 0) {
                ssize_t num = process_output_block(tty, b, nr);
                if (num < 0) {
                    if (num == -EAGAIN)
                        break;
                    retval = num;
                    goto break_out;
                }
                b += num;
                nr -= num;
                if (nr == 0)
                    break;
                c = *b;
                if (process_output(c, tty) < 0)
                    break;
                b++; nr--;
            }
            if (tty->ops->flush_chars)
                tty->ops->flush_chars(tty);
        } else {
            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;
}

static ssize_t process_output_block(struct tty_struct *tty,
                    const unsigned char *buf, unsigned int nr)
{
    int    space;
    int     i;
    const unsigned char *cp;

    mutex_lock(&tty->output_lock);

    space = tty_write_room(tty);
    if (!space) {
        mutex_unlock(&tty->output_lock);
        return 0;
    }
    if (nr > space)
        nr = space;

    for (i = 0, cp = buf; i < nr; i++, cp++) {
        unsigned char c = *cp;

        switch (c) {//c是换行等字符进行特殊处理
        case '\n':
            if (O_ONLRET(tty))
                tty->column = 0;
            if (O_ONLCR(tty))
                goto break_out;
            tty->canon_column = tty->column;
            break;
        case '\r':
            if (O_ONOCR(tty) && tty->column == 0)
                goto break_out;
            if (O_OCRNL(tty))
                goto break_out;
            tty->canon_column = tty->column = 0;
            break;
        case '\t':
            goto break_out;
        case '\b':
            if (tty->column > 0)
                tty->column--;
            break;
        default:
            if (!iscntrl(c)) {
                if (O_OLCUC(tty))
                    goto break_out;
                if (!is_continuation(c, tty))
                    tty->column++;
            }
            break;
        }
    }
break_out:
    i = tty->ops->write(tty, buf, i);

    mutex_unlock(&tty->output_lock);
    return i;
}

static int process_output(unsigned char c, struct tty_struct *tty)
{
    int    space, retval;

    mutex_lock(&tty->output_lock);

    space = tty_write_room(tty);
    retval = do_output_char(c, tty, space);

    mutex_unlock(&tty->output_lock);
    if (retval < 0)
        return -1;
    else
        return 0;
}

static int
uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port;
    struct circ_buf *circ;
    unsigned long flags;
    int c, ret = 0;

    /*
     * This means you called this function _after_ the port was
     * closed.  No cookie for you.
     */
    if (!state) {
        WARN_ON(1);
        return -EL3HLT;
    }

    port = state->port;
    circ = &state->info.xmit;

    if (!circ->buf)
        return 0;

    spin_lock_irqsave(&port->lock, flags);
    while (1) {
        c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
        if (count < c)
            c = count;
        if (c <= 0)
            break;
        memcpy(circ->buf + circ->head, buf, c);
        circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
        buf += c;
        count -= c;
        ret += c;
    }
    spin_unlock_irqrestore(&port->lock, flags);

    uart_start(tty);
    return ret;
}
阅读(1778) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~