linux串口驱动分析——发送数据

一、应用程序中write函数到底层驱动历程

  和前文提到的一样,首先先注册串口,使用uart_register_driver函数,依次分别为tty_register_driver,cdev_init函数,找到使用的file_operations,即应用程序与tty架构的统一接口。步骤不再赘述。

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_write函数

static 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;
    ...
    ret = do_tty_write(ld->ops->write, tty, file, buf, count);
    ...
}

 

  这里通过do_tty_write函数调用到了线路规程(ldisc)中的函数,结构名为tty_ldisc_N_TTY。 

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)
{
    const unsigned char *b = buf;
    DECLARE_WAITQUEUE(wait, current);
    int c;
    ssize_t retval = 0;
  ...
  c = tty->ops->write(tty, b, nr);
  ...
}

  ops为struct tty_operations类型,由上文可知该结构名为

static const struct tty_operations uart_ops = {
    ...
    .write        = uart_write,
    ...
};

  uart_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;
  ...
  uart_start(tty);
  ...
}

  uart_start函数中又调用了__uart_start函数

static void __uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->uart_port;

    if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
        !tty->stopped && !tty->hw_stopped)
        port->ops->start_tx(port);
}

  这里的port就是uart_port类型的了,终于到达底层驱动了,好累。。又是这个数组,同样的函数操作集

  

static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
    [0] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX0,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 0,
        }
    },
  ...
}

  所以在底层驱动与之对应的就是s3c24xx_serial_start_tx这个函数。层层追溯下来,最终与应用程序中的write函数千里相会。。。

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (!tx_enabled(port)) {
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_disable(port);

        enable_irq(ourport->tx_irq);
        tx_enabled(port) = 1;
    }
}

  但是要让各位看官失望了这个函数很简单,功能上来说就是如果没有打开发送使能就去打开。那么串口驱动又是在什么地方去完成发送数据相关的操作呢?

  预知后事如何,且听下回分析。

  如果有疑问或建议,欢迎指出。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。