tty driver总结

/********************************************************/
int tty_open(struct inode *inode, struct file *filp);

1.分配tty_file具体的数据,并加入到common 数据结构file
    int tty_alloc_file(struct file *file)
    {
        struct tty_file_private *priv;
        priv = kmalloc(sizeof(*priv), GFP_KERNEL);
        file->private_data = priv;
        return 0;
    }
    crash> struct file | grep private_data
        void *private_data;

    crash> tty_file_private
    struct tty_file_private {
        struct tty_struct *tty;
        struct file *file;
        struct list_head list;
    }

2. 得到tty_struct
2.1 tty_open_current_tty(device, filp);
    这里的device来自dev_t device = inode->i_rdev;
    crash> inode | grep i_rdev
       dev_t i_rdev;
    /*判断打开设备文件的dev_t是否为MKDEV(TTYAUX_MAJOR, 0),即/dev/tty
         *如果是则得到当前进程的tty_struct,即tty终端
     */
    /*从当前进程得到对应的 tty
    get_current_tty -> tty_kref_get(current->signal->tty)
    crash> task_struct | grep signal
              struct signal_struct *signal;
    crash> signal_struct | grep tty
            struct tty_struct *tty;
        */
    static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
    {
        struct tty_struct *tty;

        if (device != MKDEV(TTYAUX_MAJOR, 0))
            return NULL;

        tty = get_current_tty();
        if (!tty)
            return ERR_PTR(-ENXIO);

        filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
        /* noctty = 1; */
        tty_kref_put(tty);
        /* FIXME: we put a reference and return a TTY! */
        return tty;
    }

