在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,并通过mmap 系统调用进行地址映射,随后用 memset 将屏幕清空(这里假设显示模式是 1024×768-8 位色模式,线性内存模式):
int fb; unsigned char* fb_mem; fb = open (“/dev/fb0″, O_RDWR); fb_mem = mmap (NULL, 1024*768, PROT_READ|PROT_WRITE,MAP_SHARED,fb,0); memset (fb_mem, 0, 1024*768 );
FrameBuffer 设备还提供了若干ioctl命令,通过这些命令,可以获得显示设备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩 色模式下的调色板信息等等。
通过 FrameBuffer设备,还可以获得当前内核所支持的加速显示卡的类型(通过固定信息得到),这种类型通常是和特定显示芯片相关的。比如目前最新的内核(2.4.9)中,就包含有 对S3、Matrox、nVidia、3Dfx 等等流行显示芯片的加速支持。在获得了加速芯片类型之后,应用程序就可以将 PCI设备的内存I/O(memio)映射到进程的地址空间。这些memio
一般是用来控制显示卡的寄存器,通过对这些寄存器的操作,应用程序就可以控制特定显卡的加速功能。
PCI设备可以将自己的控制寄存器映射到物理内存空间,而后,对这些控制寄存器的访问,给变成了对物理内存的访问。因此,这些寄存器又被称 为”memio”。一旦被映 射到物理内存,Linux的普通进程就可以通过 mmap 将这些内存 I/O 映射到进程地址空间,这样就可以直接访问这些寄存器了。
当然,因为不同的显示芯片具有不同的加速能力,对memio的使用和定义也各自不同,这时,就需要针对加速芯片的不同类型来编写实现不同的加速功能。比如 大多数芯片都提供了对矩形填充的硬件加速支持,但不同的芯片实现方 式不同,这时,就需要针对不同的芯片类型编写不同的用来完成填充矩形的函数。
FrameBuffer 只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备。所以,对于应用程序而言,如果希望在FrameBuffer 之上进行图形编程,还需要自己动手完成其他许多工作。
struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible resolution */ __u32 yoffset; __u32 bits_per_pixel; /* guess what */ __u32 grayscale; /* != 0 Gray levels instead of colors */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ __u32 nonstd; /* != 0 Non standard pixel format */ __u32 activate; /* see FB_ACTIVATE_* */ __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* acceleration flags (hints) */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */ __u32 right_margin; /* time from picture to sync */ __u32 upper_margin; /* time from sync to picture */ __u32 lower_margin; __u32 hsync_len; /* length of horizontal sync */ __u32 vsync_len; /* length of vertical sync */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 reserved[6]; /* Reserved for future compatibility */ };
struct fb_fix_screeninfo { char id[16]; /* identification string eg “TT Builtin” */ unsigned long smem_start; /* Start of frame buffer mem */ /* (physical address) */ __u32 smem_len; /* Length of frame buffer mem */ __u32 type; /* see FB_TYPE_* */ __u32 type_aux; /* Interleave for interleaved Planes */ __u32 visual; /* see FB_VISUAL_* */ __u16 xpanstep; /* zero if no hardware panning */ __u16 ypanstep; /* zero if no hardware panning */ __u16 ywrapstep; /* zero if no hardware ywrap */ __u32 line_length; /* length of a line in bytes */ unsigned long mmio_start; /* Start of Memory Mapped I/O */ /* (physical address) */ __u32 mmio_len; /* Length of Memory Mapped I/O */ __u32 accel; /* Type of acceleration available */ __u16 reserved[3]; /* Reserved for future compatibility */ };
struct fb_cmap { __u32 start; /* First entry */ __u32 len; /* Number of entries */ __u16 *red; /* Red values */ __u16 *green; __u16 *blue; __u16 *transp; /* transparency, can be NULL */ };
struct fb_info { char modename[40]; /* default video mode */ kdev_t node; int flags; int open; /* Has this been open already ? */ #define FBINFO_FLAG_MODULE 1 /* Low-level driver is a module */ struct fb_var_screeninfo var; /* Current var */ struct fb_fix_screeninfo fix; /* Current fix */ struct fb_monspecs monspecs; /* Current Monitor specs */ struct fb_cmap cmap; /* Current cmap */ struct fb_ops *fbops; char *screen_base; /* Virtual address */ struct display *disp; /* initial display variable */ struct vc_data *display_fg; /* Console visible on this display */ char fontname[40]; /* default font name */ devfs_handle_t devfs_handle; /* Devfs handle for new name */ devfs_handle_t devfs_lhandle; /* Devfs handle for compat. symlink */ int (*changevar)(int); /* tell console var has changed */ int (*switch_con)(int, struct fb_info*); /* tell fb to switch consoles */ int (*updatevar)(int, struct fb_info*); /* tell fb to update the vars */ void (*blank)(int, struct fb_info*); /* tell fb to (un)blank the screen */ /* arg = 0: unblank */ /* arg > 0: VESA level (arg-1) */ void *pseudo_palette; /* Fake palette of 16 colors and the cursor’s color for non palette mode */ /* From here on everything is device dependent */ void *par; };
struct fb_ops { /* open/release and usage marking */ struct module *owner; int (*fb_open)(struct fb_info *info, int user); int (*fb_release)(struct fb_info *info, int user); /* get non settable parameters */ int (*fb_get_fix)(struct fb_fix_screeninfo *fix, int con, struct fb_info *info); /* get settable parameters */ int (*fb_get_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info); /* set settable parameters */ int (*fb_set_var)(struct fb_var_screeninfo *var, int con, struct fb_info *info); /* get colormap */ int (*fb_get_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); /* set colormap */ int (*fb_set_cmap)(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info); /* pan display (optional) */ int (*fb_pan_display)(struct fb_var_screeninfo *var, int con, struct fb_info *info); /* perform fb specific ioctl (optional) */ int (*fb_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg, int con, struct fb_info *info); /* perform fb specific mmap */ int (*fb_mmap)(struct fb_info *info, struct file *file, struct vm_area_struct *vma); /* switch to/from raster image mode */ int (*fb_rasterimg)(struct fb_info *info, int start); };
struct fb_info_gen | struct fb_info | fb_var_screeninfo | | fb_fix_screeninfo | | fb_cmap | | modename[40] | | fb_ops —|—>ops on var | | … | fb_open | | | fb_release | | | fb_ioctl | | | fb_mmap | struct fbgen_hwswitch -|-> detect | | encode_fix | | encode_var | | decode_fix | | decode_var | | get_var | | set_var | | getcolreg | | setcolreg | | pan_display | | blank | | set_disp
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); } fb_drivers[] __initdata= { ….};
如果FrameBuffer设备被静态链接到内核,其对应的入口就会添加到这个表中;如果是动态加载的,即使用insmod/rmmod,就不需要关心这个表。
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);
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显示器上。
/******************************** 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]; } FBDEV, *PFBDEV; //open & init a frame buffer //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; } //close frame buffer int fb_close(PFBDEV pFbdev) { close(pFbdev->fb); pFbdev->fb=-1; } //get display depth 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; } //full screen clear void fb_memset (void *addr, int c, size_t len) { memset(addr, c, len); }
//use by test #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); }