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

  • 相关阅读:
    键盘弹出与隐藏对TextView的影响
    iOS9 警告框
    计时器的写法
    iOS提交被拒
    新生活
    批量删除wps文档里的回车符的方法!WPS使用技巧分享!
    学习笔记计划
    监控服务器的注册及登陆并邮件通知的代码(go / python)
    Python调用C代码
    导入用户到Discuz论坛
  • 原文地址:https://www.cnblogs.com/053179hu/p/13831003.html
Copyright © 2020-2023  润新知