2、 fbmem.c 
fbmem.c 处于Framebuffer设备驱动技术的中心位置.它为上层应用程序提供系统调用也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己. 
fbmem.c 为所有支持FrameBuffer的设备驱动提供了通用的接口,避免重复工作. 
1) 全局变量 
struct fb_info *registered_fb[FB_MAX]; 
int num_registered_fb; 
这两变量记录了所有fb_info 结构的实例,fb_info 结构描述显卡的当前状态,所有设备对应的fb_info结构都保存在这个数组中,当一个FrameBuffer设备驱动向系统注册自己时,其对应的fb_info结构就会添加到这个结构中,同时num_registered_fb 为自动加1.
static struct { 
const char *name; 
int (*init)(void); 
int (*setup)(void); 
static struct file_operations fb_ops ={ 
owner: THIS_MODULE, 
read: fb_read, 
write: fb_write, 
ioctl: fb_ioctl, 
mmap: fb_mmap, 
open: fb_open, 
release: fb_release 
2)fbmem.c 实现了如下函数. 
register_framebuffer(struct fb_info *fb_info); 
unregister_framebuffer(struct fb_info *fb_info); 
这两个是提供给下层FrameBuffer设备驱动的接口,设备驱动通过这两函数向系统注册或注销自己。几乎底层设备驱动所要做的所有事情就是填充fb_info结构然后向系统注册或注销它。 
(二)一个LCD显示芯片的驱动实例 
    以Skeleton LCD控制器驱动为例,在LINUX中存有一个/fb/skeleton.c的skeleton的Framebuffer驱动程序,很简单,仅仅是填充了fb_info结构,并且注册/注销自己。设备驱动是向用户程序提供系统调用接口,所以我们需要实现底层硬件操作并且定义file_operations结构来向系统提供系统调用接口,从而实现更有效的LCD控制器驱动程序。 
1)在系统内存中分配显存 
在fbmem.c文件中可以看到,file_operations结构中的open()和release()操作不需底层支持,但read()、write()和 mmap()操作需要函数fb_get_fix()的支持.因此需要重新实现函数fb_get_fix()。另外还需要在系统内存中分配显存空间,大多数的LCD控制器都没有自己的显存空间,被分配的地址空间的起始地址与长度将会被填充到fb_fix_screeninfo结构的smem_start 和smem_len 的两个变量中.被分配的空间必须是物理连续的。 
2)实现 fb_ops 中的函数 
用户应用程序通过ioctl()系统调用操作硬件,fb_ops 中的函数就用于支持这些操作。(注: fb_ops结构与file_operations结构不同,fb_ops是底层操作的抽象,而file_operations是提供给上层系统调用的接口,可以直接调用ioctl()系统调用在文件fbmem.c中实现,通过观察可以发现ioctl()命令与fb_ops‘s 中函数的关系: 
FBIOGET_VSCREENINFO fb_get_var 
FBIOPUT_VSCREENINFO fb_set_var 
FBIOGET_FSCREENINFO fb_get_fix 
FBIOPUTCMAP fb_set_cmap 
FBIOGETCMAP fb_get_cmap 
FBIOPAN_DISPLAY fb_pan_display 
如果我们定义了fb_XXX_XXX 方法,用户程序就可以使用FBIOXXXX宏的ioctl()操作来操作硬件。 
文件linux/drivers/video/fbgen.c或者linux/drivers/video目录下的其它设备驱动是比较好的参考资料。在所有的这些函数中fb_set_var()是最重要的,它用于设定显示卡的模式和其它属性,下面是函数fb_set_var()的执行步骤: 
1)检测是否必须设定模式 
2)设定模式 
3)设定颜色映射 
4) 根据以前的设定重新设置LCD控制器的各寄存器。 
第四步表明了底层操作到底放置在何处。在系统内存中分配显存后,显存的起始地址及长度将被设定到LCD控制器的各寄存器中(一般通过fb_set_var()函数),显存中的内容将自动被LCD控制器输出到屏幕上。另一方面,用户程序通过函数mmap()将显存映射到用户进程地址空间中,然后用户进程向映射空间发送的所有数据都将会被显示到LCD显示器上。 
三、FrameBuffer的应用
(一)、一个使用FrameBuffer的例子 
   1. FrameBuffer主要是根据VESA标准的实现的,所以只能实现最简单的功能。 
   2. 由于涉及内核的问题,FrameBuffer是不允许在系统起来后修改显示模式等一系列操作。(好象很多人都想要这样干,这是不被允许的,当然如果你自己写驱动的话,是可以实现的). 
   3. 对FrameBuffer的操作,会直接影响到本机的所有控制台的输出,包括XWIN的图形界面。 
