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进行注册即可

posted on 2020-10-18 22:02  quinoa  阅读(471)  评论(0编辑  收藏  举报