Chinaunix首页 | 论坛 | 博客
  • 博客访问: 175147
  • 博文数量: 28
  • 博客积分: 817
  • 博客等级: 军士长
  • 技术积分: 947
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-16 15:32
文章分类
文章存档

2012年(4)

2011年(18)

2010年(6)

我的朋友

分类: LINUX

2011-10-12 21:00:13

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:amingriyue.blog.chinaunix.net
------------------------------------------
我们上一节分析了tty_open,这一节我们分析tty_read。
static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
            loff_t *ppos)
{
    int i;
    struct tty_struct *tty;
    struct inode *inode;
    struct tty_ldisc *ld;

    tty = (struct tty_struct *)file->private_data;//这open函数中有设置
    inode = file->f_path.dentry->d_inode;
    if (tty_paranoia_check(tty, inode, "tty_read"))
        return -EIO;
    if (!tty || (test_bit(TTY_IO_ERROR, &tty->flags)))
        return -EIO;

    /* We want to wait for the line discipline to sort out in this
       situation */
    ld = tty_ldisc_ref_wait(tty);//等待线路规程准备好
    if (ld->ops->read)
        i = (ld->ops->read)(tty, file, buf, count);//从线路规程中读出数据
    else
        i = -EIO;
    tty_ldisc_deref(ld);//释放线路规程的引用
    if (i > 0)
        inode->i_atime = current_fs_time(inode->i_sb);
    return i;
}

我们看到调用线路规程的read操作,与上一节open同属一个ops,同理对应于n_tty_read:
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
             unsigned char __user *buf, size_t nr)
{
......

        if (tty->icanon) {//经过处理过后的数据
            /* N.B. avoid overrun if nr == 0 */
            while (nr && tty->read_cnt) {
                int eol;

                eol = test_and_clear_bit(tty->read_tail,
                        tty->read_flags);
                c = tty->read_buf[tty->read_tail];
                spin_lock_irqsave(&tty->read_lock, flags);
                tty->read_tail = ((tty->read_tail+1) &
                          (N_TTY_BUF_SIZE-1));
                tty->read_cnt--;
                if (eol) {
                    /* this test should be redundant:
                     * we shouldn't be reading data if
                     * canon_data is 0
                     */
                    if (--tty->canon_data < 0)
                        tty->canon_data = 0;
                }
                spin_unlock_irqrestore(&tty->read_lock, flags);

                if (!eol || (c != __DISABLED_CHAR)) {
                    if (tty_put_user(tty, c, b++)) {//拷贝到user buffer里面
                        retval = -EFAULT;
                        b--;
                        break;
                    }
                    nr--;
                }
                if (eol) {
                    tty_audit_push(tty);
                    break;
                }
            }
            if (retval)
                break;
        } else {//原生数据,raw
            int uncopied;
            /* The copy function takes the read lock and handles
               locking internally for this case */
            uncopied = copy_from_read_buf(tty, &b, &nr);//拷贝read_buf数据到用户空间
            uncopied += copy_from_read_buf(tty, &b, &nr);
            if (uncopied) {
                retval = -EFAULT;
                break;
            }
        }

......
}

可以看到直接从read_buf中读取到用户空间。疑问来了,那这个read_buf的数据是怎么来的呢?还记得上一节分析的flush_to_ldisc吗,对的,就是它。那里只是发现将buffer的值拷贝到read_buffer中。那到底源头数据是怎么来的呢?肯定是uart出来的,那它是怎么过来的呢?带着这个疑问我们去查看uart的中断函数:
static irqreturn_t mxcuart_int(int irq, void *dev_id)
{
......
        if (sr2 & MXC_UARTUSR2_RDR) {//当是读中断时,读有效的数据
            mxcuart_rx_chars(umxc);//读数据寒酸
        }

......
}
继续看mxcuart_rx_chars(umxc):
static void mxcuart_rx_chars(uart_mxc_port * umxc)
{
......
        uart_insert_char(&umxc->port, status, MXC_UARTURXD_OVRRUN, ch,
                         flag);//将接收到的字符插到tty buffer中,以备下面的刷到ldisc
          ignore_char:
        sr2 = readl(umxc->port.membase + MXC_UARTUSR2);
    }
    tty_flip_buffer_push(umxc->port.info->port.tty);//将tty buffer刷到线路规程
}
再看void tty_flip_buffer_push(struct tty_struct *tty)
{
    unsigned long flags;
    spin_lock_irqsave(&tty->buf.lock, flags);
    if (tty->buf.tail != NULL)
        tty->buf.tail->commit = tty->buf.tail->used;
    spin_unlock_irqrestore(&tty->buf.lock, flags);

    if (tty->low_latency)
        flush_to_ldisc(&tty->buf.work.work);//将buffer刷到read_buffer
    else
        schedule_delayed_work(&tty->buf.work, 1);//延时一个jiffies后执行上面flush_to_ldisc
}
那我们回到上一节INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc)部分了。:)呵呵,是不是联系起来了呢?!
阅读(2184) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~