`
izuoyan
  • 浏览: 9220737 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Linux-2.6.25内核帧缓冲设备驱动分析

阅读更多

帧缓冲(framebuffer)设备应用于linux显示技术方面。因为linux的显示平台已经全部基于framebuffer,所以目前在linux环境下开发图形化界面、游戏、影视软件等可视化应用时都必须用到帧缓冲技术,而现在随着消费逐渐娱乐化的大趋势,可视化应用已经在产品开发中越来越重要,因此,对于帧缓冲技术的理解和掌握就非常重要了。

1 Frambuffer介绍
帧缓冲在linux体系中居于上层应用和底层显示设备之间,如下图所示。它的设计意图是对上层应用屏蔽掉低层不同硬件的操作细节:对于不同厂家不同类型的显示硬件,由于各自厂商在技术上扬长避短的需要,所以在具体的细节比如寄存器数量和种类的设计上就会存在相当大的差异,而且对于各个寄存器的初始化的定义和引脚信号的定义也不近相同,这样即便是两种性能相近的产品,例如夏普3.5寸LCD模块与三星的3.5寸模块其操作细节也截然不同。如果让擅长于开发图形界面的开发人员费尽心思去琢磨属于硬件范畴的液晶模块寄存器写入时序问题,无疑是很大的浪费。因此,需要在上层开发和底层设备之间加入一个中间层以节省开发人员的时间和精力。


2 Frambuffer显示原理
帧缓冲类似一个蓄水池,存放来自用户进程的数据,然后把这些数据再输入显示设备中。对于用户而言,帧缓冲就是内存中的一块区域,可以读、写、映射。只要在初始化阶段把显示设备映射到用户进程空间,可以理解为将屏幕中的每一点和帧缓冲的每一点一一对应起来。这样接下来就可以对这块内存区域填充任何已经定义的像素以及颜色,而屏幕也就可以根据刚才写入的像素及颜色呈现出五彩缤纷的画面。
3 2.6.25内核Frambuffer相关的数据结构
相关的数据结构从运行环境可以分为用户空间和内核空间两类。在用户空间内使用的数据结构主要有fb_fix_screeninfo和fb_var_screeninfo。在内核空间使用的主要数据结构为fb_info。
首先介绍fb_fix_screeninfo,该数据结构定义了一些系统运行期间不能改变的信息,例如设备名,屏幕的像素数量,缓冲区的首址和长度等。这类信息一般通过ioctl函数获得。下面列出了fb_fix_screeninfo 的主要内容:

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->struct fb_fix_screeninfo { char id[16]; /* 设备名*/ unsigned long smem_start; /* frame buffer 缓冲区起始地址(物理地址)*/ __u32 smem_len; /* 缓冲区长度*/ __u32 type; /* 设备类型,例如TFT或STN*/ …… __u32 visual; /* 色彩类型,真彩色、假彩色或单色*/ …… __u32 line_length; /* 屏幕上每行的字节数 */ unsigned long mmio_start; /* IO映射区起始地址(物理地址) */ __u32 mmio_len; /* IO 映射区长度 */ __u32 accel; /* 指出使用的加速卡是哪些特定的芯片 */ __u16 reserved[3]; /* 系统保留*/ };



相对应的,fb_var_screeninfo定义了一些在系统运行期间可以改变的信息。例如像素深度、灰度级、颜色格式、时序,屏幕边缘空白区等。下表中列出了fb_var_screeninfo的主要内容:

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->struct fb_var_screeninfo { __u32 xres; /* visible resolution 可见分辨率 */ __u32 yres; __u32 xres_virtual; /* virtual resolution 虚拟分辨率 */ __u32 yres_virtual; __u32 xoffset; /* 从虚拟分辨率到可见分辨率的偏移量*/ __u32 yoffset; __u32 bits_per_pixel; /* 像素深度 */ __u32 grayscale; /* 灰度级 */ struct fb_bitfield red; struct fb_bitfield green; struct fb_bitfield blue; struct fb_bitfield transp; /* 透明度 */ __u32 nonstd; /* 非标准像素格式 */ …… __u32 pixclock; /* 像素时钟,单位是皮秒*/ __u32 left_margin; /* 左侧边缘区*/ __u32 right_margin; /*右侧边缘区 */ __u32 upper_margin; /*顶部边缘区 */ __u32 lower_margin; __u32 hsync_len; /*水平扫描边缘区 */ __u32 vsync_len; /*垂直扫描边缘区 */ ……. };


下图标出了各种边缘区在整个屏幕上的位置。

内核级Fb_info

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->struct fb_info { int node; /* 设备节点 */ int flags; struct fb_var_screeninfo var; /* 当前可变参数 */ struct fb_fix_screeninfo fix; /* 当前固定参数 */ struct fb_monspecs monspecs; /* 当前监视器特征 */ struct work_struct queue; /* 帧缓冲事件队列 */ struct fb_pixmap pixmap; /* 图象硬件映射变量 */ struct fb_pixmap sprite; /* 光标硬件映射变量 */ struct fb_cmap cmap; /* 当前颜色映射变量 */ struct list_head modelist; /* 模式列表*/ struct fb_videomode *mode; /* 当前模式*/ ...... struct fb_ops *fbops; /* 该指针指向驱动函数集 */ …… struct device *dev; /* 代表此帧缓冲设备 */ …… char __iomem *screen_base; /* IO映射基址(虚地址) */ unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ void *pseudo_palette; /* 调色板内存地址 */ …… };


fb_info是显示驱动工作的主要载体,它定义了当前显示驱动和控制台有关的全部信息。显示驱动的实现形式就是先初始化fb_info各项,用来设置LCD控制器。以后大部分工作是:应用层通过ioctl系统调用fb_ops中的函数,来获得或修改fix、var变量中值,再写到寄存器中;或修改调色板等操作。如果在应用层中将要显示的两帧图像使用的调色板不同,就由fb_ops中的函数实现,后者获得调色板在内存的地址,修改其中的像素值来实现。
Fb_ops里面的函数指针很多,这些函数指针所指向的一般是各个硬件显卡自带的底层驱动函数,如果读者要自己开发一种专门的显卡,在fb_ops这个结构里必然要用指针把这种显卡专用的那些函数列出来。我们这里只简单介绍其中两个函数指针:

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);


此指针指向的函数是用来检查前面提到的可变显示参数的,例如像素深度、边缘区宽度或深度等。注意这里的不会修改参数。

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->int (*fb_set_par)(struct fb_info *info);


此处所指的函数就可以对显示参数作出实质性的修改了。
4 Frambuffer驱动实现框架
这里我们不再重复大家已经比较熟悉的驱动程序注册和注销两个过程。我们以fbmem.c中的几个重要函数为对象,解释一下在注册之后到注销之前帧缓冲驱动的大致步骤。之所以选择fbmem.c中的函数,还是因为这个文件中的函数具有一定的代表性,读者在了解它们的大致结构之后,就可以举一反三,来分析和理解其它具体显卡的驱动程序了。
这几个函数分别是fb_mmap,fb_set_var和 fb_ioctl。
Fb_mmap顾名思义其任务是完成设备到系统内存(虚拟地址)之间的映射。

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->static int fb_mmap(struct file *file, struct vm_area_struct * vma) { int fbidx = iminor(file->f_path.dentry->d_inode); struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; unsigned long off; unsigned long start; u32 len; if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; off = vma->vm_pgoff << PAGE_SHIFT; if (!fb) return -ENODEV; if (fb->fb_mmap) { //意思是:如果驱动程序自带了mmap函数,那就用它自己的 int res; lock_kernel(); //上大内核锁 res = fb->fb_mmap(info, vma); //调用驱动程序自己的mmap函数 unlock_kernel(); return res; } lock_kernel(); start = info->fix.smem_start; //注意,这里指向了设备的物理地址 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); //调整对齐后长度 if (off >= len) { /* 对应于I/O端口统一编址的情况 */ off -= len; if (info->var.accel_flags) { unlock_kernel(); return -EINVAL; } start = info->fix.mmio_start;//当I/O端口统一编址时就使用端口的物理地址 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); } unlock_kernel(); start &= PAGE_MASK; if ((vma->vm_end - vma->vm_start + off) > len) //判断虚拟内存长度是否超越实际长度 return -EINVAL; off += start; vma->vm_pgoff = off >> PAGE_SHIFT; /* 本内存页作为IO之用,已经保留 */ vma->vm_flags |= VM_IO | VM_RESERVED; fb_pgprotect(file, vma, off); //对帧缓冲的内存页进行标识,不要挪作它用 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) //实际的映射操作 return -EAGAIN; return 0; }


这里的映射行为发生在驱动程序初始化阶段。请注意,这些行为是否被囊括在一个名为mmap的函数中并不是问题的关键,我们在开发中真正应该关心的是映射所需要的前提条件如设备物理地址的提取,映射长度的确认以及实际的映射操作。只要在驱动程序的初始化中完成了上述动作,那就算是成功了。因此,不少显卡的驱动程序里是找不到mmap这个函数的,但它们一样工作得很好,原因就是它们已经完成了实际的映射操作。
下面我们看看fb_set_var函数。它主要完成了显示模式、可变参数的设置。Fb_set_var这样的函数在不同的显示驱动中的具体名称也不一样,但基本上的功能都是完成对于模式和可变参数的控制。某些系统的驱动里fb_set_var是不含fb_check_var这样的函数的。

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) { int flags = info->flags; int ret = 0; ……. if (!info->fbops->fb_check_var) { *var = info->var; goto done; } ret = info->fbops->fb_check_var(var, info); //在这里对设备的诸多参数进行检查 if (ret) goto done; if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) { struct fb_videomode mode; …… info->var = *var; if (info->fbops->fb_set_par) //如果驱动程序自带fb_set_par函数就使用它 info->fbops->fb_set_par(info); //这个函数设置例如控制寄存器等可变参数 fb_pan_display(info, &info->var);//硬件虚拟显示 fb_set_cmap(&info->cmap, info); //调色板设置 fb_var_to_videomode(&mode, &info->var);//把可变参数转为显示模式参数 ...... } } } done: return ret; }



Fb_ioctl函数汇集了很多的功能,包括从内核读取显示设备参数(可变的和固定的都有),设置参数(就是调用上面提到的fb_set_var函数)。这些功能一般没有什么分类方面的限制,开发人员可以把各种自己实现的功能都一古脑放进fb_ioctl中。而且,开发人员也完全可以抛开系统提供的这个fb_ioctl转而实现自己的fb_ioctl。

C/C++ code
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->static int fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int fbidx = iminor(inode); struct fb_info *info = registered_fb[fbidx]; //设备 struct fb_ops *fb = info->fbops; //设备函数指针 struct fb_var_screeninfo var; //可变参数 struct fb_fix_screeninfo fix; //固定参数 struct fb_con2fbmap con2fb; struct fb_cmap_user cmap; //调色板 struct fb_event event; void __user *argp = (void __user *)arg; int i; if (!fb) return -ENODEV; switch (cmd) { case FBIOGET_VSCREENINFO: return copy_to_user(argp, &info->var, sizeof(var)) ? -EFAULT : 0;//这里是读取可变参数 case FBIOPUT_VSCREENINFO: if (copy_from_user(&var, argp, sizeof(var))) return -EFAULT; //尝试是否可以设置参数? acquire_console_sem(); //控制台上锁 info->flags |= FBINFO_MISC_USEREVENT; i = fb_set_var(info, &var); //设置可变参数 info->flags &= ~FBINFO_MISC_USEREVENT; release_console_sem(); //解锁 if (i) return i; if (copy_to_user(argp, &var, sizeof(var))) return -EFAULT; return 0; case FBIOGET_FSCREENINFO: return copy_to_user(argp, &info->fix, //读取固定参数 sizeof(fix)) ? -EFAULT : 0; case FBIOPUTCMAP: //设置调色板参数 if (copy_from_user(&cmap, argp, sizeof(cmap))) return -EFAULT; return (fb_set_user_cmap(&cmap, info)); …… case FBIOBLANK: acquire_console_sem(); info->flags |= FBINFO_MISC_USEREVENT; i = fb_blank(info, arg); //关闭显示器 info->flags &= ~FBINFO_MISC_USEREVENT; release_console_sem(); return i; default: if (fb->fb_ioctl == NULL) //如果存在自定义的fb_ioctl就使用它 return -EINVAL; return fb->fb_ioctl(info, cmd, arg); } }


从上面的叙述过程中可以看到,对于帧缓冲驱动程序,尽管不同的显示设备可能有不同的特定功能,但是在向内核注册了驱动程序后运行的共同点都是要先进行物理设备与虚拟内存之间的映射(fb_mmap);在操作设备的过程中,fb_set_var由于可以对于设备的运行参数进行控制因而尤为重要。类似的设备参数读写函数还有fb_check_var等,这些函数一般会被包含在fb_ioctl 函数中并被调用。而上述参数的操作对象就是我们在上一节中已经介绍的 fb_info,fb_fix_screeninfo ,fb_var_screeninfo 等数据结构。

分享到:
评论

相关推荐

    linux-2.6.25-android-1.0_r1.tar.gz

    本压缩包"linux-2.6.25-android-1.0_r1.tar.gz"包含了Android开发初期所依赖的Linux内核源代码,版本号为2.6.25,这标志着一个里程碑,因为它是Android 1.0_r1版本的一部分。让我们一同深入探讨这个版本的内核以及它...

    linux-2.6.25.tar.bz2

    linux-2.6.25.tar.bz2 正版 编译很成功

    Linux-2.6.25 TCPIP函数调用大致流程

    博文链接:https://zhangyafeikimi.iteye.com/blog/250511

    linux2.6.25移植手册

    内核移植是指将Linux内核修改适配到新的硬件平台,包括处理器架构、存储器管理、设备驱动等模块的调整。在TQ2440和Sky2440这样的基于ARM926EJ-S的SoC(System on Chip)上进行移植,需要对ARM体系结构有深入理解,...

    kernel-devel-2.6.25-14.fc9.i686.rpm.iso

    虚拟机vm下fedora系统下安装vmtools时用到的一个软件。为了方便虚拟机系统读取该文件。已经把他转成了.iso文件。只需加载到光驱就可以在虚拟机里读取了。 安装过程参考:...

    kernel-devel-2.6.25-14.fc9.i686

    "kernel-devel-2.6.25-14.fc9.i686" 是一个针对FEDORA9操作系统的内核开发包,主要用于为开发者提供构建内核...无论是为了优化系统性能,还是为了支持新的硬件设备,这个包都是开发和维护Linux内核模块不可或缺的资源。

    kernel-devel-2.6.25-14.fc9.i686.rpm的iso文件

    在安装vmware tools时需要,这是.iso文件,直接在虚拟机中光盘中打开并拷贝到根目录下,在终端中运行rpm -ivh kernel-devel-2.6.25-14.fc9.i686.rpm即可

    kernel-2.6.25-14.fc9.src.rpm

    kernel-2.6.25-14.fc9.src.rpm 这个更详细点 带各种配置文件

    libxml2-2.6.25.tar.gz

    《libxml2-2.6.25:Linux系统中的XML解析库详解》 libxml2是开源软件项目,它提供了一个强大的XML解析库,广泛应用于各种操作系统,包括Linux。这个压缩包“libxml2-2.6.25.tar.gz”就是针对Linux系统的特定版本,...

    PyPI 官网下载 | efel-2.6.25.tar.gz

    ".tar.gz"是一种常见的归档格式,由Unix/Linux系统中的tar命令创建,然后使用gzip工具进行压缩,以减少文件大小,便于传输和存储。解压这个文件通常需要两个步骤:首先使用tar命令解压.tar文件,然后使用gunzip或...

    Smarty-2.6.25.tar.gz

    Smarty-2.6.25.tar.gz 是这个特定版本的Smarty模板引擎的压缩包,包含了该版本的所有源代码和相关文件。 Smarty的核心理念是将程序逻辑与展示逻辑分离。在传统的PHP开发中,开发者往往会在PHP代码中混杂HTML,使得...

    Smarty-2.6.25.zip

    Smarty-2.6.25.zip 是一个包含 Smarty 模板引擎版本 2.6.25 的压缩包。Smarty 是一个广泛使用的 PHP 类库,它将应用逻辑与展示逻辑分离,使得 PHP 开发者可以更方便地创建和管理 Web 应用程序的前端模板。在 Web ...

    Smarty-2.6.25

    Smarty-2.6.25是该模板系统的特定版本,发布于某个时间点,提供了稳定性和功能性的更新。 Smarty的核心理念是通过引入一种专门的模板语言,使得非程序员(如网页设计师)可以独立处理页面布局和样式,而程序员则...

    Linux2.6.25平台下的I2C驱动架构分析

    总的来说,Linux 2.6.25 中的 I2C 驱动架构提供了一个灵活且可扩展的框架,允许开发者针对不同平台和设备轻松实现 I2C 通信,同时保持内核的稳定性和一致性。这种分层设计使得驱动开发更加模块化,便于维护和调试。

    android内核移植[收集].pdf

    - **图像显示设备**:`kernel/drivers/video/goldfishfb.c`为帧缓冲设备,用于图形显示。 - **键盘输入设备**:`kernel/drivers/input/keyboard/goldfish_events.c`处理键盘输入事件。 - **RTC设备**:`kernel/...

    smarty-2.6.25.zip

    Smarty是一个使用PHP写出来的模板引擎,是目前业界最著名的PHP模板引擎之一。它分离了逻辑代码和外在的内容,提供了一种易于管理和使用的方法,用来将原本与HTML代码混杂在一起PHP代码逻辑分离。...

    peak-linux-driver-6.20.rar_pcan_peak_peak dongle_peak-linux-driv

    标题中的"peak-linux-driver-6.20.rar_pcan_peak_peak dongle_peak-linux-driver"揭示了这个压缩包的内容主要是关于PEAK-System公司的PCAN接口卡的Linux驱动程序,版本为6.20。"pcan"是PCAN的缩写,代表了控制器局域...

    kernel-devel-2.6.25-14.fc9.i686.rpm

    你是不是也在安装Fedora的VMware-Tools遇到麻烦呢?当你找到答案的时候,你就需要这个软件包了!

    Linux设备驱动之HID驱动

    在Linux系统中,HID设备驱动主要集中在`linux-2.6.25/drivers/hid`目录下。该目录包含了HID设备驱动的核心组件,如hid-core.c,以及一系列用于支持特定设备的子模块。这些子模块通常用于处理特定厂商或特定类型的HID...

Global site tag (gtag.js) - Google Analytics