现在的位置: 首页 > 综合 > 正文

tty serial 架构分析

2013年11月05日 ⁄ 综合 ⁄ 共 3649字 ⁄ 字号 评论关闭

     一、  tty设备,在用户空间看来也为字符型设备,从tty_register_driver()中可以看出

     int tty_register_driver(struct     tty_driver * driver)
     {
                ...
                cdev_init(&driver->cdev, &tty_fops);
                ...
      }

   二、 用户空间访问的API也及这里的tty_fops,

     drivers/char目录下的n_tty.c,tty_io.c等文件中
     static const struct file_operations tty_fops = {
                     .llseek        = no_llseek,
                     .read        = tty_read,
                     .write        = tty_write,
                     .poll        = tty_poll,
                     .unlocked_ioctl    = tty_ioctl,
                     .compat_ioctl    = tty_compat_ioctl,
                     .open        = tty_open,
                     .release    = tty_release,
                    .fasync        = tty_fasync,
     };

 三、下面来看一下tty_read的实现

     static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
            loff_t *ppos)
      {
               ...
             ld = tty_ldisc_ref_wait(tty);
             if (ld->ops->read)
                 i = (ld->ops->read)(tty, file, buf, count);
                 //调用到了ldisc层(线路规程)的read函数
             else
                 i = -EIO;
             tty_ldisc_deref(ld);
             ...
     }

    这里调用的线路规程里的函数,

  四、线路规程的实现

       struct tty_ldisc_ops tty_ldisc_N_TTY = {
              .magic           = TTY_LDISC_MAGIC,
              .name            = "n_tty",
              .open            = n_tty_open,
              .close           = n_tty_close,
              .flush_buffer    = n_tty_flush_buffer,
              .chars_in_buffer = n_tty_chars_in_buffer,
              .read            = n_tty_read,
              .write           = n_tty_write,
              .ioctl           = n_tty_ioctl,
             .set_termios     = n_tty_set_termios,
             .poll            = n_tty_poll,
             .receive_buf     = n_tty_receive_buf,
             .write_wakeup    = n_tty_write_wakeup
        };

        接下来看一下n_tty_write实现

static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
               const unsigned char *buf, size_t nr)
{
    ...
    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);
                //调用到具体的驱动中的write函数

                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;
        }
        /*
        假如是以非阻塞的方式打开的,那么也直接返回。否则,让出cpu,等条件满足以后再继续执行。
        */        

        schedule();//执行到这里,当前进程才会真正让出cpu!!!
    }
break_out:
    __set_current_state(TASK_RUNNING);
    remove_wait_queue(&tty->write_wait, &wait);
    ...
}

     从这里看以看出线路规程里的函数最终调用tty_struct里的ops函数

五、让让我们一起看看tty_struct里的ops是何时被赋值的

     tty_open -> tty_init_dev -> initialize_tty_struct

    driver/char/tty_io.c
    void initialize_tty_struct(struct tty_struct *tty,  struct tty_driver *driver, int idx)
      {
           ...
          tty->ops = driver->ops;
           ...
    }

    大家至此是否恍然大悟,好了,tty驱动至此基本结束,细节问题还要大家自己去解决。

六、接下来,简要分析一下serial驱动架构

     请看下一篇

  

抱歉!评论已关闭.