• 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)
    ",
            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
    ", ourport->rx_irq);
            return ret;
        }
    
        ourport->rx_claimed = 1;
    
        dbg("requesting tx irq...
    ");
    
        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
    ", ourport->tx_irq);
            goto err;
        }
    
        ourport->tx_claimed = 1;
    
        dbg("s3c24xx_serial_startup ok
    ");
    
        /* 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串口驱动程序打开设备的实现已分析完毕。如果有疑问或建议,欢迎指出。

  • 相关阅读:
    软件工程学习报告
    WC项目
    ListView设置某一项item的文本居中
    rpm安装mysql
    批处理备份mysql数据
    SELinux下更改mysql端口
    PHP处理Android的POST数据
    Linux下设置开机启动
    设置ListView的item不能点击
    Android下设置ListView数据加载完成后执行layoutanimation
  • 原文地址:https://www.cnblogs.com/51qianrushi/p/4323656.html
Copyright © 2020-2023  润新知