[轉載請註明出處] http://kezeodsnx.pixnet.net/blog
作者: kezeodsnx
簡介
framebuffer (fbdev)的特色是與硬體無關的抽像層,用來在console做圖形的處理,好處是不需要如Xwindow這些loading很大的東西來完成圖形化的display。目前來說,包括gtk,qt都可直接操作framebuffer,而不需透過X-windows。意思是在一些resource相對珍貴的embedded system,可以享受到gtk的library,直接對framebuffer做render (GtkFB),而不用透過X-windows這麼龐大的東西。然而,bottleneck是performance,但DirectFB解決了這個問題,它提供了硬體加速的framework。
在kernel 2.1.x的開發,最早是在intel平台上的一個不同的console driver (bitmapped graphical consoles),這也是開機看到的那隻企鵝第一次出現。開啟framebuffer,除了kernel要把console driver build-in外,在bootloader加上vga mode的參數 (解析度跟bpp),此時指定的參數就固定了,不能再改。
kernel:
[*] VGA text console
[*] Video mode selection support
<*> Framebuffer console support
bootloader:
kernel /boot/vmlinuz-2.6.27-7-generic root=UUID=b3306e27-17fd-477e-b6f4-4e28a8492e69 ro locale=zh_TW quiet splash vga=798
基本上,framebuffer是一塊連續的記憶體,其內容就是從LCD看到的螢幕內容(display),由CPU寫入。因此,螢幕的改變,包括滑鼠的移動,視窗拖移,都會改變framebuffer的內容。所以可以如下清空螢幕:
dd if=/dev/zero of=/dev/fb
也可複製螢幕的snapshot:
dd if=/dev/fb of=snapshot
然後再把它寫回去
dd if=./snapshot of=/dev/fb0
操作
/dev/fb0讓應用程式可以在userspace來改變目前的display,其方式是透過ioctl取得相關的屬性,然後透過mmap將這塊記憶體map到userspace,然後改變這塊map過後的記憶體內容。
在kernel也實作了ioctl的操作手則,在kernel/driver/video裡的fb.h和fbmem.c。
fb.h是描述顯示的的屬性:在fb_var_screeninfo 中,描述了resolution,PCLK,timing parameter,hsync,vsync等。這些屬性是可以被改變的,也就是說可以改resolution,調PCLK等操作。
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 */
};
fb_fix_screeninfon也是描述顯卡的屬性,不過這些是不能被修改的,如framebuffer的起始位址,長度等,通常只供讀取。
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 */
};
fb_info則描述顯卡當前的狀態,因此也包括了fb_var_screeninfo和fb_fix_screeninfo
fb_ops定義了一堆callback function,包括fb_open,fb_get_fix,fb_get_var,fb_set_var,fb_ioctl,fb_mmap等,並實作在fbmem.c裡
而fbmem.c主要是實作fb_ioctl裡的callback function。以待會範例程式會用到的ioctl來說,實作了如FBIOGET_VSCREENINFO, FBIOPUT_VSCREENINFO ,FBIOGET_FSCREENINFO等等的cmd。
範例程式
類似的code在網路上還蠻多的。
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>
int main(int argc, char* argv[])
{
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;
int x = 0, y = 0;
long int location = 0;
// Open the file for reading and writing
fbfd = open("/dev/fb0", O_RDWR);//打開framebuffer,取得其fd
if (!fbfd) {
printf("Error: cannot open framebuffer device.\n");
return 1;
}
printf("The framebuffer device was opened successfully.\n");
// 取得顯卡的fix屬性
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
printf("Error reading fixed information.\n");
return 1;
}
// 取得顯卡的var屬性
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
printf("Error reading variable information.\n");
return 1;
}
printf("%dx%d, %dbpp\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);
// 算出整個screen所需要的size,以byte為單位,以便稍後將記憶體map到userspace
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
// 這樣就map到userspace了
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
fbfd, 0);
if ((int)fbp == -1) {
printf("Error: failed to map framebuffer device to memory.\n");
return 1;
}
printf("The framebuffer device was mapped to memory successfully.\n");
x = 100; y = 100; // screen左上角為0,0,在座標為(100,100)的地方開始畫
// 畫一個200x200個pixel的正方形
for (y = 100; y < 300; y++)
for (x = 100; x < 300; x++) {
location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) +
(y+vinfo.yoffset) * finfo.line_length;
if (vinfo.bits_per_pixel == 32) {
//以下是填這個pixel的屬性,包括RGB的值,以及透明的程度
*(fbp + location) = 100; // Some blue
*(fbp + location + 1) = 15+(x-100)/2; // A little green
*(fbp + location + 2) = 200-(y-100)/5; // A lot of red
*(fbp + location + 3) = 0; // No transparency
}
}
munmap(fbp, screensize); //歸還記憶體
close(fbfd);//記得要關閉framebuffer的fd
return 0;
}
用gcc編譯:gcc -o fb fb.c,並在console(ctrl+alt+f2)下以root權限執行 (勿在terminal),就會看到一個有點粉色的正方形了。
Reference: