zoukankan      html  css  js  c++  java
  • 4 linux lcd驱动框架分析

    4 linux lcd驱动框架

    Linux内核中lcd的驱动是基于帧缓冲framebuffer驱动框架设计的。帧缓冲framebuffer框架是在linux2.2.xx以后的版本中为显示设备提供的一种驱动程序接口,它将显示缓冲区framebuffer进行抽象,屏蔽掉硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区framebuffer进行读写和I/O控制操作。Framebuffer机制模仿显卡的功能,将显卡硬件抽象为一系列的数据结构,通过framebuffer的读写实现对显存的操作。用户可将他看成是显示内存的一个映像,将其映射到进程地址空间,就可以进行读写操作,而读写操作可反映到屏幕上。

    framebuffer是个字符设备,主设备号是29,对应/dev/fb%d设备文件。

    Linux内核中,framebuffer设备驱动的源码主要包含在includelinuxFb.h和driversvideoFbmem.h文件中。这两个文件是framebuffer设备驱动框架的中间层,为上层提供系统调用,为底层驱动提供接口。

    下面就从这两个文件入手,开始分析framebuffer驱动程序的框架。

    首先,从driversvideoFbmem.h的入口函数开始,module_init(fbmem_init)。分析fbmem_init的代码。

    1. static int __init  
    2. fbmem_init(void)  
    3. {  
    4.     create_proc_read_entry("fb", 0, NULL, fbmem_read_proc, NULL);  
    5.     
    6.     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))  //注册一个字符设备
    7.         printk("unable to get major %d for fb devs ", FB_MAJOR);  
    8.     
    9.     fb_class = class_create(THIS_MODULE, "graphics");  //创建一个类
    10.     if (IS_ERR(fb_class)) {  
    11.         printk(KERN_WARNING "Unable to create fb class; errno = %ld ", PTR_ERR(fb_class));  
    12.         fb_class = NULL;  
    13.     }  
    14.     return 0;  
    15. }  

    从上述代码可以看到,fbmem_init函数实现的工作和之前编写驱动程序时入口函数的工作相同。其中,

    (1)、代码第6行,调用register_chrdev注册设备。FB_MAJOR主设备号,在Fb.h文件中定义为29。"fb"为设备名称。fb_fops是定义的file_operations结构体,包含了read、write、ioctl等和硬件相关的接口函数。

    (2)、代码第9行,创建一个名为"graphics"的类。

    用户通过open 函数打开设备,这里就是调用fb_open函数,下面来分析fb_open函数。

    1. static int  
    2. fb_open(struct inode *inode, struct file *file)  
    3. {  
    4.     int fbidx = iminor(inode);  //得到次设备号
    5.     struct fb_info *info;       //定义一个fb_info类型的结构体指针
    6.     int res = 0;  
    7.     
    8.     if (fbidx >= FB_MAX)  
    9.         return -ENODEV;  
    10. #ifdef CONFIG_KMOD  
    11.     if (!(info = registered_fb[fbidx]))  
    12.         try_to_load(fbidx);  
    13. #endif /* CONFIG_KMOD */  
    14.     if (!(info = registered_fb[fbidx]))  //指针指向registered_fb[fbidx]
    15.         return -ENODEV;  
    16.     if (!try_module_get(info->fbops->owner))  
    17.         return -ENODEV;  
    18.     file->private_data = info;  
    19.     if (info->fbops->fb_open) {  
    20.         res = info->fbops->fb_open(info,1);  //调用registered_fb[fbidx]-> fbops->fb_open
    21.         if (res)  
    22.             module_put(info->fbops->owner);  
    23.     }  
    24.     return res;  
    25. }  
      1. 代码第4行,得到设备的次设备号fbidx 
      2. 代码第5行,定义一个fb_info类型的结构体指针。
      3. 代码第14行,将第5行定义的指针info指向以次设备号为索引的registered_fb[fbidx]数组成员。
      4. 代码第20行,调用registered_fb[fbidx]-> fbops->fb_open打开函数。

    由上述代码分析可知,registered_fb[fbidx]才是最终被用户空间调用的接口函数。那么数组registered_fb[]又是被谁赋值的,在哪里赋值呢?猜测一下,因为registered_fb[]数组中存放的就是和硬件相关的接口函数,所以应该在驱动层会有相关的注册函数,将实际的硬件接口和registered_fb[]数组关联起来。

    通过搜索registered_fb,发现在register_framebuffer函数中有关于该数组的赋值操作。接下来来看看函数register_framebuffer。

    1. int  
    2. register_framebuffer(struct fb_info *fb_info)  
    3. {  
    4.     int i;  
    5.     struct fb_event event;  
    6.     struct fb_videomode mode;  
    7.     
    8.     if (num_registered_fb == FB_MAX)  
    9.         return -ENXIO;  
    10.     num_registered_fb++;  
    11.     for (i = 0 ; i < FB_MAX; i++)  
    12.         if (!registered_fb[i])  
    13.             break;  
    14.     fb_info->node = i;  
    15.     
    16.     fb_info->dev = device_create(fb_class, fb_info->device,  
    17.                      MKDEV(FB_MAJOR, i), "fb%d", i);  
    18.     if (IS_ERR(fb_info->dev)) {  
    19.         /* Not fatal */  
    20.         printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld ", i, PTR_ERR(fb_info->dev));  
    21.         fb_info->dev = NULL;  
    22.     } else  
    23.         fb_init_device(fb_info);  
    24.     
    25.     if (fb_info->pixmap.addr == NULL) {  
    26.         fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);  
    27.         if (fb_info->pixmap.addr) {  
    28.             fb_info->pixmap.size = FBPIXMAPSIZE;  
    29.             fb_info->pixmap.buf_align = 1;  
    30.             fb_info->pixmap.scan_align = 1;  
    31.             fb_info->pixmap.access_align = 32;  
    32.             fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;  
    33.         }  
    34.     }     
    35.     fb_info->pixmap.offset = 0;  
    36.     
    37.     if (!fb_info->pixmap.blit_x)  
    38.         fb_info->pixmap.blit_x = ~(u32)0;  
    39.     
    40.     if (!fb_info->pixmap.blit_y)  
    41.         fb_info->pixmap.blit_y = ~(u32)0;  
    42.     
    43.     if (!fb_info->modelist.prev || !fb_info->modelist.next)  
    44.         INIT_LIST_HEAD(&fb_info->modelist);  
    45.     
    46.     fb_var_to_videomode(&mode, &fb_info->var);  
    47.     fb_add_videomode(&mode, &fb_info->modelist);  
    48.     registered_fb[i] = fb_info;  
    49.     
    50.     event.info = fb_info;  
    51.     fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);  
    52.     return 0;  
    53. }  

    首先我们来看一下结构体fb_info,他是linux内核中为实现framebuffer驱动框架定义的驱动层接口,在Fb.h文件中定义。它包含了许多底层函数,还包括了有关设备状态的数据。每一个framebuffer设备都与一个fb_info结构体相对应。其代码如下:

    (1)node成员代表framebuffer设备的次设备号;

    (2)fb_var_screeninfo结构体是用户可修改的显示控制器参数;

    1. struct fb_var_screeninfo {  
    2.     __u32 xres;         /* visible resolution       */  
    3.     __u32 yres;  
    4.     __u32 xres_virtual;     /* virtual resolution       */  
    5.     __u32 yres_virtual;  
    6.     __u32 xoffset;          /* offset from virtual to visible */  
    7.     __u32 yoffset;          /* resolution           */  
    8.     
    9.     __u32 bits_per_pixel;       /* guess what           */  
    10.     __u32 grayscale;        /* != 0 Graylevels instead of colors */  
    11.     
    12.     struct fb_bitfield red;     /* bitfield in fb mem if true color, */  
    13.     struct fb_bitfield green;   /* else only length is significant */  
    14.     struct fb_bitfield blue;  
    15.     struct fb_bitfield transp;  /* transparency         */    
    16.     
    17.     ......  
    18. };  

    (3)fb_fix_screeninfo成员主要记录用户不能修改的显示控制器参数;

    (4)fbops结构体是提供给底层设备的一个接口,上面提到的open函数,就是该结构体的成员。编写字符设备驱动时就需要实现一个file_operations结构体,和这个结构体的作用类似。

    (1)、代码第16行,在类下面创建一个设备;

    (2)、代码第23行,初始化刚刚创建的设备;

    (3)、代码第26行,申请一个framebuffer空间;

    (4)、代码第27~47行,对申请的framebuffer空间进行配置;

    (5)、代码第48行,将已经配置好的fb_info节点放到registered_fb[]数组中。

    由上述分析可知,register_framebuffer函数主要用于生成一个新的fb_info节点,并将其存放到registered_fb[]数组中。那么该函数由谁调用呢?

    搜索register_framebuffer函数,在s3c2410fb.c文件中,发现s3c2410fb_probe函数调用了注册函数。

    那么这里就以s3c2410fb.c为例,来分析一下lcd驱动。

    首先进入drivers/video/s3c2410fb.c文件中,从入口函数s3c2410fb_init开始。

    1. int __devinit s3c2410fb_init(void)  
    2. {  
    3.     return platform_driver_register(&s3c2410fb_driver);  
    4. }  

    入口函数中通过调用platform_driver_register函数注册平台驱动设备,说明该驱动时基于bus-drv-dev模型的,那么就将重点集中到probe函数上,即s3c2410fb_probe。

    1. static int __init s3c2410fb_probe(struct platform_device *pdev)  
    2. {  
    3.     struct s3c2410fb_info *info;  
    4.     struct fb_info     *fbinfo;  
    5.     struct s3c2410fb_hw *mregs;  
    6.     int ret;  
    7.     int irq;  
    8.     int i;  
    9.     u32 lcdcon1;  
    10.   //根据平台设备,获得某些硬件相关的信息
    11.     mach_info = pdev->dev.platform_data;  
    12.     mregs = &mach_info->regs;  
    13.     irq = platform_get_irq(pdev, 0);  
    14.      //1)申请一个 framebuffer空间
    15.     fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);  
    16.      //2)设置fbinfo
    17.     info = fbinfo->par;  
    18.     info->fb = fbinfo;  
    19.     info->dev = &pdev->dev;  
    20.         .....  
    21.   //3)硬件相关的操作,设置中断,lcd时钟,显存地址,
    22.     ret = request_irq(irq, s3c2410fb_irq, IRQF_DISABLED, pdev->name, info);  
    23.     info->clk = clk_get(NULL, "lcd");  
    24.     clk_enable(info->clk);  
    25.     msleep(1);  
    26.     
    27.     /* Initialize video memory */  
    28.     ret = s3c2410fb_map_video_memory(info);  
    29.     ret = s3c2410fb_init_registers(info);  
    30.     ret = s3c2410fb_check_var(&fbinfo->var, fbinfo);  
    31.   //4)注册fbinfo结构体
    32.     ret = register_framebuffer(fbinfo);  
    33.     if (ret < 0) {  
    34.         printk(KERN_ERR "Failed to register framebuffer device: %d ", ret);  
    35.         goto free_video_memory;  
    36.     }  
    37.     /* create device files */  
    38.     device_create_file(&pdev->dev, &dev_attr_debug);  
    39.     return 0;  
    40.        .....  
    41. }  

    分析s3c2410fb_probe函数,大致可以梳理出framebuffer驱动的编写流程:

    1. 申请一个fbinfo 结构体空间;
    2. 设置fbinfo 
    3. 硬件相关的操作;
    4. 注册fbinfo 
  • 相关阅读:
    codeforces 1065F Up and Down the Tree
    初探莫比乌斯反演
    IOI2008 island
    miller——rabin判断素数
    NOIP2018游记
    NP是什么意思?
    word2016如何英汉互译
    2.1数字图像化
    Windows程序内部运行机制
    2.2图像灰度直方图
  • 原文地址:https://www.cnblogs.com/beijiqie1104/p/11445225.html
Copyright © 2011-2022 走看看