linux串口驱动分析——打开设备

  串口驱动是由tty_driver架构实现的。一个应用程序中的函数要操作硬件,首先会经过tty,级级调用之后才会到达驱动之中。本文先介绍应用程序中打开设备的open函数的整个历程。

  首先在串口初始化中会先注册一个串口驱动,函数原型为

  int uart_register_driver(struct uart_driver *drv)

  在这个函数中会调用注册tty驱动的函数

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

从这一句代码可以看出串口实质上也是一个字符设备。用soucesight对参数tty_fops进行回溯,可以找出应用程序与tty架构的函数调用关系表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函数实际上是tty架构中的tty_open函数,查看该函数

static int tty_open(struct inode *inode, struct file *filp)
{
  struct tty_struct *tty = NULL;
    int noctty, retval;
    struct tty_driver *driver;
    int index;
    dev_t device = inode->i_rdev;
    unsigned saved_flags = filp->f_flags;
  ...
  if (tty->ops->open)
  ...
}

这里调用到了tty->ops中的open函数,是struct tty_operations类型的,实际上是uart_ops这一结构

static const struct tty_operations uart_ops = {

    .open        = uart_open,

    ...

};

可以看出这里又调用到了uart_open函数

static int uart_open(struct tty_struct *tty, struct file *filp)
{
  ...
  retval = uart_startup(tty, state, 0);
  ...
}
 

 

static int uart_startup(struct tty_struct *tty, struct uart_state *state, int init_hw)

{

  struct uart_port *uport = state->uart_port;

    struct tty_port *port = &state->port;

    unsigned long page;

    int retval = 0;

  ...

  retval = uport->ops->startup(uport);

  ...

}

层层调用之后到这里,调用到uport结构中的函数,uport为struct uart_port类型,每一个uart_port对应一个串口设备,也就是说这里已经调用到了底层驱动的startup函数。在串口初始化时用数组来初始化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,

        }

    },

    ...

}

函数操作集

static struct uart_ops s3c24xx_serial_ops = {

    .pm        = s3c24xx_serial_pm,

    .tx_empty    = s3c24xx_serial_tx_empty,

    .get_mctrl    = s3c24xx_serial_get_mctrl,

    .set_mctrl    = s3c24xx_serial_set_mctrl,

    .stop_tx    = s3c24xx_serial_stop_tx,

    .start_tx    = s3c24xx_serial_start_tx,

    .stop_rx    = s3c24xx_serial_stop_rx,

    .enable_ms    = s3c24xx_serial_enable_ms,

    .break_ctl    = s3c24xx_serial_break_ctl,

    .startup = s3c24xx_serial_startup,

    .shutdown    = s3c24xx_serial_shutdown,

    .set_termios    = s3c24xx_serial_set_termios,

    .type        = s3c24xx_serial_type,

    .release_port    = s3c24xx_serial_release_port,

    .request_port    = s3c24xx_serial_request_port,

    .config_port    = s3c24xx_serial_config_port,

    .verify_port    = s3c24xx_serial_verify_port,

};

所以,retval = uport->ops->startup(uport);这里最终调用了s3c24xx_serial_startup函数,真相基本上已经浮出水面。应用程序中的open函数通过tty架构,层层调用,最后调用到了samsung.c驱动文件中的s3c24xx_serial_startup函数。

static int s3c24xx_serial_startup(struct uart_port *port)

{

    struct s3c24xx_uart_port *ourport = to_ourport(port);

    int ret;



    dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",

        port->mapbase, port->membase);



    rx_enabled(port) = 1;



    ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,

              s3c24xx_serial_portname(port), ourport);



    if (ret != 0) {

        printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);

        return ret;

    }



    ourport->rx_claimed = 1;



    dbg("requesting tx irq...\n");



    tx_enabled(port) = 1;



    ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,

              s3c24xx_serial_portname(port), ourport);



    if (ret) {

        printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);

        goto err;

    }



    ourport->tx_claimed = 1;



    dbg("s3c24xx_serial_startup ok\n");



    /* the port reset code should have done the correct

     * register setup for the port controls */



    return ret;



 err:

    s3c24xx_serial_shutdown(port);

    return ret;

这个函数主要做了四件事情,代码已高亮标出:

  1、打开接收使能

  2、注册数据接收中断

  3、打开发送使能

  4、注册数据发送中断

至此,linux串口驱动程序打开设备的实现已分析完毕。如果有疑问或建议,欢迎指出。

 

你可能感兴趣的:(linux)