2.2 如果打开的设备文件不是/dev/tty:
    2.2.1 通过函数tty_lookup_driver得到设备文件对应的 tty_driver;
    /*根据设备文件的类型得到对应的tty_driver
     dev/console:
    tty_init -> vty_init:
    struct tty_driver *console_driver;
    console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
    console_driver->name = "tty";
    console_driver->name_base = 1;
    console_driver->major = TTY_MAJOR;
    console_driver->minor_start = 1;
    tty_set_operations(console_driver, &con_ops);
    tty_register_driver(console_driver);
     dev/tty:
    从console 得到tty_driver:通过函数uart_register_driver(&serial8250_reg);建立了
    console 和 tty_driver的关系
    struct tty_driver *console_device(int *index)
    {
        struct console *c;
        struct tty_driver *driver = NULL;

        console_lock();
        for_each_console(c) {
            if (!c->device)
                continue;
            driver = c->device(c, index);
            if (driver)
                break;
        }
        console_unlock();
        return driver;
    }
    crash> console_drivers
    console_drivers = $5 = (struct console *) 0xc05de228 <serial8250_console>
    crash> struct console 0xc05de228
    struct console {
      name = "ttyS\000\000\000\000\000\000\000\000\000\000\000",
      write = 0xc01f06f8 <serial8250_console_write>,
      read = 0x0,
      device = 0xc01eafc8 <uart_console_device>,
      unblank = 0x0,
      setup = 0xc059f7e8 <serial8250_console_setup>,
      early_setup = 0xc01ee838 <serial8250_console_early_setup>,
      flags = 23,
      index = 0,
      cflag = 0,
      data = 0xc05de198 <serial8250_reg>,
      next = 0x0
    }
    怎样从console 得到 tty_driver?
    struct tty_driver *uart_console_device(struct console *co, int *index)
    {
        struct uart_driver *p = co->data;
        *index = co->index;
        return p->tty_driver;
    }
    crash> serial8250_reg
    serial8250_reg = $6 = {
      owner = 0x0,
      driver_name = 0xc05407c1 "serial",
      dev_name = 0xc050ef55 "ttyS",
      major = 4,
      minor = 64,
      nr = 4,
      cons = 0xc05de228 <serial8250_console>,
      state = 0xed8ad400,
      tty_driver = 0xee0a3200
    }
     default:
    tty_drivers是双向链表的head,每个tty_driver都在这个链表中,使用dev_t和 tty_drivers链表中
     的每一项比较,如果设备文件的dev_t在某个tty_driver的dev_t范围内,在找到对应的tty_driver和设备对应的index
    static struct tty_driver *get_tty_driver(dev_t device, int *index)
    {
        struct tty_driver *p;

        list_for_each_entry(p, &tty_drivers, tty_drivers) {
            dev_t base = MKDEV(p->major, p->minor_start);
            if (device < base || device >= base + p->num)
                continue;
            *index = device - base;
            return tty_driver_kref_get(p);
        }
        return NULL;
    }
     */
    tty_driver driver = tty_lookup_driver(device, filp, &noctty, &index);
    
    static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
        int *noctty, int *index)
    {
    struct tty_driver *driver;
    switch (device) {
    case MKDEV(TTY_MAJOR, 0):
        driver = console_driver;
    case MKDEV(TTYAUX_MAJOR, 1):
        driver = console_device(index);
    default:
        driver = get_tty_driver(device, index);
    return driver;
    }

    2.2.2 check whether we're reopening an existing tty
    /*如果函数tty_driver_lookup_tty能得到tty,说明tty已经创建,这次是reopen.
    否则使用函数tty_init_dev新创建一个tty_struct.
    
    怎样从tty_driver得到对应的tty?
    static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
        struct inode *inode, int idx)
    {
        return driver->ttys[idx];
    }
    怎样创建一个新的tty?函数tty_init_dev比较复杂,后面详细分析
    struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
    {
    struct tty_struct *tty;
    tty = alloc_tty_struct();
    initialize_tty_struct(tty, driver, idx);
    tty_driver_install_tty(driver, tty);
    tty_ldisc_setup(tty, tty->link);
    */
    tty = tty_driver_lookup_tty(driver, inode, index);
    if (tty) retval = tty_reopen(tty);
    else
    tty = tty_init_dev(driver, index);

2.3 Associate a new file with the tty structure
tty_add_file(tty, filp);
/* Associate a new file with the tty structure */
void tty_add_file(struct tty_struct *tty, struct file *file)
{
    struct tty_file_private *priv = file->private_data;

    priv->tty = tty;
    priv->file = file;

    spin_lock(&tty_files_lock);
    list_add(&priv->list, &tty->tty_files);
    spin_unlock(&tty_files_lock);
}

2.4 调用tty_driver中注册的 tty_operation的 open函数
 if (tty->ops->open)
    retval = tty->ops->open(tty, filp);

2.5 如果return value  错误,进行错误处理
    使当前进程睡眠,唤醒后重新open等
    if (retval) {
        tty_unlock(); /* need to call tty_release without BTM */
        tty_release(inode, filp);
        if (retval != -ERESTARTSYS)
            return retval;

        if (signal_pending(current))
            return retval;
        
        schedule();
        /*
         * Need to reset f_op in case a hangup happened.
         */
        tty_lock();
        if (filp->f_op == &hung_up_tty_fops)
            filp->f_op = &tty_fops;
        tty_unlock();
        goto retry_open;
    }

2.6  设置当前进程的tty
  如果tty_driver是pty 且是 master,则设置noctty
    if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
        tty->driver->subtype == PTY_TYPE_MASTER)
        noctty = 1;

 if (!noctty &&
        current->signal->leader &&
        !current->signal->tty && tty->session == NULL)
    __proc_set_tty(current, tty);

void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
{
    if (tty) {
        unsigned long flags;
        /* We should not have a session or pgrp to put here but.... */
        spin_lock_irqsave(&tty->ctrl_lock, flags);
        put_pid(tty->session);
        put_pid(tty->pgrp);
        tty->pgrp = get_pid(task_pgrp(tsk));
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        tty->session = get_pid(task_session(tsk));
        if (tsk->signal->tty) {
            printk(KERN_DEBUG "tty not NULL!!\n");
            tty_kref_put(tsk->signal->tty);
        }
    }
    put_pid(tsk->signal->tty_old_pgrp);
    tsk->signal->tty = tty_kref_get(tty);
    tsk->signal->tty_old_pgrp = NULL;
}

3. 关于tty_init_dev
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
{
    struct tty_struct *tty;
    tty = alloc_tty_struct();
    initialize_tty_struct(tty, driver, idx);
    tty_driver_install_tty(driver, tty);
    tty_ldisc_setup(tty, tty->link);
}
3.1 为tty_struct分配memory并初始化为0
    struct tty_struct *tty;
    tty = alloc_tty_struct();

    struct tty_struct *alloc_tty_struct(void)
    {
        return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
    }

