LCD驱动程序之框架分析
学习目的:
- 分析linux中LCD的驱动框架
市面上大多数消费类的电子产品都带有炫酷的图形界面,大到智能手机、电脑,小到手表、手环,图形画给人们生活带来了良好的体验。实现图像界面的前提是设备都有一块可用于显示的屏幕,LCD就是广泛运用于显示的一种屏幕,今天我们就开始学习linux内核中LCD的驱动框架。弄清楚软件设计框架,就能明白内核帮我们做好了那些工作,我们自己又该做那些工作,如何将我们做的东西融入进去。
1、内核LCD驱动框图
Linux中,LCD驱动采用了帧缓冲技术,所以LCD的驱动也称为FrameBuffer驱动,如下图所示为FrameBuffer的总驱动框架。
用户空间的应用程序通过FrameBuffer设备文件与Framebuffer进行交互,直接参与交互的是fbmem.c文件。fbmem.c文件注册了FrameBuffer的字符设备驱动程序,实现了FrameBuffer这一类驱动的访问接口file_opearations结构体成员,包括open、read、write、ioctl、mmap等等。同时,fbmem向下为硬件相关的驱动程序提供了注册的通道,xxxfb.c文件则是与硬件相关的设备驱动程序,也是我们编写驱动程序中所要完成的,其中主要的工作就是分配并初始化fb_info结构体并向上进行注册。
2、FrameBuffer核心接口fbmem.c
2.1 fbmem入口函数
static int __init fbmem_init(void) { ........ if (register_chrdev(FB_MAJOR,"fb",&fb_fops))---------------------->① ......... fb_class = class_create(THIS_MODULE, "graphics");----------------->② ........ }
① 注册FrameBuffer字符设备程序,主设备号为29
② 创建graphics类,udev机制会根据graphics类下的设备信息在/dev目录下自动创建设备节点
2.2 注册file_opeartion结构体fb_fops
应用程序访问主设备号为29的FrameBuffer设备文件时,最终会调用到fb_fops结构体中函数指针指向的函数,先来看fb_fops结构体中实现了有那些内容。
static const struct file_operations fb_fops = { .owner = THIS_MODULE, .read = fb_read, .write = fb_write, .ioctl = fb_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = fb_compat_ioctl, #endif .mmap = fb_mmap, .open = fb_open, .release = fb_release, #ifdef HAVE_ARCH_FB_UNMAPPED_AREA .get_unmapped_area = get_fb_unmapped_area, #endif #ifdef CONFIG_FB_DEFERRED_IO .fsync = fb_deferred_io_fsync, #endif };
fb_fops中实现了open、write、read、ioctl、mmap等,先从open函数开始分析
fb_open函数
static int fb_open(struct inode *inode, struct file *file) { int fbidx = iminor(inode); ---------------->① struct fb_info *info; int res = 0; if (fbidx >= FB_MAX) return -ENODEV; #ifdef CONFIG_KMOD if (!(info = registered_fb[fbidx]))-------->② try_to_load(fbidx); #endif /* CONFIG_KMOD */ if (!(info = registered_fb[fbidx])) return -ENODEV; if (!try_module_get(info->fbops->owner)) return -ENODEV; file->private_data = info; if (info->fbops->fb_open) {---------------->③ res = info->fbops->fb_open(info,1); if (res) module_put(info->fbops->owner); } return res; }
① 获取打开设备文件的次设备号
② 以次设备号为索引,找到register_fb指针数组中的某一项,赋值给info指针
③ 调用info指针指向的fb_info结构体成员fbops中的fb_open函数
接着再进行分析fb_read函数
static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { ... int fbidx = iminor(inode);-------------------------->① struct fb_info *info = registered_fb[fbidx];-------->② ... if (!info || ! info->screen_base) return -ENODEV; if (info->state != FBINFO_STATE_RUNNING) return -EPERM; if (info->fbops->fb_read)--------------------------->③ return info->fbops->fb_read(info, buf, count, ppos); ... }
① 获取打开设备文件的次设备号
② 以次设备号为索引,找到register_fb指针数组中的某一项,赋值给info指针
③ 调用info指针指向的fb_info结构体成员fbops中的fb_read函数
从fb_open、fb_read分析可以看出,应用程序对framebuffer设备驱动的读写最终会根据打开文件的次设备号为索引,在fb_info类型的指针数组中找到一个fb_info成员,并调用其成员fbops中的读写函数
接着看registered_fb数组在那个函数中被设置
int register_framebuffer(struct fb_info *fb_info) { ... if (num_registered_fb == FB_MAX) return -ENXIO; num_registered_fb++; for (i = 0 ; i < FB_MAX; i++)------------------------------>① if (!registered_fb[i]) break; fb_info->node = i; fb_info->dev = device_create(fb_class, fb_info->device,---->② MKDEV(FB_MAJOR, i), "fb%d", i); ... registered_fb[i] = fb_info;-------------------------------->③ ... }
① 找出register_fb数组指针中未被填充项
② 在fbmem.c入口函数中创建的fb_class类中,创建fbn设备,udev机制根据创建的设备信息自动在/dev目录中创建fbn设备节点
③ 将传入的fb_info结构体指针存放到①中找到的register_fb指针数组未被填充的一项
继续分析看看register_framebuffer函数在那些地方被调用
经查找,内核的s3c2410fb.c文件中调用了register_framebuffer函数,后面我们以这个文件为例进行进行分析
3、s3c2410fb.c
从入口函数进行分析
int __devinit s3c2410fb_init(void) { return platform_driver_register(&s3c2410fb_driver); }
注册了平台总线驱动程序,我们知道平台总线的驱动程序在设备和驱动匹配成功时会调用driver结构体中的probe函数,我们这里假设能匹配成功,直接看s3c2410fb_driver结构体中的probe指针指向函数
probe指针指向名为s3c2410fb_probe的probe函数
static int __init s3c2410fb_probe(struct platform_device *pdev) { ... fbinfo->fbops = &s3c2410fb_ops; ------------>① ... ret = s3c2410fb_init_registers(info); ... ret = register_framebuffer(fbinfo);-------------------->② ... }
s3c2410fb_probe函数中执行的是一些硬件操作,并且设置了fb_info结构体,最后调用了register_framebuffer注册了fb_info结构体
我们前面分析了对framebuffer设备文件的读写,最终会调用到fb_info中的fbops结构体的成员函数,s3c2410fb_ops结构体中实现了是真正的对LCD控制器、显存的操作
4、总结
经分析可以看出内核中针对LCD的显示设备已经有对应驱动框架FrameBuffer,内核的fbmem.c中已经帮我们注册了相应的字符设备驱动,预留了硬件设备的注册接口。驱动设计者只需根据自己的LCD设备特点,实现内核中s3c2410fb.c文件中实现的内容,根据自己硬件特点设置fb_info结构体,初始化硬件的LCD控制器,并调用register_framebuffer进行注册即可