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串口驱动程序打开设备的实现已分析完毕。如果有疑问或建议,欢迎指出。

时间: 2024-10-13 11:07:19

linux串口驱动分析——打开设备的相关文章

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC

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_wr

Smart210学习记录------linux串口驱动

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有3个核心数据结构,它们都定义在<#include linux/serial_core.h> 1.uart_driver uart_driver包含了串口设备名.串口驱动名.主次设备号.串口控制台(可选)等信息,还封装了tty_driver(底层串口驱动无需关心tty_driver). struct

linux gsensor驱动分析【转】

本文转载自:http://blog.sina.com.cn/s/blog_89f592f501013sr2.html 本文以Bma250驱动为例子,详细介绍Gsensor设计的一个模板. gsensor驱动在系统中的层次如下图所示: 图中包含三个部分:hardware, driver, input: n         Hardware:其实我们可以认为Gsensor也是一个I2C设备.整个Gsensor芯片分为两部分,一个是sensor传感器,另一个是controller控制器,用于将sens

LInux LCD驱动分析

一.让LCD显示可爱的小企鹅还是先说说环境吧,处理器为S3C2410,linux的版本当然是2.6.20的.下面先说说怎样让LCD上显示出可爱的小企鹅.最直接的步骤如下(记住不要问为什么哈-_-,一步一步跟着走就行了):1.       添加s3c2410处理器的LCD控制寄存器的初始值,具体做法为在文件arch/arm/mach-s3c2410/mach-smdk2410.c中添加struct s3c2410fb_mach_info类型的寄存器描述讯息,如下所示:static struct s

tiny4412 串口驱动分析四 --- 修改默认的串口输出

作者:彭东林 邮箱:[email protected] 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 u-boot:U-Boot 2010.12 Linux内核版本:linux-3.0.31 Android版本:android-4.1.2 tiny4412默认是从uart0来输出和读取信息的,而tiny4412上留了两个串口,分别对应的是uart0和uart3,下面我们修改配置,使控制终端从uart0变成ua

tiny4412 串口驱动分析 --- u-boot中的串口驱动

作者:彭东林 邮箱:[email protected] 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 u-boot:U-Boot 2010.12 Linux内核版本:linux-3.0.31 Android版本:android-4.1.2 我们以tiny4412为例分析串口驱动,下面我们从u-boot开始分析,然后再分析到Linux. 串口初始化 关于这部分代码流程参考件:tiny4412 u-boot 启动

tiny4412 串口驱动分析五 --- LDD3上TTY驱动程序源码

关于tty这部分请参考: <Linux设备驱动开发详解 第二版>第14章 Linux终端设备驱动 <精通Linux设备驱动程序开发>第6章 串行设备驱动程序 <Linux设备驱动程序 第三版>第18章 TTY驱动程序 下面是一些串口相关的文档: http://pan.baidu.com/s/1mg20Umc Makefile: # Comment/uncomment the following line to disable/enable debugging #DEBU

tiny4412 串口驱动分析七 --- log打印的几个阶段之内核启动阶段(earlyprintk)

作者:彭东林 邮箱:[email protected] 开发板:tiny4412ADK+S700 4GB Flash 主机:Wind7 64位 虚拟机:Vmware+Ubuntu12_04 u-boot:U-Boot 2010.12 Linux内核版本:linux-3.0.31 Android版本:android-4.1.2 下面要分析的是内核Log打印的几个阶段 自解压阶段 内核启动阶段 内核启动完全以后 shell终端下 在这个阶段内核log打印可以调用printk和printascii,同