3.2 initialize_tty_struct
void initialize_tty_struct(struct tty_struct *tty,
        struct tty_driver *driver, int idx)
{
    kref_init(&tty->kref);
    /*line discipline*/
    tty_ldisc_init(tty);
    /*wait queue*/
    init_waitqueue_head(&tty->write_wait);
    init_waitqueue_head(&tty->read_wait);
    /*mutext*/
    mutex_init(&tty->termios_mutex);
    mutex_init(&tty->ldisc_mutex);
    mutex_init(&tty->atomic_read_lock);
    mutex_init(&tty->atomic_write_lock);
    mutex_init(&tty->output_lock);
    mutex_init(&tty->echo_lock);
    /*spin lock*/
    spin_lock_init(&tty->read_lock);
    spin_lock_init(&tty->ctrl_lock);

    /*work*/
    INIT_WORK(&tty->hangup_work, do_tty_hangup);
    INIT_WORK(&tty->SAK_work, do_SAK_work);
    /*list header*/
    INIT_LIST_HEAD(&tty->tty_files);
    /*memory?*/
    tty_buffer_init(tty);
    /*设备驱动模型*/
    tty->dev = tty_get_device(tty);
    /*others*/
    tty->magic = TTY_MAGIC;
    tty->session = NULL;
    tty->pgrp = NULL;
    tty->overrun_time = jiffies;
    tty->driver = driver;
    tty->ops = driver->ops;
    tty->index = idx;
    tty_line_name(driver, idx, tty->name);

}

3.2.1 读buffer的管理
/*prepare a tty buffer structure
 *Set up the initial state of the buffer management for a tty device.
 */
void tty_buffer_init(struct tty_struct *tty)
{
    spin_lock_init(&tty->buf.lock);
    tty->buf.head = NULL;
    tty->buf.tail = NULL;
    tty->buf.free = NULL;
    tty->buf.memory_used = 0;
    INIT_WORK(&tty->buf.work, flush_to_ldisc);
}
crash> tty_struct | grep buf
    struct tty_bufhead buf;
crash> struct tty_bufhead
struct tty_bufhead {
    struct work_struct work;
    spinlock_t lock;
    struct tty_buffer *head;
    struct tty_buffer *tail;
    struct tty_buffer *free;
    int memory_used;
}
crash> tty_buffer
struct tty_buffer {
    struct tty_buffer *next;
    char *char_buf_ptr;
    unsigned char *flag_buf_ptr;
    int used;
    int size;
    int commit;
    int read;
    unsigned long data[];
}

3.2.2 有关work
    INIT_WORK(&tty->hangup_work, do_tty_hangup);
    INIT_WORK(&tty->SAK_work, do_SAK_work);

3.2.3 tty_ldisc_init
    有关tty_ldisc有两个数据结构tty_ldisc_ops and tty_ldisc,从数组tty_ldiscs中得到tty_ldisc_ops,
    得到tty_ldisc_ops后创建tty_ldisc
    void tty_ldisc_init(struct tty_struct *tty)
    {
        struct tty_ldisc *ld = tty_ldisc_get(N_TTY);
        if (IS_ERR(ld))
            panic("n_tty: init_tty");
        tty_ldisc_assign(tty, ld);
    }
    struct tty_ldisc {
        struct tty_ldisc_ops *ops;
        atomic_t users;
    };
    struct tty_ldisc_ops *get_ldops(int disc) -> ldops = tty_ldiscs[disc];
    crash> tty_ldiscs
    tty_ldiscs = $7 =
     {0xc05daea4 <tty_ldisc_N_TTY>, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
     0x0, 0x0, 0x0}

    static struct tty_ldisc *tty_ldisc_get(int disc)
    {
        struct tty_ldisc *ld;
        struct tty_ldisc_ops *ldops;


        ldops = get_ldops(disc);

        ld = kmalloc(sizeof(struct tty_ldisc), GFP_KERNEL);


        ld->ops = ldops;
        atomic_set(&ld->users, 1);
        return ld;
    }

    static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
    {
        tty->ldisc = ld;
    }
