十一、UART&TTY驱动
Linux系统中UART驱动和TTY驱动两者有着紧密的关系,它们不像I2C和SPI驱动是单独一个模块,分析时应当将它们看成一个整体来分析。UART驱动部分依赖于硬件平台,而TTY驱动和具体的平台无关。本文的分析内容基于IMX6DL硬件平台和Kernel 3.0.35版本,虽然UART部分依赖于平台,但是不管是哪个硬件平台,驱动的思路都是一致的,下面分模块来分别介绍。
一、UART驱动
UART驱动主要涉及的驱动文件是imx.c、serial_core.c两个文件。首先我们找到驱动的入口函数module_init(imx_serial_init),在函数imx_serial_init中调用uart_register_driver向内核注册了一个驱动,在该函数中除了做常规的初始化驱动之外,有两个关键点的函数调用需要我们注意一下,如下图:
先是调用tty_set_operations将uart_ops这一个tty设备的操作函数集设置到了tty驱动中,同时调用tty_register_driver函数向内核注册了tty驱动,其中uart_ops的数据类型及内容如下:
当调用tty_open函数时就会调用这里的uart_open,具体是怎么调用的,我们后面会分析到。imx_serial_init函数中还调用platform_driver_register向内核注册了一个平台设备,所以UART驱动即是平台设备又是字符设备。当驱动和设备匹配时会调用serial_imx_probe函数,在该函数中除了做具体平台相关的串口端口设置,比如调用platform_get_resource获取中断资源,赋值sport->timer.functioni = mx_timeout设置定时器之外,还有一个关键的操作就是sport->port.ops = &imx_pops,赋值了跟具体硬件平台的底层操作函数,当中的imx_pops结构体如下:
该结构体中的函数都是和具体的硬件平台相关,串口的数据接收、注册中断接收函数、使用DMA接收数据等操作都是在上面的函数中完成,这些函数由NXP官方提供,是和底层硬件最接近的函数。
跟其他的驱动一样,当打开串口设备时,uart_open函数得到调用,在tty_open函数中调用了uart_startup函数来启动串口,如下:
在uart_startup函数中通过uport->ops->startup(uport);间接调用到了imx_startup函数,因为我们在前面已经通过sport->port.ops = &imx_pops将相关硬件平台的串口操作函数赋值给了抽象的串口端口操作函数,所以到这里我们转去分析imx_startup看看里面做了什么操作。
在imx_startup中通过调用request_irq(sport->rxirq, imx_rxint, 0, DRIVER_NAME, sport)注册了串口中断接收函数imx_rxint,串口中断发送函数同理,同时如果板级文件中设置启用了DMA,还初始化了用于DMA数据处理相关的工作队列,如下图:
我们并未配置使用DMA,所以只分析中断接收函数imx_rxint。Imx_rxint函数如下:
imx_rxint函数在循环中读取数据寄存器的值,并在函数的末尾调用了两个很关键的函数,分别是tty_insert_flip_char(tty, rx, flg)和tty_flip_buffer_push(tty),其中tty_insert_flip_char函数的作用是将接收到的字符放入tty数据块中,如下图:
而tty_flip_buffer_push(tty)则是将tty数据块的数据推到线路规程当中,线路规程相关的知识我们后面会讲到,这个函数的作用就类似于通知tty去线路规程获取从串口过来的数据,函数内容如下:
其中有个关键的操作就是调用了工作队列,具体这个工作队列是在何时被注册或者初始化,我们后面讲tty时候会分析到。总结以上,如果中断函数中只调用tty_insert_flip_char函数的话,tty是没办法获取串口数据的,还必须使用tty_flip_buffer_push函数将数据推到线路规程当中去。至此,UART到TTY这条路径我们就分析完了,接下来分析TTY的框架。
一、TTY驱动
TTY驱动不依赖具体的硬件平台,主要涉及的文件是tty_io.c、tty_ldisc.c。TTY驱动框架中包含一个叫线路规程的核心模块,TTY驱动不能直接从UART获取数据,所有的数据都必须从ldisc(线路规程获取)。首先我们来看tty相关的初始化,在前面注册UART驱动的时候,同时调用了tty_register_driver(normal)函数向内核注册了一个tty驱动,在该函数中调用了cdev_init(&driver->cdev, &tty_fops),向设备绑定了tty设备的操作函数集,tty_fops的数据类型是struct file_operations,该变量如下图:
因此当应用层打开一个tty设备时候会调用这个函数集当中的tty_open函数,接下来我们看tty_open函数里面做了什么操作。在tty_open函数中调用tty_init_dev(driver, index, 0)函数对tty设备进行了初始化,在tty_init_dev函数中又调用了initialize_tty_struct(tty, driver, idx)函数对tty相关的结构体进行了初始化,如下图所示:
其中有三个地方需要我们重点关注,第一个是tty_ldisc_init(tty),调用该函数完成了线路规程的初始化,在tty_ldisc_init函数里面通过调用tty_ldisc_get获得线路规程,在tty_ldisc_get函数中通过调用get_ldops(disc)获得线路规程的操作函数,如图所示:
其中tty_ldiscs是一个全局数组,数组元素类型是struct tty_ldisc_ops,也就是线路规程的操作函数集,类型如下图:
线路规程的操作函数具体是在什么时候被赋值初始化的,我们后面会分析到。
在initialize_tty_struct函数中第二个需要我们关注的函数调用是tty_buffer_init(tty),,
调用该函数完成了tty数据块相关的初始化,如下图所示:
在初始化函数中还初始化了一个工作队列,INIT_WORK(&tty->buf.work, flush_to_ldisc)。
具体这个工作队列是在何时被调用呢?就是在我们前面分析imx_rxint中断接收函数时,调用了tty_flip_buffer_push,在该函数中通过schedule_work(&tty->buf.work)调度了该工作队列。至此,TTY也和UART联系上了。
在initialize_tty_struct函数中需要我们关注的地方是tty->ops = driver->ops语句。前面我们分析到,在串口注册时候调用tty_set_operations函数,通过driver->ops = op将tty的操作函数赋值给了uart驱动,在这里则是将注册进去的函数给拿出来赋值给了tty设备,等于是应用层操作tty设备就是操作uart串口。在tty_init_dev函数中,除了初始化tty设备之外,还调用tty_ldisc_setup(tty, tty->link)函数对线路规程进行了设置。在tty_ldisc_setup函数中调用了tty_ldisc_open函数,该函数中使用ld->ops->open(tty)打开了线路规程,但是线路规程的操作函数是在哪里进行赋值的呢?保留这个疑问,我们接下来分析线路规程相关的初始化流程。
记得前面我们提到的一个全局数组tty_ldiscs吗?这个数组的元素类型就是线路规程的操作函数。我们在内核代码中进行全局搜索,发现在tty_register_ldisc函数中进行了设置,如下图:
调用该函数的话,就会将线路规程设置到全局数组tty_ldiscs中,那么tty_register_ldisc函数是在哪里被调用的呢?答案是,在tty_ldisc_begin函数中被调用,如下图:
而tty_ldisc_N_TTY变量就是线路规程的操作函数,变量赋值如下图:
tty_ldisc_begin这个函数被console_init调用,那是谁又调用了console_init呢?答案是在/init/main.c文件中,asmlinkage void __init start_kernel(void)函数调用了console_init。而start_kernel函数正是内核的入口函数。也就是说,在进入内核的时候,第一时间就先初始化了tty的线路规程,赋值了线路规程的相关操作函数。那线路规程的操作函数又是在哪里被调用的呢?
前面我们讲过,tty驱动不能直接从串口获得数据,数据的来源是线路规程,那么调用线路规程的读写函数只能是tty的操作函数,所以我们来看看之前从未分析的tty_read和tty_write函数。首先来看tty_read函数,如下图:
果不其然,在tty_read中通过ld->ops->read调用了线路规程的read函数,也就是调用了tty_ldisc_N_TTY的ntty_read函数。我们再来看tty_write函数,如下图:
同样是调用到了线路规程的n_tty_write函数。
综上,在进入内核的时候,先是设置了线路规程的操作函数,然后在tty驱动注册的时候设置了tty的操作函数,并在后续打开tty设备时调用tty_open函数,在open函数中通过get_ldops(disc)获得线路规程的操作函数。当应用层调用tty_read读取数据时就调用了n_tty_read获得了数据。
十一、UART&TTY驱动的更多相关文章
- 一、uart&tty驱动
一.I.MX6 UART驱动 文件路径:\linux_IMX6_CoreC_3..35_for_Linux\drivers\tty\serial\imx.c .驱动入口函数:imx_serial_in ...
- [tty与uart]3.tty驱动分析
转自:http://www.wowotech.net/linux_kenrel/183.html 目录: 1 首先分析设备驱动的注册 1.1 uart_register_driver分析 1.2 tt ...
- [uart]3.tty驱动分析
转自:http://www.wowotech.net/linux_kenrel/183.html 目录: 1 首先分析设备驱动的注册 1.1 uart_register_driver分析 1.2 tt ...
- linux UART串口驱动开发文档
转:http://www.360doc.com/content/10/0417/18/829197_23519037.shtml linux UART串口驱动开发文档时间:2010-01-09 14: ...
- Linux TTY驱动--Serial Core层【转】
转自:http://blog.csdn.net/sharecode/article/details/9197567 版权声明:本文为博主原创文章,未经博主允许不得转载. 接上一节: Linux TTY ...
- tiny4412 串口驱动分析六 --- TTY驱动架构
转载: http://www.linuxidc.com/Linux/2013-11/92639.htm 参考: http://blog.csdn.net/lamdoc/article/details/ ...
- Linux tty驱动架构
Linux tty子系统包含:tty核心,tty线路规程和tty驱动.tty核心是对整个tty设备的抽象,对用户提供统一的接口,tty线路规程是对传输数据的格式化,tty驱动则是面向tty设备的硬件驱 ...
- STC8H开发(十一): GPIO单线驱动多个DS18B20数字温度计
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- Linux TTY驱动--Uart_driver底层【转】
转自:http://blog.csdn.net/sharecode/article/details/9196591 版权声明:本文为博主原创文章,未经博主允许不得转载. Linux 中将串口驱动进行了 ...
随机推荐
- html 10-HTML基础回顾
10-HTML基础回顾 #本文主要内容 html 的常见元素 html 元素的分类 html 元素的嵌套关系 html 元素的默认样式和 CSS Reset html 常见面试题 #html 的常见元 ...
- [TSCTF-J] relax
[TSCTF-J] relax 1.源码审计 利用扫描器可以扫到robots.txt 进入发现三个文件 flag.php heicore.php relax.php 我们只能进入relax.php 发 ...
- Solr:Slor初识(概述、Windows版本的安装、添加IK分词器)
1.Solr概述 (1)Solr与数据库相比的优势 搜索速度更快.搜索结果能够按相关度排序.搜索内容格式不固定等 (2)Lucene与Solr的区别 Lucene提供了完整的查询引擎和索引引擎,目的是 ...
- 将两个ListMap中同下标的map去重合并
public static void main(String[] args) { Map<String,Object> oneMap = new HashMap<>(); on ...
- Spring中的注解@Value("#{}")与@Value("${}")的区别
1 @Value("#{}") SpEL表达式 @Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法.当然还有可 ...
- Linux课程知识点总结(二)
Linux课程知识点总结(二) 七.Shell实用功能 7.1 命令行自动补全 在Linux系统中,有太多的命令和文件名称需要记忆,使用命令行补全功能[Tab]可以快速的写出文件名和命令名 7.2 命 ...
- Redis缓存篇(一)Redis是如何工作的
Redis提供了高性能的数据存取功能,所以广泛应用在缓存场景中,既能有效地提升业务应用的响应速度,还可以避免把高并发压力发送到数据库层. 因为Redis用作缓存的普遍性以及它在业务应用中的重要作用,所 ...
- 一张图看懂sql的各种join
下图展示了 LEFT JOIN.RIGHT JOIN.INNER JOIN.OUTER JOIN 相关的 7 种用法.
- python之shutil 模块
一.shutil 高级的 文件.文件夹.压缩包 处理模块 shutil.copyfileobj(fsrc, fdst[, length]) 将文件内容拷贝到另一个文件中 import shutil s ...
- PHPExcel-Helper快速构建Excel
项目介绍 PHPExcel-Helper是什么? PHPExcel辅助开发类,帮助开发者快速创建各类excel. github PHPExcel-Helper存在的意义? 官方phpexcel库功能全 ...