Linux系统LCD驱动架构分析
来源:互联网 发布:知豆电动汽车怎么样 编辑:程序博客网 时间:2024/06/14 00:45
我们一起来看一个Linux系统中最重要的输出系统的驱动架构---LCD驱动。在Linux中,LCD驱动采用了帧缓冲(FrameBuffer)技术,所以LCD驱动又叫FrameBuffer驱动。在很多地方,这两种叫法是通用的。本文章的核心内容就是搞清楚FrameBuffer的程序架构,弄明白内核已经帮我们做了多少工作,我们自己又该做哪些工作。需要跟大家剧透一点信息,FrameBuffer驱动架构和input子系统驱动架构的实现思路上面有很多共性,在学习的时候对比着学效果更佳。如果您电脑上已经安装了SourceInsignt软件,并且也有现成的Linux内核源码的话,那请打开软件边操作边学吧,效果更佳哦!
如图所示是FrameBuffer驱动的总体架构。用户空间的应用程序通过FrameBuffer设备文件(/dev/graphics/fbX)与FrameBuffer进行交互,而直接参与交互的是fbmem.c文件,从图中可以看出fbmem.c提供了fb_read(), fb_write()等回调函数。fbmem.c是内核帮我们完成的framebuffer驱动的核心文件,类似于平台设备驱动总线的platform_driver,它为上层应用程序提供回调函数接口,为下层硬件相关的设备驱动程序提供注册通道。xxxfb.c文件则是与硬件相关的设备驱动程序,类似与平台设备总线的platform_device,该文件由驱动工程师完成,其最主要的工作就是初始化fb_info结构体并进行注册。
介绍该架构中最重要的一个结构体------fb_info结构体。
代码清单 fb_info结构体
fb_info结构体比较复杂,但是我们不需要了解每一个成员变量的功能和使用方法。在这里面有3个最主要的结构体,分别是fb_var_screeninfo,fb_fix_screeninfo和fb_ops。其中,fb_var_screeninfo结构体记录了用户可以修改的显示参数,比如屏幕分辨率,屏幕颜色位域等;fb_fix_screeninfo结构体则记录了用户不可修改的参数,比如屏幕缓冲区的物理地址和缓存的长度等。fb_ops结构体记录了对底层硬件操作的函数指针,也就是充当了file_operations结构体的角色,如果fb_info结构体对象定义了fb_ops,那应用程序发出的read命令最终访问到的将是fb_info->fb_ops->fb_read函数,如果fb_info结构体对象没有定义fb_ops则read命令访问的是input.c中定义的fb_read函数。这些知识当然都是由内核告诉我们的,下文分析内核源码的时候大家就明白了。
如果您了解input子系统的话知道input子系统有一个核心文件input.c,它为应用程序提供访问接口。Framebuffer架构则以fbmem.c为核心,也是应用程序访问的接口。现在以应用程序发出read命令访问设备节点为线索分析以下源代码。首先利用SourceInsight软件定位到fbmem.c的入口函数。
代码清单 fbmem_init(void)入口函数
虽然fbmem_init入口函数代码不长,但是结合我们之前所学的知识分析的话,这里面信息量很大。分几点说明
1) 首先看到一句非常熟悉的register_chrdev(FB_MAJOR,”fb”,&fb_fops)注册函数,这就证明了FrameBuffer驱动也是一个字符型设备驱动。主设备号FB_MAJOR = 29。
2) 接着看到了class_create()创建类,然而并没有创建设备文件。可以大胆的做如下猜测:fbmem.c是framebuffer架构的纯软件部分,是内核帮我们提取出来的。我们自己编写LCD驱动程序的时候只需要编写与硬件息息相关的设备驱动程序即可,然后将某一个代表硬件的结构体注册进内核,与此同时内核才会创建与该硬件相关的设备文件节点。那是不是这样的呢?肯定是,不是就见鬼了,不信就接着往下分析。
3) register_chrdev()函数中第三个参数&fb_fops代表file_operations结构体,是字符设备驱动的核心部分,这里面肯定会提供read、write等回调函数。或者像input子系统那样虽然只提供了一个open函数,但是在open函数里面又进一步的访问到了特定handler的fops。定位到fb_fops看一下。
代码清单 fb_ops
果然看到了read、write等非常熟悉的老朋友。假如应用程序发出read命令,则会触发fb_read回调函数。点进去看一下。
代码清单 input.c中的fb_read() 函数
fb_read函数的思路是这样子的。首先通过file_fb_info(file)函数获得了一个fb_info结构体,该结构体代表了一款特定的LCD硬件。如果fb_info结构体中定义了fops成员变量,并且fops中定义了read成员函数,则调用这个read函数。否则继续执行fb_read中的copy_to_user()。可以看出来,fbmem.c在某种程度上起到了中转站的作用。目前的线索指向了file_fb_info(file)函数,它到底是如何返回的fb_info结构体呢?追踪进去看一下。
代码清单 file_fb_info()
原来这个fb_info结构体是一个叫registered_fb[]数组中的一个元素,这个数组从字面上解释叫“已经注册的fb”,猜也能猜到这个数组肯定是在注册fb_info的时候赋值的。这是不是跟输入子系统中那个input_table[]数组比较类似呢。不同之处是input_table[]是在注册纯软件驱动的input_handler时赋值,而registered_fb[]是在注册fb_info这个硬件相关的结构体时赋值的。查找一个registered_fb[]数组的赋值位置。
代码清单 do_register_framebuffer()函数
在do_register_framebuffer中有两个重大发现:
1). 调用了device_craate()函数,创建了我们非常熟悉的字符型设备驱动的设备文件节点。
2). 找到了registered_fb[]的赋值位置。继续搜索do_register_framebuffer的调用位置。
代码清单 register_framebuffer()
原来是这个叫做register_framebuffer的函数调用了do_register_framebuffer。这个register_framebuffer和我们学过的register_chrdev长得好像,估计它就是用来注册fb_info结构体的核心函数了。全文搜索一下register_framebuffer的调用位置,会发现好多与硬件相关的驱动程序中都调用了register_framebuffer来注册fb_info结构体。
分析到这,framebuffer驱动的架构层次也就出来了。驱动工程师在编写LCD驱动的时候最重要的是初始化一个fb_info结构体。当设置好fb_info结构体之后,使用register_framebuffer(struct fb_info *)函数进行注册,这时内核就会把该fb_info放到registered_fb[]数组中,并创建一个设备文件节点。当应用程序发出read等请求访问设备文件节点的时候,input.c中的read回调函数会首先根据次设备号将fb_info从registered_fb[]中取出,然后看看fb_info中是否设置了fops成员变量如设置,则调用fops中的read回调函数,否则继续执行input.c中的read回调函数。
那我们该如何编写一个LCD驱动的设备驱动程序呢?其实从上文的源码分析中我们应该已经知道具体该做什么了。今天就先到这吧,等我有时间了,或者大家迫切需要这方面知识了,我再把“如何一步一步编写LCD驱动”的文章贴出来。
- Linux系统LCD驱动架构分析
- Linux系统LCD驱动架构分析
- Linux系统LCD驱动架构分析
- Linux LCD驱动分析
- Linux监控系统开发详解(三)--LCD驱动分析
- 《Linux驱动基础篇》- LCD驱动架构
- linux lcd驱动分析一
- linux lcd驱动分析二
- linux lcd驱动分析三
- linux lcd驱动分析四
- linux lcd驱动分析五
- Linux的LCD驱动分析
- LCD(二) linux驱动分析
- LCD(二) linux驱动分析
- LCD(二) linux驱动分析
- 【嵌入式linux驱动开发】第十节 LCD 背光驱动代码架构分析(1)
- Linux驱动:LCD驱动框架分析
- Linux-2.6.20的LCD驱动分析
- selenium爬虫and自动化测试
- Ubuntu14.04+QT3.0.1+opencv3.0.0alpha的人脸检测
- PAT Basic 1027
- Java基础知识梳理--线程
- mysql的查询、子查询及连接查询
- Linux系统LCD驱动架构分析
- Python爬虫学习纪要(八):Requests 库学习笔记3
- codeforces 894B
- 怎么获取到View的位置View.getLocationInWindow()的为0
- Java基础知识梳理--IO
- tensorflow初学代码系列一(基于莫烦视频)
- 实验5 哈夫曼树
- 引用类型(数组)
- 多线程新浪新闻搜索网络爬虫-基于关键字