3.3 tty_driver_install_tty/*install a tty entry in the driver*/
    static int tty_driver_install_tty(struct tty_driver *driver,
                            struct tty_struct *tty)
    {
        return driver->ops->install ? driver->ops->install(driver, tty) :
            tty_standard_install(driver, tty);
    }
    int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
    {
        driver->ttys[tty->index] = tty;
        return 0;
    }

3.4  tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty);
    /*@o_tty: pair tty for pty/tty pairs,虚拟设备pty是成对的*/
    int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
    {
        struct tty_ldisc *ld = tty->ldisc;
        int retval;

        retval = tty_ldisc_open(tty, ld);
        if (retval)
            return retval;

        if (o_tty) {
            retval = tty_ldisc_open(o_tty, o_tty->ldisc);
            if (retval) {
                tty_ldisc_close(tty, ld);
                return retval;
            }
            tty_ldisc_enable(o_tty);
        }
        tty_ldisc_enable(tty);
        return 0;
    }

    static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
    {
        if (ld->ops->open) {
            int ret;
                /* BTM here locks versus a hangup event */
            ret = ld->ops->open(tty);
        }
        return 0;
    }


    /*这里分配的大小为N_TTY_BUF_SIZE的read_buf 和tty_buffer什么关系
         *read_buf是在n_tty中分配的,但是在tty_io中使用。
         */
    static int n_tty_open(struct tty_struct *tty)
    {

        /* These are ugly. Currently a malloc failure here can panic */
        if (!tty->read_buf) {
            tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
        }
        if (!tty->echo_buf) {
            tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);

        }
        reset_buffer_flags(tty);
        tty_unthrottle(tty);
        tty->column = 0;
        n_tty_set_termios(tty, NULL);
        tty->minimum_to_wake = 1;
        tty->closing = 0;
        return 0;
    }
    
    /* tty_struct中与read有关的成员
    char *read_buf;
    int read_head;
    int read_tail;
    int read_cnt;
    read_flags是个挺大的数组
    crash> tty_struct | grep read_flags
        unsigned long read_flags[128];
    unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
     */
    static void reset_buffer_flags(struct tty_struct *tty)
    {
        unsigned long flags;

        spin_lock_irqsave(&tty->read_lock, flags);
        tty->read_head = tty->read_tail = tty->read_cnt = 0;
        spin_unlock_irqrestore(&tty->read_lock, flags);

        mutex_lock(&tty->echo_lock);
        tty->echo_pos = tty->echo_cnt = tty->echo_overrun = 0;
        mutex_unlock(&tty->echo_lock);

        tty->canon_head = tty->canon_data = tty->erasing = 0;
        memset(&tty->read_flags, 0, sizeof tty->read_flags);
        n_tty_set_room(tty);
    }

    /*n_tty_set_room函数是read操作的主要函数,
         *注意这里的read_cnt and receive_room
     */
    static void n_tty_set_room(struct tty_struct *tty)
    {
        /* tty->read_cnt is not read locked ? */
        int    left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
        int old_left;

        /*
         * If we are doing input canonicalization, and there are no
         * pending newlines, let characters through without limit, so
         * that erase characters will be handled.  Other excess
         * characters will be beeped.
         */
        if (left <= 0)
            left = tty->icanon && !tty->canon_data;
        old_left = tty->receive_room;
        tty->receive_room = left;

        /* Did this open up the receive buffer? We may need to flip */
        if (left && !old_left)
            schedule_work(&tty->buf.work);
    }

4 tty_write
/*tty_write入口参数检查,如果没有 tty->ops->write这返回ioerror
 *write_room method好像也是不需的
 *使用ldisc_ops锁,调用do_tty_write
 */
    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;
    }

4.1 do_tty_write
/*tty->write_cnt初始化为0,所以开始时tty->write_cnt肯定小于2048
 *所以这里肯定分配memory, 且赋值
 *tty->write_cnt = chunk; write_cnt就这点用途,并不是用来标识写了多大的数据
 *and tty->write_buf = buf_chunk;不管用户请求写入的数据多大,总是使用这么大的buffer慢慢传
 */
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)
{
    chunk = 2048;
    if (test_bit(TTY_NO_WRITE_SPLIT, &tty->flags))
        chunk = 65536;
    if (count < chunk)
        chunk = count;
    /**/
    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();
    }
}

