我对linux理解之tty三

来源:互联网 发布:mac上网速度慢 编辑:程序博客网 时间:2024/04/30 19:41
我们现在congtty core层的file operations开始分析。
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,
};
先看open函数:
static int tty_open(struct inode *inode, struct file *filp)
{
    int ret;

    lock_kernel(); //哇哈哈,第一次看到传说中的大内核锁
    ret = __tty_open(inode, filp);
    unlock_kernel();
    return ret;
}
转而调用__tty_open(inode, filp):
static int __tty_open(struct inode *inode, struct file *filp)
{
......
got_driver:
    if (!tty) {
        /* check whether we're reopening an existing tty */
        tty = tty_driver_lookup_tty(driver, inode, index);

        if (IS_ERR(tty)) {
            mutex_unlock(&tty_mutex);
            return PTR_ERR(tty);
        }
    }

    if (tty) {
        retval = tty_reopen(tty);
        if (retval)
            tty = ERR_PTR(retval);
    } else
        tty = tty_init_dev(driver, index, 0);//初始化一个tty设备,初始化线路规程和打开线路规程, 见1

    mutex_unlock(&tty_mutex);
    tty_driver_kref_put(driver);
    if (IS_ERR(tty))
        return PTR_ERR(tty);

    filp->private_data = tty;//私有数据设置,read/write函数都将用到这个变量
    file_move(filp, &tty->tty_files);
    check_tty_count(tty, "tty_open");
    if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
        tty->driver->subtype == PTY_TYPE_MASTER)
        noctty = 1;
#ifdef TTY_DEBUG_HANGUP
    printk(KERN_DEBUG "opening %s...", tty->name);
#endif
    if (!retval) {
        if (tty->ops->open)
            retval = tty->ops->open(tty, filp);//ops对应driver的ops,即uart_ops,也就是调用serial_core中的uart_open,见2
        else
            retval = -ENODEV;
    }
    filp->f_flags = saved_flags;
......
}
1,tty_init_dev(driver, index, 0):
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
                                int first_ok)
{
......

    tty = alloc_tty_struct();
    if (!tty)
        goto fail_no_mem;
    initialize_tty_struct(tty, driver, idx);//初始化tty结构,见1-1

    retval = tty_driver_install_tty(driver, tty);
    if (retval < 0) {
        free_tty_struct(tty);
        module_put(driver->owner);
        return ERR_PTR(retval);
    }

    /*
     * Structures all installed ... call the ldisc open routines.
     * If we fail here just call release_tty to clean up.  No need
     * to decrement the use counts, as release_tty doesn't care.
     */

    retval = tty_ldisc_setup(tty, tty->link);//打开线路规程,在initialize_tty_struct中有对线路规程初始化,见1-2
    if (retval)
        goto release_mem_out;
    return tty;

......
}
1-1,initialize_tty_struct(tty, driver, idx):
void initialize_tty_struct(struct tty_struct *tty,
        struct tty_driver *driver, int idx)
{
    memset(tty, 0, sizeof(struct tty_struct));
    kref_init(&tty->kref);
    tty->magic = TTY_MAGIC;
    tty_ldisc_init(tty);//线路规程初始化,见1-1-1
    tty->session = NULL;
    tty->pgrp = NULL;
    tty->overrun_time = jiffies;
    tty->buf.head = tty->buf.tail = NULL;
    tty_buffer_init(tty); //buffer初始化,见1-1-2
    mutex_init(&tty->termios_mutex);
    mutex_init(&tty->ldisc_mutex);
    init_waitqueue_head(&tty->write_wait);
    init_waitqueue_head(&tty->read_wait);
    INIT_WORK(&tty->hangup_work, do_tty_hangup);
    mutex_init(&tty->atomic_read_lock);
    mutex_init(&tty->atomic_write_lock);
    mutex_init(&tty->output_lock);
    mutex_init(&tty->echo_lock);
    spin_lock_init(&tty->read_lock);
    spin_lock_init(&tty->ctrl_lock);
    INIT_LIST_HEAD(&tty->tty_files);
    INIT_WORK(&tty->SAK_work, do_SAK_work);

