[轉載請註明出處] 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:

display相關原理

external display實作心得

framebuffer howto

framebuffer wiki

 

kezeodsnx 發表在 痞客邦 PIXNET 留言(0) 人氣()