4.2 n_tty_write
/*调用tty具体的或者是tty_driver具体的 write 函数
 *如果write函数的返回值等于0,则跳出 while(nr>0)循环
 *如果此时请求的数据都传输完毕,则跳出 while(1)循环
 *如果此时请求的数据没有传输完毕,则当前进程睡眠,重新调度schedule()。
 *就是说请求底层的驱动发送nr个数据,却发送了0个或者说没有发送,所以此时当前进程选择睡眠。
 *当函数wake_up_interruptible(&tty->write_wait);被调用后,该进程恢复运行
 */
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) {
        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;
        }

        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;
}

5. tty_read
/*调用line descipline的read函数,输入参数为已知的tty, file,user buf and size
 **/
ssize_t tty_read(struct file *file, char __user *buf, size_t count,
            loff_t *ppos)
{
    int i;
    struct inode *inode = file->f_path.dentry->d_inode;
    struct tty_struct *tty = file_tty(file);
    struct tty_ldisc *ld;

    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;
}

5.1 n_tty_read
/*这里涉及icanon ICANON
 *启用标准模式 (canonical mode)。允许使用特殊字符 EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS,
 *和 WERASE,以及按行的缓冲
 *#define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN]): VMIN 非 canonical 模式读的最小字符数。

 *1]如果read_cnt >= 1,则调用函数copy_from_read_buf拷贝read_buf中的数据到user buffer
 *2]如果n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE, read_buf读到的数据 <= xx, 就调用n_tty_set_room
 *  触发一次tty_buf到read_buf的copy;
 *3]如果read_cnt的数据==0,则当前进程睡眠,使用函数schedule_timeout(timeout)调度。
 */
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
             unsigned char __user *buf, size_t nr)
{
    unsigned char __user *b = buf;
    DECLARE_WAITQUEUE(wait, current);
    int c;
    int minimum, time;
    ssize_t retval = 0;
    ssize_t size;
    long timeout;
    unsigned long flags;
    int packet;

    minimum = time = 0;
    timeout = MAX_SCHEDULE_TIMEOUT;    

    if (!tty->icanon) {
        time = (HZ / 10) * TIME_CHAR(tty);
        minimum = MIN_CHAR(tty);

        timeout = 0;
        if (time) {
            timeout = time;
            time = 0;
        }
        tty->minimum_to_wake = minimum = 1;
    }

    packet = tty->packet;
    add_wait_queue(&tty->read_wait, &wait);
    while (nr) {
        set_current_state(TASK_INTERRUPTIBLE);

        if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
            ((minimum - (b - buf)) >= 1))
            tty->minimum_to_wake = (minimum - (b - buf));
        if (!input_available_p(tty, 0)) {
            /* FIXME: does n_tty_set_room need locking ? */
            n_tty_set_room(tty);
            timeout = schedule_timeout(timeout);
            BUG_ON(!tty->read_buf);
            continue;
        }

        __set_current_state(TASK_RUNNING);

        {
            int uncopied;
            /* The copy function takes the read lock and handles
               locking internally for this case */
            uncopied = copy_from_read_buf(tty, &b, &nr);
            uncopied += copy_from_read_buf(tty, &b, &nr);
            if (uncopied) {
                retval = -EFAULT;
                break;
            }
        }
        if (n_tty_chars_in_buffer(tty) <= TTY_THRESHOLD_UNTHROTTLE) {
            n_tty_set_room(tty);
            check_unthrottle(tty);
        }

        if (b - buf >= minimum)
            break;
        if (time)
            timeout = time;
    }
    remove_wait_queue(&tty->read_wait, &wait);

    __set_current_state(TASK_RUNNING);
    size = b - buf;
    if (size) {
        retval = size;
        if (nr)
            clear_bit(TTY_PUSH, &tty->flags);
    } else if (test_and_clear_bit(TTY_PUSH, &tty->flags))
        goto do_it_again;

    n_tty_set_room(tty);
    return retval;
}

5.1.1 input_available_p
input_available_p(tty, 0);
/*等待read_cnt >=1, 否则会睡眠*/
static inline int input_available_p(struct tty_struct *tty, int amt)
{
    tty_flush_to_ldisc(tty);
    if (tty->icanon && !L_EXTPROC(tty)) {
        if (tty->canon_data)
            return 1;
    } else if (tty->read_cnt >= (amt ? amt : 1))
        return 1;

    return 0;
}

