Linux系统LCD驱动架构分析

来源:互联网 发布:电子版通讯录制作软件 编辑:程序博客网 时间:2024/06/10 11:31

转载地址:http://blog.csdn.net/u012452561/article/details/50352732

我们一起来看一个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结构体

[cpp] view plain copy
  1. struct fb_info {  
  2.     atomic_t count;  
  3.     int node;  
  4.     int flags;  
  5.     struct mutex lock;      /* Lock for open/release/ioctl funcs */  
  6.     struct mutex mm_lock;       /* Lock for fb_mmap and smem_* fields */  
  7.     struct fb_var_screeninfo var;   /* Current var */  
  8.     struct fb_fix_screeninfo fix;   /* Current fix */  
  9.     struct fb_monspecs monspecs;    /* Current Monitor specs */  
  10.     struct work_struct queue;   /* Framebuffer event queue */  
  11.     struct fb_pixmap pixmap;    /* Image hardware mapper */  
  12.     struct fb_pixmap sprite;    /* Cursor hardware mapper */  
  13.     struct fb_cmap cmap;        /* Current cmap */  
  14.     struct list_head modelist;      /* mode list */  
  15.     struct fb_videomode *mode;  /* current mode */  
  16.   
  17. #ifdef CONFIG_FB_BACKLIGHT  
  18.     /* assigned backlight device */  
  19.     /* set before framebuffer registration,  
  20.        remove after unregister */  
  21.     struct backlight_device *bl_dev;  
  22.   
  23.     /* Backlight level curve */  
  24.     struct mutex bl_curve_mutex;      
  25.     u8 bl_curve[FB_BACKLIGHT_LEVELS];  
  26. #endif  
  27. #ifdef CONFIG_FB_DEFERRED_IO  
  28.     struct delayed_work deferred_work;  
  29.     struct fb_deferred_io *fbdefio;  
  30. #endif  
  31.   
  32.     struct fb_ops *fbops;  
  33.     struct device *device;      /* This is the parent */  
  34.     struct device *dev;     /* This is this fb device */  
  35.     int class_flag;                    /* private sysfs flags */  
  36. #ifdef CONFIG_FB_TILEBLITTING  
  37.     struct fb_tile_ops *tileops;    /* Tile Blitting */  
  38. #endif  
  39.     char __iomem *screen_base;  /* Virtual address */  
  40.     unsigned long screen_size;  /* Amount of ioremapped VRAM or 0 */   
  41.     void *pseudo_palette;       /* Fake palette of 16 colors */   
  42. #define FBINFO_STATE_RUNNING    0  
  43. #define FBINFO_STATE_SUSPENDED  1  
  44.     u32 state;          /* Hardware state i.e suspend */  
  45.     void *fbcon_par;                /* fbcon use-only private area */  
  46.     /* From here on everything is device dependent */  
  47.     void *par;  
  48.     /* we need the PCI or similar aperture base/size not 
  49.        smem_start/size as smem_start may just be an object 
  50.        allocated inside the aperture so may not actually overlap */  
  51.     struct apertures_struct {  
  52.         unsigned int count;  
  53.         struct aperture {  
  54.             resource_size_t base;  
  55.             resource_size_t size;  
  56.         } ranges[0];  
  57.     } *apertures;  
  58. };  


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)入口函数

[cpp] view plain copy
  1. static int __init fbmem_init(void)  
  2. {  
  3.     proc_create("fb", 0, NULL, &fb_proc_fops);  
  4.   
  5.     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))  
  6.         printk("unable to get major %d for fb devs\n", FB_MAJOR);  
  7.   
  8.     fb_class = class_create(THIS_MODULE, "graphics");  
  9.     if (IS_ERR(fb_class)) {  
  10.         printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));  
  11.         fb_class = NULL;  
  12.     }  
  13.     return 0;  
  14. }  


虽然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

[cpp] view plain copy
  1. static const struct file_operations fb_fops = {  
  2.     .owner =    THIS_MODULE,  
  3.     .read =     fb_read,  
  4.     .write =    fb_write,  
  5.     .unlocked_ioctl = fb_ioctl,  
  6. #ifdef CONFIG_COMPAT  
  7.     .compat_ioctl = fb_compat_ioctl,  
  8. #endif  
  9.     .mmap =     fb_mmap,  
  10.     .open =     fb_open,  
  11.     .release =  fb_release,  
  12. #ifdef HAVE_ARCH_FB_UNMAPPED_AREA  
  13.     .get_unmapped_area = get_fb_unmapped_area,  
  14. #endif  
  15. #ifdef CONFIG_FB_DEFERRED_IO  
  16.     .fsync =    fb_deferred_io_fsync,  
  17. #endif  
  18.     .llseek =   default_llseek,  
  19. };  


果然看到了read、write等非常熟悉的老朋友。假如应用程序发出read命令,则会触发fb_read回调函数。点进去看一下。


代码清单 input.c中的fb_read() 函数