    tty->driver = driver;
    tty->ops = driver->ops;/*请注意这里的赋值,它将driver的ops赋值给了tty->ops,后面有很多用到该ops的使用,那我们
到时候要想起来它实际是对driver的ops的使用,这里driver就对应serial_core中的注册时的使用的那个driver*/
    tty->index = idx;
    tty_line_name(driver, idx, tty->name);
}
这个函数主要对tty_struct各个部分进行初始化。我们着重分析1-1-1和1-1-2。
1-1-1,tty_ldisc_init(tty):
void tty_ldisc_init(struct tty_struct *tty)
{
    struct tty_ldisc *ld = tty_ldisc_get(N_TTY); //得到tty的线路规程,N_TTY是tty的线路规程号,内核启动时,将会注册这个N_TTY对应的ops:tty_ldisc_N_TTY
    if (IS_ERR(ld))
        panic("n_tty: init_tty");
    tty_ldisc_assign(tty, ld); //设置线路规程给tty
}
所以经过这个初始化函数后,tty->ldisc将会被赋值为N_TTY对应的线路规程
1-1-2,tty_buffer_init(tty)
void tty_buffer_init(struct tty_struct *tty)
{//初始化buffer结构
    spin_lock_init(&tty->buf.lock);
    tty->buf.head = NULL;
    tty->buf.tail = NULL;
    tty->buf.free = NULL;
    tty->buf.memory_used = 0;
    INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);//这个工作队列非常重要!主要功能是将buf数据刷到线路规程,见1-1-2-1
}
这个函数主要初始化buf结构,我们主要分析一下tty->buf.work的具体作用。
1-1-2-1,INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc)
static void flush_to_ldisc(struct work_struct *work)
{
    struct tty_struct *tty =
        container_of(work, struct tty_struct, buf.work.work);
    unsigned long     flags;
    struct tty_ldisc *disc;

    disc = tty_ldisc_ref(tty);//得到tty的线路规程引用
    if (disc == NULL)    /*  !TTY_LDISC */
        return;

    spin_lock_irqsave(&tty->buf.lock, flags);

    if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {//设置flush标记
        struct tty_buffer *head;
        while ((head = tty->buf.head) != NULL) {
            int count;
            char *char_buf;
            unsigned char *flag_buf;

            count = head->commit - head->read;
            if (!count) {
                if (head->next == NULL)
                    break;
                tty->buf.head = head->next;
                tty_buffer_free(tty, head);
                continue;
            }
            /* Ldisc or user is trying to flush the buffers
               we are feeding to the ldisc, stop feeding the
               line discipline as we want to empty the queue */
            if (test_bit(TTY_FLUSHPENDING, &tty->flags))//如果想停止buffer转移到线路规程
                break;
            if (!tty->receive_room) { //当tty没接收空间的时候,延迟1个jiffies调用tty->buf.work,即flush_to_ldisc,把buf数据移到线路规程
                schedule_delayed_work(&tty->buf.work, 1);
                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);
            disc->ops->receive_buf(tty, char_buf,
                                   flag_buf, count);//执行线路规程的receive_buf,把buffer拷贝过来,这里的ops就是上面的tty_ldisc_N_TTY,见1-1-2-1-1
            spin_lock_irqsave(&tty->buf.lock, flags);
        }
        clear_bit(TTY_FLUSHING, &tty->flags);//结束flush,设置结束标记
    }

    /* We may have a deferred request to flush the input buffer,
       if so pull the chain under the lock and empty the queue */
    if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {//如果设置停止flush标记,则清空buffer
        __tty_buffer_flush(tty);
        clear_bit(TTY_FLUSHPENDING, &tty->flags);
        wake_up(&tty->read_wait);
    }
    spin_unlock_irqrestore(&tty->buf.lock, flags);

    tty_ldisc_deref(disc);//释放一个线路规程的引用
}
可以看出其主要作用是将tty core的buffer刷到线路规程。
1-1-2-1-1,disc->ops->receive_buf(tty, char_buf, flag_buf, count):
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
};
可以看出tty_ldisc_N_TTY->receive_buf对应于n_tty_receive_buf:
static 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);//拷贝到read buf
        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);