/*Push the terminal flip buffers to the line discipline.
 *哪里看到了那个filp buffer?
 */
void tty_flush_to_ldisc(struct tty_struct *tty)
{
    flush_work(&tty->buf.work);
}

/*about the work, 这个函数会触发work的执行?
 *
 */
bool flush_work(struct work_struct *work)
{
    struct wq_barrier barr;

    if (start_flush_work(work, &barr, true)) {
        wait_for_completion(&barr.done);
        destroy_work_on_stack(&barr.work);
        return true;
    } else
        return false;
}

/*about the work, 这个函数会触发work的执行?
 *test_and_set_bit(TTY_FLUSHING, &tty->flags); 设置某值的第几位,并返回原来的值;
 *clear/set_bit(TTY_FLUSHING, &tty->flags);
 
 描述read buffer相关的数据结构
 *crash> tty_struct | grep buf
    struct tty_bufhead buf;
 *struct tty_bufhead {
    struct work_struct work;
    spinlock_t lock;
    struct tty_buffer *head;
    struct tty_buffer *tail;
    struct tty_buffer *free;
    int memory_used;
 }
 struct tty_buffer {
    struct tty_buffer *next;
    char *char_buf_ptr;
    unsigned char *flag_buf_ptr;
    int used;
    int size;
    int commit;
    int read;
    unsigned long data[];
 }


 */
void flush_to_ldisc(struct work_struct *work)
{
    struct tty_struct *tty = container_of(work, struct tty_struct, buf.work);
    unsigned long     flags;
    struct tty_ldisc *disc = tty_ldisc_ref(tty);
    if (disc == NULL)    /*  !TTY_LDISC */
        return;

    if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {
        struct tty_buffer *head;
        while ((head = tty->buf.head) != NULL) {
            int count;
            char *char_buf;
            unsigned char *flag_buf;
            /*根据tty_buffer的commit[还没读出的位置] and read[已经读出的位置]
              *如果commit等于read, 则移动tty_buf, 释放tty_buf的内存
             */
            count = head->commit - head->read;
            if (!count) {
                if (head->next == NULL)
                    break;
                tty->buf.head = head->next;
                tty_buffer_free(tty, head);
                continue;
            }
            /*此时找到一个commit > read的tty_buffer
             *receive_room表示readbuffer中还有多少空间
             */
            if (!tty->receive_room)
                break;

            if (count > tty->receive_room)
                count = tty->receive_room;
            char_buf = head->char_buf_ptr + head->read;
            flag_buf = head->flag_buf_ptr + head->read;
            head->read += count;
            spin_unlock_irqrestore(&tty->buf.lock, flags);
            /*该函数肯定要释放receive_room*/
            disc->ops->receive_buf(tty, char_buf,flag_buf, count);
        }
        clear_bit(TTY_FLUSHING, &tty->flags);
    }
    tty_ldisc_deref(disc);
}

/*描述read buffer的数据成员:read_buffer的基地址,读位置,读到的数据
 *crash> tty_struct | grep read_buf
 * char *read_buf;
 * crash> tty_struct | grep read_head
    int read_head;
 *对循环 buffer:char *read_buf的操作, 把数据从 tty_buf拷贝到read_buf中
 *拷贝数据后调用wake_up_interruptible(&tty->read_wait)唤醒睡眠在该等待队列上的进程
 */
