一、Linux的幀緩沖設(shè)備
幀緩沖(framebuffer)是 Linux 為顯示設(shè)備提供的一個(gè)接口,把顯存抽象后的一種設(shè)備,他允許上層應(yīng)用程序在圖形模式下直接對(duì)顯示緩沖區(qū)進(jìn)行讀寫操作。這種操作是抽象的,統(tǒng)一的。用戶不必關(guān)心物理顯存的位置、換頁(yè)機(jī)制等等具體細(xì)節(jié)。這些都是由Framebuffer 設(shè)備驅(qū)動(dòng)來(lái)完成的。幀緩沖驅(qū)動(dòng)的應(yīng)用廣泛,在 linux 的桌面系統(tǒng)中,Xwindow 服務(wù)器就是利用幀緩沖進(jìn)行窗口的繪制。尤其是通過(guò)幀緩沖可顯示漢字點(diǎn)陣,成為 Linux漢化的唯一可行方案。
幀緩沖設(shè)備對(duì)應(yīng)的設(shè)備文件為/dev/fb*,如果系統(tǒng)有多個(gè)顯示卡,Linux 下還可支持多個(gè)幀緩沖設(shè)備,最多可達(dá) 32 個(gè),分別為/dev/fb0 到/dev/fb31,而/dev/fb 則為當(dāng)前缺省的幀緩沖設(shè)備,通常指向/dev/fb0。當(dāng)然在嵌入式系統(tǒng)中支持一個(gè)顯示設(shè)備就夠了。幀緩沖設(shè)備為標(biāo)準(zhǔn)字符設(shè)備,主設(shè)備號(hào)為29,次設(shè)備號(hào)則從0到31。分別對(duì)應(yīng)/dev/fb0-/dev/fb31。
通過(guò)/dev/fb,應(yīng)用程序的操作主要有這幾種:
1.讀/寫(read/write)/dev/fb:相當(dāng)于讀/寫屏幕緩沖區(qū)。例如用 cp /dev/fb0 tmp 命令可將當(dāng)前屏幕的內(nèi)容拷貝到一個(gè)文件中,而命令 cp tmp > /dev/fb0 則將圖形文件tmp直接顯示在屏幕上。
2.映射(map)操作:由于 Linux 工作在保護(hù)模式,每個(gè)應(yīng)用程序都有自己的虛擬地址空間,在應(yīng)用程序中是不能直接訪問(wèn)物理緩沖區(qū)地址的。為此,Linux 在文件操作 file_operations 結(jié)構(gòu)中提供了?mmap?函數(shù),可將文件的內(nèi)容映射到用戶空間。對(duì)于幀緩沖設(shè)備,則可通過(guò)映射操作,可將屏幕緩沖區(qū)的物理地址映射到用戶空間的一段虛擬地址中,之后用戶就可以通過(guò)讀寫這段虛擬地址訪問(wèn)屏幕緩沖區(qū),在屏幕上繪圖了。
3.I/O控制:對(duì)于幀緩沖設(shè)備,對(duì)設(shè)備文件的 ioctl操作可讀取/設(shè)置顯示設(shè)備及屏幕的參數(shù),如分辨率,顯示顏色數(shù),屏幕大小等等。ioctl 的操作是由底層的驅(qū)動(dòng)程序來(lái)完成的。
在應(yīng)用程序中,操作/dev/fb的一般步驟如下:
1.打開/dev/fb設(shè)備文件。
2.用 ioctl 操作取得當(dāng)前顯示屏幕的參數(shù),如屏幕分辨率,每個(gè)像素點(diǎn)的比特?cái)?shù)。根據(jù)屏幕參數(shù)可計(jì)算屏幕緩沖區(qū)的大小。
3.將屏幕緩沖區(qū)映射到用戶空間(mmap)。
4.映射后就可以直接讀寫屏幕緩沖區(qū),進(jìn)行繪圖和圖片顯示了。
典型程序段如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include
int?main()
{
int?fbfd = 0;
struct?fb_var_screeninfo vinfo;
struct?fb_fix_screeninfo finfo;
long?int?screensize = 0;
/*打開設(shè)備文件*/
fbfd = open("/dev/fb0", O_RDWR);
/*取得屏幕相關(guān)參數(shù)*/
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo);
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
/*計(jì)算屏幕緩沖區(qū)大小*/
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
/*映射屏幕緩沖區(qū)到用戶地址空間*/
fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED, fbfd, 0);
/*下面可通過(guò) fbp指針讀寫緩沖區(qū)*/
……
/*釋放緩沖區(qū),關(guān)閉設(shè)備*/
munmap(fbp, screensize);
close(fbfd);
}
二、ioctl操作
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)
獲取fb_var_screeninfo結(jié)構(gòu)的信息,在linux/include/linux/fb.h定義。
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)
獲取fb_fix_screeninfon結(jié)構(gòu)的信息。在linux/include/linux/fb.h定義。
fbfd為設(shè)備文件號(hào)。
三.mmap函數(shù)
功能描述:
mmap函數(shù)是unix/linux下的系統(tǒng)調(diào)用
mmap將一個(gè)文件或者其它對(duì)象映射進(jìn)內(nèi)存。文件被映射到多個(gè)頁(yè)上,如果文件的大小不是所有頁(yè)的大小之和,最后一個(gè)頁(yè)不被使用的空間將會(huì)清零。munmap執(zhí)行相反的操作,刪除特定地址區(qū)域的對(duì)象映射。
基于文件的映射,在mmap和munmap執(zhí)行過(guò)程的任何時(shí)刻,被映射文件的st_atime可能被更新。如果st_atime字段在前述的情況下沒(méi)有得到更新,首次對(duì)映射區(qū)的第一個(gè)頁(yè)索引時(shí)會(huì)更新該字段的值。用PROT_WRITE 和 MAP_SHARED標(biāo)志建立起來(lái)的文件映射,其st_ctime 和 st_mtime
在對(duì)映射區(qū)寫入之后,但在msync()通過(guò)MS_SYNC 和 MS_ASYNC兩個(gè)標(biāo)志調(diào)用之前會(huì)被更新。
用法:
1
2
3
4
5
6
7
#include
void?*mmap(void?*start, size_t length, int?prot, int?flags,
int?fd, off_t offset);
int?munmap(void?*start, size_t length);
參數(shù):
start:映射區(qū)的開始地址。
length:映射區(qū)的長(zhǎng)度。
prot:期望的內(nèi)存保護(hù)標(biāo)志,不能與文件的打開模式?jīng)_突。是以下的某個(gè)值,可以通過(guò)or運(yùn)算合理地組合在一起
PROT_EXEC //頁(yè)內(nèi)容可以被執(zhí)行
PROT_READ //頁(yè)內(nèi)容可以被讀取
PROT_WRITE //頁(yè)可以被寫入
PROT_NONE //頁(yè)不可訪問(wèn)
flags:指定映射對(duì)象的類型,映射選項(xiàng)和映射頁(yè)是否可以共享。它的值可以是一個(gè)或者多個(gè)以下位的組合體
MAP_FIXED //使用指定的映射起始地址,如果由start和len參數(shù)指定的內(nèi)存區(qū)重疊于現(xiàn)存的映射空間,重疊部分將會(huì)被丟棄。如果指定的起始地址不可用,操作將會(huì)失敗。并且起始地址必須落在頁(yè)的邊界上。
MAP_SHARED //與其它所有映射這個(gè)對(duì)象的進(jìn)程共享映射空間。對(duì)共享區(qū)的寫入,相當(dāng)于輸出到文件。直到msync()或者munmap()被調(diào)用,文件實(shí)際上不會(huì)被更新。
MAP_PRIVATE //建立一個(gè)寫入時(shí)拷貝的私有映射。內(nèi)存區(qū)域的寫入不會(huì)影響到原文件。這個(gè)標(biāo)志和以上標(biāo)志是互斥的,只能使用其中一個(gè)。
MAP_DENYWRITE //這個(gè)標(biāo)志被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要為這個(gè)映射保留交換空間。當(dāng)交換空間被保留,對(duì)映射區(qū)修改的可能會(huì)得到保證。當(dāng)交換空間不被保留,同時(shí)內(nèi)存不足,對(duì)映射區(qū)的修改會(huì)引起段違例信號(hào)。
MAP_LOCKED //鎖定映射區(qū)的頁(yè)面,從而防止頁(yè)面被交換出內(nèi)存。
MAP_GROWSDOWN //用于堆棧,告訴內(nèi)核VM系統(tǒng),映射區(qū)可以向下擴(kuò)展。
MAP_ANONYMOUS //匿名映射,映射區(qū)不與任何文件關(guān)聯(lián)。
MAP_ANON //MAP_ANONYMOUS的別稱,不再被使用。
MAP_FILE //兼容標(biāo)志,被忽略。
MAP_32BIT //將映射區(qū)放在進(jìn)程地址空間的低2GB,MAP_FIXED指定時(shí)會(huì)被忽略。當(dāng)前這個(gè)標(biāo)志只在x86-64平臺(tái)上得到支持。
MAP_POPULATE //為文件映射通過(guò)預(yù)讀的方式準(zhǔn)備好頁(yè)表。隨后對(duì)映射區(qū)的訪問(wèn)不會(huì)被頁(yè)違例阻塞。
MAP_NONBLOCK //僅和MAP_POPULATE一起使用時(shí)才有意義。不執(zhí)行預(yù)讀,只為已存在于內(nèi)存中的頁(yè)面建立頁(yè)表入口。
fd:有效的文件描述詞。如果MAP_ANONYMOUS被設(shè)定,為了兼容問(wèn)題,其值應(yīng)為-1。
offset:被映射對(duì)象內(nèi)容的起點(diǎn)。
返回說(shuō)明:
成功執(zhí)行時(shí),mmap()返回被映射區(qū)的指針,munmap()返回0。失敗時(shí),mmap()返回MAP_FAILED[其值為(void *)-1],munmap返回-1。errno被設(shè)為以下的某個(gè)值
EACCES:訪問(wèn)出錯(cuò)
EAGAIN:文件已被鎖定,或者太多的內(nèi)存已被鎖定
EBADF:fd不是有效的文件描述詞
EINVAL:一個(gè)或者多個(gè)參數(shù)無(wú)效
ENFILE:已達(dá)到系統(tǒng)對(duì)打開文件的限制
ENODEV:指定文件所在的文件系統(tǒng)不支持內(nèi)存映射
ENOMEM:內(nèi)存不足,或者進(jìn)程已超出最大內(nèi)存映射數(shù)量
EPERM:權(quán)能不足,操作不允許
ETXTBSY:已寫的方式打開文件,同時(shí)指定MAP_DENYWRITE標(biāo)志
SIGSEGV:試著向只讀區(qū)寫入
SIGBUS:試著訪問(wèn)不屬于進(jìn)程的內(nèi)存區(qū)
四、編程實(shí)例:
內(nèi)核:linux-2.6.29.1
目標(biāo)板:友善之臂mini2440
arm-linux-gcc-4.3.2
下載?
==========================
Framebuffer驅(qū)動(dòng)程序模型?
? 下圖會(huì)向你展示目前的framebuffer設(shè)備驅(qū)動(dòng)的結(jié)構(gòu),最常用的是非標(biāo)準(zhǔn)驅(qū)動(dòng)。很明顯他所處的層次最高,程序編寫是最容易的。理解了這個(gè)圖的,你已經(jīng)很輕松的去完成一個(gè)fb驅(qū)動(dòng),比如給sa1100,s2410,s2440系列的ARM的LCD控制器寫驅(qū)動(dòng)。?
Color Map 剖析?
在framebuffer驅(qū)動(dòng)程序設(shè)計(jì)中,cmap這個(gè)東東太暈了?,F(xiàn)在我要把他赤裸裸的剖析給大家:)?
1. struct fb_cmap?
1
2
3
4
5
6
7
8
9
/*顏色映射表*/
struct?fb_cmap {
__u32 start; /* First entry */
__u32 len; /* Number of entries */
__u16 *red; /* 紅色 */
__u16 *green; /*綠色*/
__u16 *blue; /*藍(lán)色*/
__u16 *transp; /* 透明度,允許 NULL */
};
該結(jié)構(gòu)在fb.h文件中定義,在struct fb_ops結(jié)構(gòu)中有兩個(gè)成員函數(shù)與其相關(guān):
1
2
3
4
/*獲取顏色表*/
int?(*fb_get_cmap)(struct?fb_cmap *cmap, int?kspc, int?con, struct?fb_info *info);
/*設(shè)定顏色表*/
int?(*fb_set_cmap)(struct?fb_cmap *cmap, int?kspc, int?con, struct?fb_info *info);
在struct fb_info結(jié)構(gòu)中有變量:
struct fb_cmap cmap; /* Current cmap */
在fpgen基礎(chǔ)操作下提供:
1
2
3
4
5
6
7
8
extern?int
fbgen_get_cmap(struct?fb_cmap *cmap, int?kspc, int?con, struct?fb_info *info);
extern?int
fbgen_set_cmap(struct?fb_cmap *cmap, int?kspc, int?con, struct?fb_info *info);
在文件/* drivers/video/fbcmap.c */中提供更多的cmap應(yīng)用
1
2
3
4
5
6
7
8
9
10
extern?int?fb_alloc_cmap(struct?fb_cmap *cmap, int?len, int?transp);
extern?void?fb_copy_cmap(struct?fb_cmap *from, struct?fb_cmap *to, int?fsfromto);
extern?int?fb_get_cmap(struct?fb_cmap *cmap, int?kspc,
int?(*getcolreg)(u_int, u_int *, u_int *, u_int *,u_int *, struct?fb_info *),
struct?fb_info *fb_info);
extern?int?fb_set_cmap(struct?fb_cmap *cmap, int?kspc,
int?(*setcolreg)(u_int, u_int, u_int, u_int, u_int,struct?fb_info *),
struct?fb_info *fb_info);
extern?struct?fb_cmap *fb_default_cmap(int?len);
extern?void?fb_invert_cmaps(void);
2. 通過(guò)文件解析?
在anakinfb.c文件中,cmap如圖?
?
在stifb.c?
本文介紹的設(shè)備是位于/video目錄下面的anakinfb.c驅(qū)動(dòng)程序。雖然我不清楚那個(gè)設(shè)備的特性,但是從對(duì)程序的分析中我們?nèi)匀恢廊绾尉帉懸粋€(gè)frame buffer設(shè)備驅(qū)動(dòng)。
本文是個(gè)標(biāo)準(zhǔn)的fb驅(qū)動(dòng)。共221行,包含函數(shù)如下:
1
2
3
4
5
6
7
8
9
10
11
static?int??anakinfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *transp, struct?fb_info *info) 31行
static?int?anakinfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,u_int transp, struct?fb_info *info) 45行
static?int?anakinfb_get_fix(struct?fb_fix_screeninfo *fix, int?con, struct?fb_info *info) 57行
static?int?anakinfb_get_var(struct?fb_var_screeninfo *var, int?con, struct?fb_info *info) 75行
static?int?anakinfb_set_var(struct?fb_var_screeninfo *var, int?con, struct?fb_info *info) 111行
static?int?anakinfb_get_cmap(struct?fb_cmap *cmap, int?kspc, int?con,???? struct?fb_info *info) 117行
static?int?anakinfb_set_cmap(struct?fb_cmap *cmap, int?kspc, int?con,???? struct?fb_info *info) 130行
static?int?anakinfb_switch_con(int?con, struct?fb_info *info) 147行
static?int?anakinfb_updatevar(int?con, struct?fb_info *info) 155行
static?void?anakinfb_blank(int?blank, struct?fb_info *info) 161行
int?__init anakinfb_init(void) 178行
函數(shù)1,2是寄存器操作用。函數(shù)3,4,5,6,7是fb_ops函數(shù)。函數(shù)8用于切換控制臺(tái)。函數(shù)9用于更新變量。函數(shù)10用于閃爍屏幕。函數(shù)11用于初始化設(shè)備。?
??? 很奇怪,對(duì)fb設(shè)備的讀寫函數(shù)怎么沒(méi)有!值得說(shuō)明的是open,release,read,write,ioctl,mmap等函數(shù)的實(shí)現(xiàn)是由 fbmem.c文件實(shí)現(xiàn)了。也就是說(shuō)所有的fb設(shè)備在給定了fb_info后,所有的操作都是一樣的。在明確的fb_info前提下,fbmem.c中的函數(shù)可以工作的很好。這樣大家應(yīng)該感到非常輕松了吧,只要完成上述的幾個(gè)設(shè)備相關(guān)的函數(shù),frame buffer設(shè)備的驅(qū)動(dòng)就寫完了:)??
??? 系統(tǒng)的結(jié)構(gòu)如圖:??
Stifb驅(qū)動(dòng)模型?
linux/drivers/video/stifb.c - Generic frame buffer driver for HP * workstations with STI (standard text interface) video firmware.?
這個(gè)驅(qū)動(dòng)程序和前面的anakin設(shè)備完全不同,因?yàn)樗皇遣捎脴?biāo)準(zhǔn)的格式,而是根據(jù) based on skeletonfb, which wasCreated 28 Dec 1997 by Geert Uytterhoeven也就是skeletonfb.c提供的框架完成的。
共230行,包含函數(shù)如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
static?int?sti_encode_fix(struct?fb_fix_screeninfo *fix, const?void?*par, struct?fb_info_gen *info) 60行
static?int?sti_decode_var(const?struct?fb_var_screeninfo *var,void?*par, struct?fb_info_gen *info) 71行
static?int?sti_encode_var(struct?fb_var_screeninfo *var, const?void?*par, struct?fb_info_gen *info) 78行
static?void?sti_get_par(void?*par, struct?fb_info_gen *info) 94行
static?void?sti_set_par(const?void?*par, struct?fb_info_gen *info) 99行
static?int?sti_getcolreg(unsigned regno, unsigned *red, unsigned *green, unsigned *blue, unsigned *transp, ?struct?fb_info *info) 104行
static?int?sti_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct?fb_info *info) 111行
static?void?sti_set_disp(const?void?*par, struct?display *disp, struct?fb_info_gen *info) 118行
static?void?sti_detect(void) 127行
static?int?sti_blank(int?blank_mode, const?struct?fb_info *info) 132行
int?__init stifb_init(void) 161行
void?stifb_cleanup(struct?fb_info *info) 201行
int?__init stifb_setup(char?*options) 208行
?
評(píng)論
查看更多