/*这里你可能奇怪为什么要拷贝两次呢?是因为数据读取缓存read_buf(0~N_TTY_BUF_SIZE)为一环形缓冲区。tty->read_tail, tty->read_tail指向第一个未被读取的数据,
tty->read_cnt缓存中的数据,tty->read_head指向 第一个未被占用的空间。由于是环形缓存tty->read_cnt不一定等于tty->read_head - tty->read_tail。
tty->read_head可能小于tty->read_tail所以可能有以下关系:
tty->read_cnt = N_TTY_BUF_SIZE -  tty->read_tail + tty->read_head。
所以将read_buf中的值考到用户空间需要考两次,*nr的值可能大于N_TTY_BUF_SIZE -  tty->read_tail
而小于tty->read_cnt。拷数据时是从tty->read_tail开始,第一次考取N_TTY_BUF_SIZE -  tty->read_tail, 第二次在read_buf的开始位置到tty->read_head之间获取还需的数据。*/
    } else {
        for (i = count, p = cp, f = fp; i; i--, p++) {
            if (f)
                flags = *f++;
            switch (flags) {
            case TTY_NORMAL:
                n_tty_receive_char(tty, *p);
                break;
            case TTY_BREAK:
                n_tty_receive_break(tty);
                break;
            case TTY_PARITY:
            case TTY_FRAME:
                n_tty_receive_parity_error(tty, *p);
                break;
            case TTY_OVERRUN:
                n_tty_receive_overrun(tty);
                break;
            default:
                printk(KERN_ERR "%s: unknown flag %d\n",
                       tty_name(tty, buf), flags);
                break;
            }
        }
        if (tty->ops->flush_chars)
            tty->ops->flush_chars(tty);
    }

    n_tty_set_room(tty);//将剩下的空间赋值给tty->receive_room,以备查询

    if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
        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);//指示空间低于预定阀值
}
这个函数主要是将tty core层的数据刷到read_buf环形缓冲区中,下面我们回到tty_init_dev中的1-2部分。
1-2,tty_ldisc_setup(tty, tty->link)
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
    struct tty_ldisc *ld = tty->ldisc;//tty结构初始化的时候已经对它赋值,故这里可以使用
    int retval;

    retval = tty_ldisc_open(tty, ld);//如果是N_TTY,则对应的ops是tty_ldisc_N_TTY
    if (retval)
        return retval;

    if (o_tty) {
        retval = tty_ldisc_open(o_tty, o_tty->ldisc);//打开配对的tty
        if (retval) {
            tty_ldisc_close(tty, ld);
            return retval;
        }
        tty_ldisc_enable(o_tty); //使能线路规程
    }
    tty_ldisc_enable(tty);
    return 0;
}
这里通过线路规程的ops的open函数打开该tty。
2,tty->ops->open(tty, filp)
从initialize_tty_struct分析中知道,这里的ops就是driver对应的ops,也就是serial中的uart_ops,那我们看下uart_ops的open的函数:
static int uart_open(struct tty_struct *tty, struct file *filp)
{
......
    /*
     * Start up the serial port.
     */
    retval = uart_startup(state, 0);//这是open的核心操作,见2-1
......
}
2-1,uart_startup(state, 0)
static int uart_startup(struct uart_state *state, int init_hw)
{
......
    retval = port->ops->startup(port);//调用port口的ops,即mxc_ops,主要是对uart初始化,见2-1-1
    if (retval == 0) {
        if (init_hw) {//从open函数传下来的是0,这里不执行,但其它情况不一定
            /*
             * Initialise the hardware port settings.
             */
            uart_change_speed(state, NULL);

            /*
             * Setup the RTS and DTR signals once the
             * port is open and ready to respond.
             */
            if (info->port.tty->termios->c_cflag & CBAUD)
                uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
        }

        if (info->flags & UIF_CTS_FLOW) {
            spin_lock_irq(&port->lock);
            if (!(port->ops->get_mctrl(port) & TIOCM_CTS))
                info->port.tty->hw_stopped = 1;
            spin_unlock_irq(&port->lock);
        }

        info->flags |= UIF_INITIALIZED;

        clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
    }

    if (retval && capable(CAP_SYS_ADMIN))
        retval = 0;

    return retval;
}
2-1-1,port->ops->startup(port)
port口子的ops在mxc_uart中,
static struct uart_ops mxc_ops = {
    .tx_empty = mxcuart_tx_empty,
    .set_mctrl = mxcuart_set_mctrl,
    .get_mctrl = mxcuart_get_mctrl,
    .stop_tx = mxcuart_stop_tx,
    .start_tx = mxcuart_start_tx,
    .stop_rx = mxcuart_stop_rx,
    .enable_ms = mxcuart_enable_ms,
    .break_ctl = mxcuart_break_ctl,
    .startup = mxcuart_startup,
    .shutdown = mxcuart_shutdown,
    .set_termios = mxcuart_set_termios,
    .type = mxcuart_type,
    .pm = mxcuart_pm,
    .release_port = mxcuart_release_port,
    .request_port = mxcuart_request_port,
    .config_port = mxcuart_config_port,
    .verify_port = mxcuart_verify_port,
    .send_xchar = mxcuart_send_xchar,
};
我们看startup对应到了mxcuart_startup:
static int mxcuart_startup(struct uart_port *port)
{
    uart_mxc_port *umxc = (uart_mxc_port *) port;
    int retval;
    volatile unsigned int cr, cr1 = 0, cr2 = 0, ufcr = 0;

    /*
     * Some UARTs need separate registrations for the interrupts as
     * they do not take the muxed interrupt output to the ARM core
     */
    if (umxc->ints_muxed == 1) {//从设备定义看,这个值为1
        retval = request_irq(umxc->port.irq, mxcuart_int, 0,//定义中断函数
                     "mxcintuart", umxc);
        if (retval != 0) {
            return retval;
        }
    } else {
        retval = request_irq(umxc->port.irq, mxcuart_tx_int,
                     0, "mxcintuart", umxc);
        if (retval != 0) {
            return retval;
        } else {
            retval = request_irq(umxc->irqs[0], mxcuart_rx_int,
                         0, "mxcintuart", umxc);
            if (retval != 0) {
                free_irq(umxc->port.irq, umxc);
                return retval;
            } else {
                retval =
                    request_irq(umxc->irqs[1], mxcuart_mint_int,
                        0, "mxcintuart", umxc);
                if (retval != 0) {
                    free_irq(umxc->port.irq, umxc);
                    free_irq(umxc->irqs[0], umxc);
                    return retval;
                }
            }
        }
    }

    /* Initialize the DMA if we need SDMA data transfer */
    if (umxc->dma_enabled == 1) {//是否需要dma传输
        retval = mxcuart_initdma(dma_list + umxc->port.line, umxc);
        if (retval != 0) {
            printk
                (KERN_ERR
                 "MXC UART: Failed to initialize DMA for UART %d\n",
                 umxc->port.line);
            mxcuart_free_interrupts(umxc);
            return retval;
        }
        /* Configure the GPR register to receive SDMA events */
        config_uartdma_event(umxc->port.line);
    }

    /*
     * Clear Status Registers 1 and 2
     */
    writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
    writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);

    /* Configure the IOMUX for the UART */
    gpio_uart_active(umxc->port.line, umxc->ir_mode);

    /*
     * Set the transceiver invert bits if required
     */
    if (umxc->ir_mode == IRDA) {
        echo_cancel = 1;
        writel(umxc->ir_rx_inv | MXC_UARTUCR4_IRSC, umxc->port.membase
               + MXC_UARTUCR4);
        writel(umxc->rxd_mux | umxc->ir_tx_inv,
               umxc->port.membase + MXC_UARTUCR3);
    } else {
        writel(umxc->rxd_mux, umxc->port.membase + MXC_UARTUCR3);
    }

    /*
     * Initialize UCR1,2 and UFCR registers
     */
    if (umxc->dma_enabled == 1) {
        cr2 = (MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
    } else {
        cr2 =
            (MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
    }

    writel(cr2, umxc->port.membase + MXC_UARTUCR2);
    /* Wait till we are out of software reset */
    do {
        cr = readl(umxc->port.membase + MXC_UARTUCR2);
    } while (!(cr & MXC_UARTUCR2_SRST));

    if (umxc->mode == MODE_DTE) {
        ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
             MXC_UARTUFCR_DCEDTE | MXC_UARTUFCR_RFDIV | umxc->
             rx_threshold);
    } else {
        ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
             MXC_UARTUFCR_RFDIV | umxc->rx_threshold);
    }
    writel(ufcr, umxc->port.membase + MXC_UARTUFCR);

    /*
     * Finally enable the UART and the Receive interrupts
     */
    if (umxc->ir_mode == IRDA) {
        cr1 |= MXC_UARTUCR1_IREN;
    }
    if (umxc->dma_enabled == 1) {
        cr1 |= (MXC_UARTUCR1_RXDMAEN | MXC_UARTUCR1_ATDMAEN |
            MXC_UARTUCR1_UARTEN);
    } else {
        cr1 |= (MXC_UARTUCR1_RRDYEN | MXC_UARTUCR1_UARTEN);
    }
    writel(cr1, umxc->port.membase + MXC_UARTUCR1);

    return 0;
}
我们看下这个mxcuart_startup,主要是根据device定义对uart的初始化。

   综上,tty_open的流程是tty_core->tty_ldisc和tty_core->serial_core->mxc_uart。它主要执行了tty_init_dev(driver, index, 0),这个函数主要是初始化一个tty设备,初始化线路规程和打开线路规程。还有执行了tty->ops->open(tty, filp),主要初始化了要使用的uart口子,有了这个基础,我们就可以对tty进行读写了。


http://qrsdev.com/forum.php?mod=viewthread&tid=398&extra=page%3D1