void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
                  char *fp, int count)
{
    const unsigned char *p;
    char *f, flags = TTY_NORMAL;
    int    i;
    char    buf[64];
    unsigned long cpuflags;

    if (!tty->read_buf)
        return;

    if (tty->real_raw) {
        spin_lock_irqsave(&tty->read_lock, cpuflags);
        i = min(N_TTY_BUF_SIZE - tty->read_cnt,
            N_TTY_BUF_SIZE - tty->read_head);
        i = min(count, i);
        memcpy(tty->read_buf + tty->read_head, cp, i);
        tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
        tty->read_cnt += i;
        cp += i;
        count -= i;

        i = min(N_TTY_BUF_SIZE - tty->read_cnt, N_TTY_BUF_SIZE - tty->read_head);
        i = min(count, i);
        memcpy(tty->read_buf + tty->read_head, cp, i);
        tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
        tty->read_cnt += i;
        spin_unlock_irqrestore(&tty->read_lock, cpuflags);
    }
    n_tty_set_room(tty);

    if ((!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) ||
        L_EXTPROC(tty)) {
        kill_fasync(&tty->fasync, SIGIO, POLL_IN);
        if (waitqueue_active(&tty->read_wait))
            wake_up_interruptible(&tty->read_wait);
    }

    /*
     * Check the remaining room for the input canonicalization
     * mode.  We don't want to throttle the driver if we're in
     * canonical mode and don't have a newline yet!
     */
    if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
        tty_throttle(tty);
}

/*更改read_cnt后,或者说从tty_buf拷贝数据到read_buf后,read_cnt发生变化,
 *调用该函数后更改receiver_room,如果前后的receive_room不等,则调用schedule_work(&tty->buf.work)
 *这里可以看出flush_to_ldisc本身也可能flush_to_ldisc,注意这是才能看到flush_to_ldisc函数中
 test_and_set_bit(TTY_FLUSHING, &tty->flags)的用途,如果嵌套将直接返回
 **/
static void n_tty_set_room(struct tty_struct *tty)
{
    /* tty->read_cnt is not read locked ? */
    int    left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
    int old_left;
    old_left = tty->receive_room;
    tty->receive_room = left;

    /* Did this open up the receive buffer? We may need to flip */
    if (left && !old_left)
        schedule_work(&tty->buf.work);
}

/*从read buf拷贝数据到user buf: 使用的数据成员是read_tail
 *从read_tail开始处拷贝数据到用户空间
 **/
5.1.2 static int copy_from_read_buf(struct tty_struct *tty,
                      unsigned char __user **b,
                      size_t *nr)
{
    int retval;
    size_t n;
    unsigned long flags;

    retval = 0;
    spin_lock_irqsave(&tty->read_lock, flags);
    n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
    n = min(*nr, n);
    spin_unlock_irqrestore(&tty->read_lock, flags);
    if (n) {
        retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);
        n -= retval;
        tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
        spin_lock_irqsave(&tty->read_lock, flags);
        tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
        tty->read_cnt -= n;
        /* Turn single EOF into zero-length read */
        if (L_EXTPROC(tty) && tty->icanon && n == 1) {
            if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
                n--;
        }
        spin_unlock_irqrestore(&tty->read_lock, flags);
        *b += n;
        *nr -= n;
    }
    return retval;
}

5.2 数据是怎样到tty_buffer的?
/*在中断处理程序中,根据实际的需求,申请memory即tty_buf, 然后调用tty_flip_buffer_push
 *触发flush_to_ldisc
 **/
/* tty_prepare_flip_string -make room for characters */
int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
                                size_t size)
{
    int space = tty_buffer_request_room(tty, size);
    if (likely(space)) {
        struct tty_buffer *tb = tty->buf.tail;
        *chars = tb->char_buf_ptr + tb->used;
        memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
        tb->used += space;
    }
    return space;
}

/*Queue a push of the terminal flip buffers to the line discipline.
 *最终调用flush_to_ldisc,虽然从名字上看应该是copy tty_buf的数据到line discipline的某个变量出,
 *而实际上copy的tty的read_buf内,看上去有点令人费解。而如果知道read_buf的分配是在函数n_tty_open
 *可理解为tty_buf到n_tty分配的内存。
 *tty_buf的内存是在中断处理函数中根据需要分配的,管理形式是list,而
 *read_buf是循环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);
    else
        schedule_work(&tty->buf.work);
}

/*假象这种情形:用户程序调用read读取大小为 4字节的数据,此时没有数据。当期进程堵塞在【tty->read_wait】 wait_head上。
 *当有数据收到后,在中断处理函数中分配tty_buf,并调用flush_to_ldisc函数把数据从tty_buf拷贝到read_buf中,并调用函数
 *wake_up_interruptible(&tty->read_wait)唤醒睡眠的进程,把数据从read_buf拷贝到user buf中。
 * flush_to_ldisc->disc->ops->receive_buf();
 */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值