[cpp] view plain copy
  1. static ssize_t  
  2. fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)  
  3. {  
  4.     unsigned long p = *ppos;  
  5.     struct fb_info *info = file_fb_info(file);  
  6.     u8 *buffer, *dst;  
  7.     u8 __iomem *src;  
  8.     int c, cnt = 0, err = 0;  
  9.     unsigned long total_size;  
  10.   
  11.     if (!info || ! info->screen_base)  
  12.         return -ENODEV;  
  13.   
  14.     if (info->state != FBINFO_STATE_RUNNING)  
  15.         return -EPERM;  
  16.   
  17.     if (info->fbops->fb_read)  
  18.         return info->fbops->fb_read(info, buf, count, ppos);  
  19.       
  20.     total_size = info->screen_size;  
  21.   
  22.     if (total_size == 0)  
  23.         total_size = info->fix.smem_len;  
  24.   
  25.     if (p >= total_size)  
  26.         return 0;  
  27.   
  28.     if (count >= total_size)  
  29.         count = total_size;  
  30.   
  31.     if (count + p > total_size)  
  32.         count = total_size - p;  
  33.   
  34.     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,  
  35.              GFP_KERNEL);  
  36.     if (!buffer)  
  37.         return -ENOMEM;  
  38.   
  39.     src = (u8 __iomem *) (info->screen_base + p);  
  40.   
  41.     if (info->fbops->fb_sync)  
  42.         info->fbops->fb_sync(info);  
  43.   
  44.     while (count) {  
  45.         c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;  
  46.         dst = buffer;  
  47.         fb_memcpy_fromfb(dst, src, c);  
  48.         dst += c;  
  49.         src += c;  
  50.   
  51.         if (copy_to_user(buf, buffer, c)) {  
  52.             err = -EFAULT;  
  53.             break;  
  54.         }  
  55.         *ppos += c;  
  56.         buf += c;  
  57.         cnt += c;  
  58.         count -= c;  
  59.     }  
  60.   
  61.     kfree(buffer);  
  62.   
  63.     return (err) ? err : cnt;  
  64. }  


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()

[cpp] view plain copy
  1. static struct fb_info *file_fb_info(struct file *file)  
  2. {  
  3.     struct inode *inode = file->f_path.dentry->d_inode;  
  4.     int fbidx = iminor(inode);  
  5.     struct fb_info *info = registered_fb[fbidx];  
  6.   
  7.     if (info != file->private_data)  
  8.         info = NULL;  
  9.     return info;  
  10. }  


原来这个fb_info结构体是一个叫registered_fb[]数组中的一个元素,这个数组从字面上解释叫“已经注册的fb”,猜也能猜到这个数组肯定是在注册fb_info的时候赋值的。这是不是跟输入子系统中那个input_table[]数组比较类似呢。不同之处是input_table[]是在注册纯软件驱动的input_handler时赋值,而registered_fb[]是在注册fb_info这个硬件相关的结构体时赋值的。查找一个registered_fb[]数组的赋值位置。


代码清单 do_register_framebuffer()函数

[cpp] view plain copy
  1. static int do_register_framebuffer(struct fb_info *fb_info)  
  2. {  
  3.     ... ...  
  4. fb_info->dev = device_create(fb_class, fb_info->device,  
  5.                      MKDEV(FB_MAJOR, i), NULL, "fb%d", i);  
  6. ... ...  
  7.     registered_fb[i] = fb_info;  
  8.     ... ...  
  9. }   

在do_register_framebuffer中有两个重大发现:

1). 调用了device_craate()函数,创建了我们非常熟悉的字符型设备驱动的设备文件节点

2). 找到了registered_fb[]的赋值位置。继续搜索do_register_framebuffer的调用位置。

 

代码清单 register_framebuffer()

[html] view plain copy
  1. int register_framebuffer(struct fb_info *fb_info)  
  2. {  
  3.     int ret;  
  4.   
  5.     mutex_lock(&registration_lock);  
  6.     ret = do_register_framebuffer(fb_info);  
  7.     mutex_unlock(&registration_lock);  
  8.   
  9.     return ret;  
  10. }  


原来是这个叫做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驱动”的文章贴出来。


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 龟展 展龟法 比展龟法还厉害的东西 快穿之属下不是贱受 属下该死请主上责罚 每个世界属下都在打脸 属下不知 柯兰诏言 有情人终成眷属下一句 丈夫欠债妻子被属下带走 属兔和什么属相合 属鼠和属兔 属鸡和属兔 属虎和属兔 属兔和属虎 属狗和属兔婚姻好不好 属猪的男和属兔的女 属兔和属龙的婚姻怎么样 属兔男和属虎女的婚姻 属兔的婚姻和命运 属兔和属羊的相配吗 男属兔和女属鼠 属兔男和属羊女配吗 属兔和属马的婚姻如何 属兔女和属马男 属兔男和属马女配吗 属鼠的和属兔的婚姻怎么样 属兔男和属狗女配吗 属虎的和属兔的婚姻般配吗 属兔和兔结婚好不好 属地原则 属地管理 属地 属地管理原则 号码属地查询 属地管理是什么意思 三亚是哪个省的属地 属小龙今年多大 无敌从满级属性开始txt下载 无敌属性满级开始 无敌从满属性开始 无敌属性从满级开始