*一、FrameBuffer的原理*
FrameBuffer 是出現(xiàn)在 2.2.xx 內(nèi)核當(dāng)中的一種驅(qū)動程序接口。
Linux是工作在保護(hù)模式下,所以用戶態(tài)進(jìn)程是無法象DOS那樣使用顯卡BIOS里提供的中斷調(diào)用來實現(xiàn)直接寫屏,Linux抽象出 FrameBuffer這 個設(shè)備來供用戶態(tài)進(jìn)程實現(xiàn)直接寫屏。Framebuffer機(jī)制模仿顯卡的功能,將顯卡硬件結(jié)構(gòu)抽象掉,可以通過Framebuffer的讀寫直接對顯存 進(jìn)行操 作。用戶可以將Framebuffer看成是顯示內(nèi)存的一個映像,將其映射到進(jìn)程地址空間之后,就可以直接進(jìn)行讀寫操作,而寫操作可以立即反應(yīng)在屏幕上。 這種操 作是抽象的,統(tǒng)一的。用戶不必關(guān)心物理顯存的位置、換頁機(jī)制等等具體細(xì)節(jié)。這些都是由Framebuffer設(shè)備驅(qū)動來完成的。
但Framebuffer本身不具備任何運算數(shù)據(jù)的能力,就只好比是一個暫時存放水的水池.CPU將運算后的結(jié)果放到這個水池,水池再將結(jié)果流到顯示器. 中間不會對數(shù)據(jù)做處理. 應(yīng)用程序也可以直接讀寫這個水池的內(nèi)容.在這種機(jī)制下,盡管Framebuffer需要真正的顯卡驅(qū)動的支持,但所有顯示任務(wù)都有CPU完成,因此CPU 負(fù)擔(dān)很重.
framebuffer的設(shè)備文件一般是 /dev/fb0、/dev/fb1 等等。
可以用命令: #dd if=/dev/zero of=/dev/fb 清空屏幕.
如果顯示模式是 1024×768-8 位色,用命令:$ dd if=/dev/zero of=/dev/fb0 bs=1024 count=768 清空屏幕;
用命令: #dd if=/dev/fb of=fbfile 可以將fb中的內(nèi)容保存下來;
可以重新寫回屏幕: #dd if=fbfile of=/dev/fb;
在使用Framebuffer時,Linux是將顯卡置于圖形模式下的.
在應(yīng)用程序中,一般通過將 FrameBuffer 設(shè)備映射到進(jìn)程地址空間的方式使用,比如下面的程序就打開 /dev/fb0 設(shè)備,并通過mmap 系統(tǒng)調(diào)用進(jìn)行地址映射,隨后用 memset 將屏幕清空(這里假設(shè)顯示模式是 1024×768-8 位色模式,線性內(nèi)存模式):
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 設(shè)備還提供了若干ioctl命令,通過這些命令,可以獲得顯示設(shè)備的一些固定信息(比如顯示內(nèi)存大小)、與顯示模式相關(guān)的可變信息(比如分辨率、象素結(jié)構(gòu)、每掃描線的字節(jié)寬度),以及偽彩 色模式下的調(diào)色板信息等等。
通過 FrameBuffer設(shè)備,還可以獲得當(dāng)前內(nèi)核所支持的加速顯示卡的類型(通過固定信息得到),這種類型通常是和特定顯示芯片相關(guān)的。比如目前最新的內(nèi) 核(2.4.9)中,就包含有 對S3、Matrox、nVidia、3Dfx 等等流行顯示芯片的加速支持。在獲得了加速芯片類型之后,應(yīng)用程序就可以將 PCI設(shè)備的內(nèi)存I/O(memio)映射到進(jìn)程的地址空間。這些memio
一般是用來控制顯示卡的寄存器,通過對這些寄存器的操作,應(yīng)用程序就可以控制特定顯卡的加速功能。
PCI設(shè)備可以將自己的控制寄存器映射到物理內(nèi)存空間,而后,對這些控制寄存器的訪問,給變成了對物理內(nèi)存的訪問。因此,這些寄存器又被稱 為”memio”。一旦被映 射到物理內(nèi)存,Linux的普通進(jìn)程就可以通過 mmap 將這些內(nèi)存 I/O 映射到進(jìn)程地址空間,這樣就可以直接訪問這些寄存器了。
當(dāng)然,因為不同的顯示芯片具有不同的加速能力,對memio的使用和定義也各自不同,這時,就需要針對加速芯片的不同類型來編寫實現(xiàn)不同的加速功能。比如 大多數(shù)芯片都提供了對矩形填充的硬件加速支持,但不同的芯片實現(xiàn)方 式不同,這時,就需要針對不同的芯片類型編寫不同的用來完成填充矩形的函數(shù)。
FrameBuffer 只是一個提供顯示內(nèi)存和顯示芯片寄存器從物理內(nèi)存映射到進(jìn)程地址空間中的設(shè)備。所以,對于應(yīng)用程序而言,如果希望在FrameBuffer 之上進(jìn)行圖形編程,還需要自己動手完成其他許多工作。
*二、FrameBuffer在Linux中的實現(xiàn)和機(jī)制*
Framebuffer對應(yīng)的源文件在linux/drivers/video/目錄下。總的抽象設(shè)備文件為fbcon.c,在這個目錄下還有與各種顯卡驅(qū)動相關(guān)的源文件。
(一)、分析Framebuffer設(shè)備驅(qū)動
需要特別提出的是在INTEL平臺上,老式的VESA1.2卡,如CGA/EGA卡,是不能支持Framebuffer的,因為Framebuffer要 求顯卡支持線性幀緩沖,即CPU可以訪問顯緩沖中的每一位, 但是VESA1.2 卡只能允許CPU一次訪問64K的地址空間。
FrameBuffer設(shè)備驅(qū)動基于如下兩個文件:
1) linux/include/linux/fb.h
2) linux/drivers/video/fbmem.c 下面分析這兩個文件。
1、fb.h
幾乎主要的結(jié)構(gòu)都是在這個中文件定義的。這些結(jié)構(gòu)包括:
1)fb_var_screeninfo
這個結(jié)構(gòu)描述了顯示卡的特性:
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 */
};
2) fb_fix_screeninfon
這個結(jié)構(gòu)在顯卡被設(shè)定模式后創(chuàng)建,它描述顯示卡的屬性,并且系統(tǒng)運行時不能被修改;比如FrameBuffer內(nèi)存的起始地址。它依賴于被設(shè)定的模式,當(dāng)一個模 式被設(shè)定后,內(nèi)存信息由顯示卡硬件給出,內(nèi)存的位置等信息就不可以修改。
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 */
};
3) fb_cmap
描述設(shè)備無關(guān)的顏色映射信息。可以通過FBIOGETCMAP 和 FBIOPUTCMAP 對應(yīng)的ioctl操作設(shè)定或獲取顏色映射信息.
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 */
};
4) fb_info
定義當(dāng)顯卡的當(dāng)前狀態(tài);fb_info結(jié)構(gòu)僅在內(nèi)核中可見,在這個結(jié)構(gòu)中有一個fb_ops指針, 指向驅(qū)動設(shè)備工作所需的函數(shù)集。
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;
};
5) struct fb_ops
用戶應(yīng)用可以使用ioctl()系統(tǒng)調(diào)用來操作設(shè)備,這個結(jié)構(gòu)就是用一支持ioctl()的這些操作的。
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);
};
6) structure map
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
[編排有點困難,第一行的第一條豎線和下面的第一列豎線對齊,第一行的第二條豎線和下面的第二列豎線對齊就可以了]
這個結(jié)構(gòu) fbgen_hwswitch抽象了硬件的操作.雖然它不是必需的,但有時候很有用.
2、 fbmem.c
fbmem.c 處于Framebuffer設(shè)備驅(qū)動技術(shù)的中心位置.它為上層應(yīng)用程序提供系統(tǒng)調(diào)用也為下一層的特定硬件驅(qū)動提供接口;那些底層硬件驅(qū)動需要用到這兒的接口來向 系統(tǒng)內(nèi)核注冊它們自己.
fbmem.c 為所有支持FrameBuffer的設(shè)備驅(qū)動提供了通用的接口,避免重復(fù)工作.
1) 全局變量
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
這兩變量記錄了所有fb_info 結(jié)構(gòu)的實例,fb_info 結(jié)構(gòu)描述顯卡的當(dāng)前狀態(tài),所有設(shè)備對應(yīng)的fb_info結(jié)構(gòu)都保存在這個數(shù)組中,當(dāng)一個FrameBuffer設(shè)備驅(qū)動向系統(tǒng)注冊自己時,其對應(yīng)的 fb_info結(jié)構(gòu)就會添加到這個結(jié)構(gòu)中,同時num_registered_fb 為自動加1. static struct {
const char *name;
int (*init)(void);
int (*setup)(void);
} fb_drivers[] __initdata= { ….};
如果FrameBuffer設(shè)備被靜態(tài)鏈接到內(nèi)核,其對應(yīng)的入口就會添加到這個表中;如果是動態(tài)加載的,即使用insmod/rmmod,就不需要關(guān)心這個表。
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
};
這是一個提供給應(yīng)用程序的接口.
2)fbmem.c 實現(xiàn)了如下函數(shù).
register_framebuffer(struct fb_info *fb_info);
unregister_framebuffer(struct fb_info *fb_info);
這兩個是提供給下層FrameBuffer設(shè)備驅(qū)動的接口,設(shè)備驅(qū)動通過這兩函數(shù)向系統(tǒng)注冊或注銷自己。幾乎底層設(shè)備驅(qū)動所要做的所有事情就是填充fb_inf o結(jié)構(gòu)然后向系統(tǒng)注冊或注銷它。
(二)一個LCD顯示芯片的驅(qū)動實例
以Skeleton LCD控制器驅(qū)動為例,在LINUX中存有一個/fb/skeleton.c的skeleton的Framebuffer驅(qū)動程序,很簡單,僅僅是填充了 fb_info結(jié)構(gòu),并且注冊/注銷自己。設(shè)備驅(qū)動是向用戶程序提供系統(tǒng)調(diào)用接口,所以我們需要實現(xiàn)底層硬件操作并且定義file_operations 結(jié)構(gòu)來向系統(tǒng)提供系統(tǒng)調(diào)用接口,從而實現(xiàn)更有效的LCD控制器驅(qū)動程序。
1)在系統(tǒng)內(nèi)存中分配顯存
在fbmem.c文件中可以看到,file_operations結(jié)構(gòu)中的open()和release()操作不需底層支持,但read()、 write()和 mmap()操作需要函數(shù)fb_get_fix()的支持.因此需要重新實現(xiàn)函數(shù)fb_get_fix()。另外還需要在系統(tǒng)內(nèi)存中分配顯存空間,大多數(shù) 的LCD控制器都沒有自己的顯存空間,被分配的地址空間的起 始地址與長度將會被填充到fb_fix_screeninfo結(jié)構(gòu)的smem_start 和smem_len 的兩個變量中.被分配的空間必須是物理連續(xù)的。
2)實現(xiàn) fb_ops 中的函數(shù)
用戶應(yīng)用程序通過ioctl()系統(tǒng)調(diào)用操作硬件,fb_ops 中的函數(shù)就用于支持這些操作。(注: fb_ops結(jié)構(gòu)與file_operations結(jié)構(gòu)不同,fb_ops是底層操作的抽象,而file_operations是提供給上層系統(tǒng)調(diào)用的接 口,可以直接調(diào)用ioctl()系統(tǒng)調(diào)用在文件fbmem.c中實現(xiàn),通過觀察可以發(fā)現(xiàn)ioctl()命令與fb_ops’s 中函數(shù)的關(guān)系:
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目錄下的其它設(shè)備驅(qū)動是比較好的參考資料。在所 有的這 些函數(shù)中fb_set_var()是最重要的,它用于設(shè)定顯示卡的模式和其它屬性,下面是函數(shù)fb_set_var()的執(zhí)行步驟:
1)檢測是否必須設(shè)定模式
2)設(shè)定模式
3)設(shè)定顏色映射
4) 根據(jù)以前的設(shè)定重新設(shè)置LCD控制器的各寄存器。
第四步表明了底層操作到底放置在何處。在系統(tǒng)內(nèi)存中分配顯存后,顯存的起始地址及長度將被設(shè)定到LCD控制器的各寄存器中(一般通過 fb_set_var()函數(shù)),顯存中的內(nèi)容將自動被LCD控制器輸出到屏幕上。另一方面,用戶程序通過函數(shù)mmap()將顯存映射到用戶進(jìn)程地址空間 中,然后用戶進(jìn)程向映射空間發(fā)送 的所有數(shù)據(jù)都將會被顯示到LCD顯示器上。
評論
查看更多