Chinaunix首页 | 论坛 | 博客
  • 博客访问: 411173
  • 博文数量: 118
  • 博客积分: 294
  • 博客等级: 二等列兵
  • 技术积分: 667
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-16 20:31
文章分类

全部博文(118)

文章存档

2014年(3)

2012年(25)

2011年(90)

分类:

2011-10-17 21:19:48

原文地址:我对linux理解之tty五 作者:amingriyue

------------------------------------------
本文系本站原创,欢迎转载!
转载请注明出处:amingriyue.blog.chinaunix.net
------------------------------------------
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;//在open里面有设置
    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;
}
主要工作变为do_tty_write(ld->ops->write, tty, file, buf, count),我们下面来看这个函数:
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;
    }
       //上面准备好写的buf和大小长度
    /* Do the write .. */
    for (;;) {
        size_t size = count;
        if (size > chunk)
            size = chunk;
        ret = -EFAULT;
        if (copy_from_user(tty->write_buf, buf, size))//从用户空间的buf拷贝到write_buf
            break;
        ret = write(tty, file, tty->write_buf, size);//这个函数是传进来的write函数,即ld->ops->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;
}
我们看到最终还是执行到了线路规程的write函数,ld->ops->write:
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的driver的ops,即对应serial_core中的uart_ops
                tty->ops->flush_chars(tty);
        } else {//没加工过的数据
            while (nr > 0) {
                c = tty->ops->write(tty, b, nr);//同样调用serial_core中的uart_ops->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;
        }
        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;
}
我们逐渐看到胜利的曙光了。根据数据的格式决定调用tty->ops->flush_chars(tty)还是tty->ops->write(tty, b, nr),到serial_core中去找uart_ops:
static const struct tty_operations uart_ops = {
    .open        = uart_open,
    .close        = uart_close,
    .write        = uart_write,
    .put_char    = uart_put_char,
    .flush_chars    = uart_flush_chars,
    .write_room    = uart_write_room,
    .chars_in_buffer= uart_chars_in_buffer,
    .flush_buffer    = uart_flush_buffer,
    .ioctl        = uart_ioctl,
    .throttle    = uart_throttle,
    .unthrottle    = uart_unthrottle,
    .send_xchar    = uart_send_xchar,
    .set_termios    = uart_set_termios,
    .set_ldisc    = uart_set_ldisc,
    .stop        = uart_stop,
    .start        = uart_start,
    .hangup        = uart_hangup,
    .break_ctl    = uart_break_ctl,
    .wait_until_sent= uart_wait_until_sent,
#ifdef CONFIG_PROC_FS
    .proc_fops    = &uart_proc_fops,
#endif
    .tiocmget    = uart_tiocmget,
    .tiocmset    = uart_tiocmset,
#ifdef CONFIG_CONSOLE_POLL
    .poll_init    = uart_poll_init,
    .poll_get_char    = uart_poll_get_char,
    .poll_put_char    = uart_poll_put_char,
#endif
};
tty->ops->flush_chars对应了uart_flush_chars,tty->ops->write对应了uart_write。我们先看uart_flush_chars
static void uart_flush_chars(struct tty_struct *tty)
{
    uart_start(tty);
}
转而调用uart_start:
static void uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->port;
    unsigned long flags;

    spin_lock_irqsave(&port->lock, flags);
    __uart_start(tty);
    spin_unlock_irqrestore(&port->lock, flags);
}
转到了__uart_start:
static void __uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->port;

    if (!uart_circ_empty(&state->info.xmit) && state->info.xmit.buf &&
        !tty->stopped && !tty->hw_stopped)
        port->ops->start_tx(port);//对应到了port的ops的start_tx,在本系统中对应了mxc_ops
}
我们看下mxc_ops:
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,
};
我们看到port->ops->start_tx对应了mxcuart_start_tx:
static void mxcuart_start_tx(struct uart_port *port)
{
    uart_mxc_port *umxc = (uart_mxc_port *) port;
    struct circ_buf *xmit = &umxc->port.info->xmit;
    volatile unsigned int cr1;
    mxc_dma_requestbuf_t writechnl_request;
    int tx_num;

    cr1 = readl(port->membase + MXC_UARTUCR1);
    /* Enable Transmitter rdy interrupt */
    if (umxc->dma_enabled == 1) {//这个标记很重要,决定了是dma操作,还是普通操作
        /*
         * If the channel is in use then return immediately and use
         * the dma_tx tasklet to transfer queued data when current DMA
         * transfer is complete
         */
        if (dma_list[umxc->port.line].dma_txchnl_inuse == 1) {
            return;
        }
        tx_num = uart_circ_chars_pending(xmit);
        if (tx_num > 0) {
            dma_list[umxc->port.line].dma_txchnl_inuse = 1;
            if (xmit->tail > xmit->head) {
                memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
                       UART_XMIT_SIZE - xmit->tail);
                memcpy(umxc->tx_buf +
                       (UART_XMIT_SIZE - xmit->tail), xmit->buf,
                       xmit->head);
            } else {
                memcpy(umxc->tx_buf, xmit->buf + xmit->tail,
                       tx_num);
            }
            umxc->tx_handle =
                dma_map_single(umxc->port.dev, umxc->tx_buf,
                       TXDMA_BUFF_SIZE, DMA_TO_DEVICE);

            writechnl_request.dst_addr =
                umxc->port.mapbase + MXC_UARTUTXD;
            writechnl_request.src_addr = umxc->tx_handle;
            writechnl_request.num_of_bytes = tx_num;
            if ((mxc_dma_config
                 (dma_list[umxc->port.line].wr_channel,
                  &writechnl_request, 1,
                  MXC_DMA_MODE_WRITE)) == 0) {
                mxc_dma_enable(dma_list[umxc->port.line].
                           wr_channel);
            }
            cr1 |= MXC_UARTUCR1_TXDMAEN;
        }
    } else {
        cr1 |= MXC_UARTUCR1_TRDYEN;
    }
    writel(cr1, port->membase + MXC_UARTUCR1);
}

再看tty->ops->write:
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);//最终也调用了上面分析的uart_start
    return ret;
}
我们看到tty_write操作层次是这样的: tty_core->tty_ldisc->serial_core->mxc_uart

好了,至此我们分析了tty的注册,open,read,write流程框架。还有相关的其它一些操作可以类似地去分析。
阅读(2692) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~