好,现在可以让我们开始实现直接写屏: 
1、打开一个FrameBuffer设备 
2、通过mmap调用把显卡的物理内存空间映射到用户空间 
3、直接写内存。 
/******************************** 
File name : fbtools.h 
*/ 
#ifndef _FBTOOLS_H_ 
#define _FBTOOLS_H_ 
#include <linux/fb.h> 
//a framebuffer device structure; 
typedef struct fbdev{ 
       int fb; 
       unsigned long fb_mem_offset; 
       unsigned long fb_mem; 
       struct fb_fix_screeninfo fb_fix; 
       struct fb_var_screeninfo fb_var; 
       char dev[20]; 
//to use this function,
//you must set FBDEV.dev="/dev/fb0"
//or "/dev/fbX"
//it‘s your frame buffer.
int fb_open(PFBDEV pFbdev);
//close a frame buffer 
int fb_close(PFBDEV pFbdev); 
//get display depth 
int get_display_depth(PFBDEV pFbdev); 
//full screen clear 
void fb_memset(void *addr, int c, size_t len); 
#endif 
/****************** 
File name : fbtools.c 
*/ 
#include <stdio.h> 
#include <stdlib.h> 
#include <fcntl.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/ioctl.h> 
#include <sys/mman.h> 
#include <asm/page.h> 
#include "fbtools.h" 
#define TRUE        1 
#define FALSE       0 
#define MAX(x,y)        ((x)>(y)?(x)y)) 
#define MIN(x,y)        ((x)<(y)?(x)y)) 
//open & init a frame buffer 
int fb_open(PFBDEV pFbdev) 
{ 
       pFbdev->fb = open(pFbdev->dev, O_RDWR); 
       if(pFbdev->fb < 0) 
       { 
              printf("Error opening %s: %m. Check kernel config\n", 
pFbdev->dev); 
              return FALSE; 
       } 
       if (-1 == ioctl(pFbdev->fb,FBIOGET_VSCREENINFO,&(pFbdev->fb_var))) 
       { 
              printf("ioctl FBIOGET_VSCREENINFO\n"); 
              return FALSE; 
       } 
       if (-1 == ioctl(pFbdev->fb,FBIOGET_FSCREENINFO,&(pFbdev->fb_fix))) 
       { 
              printf("ioctl FBIOGET_FSCREENINFO\n"); 
              return FALSE; 
       } 
       //map physics address to virtual address 
       pFbdev->fb_mem_offset = (unsigned long)(pFbdev->fb_fix.smem_start) & 
(~PAGE_MASK); 
       pFbdev->fb_mem = (unsigned long int)mmap(NULL, 
pFbdev->fb_fix.smem_len + pFbdev->fb_mem_offset,              PROT_READ | 
PROT_WRITE, MAP_SHARED, pFbdev->fb, 0); 
       if (-1L == (long) pFbdev->fb_mem) 
       { 
              printf("mmap error! mem:%d offset:%d\n", pFbdev->fb_mem, 
pFbdev->fb_mem_offset); 
              return FALSE; 
       } 
       return TRUE; 
int fb_close(PFBDEV pFbdev)
{
close(pFbdev->fb);
pFbdev->fb=-1;
int get_display_depth(PFBDEV pFbdev);
{
if(pFbdev->fb<=0)
{
printf("fb device not open, open it first\n");
return FALSE;
}
return pFbdev->fb_var.bits_per_pixel;
void fb_memset (void *addr, int c, size_t len)
{
memset(addr, c, len);
#define DEBUG
#ifdef DEBUG
main()
{
FBDEV fbdev;
memset(&fbdev, 0, sizeof(FBDEV));
strcpy(fbdev.dev, "/dev/fb0");
if(fb_open(&fbdev)==FALSE)
{
printf("open frame buffer error\n");
return;
}
fb_memset(fbdev.fb_mem + fbdev.fb_mem_offset, 0,
fbdev.fb_fix.smem_len);
fb_close(&fbdev);
- 发表评论
- 
				



