Skip to content

Latest commit

 

History

History
648 lines (490 loc) · 24.2 KB

File metadata and controls

648 lines (490 loc) · 24.2 KB

二十一、帧缓冲驱动

显卡总是有一定的内存。这个内存是图像数据的位图缓冲显示的地方。从软件的角度来看,帧缓冲区是一个字符设备,提供对内存的访问。

也就是说,帧缓冲驱动提供了一个接口,用于:

  • 显示模式设置
  • 对视频缓冲区的内存访问
  • 基本 2D 加速操作(例如,滚动)

为了提供这个接口,framebuffer 驱动通常直接与硬件对话。有一些众所周知的帧缓冲驱动,例如:

  • intelfb ,是各种英特尔 8xx/9xx 兼容图形设备的帧缓冲器
  • vesafb ,这是一个帧缓冲驱动,使用 VESA 标准接口与视频硬件对话
  • mxcfb ,i.MX6 芯片系列的帧缓冲驱动

Framebuffer 驱动是 Linux 下最简单的图形驱动形式,不要把它们和 X.org 驱动混淆,后者实现了 3D 加速等高级功能,或者是内核模式设置(KMS)驱动,它同时公开了 framebuffer 和 GPU 功能(就像 X.org 驱动一样)。

i.MX6 X.org driver is a closed source and called vivante.

回到我们的帧缓冲驱动,它们是非常简单的应用编程接口驱动,通过字符设备展示显卡功能,可通过/dev/fbX条目从用户空间访问。你可以在马丁·费德勒的综合演讲 Linux 图形去神秘化:http://keyj.emphy.de/files/linuxgraphics_en.pdf中找到更多关于 Linux 图形堆栈的信息。

在本章中,我们将讨论以下主题:

  • Framebuffer 驱动数据结构和方法,从而覆盖整个驱动架构
  • 帧缓冲区设备操作,加速和非加速
  • 从用户空间访问帧缓冲区

驱动数据结构

framebuffer 驱动严重依赖于四种数据结构,它们都在include/linux/fb.h中定义,这也是为了处理 framebuffer 驱动,您应该在代码中包含的头:

#include <linux/fb.h> 

这些结构是fb_var_screeninfofb_fix_screeninfofb_cmapfb_info。前三个可以从用户空间代码获得。现在让我们描述每个结构的目的,它们的意义,以及它们的用途。

  1. 内核使用struct struct fb_var_screeninfo的一个实例来保存显卡的可变属性。这些值由用户定义,例如分辨率深度:
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; /* # of bits needed to hold a pixel */ 
   [...] 

   /* 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 rotate; /* angle we rotate counter clockwise */ 
}; 

这可以总结为如下图所示:

  1. 视频卡有一些属性是固定的,或者是由制造商固定的,或者是在设置模式时应用的,否则无法更改。这一般是硬件信息。这方面的一个很好的例子是帧缓冲存储器的启动,即使用户程序也不能改变。内核在struct fb_fix_screeninfo结构的实例中保存这样的信息:
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;      /* Indicate to driver which   */ 
                     /* specific chip/card we have */ 
   __u16 capabilities; /* see FB_CAP_* */ 
}; 
  1. struct fb_cmap结构指定颜色映射,用于以内核可以理解的方式存储用户对颜色的定义,以便将其发送到底层视频硬件。可以使用这种结构来定义不同颜色所需的 RGB 比率:
struct fb_cmap { 
    __u32 start;   /* First entry */ 
    __u32 len;     /* Number of entries */ 
    __u16 *red;    /* Red values */ 
    __u16 *green;  /* Green values */ 
    __u16 *blue;   /* Blue values */ 
    __u16 *transp; /* Transparency. Discussed later on */ 
}; 
  1. struct fb_info结构代表帧缓冲区本身,是帧缓冲区驱动的主要数据结构。与前面讨论的其他结构不同,fb_info只存在于内核中,不是用户空间 framebuffer API 的一部分:
struct fb_info { 
    [...] 
    struct fb_var_screeninfo var; /* Variable screen information. 
                                   Discussed earlier. */ 
    struct fb_fix_screeninfo fix; /* Fixed screen information. */ 
    struct fb_cmap cmap;          /* Color map. */ 
    struct fb_ops *fbops;         /* Driver operations.*/ 
    char __iomem *screen_base;    /* Frame buffer's 
                                   virtual address */ 
    unsigned long screen_size;    /* Frame buffer's size */ 
    [...] 
   struct device *device;        /* This is the parent */ 
struct device *dev;           /* This is this fb device */ 
#ifdef CONFIG_FB_BACKLIGHT 
      /* assigned backlight device */ 
      /* set before framebuffer registration,  
         remove after unregister */ 
      struct backlight_device *bl_dev; 

      /* Backlight level curve */ 
      struct mutex bl_curve_mutex;   
      u8 bl_curve[FB_BACKLIGHT_LEVELS]; 
#endif 
[...] 
void *par; /* Pointer to private memory */ 
}; 

struct fb_info结构应该总是动态分配的,使用framebuffer_alloc(),这是一个内核(帧缓冲区核心)助手函数,为帧缓冲区设备的实例分配内存,以及它们的私有数据内存:

struct fb_info *framebuffer_alloc(size_t size, struct device *dev) 

在这个原型中,size将私有区域的大小表示为一个参数,并将其附加到已分配的fb_info的末尾。可以使用fb_info结构中的.par指针来引用该私有区域。framebuffer_release()做反操作:

void framebuffer_release(struct fb_info *info) 

一旦设置好了,应该使用register_framebuffer()向内核注册一个帧缓冲区,如果错误则返回否定的errno,如果成功则返回zero:

int register_framebuffer(struct fb_info *fb_info) 

注册后,可以使用unregister_framebuffer()函数取消注册帧缓冲区,该函数在出错时也会返回一个否定的errno,如果成功则返回zero:

int unregister_framebuffer(struct fb_info *fb_info) 

分配和注册应该在设备探测期间完成,而取消注册和取消分配(释放)应该在驱动的remove()功能中完成。

设备方法

struct fb_info结构中,有一个.fbops字段,它是struct fb_ops结构的一个实例。该结构包含需要在 framebuffer 设备上执行一些操作的函数的集合。这些是fbdevfbcon工具的入口点。该结构中的一些方法是强制性的,是帧缓冲区工作所需的最低要求,而其他方法是可选的,取决于驱动需要公开的功能,假设设备本身支持这些功能。

以下是struct fb_ops结构的定义:

    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); 

   /* For framebuffers with strange nonlinear layouts or that do not 
    * work with normal memory mapped access 
    */ 
   ssize_t (*fb_read)(struct fb_info *info, char __user *buf, 
                  size_t count, loff_t *ppos); 
   ssize_t (*fb_write)(struct fb_info *info, const char __user *buf, 
                   size_t count, loff_t *ppos); 

   /* checks var and eventually tweaks it to something supported, 
    * DO NOT MODIFY PAR */ 
   int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); 

   /* set the video mode according to info->var */ 
   int (*fb_set_par)(struct fb_info *info); 

   /* set color register */ 
   int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, 
                   unsigned blue, unsigned transp, struct fb_info *info); 

   /* set color registers in batch */ 
   int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); 

   /* blank display */ 
   int (*fb_blank)(int blank_mode, struct fb_info *info); 

   /* pan display */ 
   int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); 

   /* Draws a rectangle */ 
   void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); 
   /* Copy data from area to another */ 
   void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); 
   /* Draws a image to the display */ 
   void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); 

   /* Draws cursor */ 
   int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); 

   /* wait for blit idle, optional */ 
   int (*fb_sync)(struct fb_info *info); 

   /* perform fb specific ioctl (optional) */ 
   int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, 
               unsigned long arg); 

   /* Handle 32bit compat ioctl (optional) */ 
   int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, 
               unsigned long arg); 

   /* perform fb specific mmap */ 
   int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); 

   /* get capability given var */ 
   void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps, 
                   struct fb_var_screeninfo *var); 

   /* teardown any resources to do with this framebuffer */ 
   void (*fb_destroy)(struct fb_info *info); 
   [...] 
}; 

可以根据想要实现的功能设置不同的回调。

第四章字符设备驱动中,我们了解到字符设备通过struct file_operations结构,可以导出一组文件操作,这些操作是文件相关系统调用的入口点,如open()close()read()write()mmap()ioctl()等。

也就是说,不要把fb_opsfile_operations结构混淆。fb_ops提供了底层操作的抽象,而file_operations则是上层系统调用接口。内核在drivers/video/fbdev/core/fbmem.c中实现帧缓冲区文件操作,内部调用我们在fb_ops中定义的方法。这样就可以根据系统调用接口的需要实现底层的硬件操作,即file_operations结构。比如用户open()设备时,核心的打开文件操作方式会执行一些核心操作,如果设置了fb_ops.fb_open()方式,则执行releasemmap等同样的方式。

帧缓冲设备支持一些在include/uapi/linux/fb.h中定义的 ioctl 命令,用户程序可以使用这些命令在硬件上进行操作。这些命令都由核心的fops.ioctl方法处理。对于其中一些命令,核心的 ioctl 方法可以在内部执行在fb_ops结构中定义的方法。

人们可能想知道fb_ops.ffb_ioctl是用来做什么的。只有当内核不知道给定的 ioctl 命令时,framebuffer 内核才执行fb_ops.fb_ioctl。换句话说,fb_ops.fb_ioctl在 framebuffer 核心的fops.ioctl方法的默认语句中执行。

驱动方法

驱动方法由probe()remove()功能组成。在进一步描述这些方法之前,让我们建立我们的fb_ops结构:

static struct fb_ops myfb_ops = { 
   .owner        = THIS_MODULE, 
   .fb_check_var = myfb_check_var, 
   .fb_set_par   = myfb_set_par, 
   .fb_setcolreg = myfb_setcolreg, 
   .fb_fillrect  = cfb_fillrect, /* Those three hooks are */  
   .fb_copyarea  = cfb_copyarea, /* non accelerated and */ 
   .fb_imageblit = cfb_imageblit, /* are provided by kernel */ 
   .fb_blank     = myfb_blank, 
}; 
  • Probe:驱动probe功能负责初始化硬件,使用framebuffer_alloc()功能创建struct fb_info结构,上面register_framebuffer()。以下示例假设设备是内存映射的。因此,你的非内存映射可以存在,比如屏幕坐在 SPI 总线上。在这种情况下,应使用总线专用例程:
static int myfb_probe(struct platform_device *pdev) 
{ 
   struct fb_info *info; 
   struct resource *res; 
    [...] 

   dev_info(&pdev->dev, "My framebuffer driver\n"); 

/* 
 * Query resource, like DMA channels, I/O memory, 
 * regulators, and so on. 
 */ 
   res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
   if (!res) 
         return -ENODEV; 
   /* use request_mem_region(), ioremap() and so on */ 
    [...] 
    pwr = regulator_get(&pdev->dev, "lcd"); 

   info = framebuffer_alloc(sizeof( 
struct my_private_struct), &pdev->dev); 
   if (!info) 
         return -ENOMEM; 

   /* Device init and default info value*/ 
   [...] 
   info->fbops = &myfb_ops; 

    /* Clock setup, using devm_clk_get() and so on */ 
    [...] 

    /* DMA setup using dma_alloc_coherent() and so on*/   
    [...] 

    /* Register with the kernel */ 
   ret = register_framebuffer(info); 

   hardware_enable_controller(my_private_struct); 
   return 0; 
} 
  • Remove:remove()功能应该释放在probe()获得的任何东西,并调用:
static int myfb_remove(struct platform_device *pdev) 
{ 

   /* iounmap() memory and release_mem_region() */ 
   [...] 
   /* Reverse DMA, dma_free_*();*/ 
   [...] 

   hardware_disable_controller(fbi); 

    /* first unregister, */ 
   unregister_framebuffer(info); 
    /* and then free the memory */ 
   framebuffer_release(info); 

   return 0; 
} 
  • 假设您使用管理器版本进行资源分配,您只需要使用unregister_framebuffer()framebuffer_release()。其他一切都将由内核完成。

详细 fb_ops

让我们描述一下fb_ops结构中声明的一些钩子。也就是说,关于编写 framebuffer 驱动的想法,您可以看看drivers/video/fbdev/vfb.c,它是内核中一个简单的虚拟 framebuffer 驱动。您还可以在drivers/video/fbdev/imxfb.c查看其他特定的帧缓冲驱动,比如 i.MX6 one,或者在Documentation/fb/api.txt查看关于帧缓冲驱动 API 的内核文档。

检验信息

钩子fb_ops->fb_check_var负责检查帧缓冲区参数。其原型如下:

int (*fb_check_var)(struct fb_var_screeninfo *var, 
struct fb_info *info); 

这个函数应该检查 framebuffer 变量参数并调整到有效值。var代表帧缓冲区变量参数,应检查和调整:

static int myfb_check_var(struct fb_var_screeninfo *var, 
struct fb_info *info) 
{ 
   if (var->xres_virtual < var->xres) 
         var->xres_virtual = var->xres; 

   if (var->yres_virtual < var->yres) 
         var->yres_virtual = var->yres; 

   if ((var->bits_per_pixel != 32) && 
(var->bits_per_pixel != 24) && 
(var->bits_per_pixel != 16) && 
(var->bits_per_pixel != 12) && 
       (var->bits_per_pixel != 8)) 
         var->bits_per_pixel = 16; 

   switch (var->bits_per_pixel) { 
   case 8: 
         /* Adjust red*/ 
         var->red.length = 3; 
         var->red.offset = 5; 
         var->red.msb_right = 0; 

         /*adjust green*/ 
         var->green.length = 3; 
         var->green.offset = 2; 
         var->green.msb_right = 0; 

         /* adjust blue */ 
         var->blue.length = 2; 
         var->blue.offset = 0; 
         var->blue.msb_right = 0; 

         /* Adjust transparency */ 
         var->transp.length = 0; 
         var->transp.offset = 0; 
         var->transp.msb_right = 0; 
         break; 
   case 16: 
         [...] 
         break; 
   case 24: 
         [...] 
         break; 
   case 32: 
         var->red.length = 8; 
         var->red.offset = 16; 
         var->red.msb_right = 0; 

         var->green.length = 8; 
         var->green.offset = 8; 
         var->green.msb_right = 0; 

         var->blue.length = 8; 
         var->blue.offset = 0; 
         var->blue.msb_right = 0; 

         var->transp.length = 8; 
         var->transp.offset = 24; 
         var->transp.msb_right = 0; 
         break; 
   } 

   /* 
 * Any other field in *var* can be adjusted 
 * like var->xres,      var->yres, var->bits_per_pixel, 
 * var->pixclock and so on. 
 */ 
   return 0; 
} 

上述代码根据用户选择的配置调整可变帧缓冲区属性。

设置控制器的参数

钩子fp_ops->fb_set_par是另一个硬件特定的钩子,负责向硬件发送参数。它根据用户设置(info->var对硬件进行编程:

static int myfb_set_par(struct fb_info *info) 
{ 
   struct fb_var_screeninfo *var = &info->var; 

   /* Make some compute or other sanity check */ 
   [...] 

    /* 
     * This function writes value to the hardware, 
     * in the appropriate registers 
     */ 
   set_controller_vars(var, info); 

   return 0; 
} 

屏幕消隐

钩子fb_ops->fb_blank是硬件专用钩子,负责屏幕下料。其原型如下:

int (*fb_blank)(int blank_mode, struct fb_info *info) 

blank_mode参数始终是以下值之一:

enum { 
   /* screen: unblanked, hsync: on,  vsync: on */ 
   FB_BLANK_UNBLANK       = VESA_NO_BLANKING, 

   /* screen: blanked,   hsync: on,  vsync: on */ 
   FB_BLANK_NORMAL        = VESA_NO_BLANKING + 1, 

   /* screen: blanked,   hsync: on,  vsync: off */ 
   FB_BLANK_VSYNC_SUSPEND = VESA_VSYNC_SUSPEND + 1, 

   /* screen: blanked,   hsync: off, vsync: on */ 
   FB_BLANK_HSYNC_SUSPEND = VESA_HSYNC_SUSPEND + 1, 

   /* screen: blanked,   hsync: off, vsync: off */ 
   FB_BLANK_POWERDOWN     = VESA_POWERDOWN + 1 
}; 

空白显示的通常方式是在blank_mode参数上做switch case,如下所示:

static int myfb_blank(int blank_mode, struct fb_info *info) 
{ 
   pr_debug("fb_blank: blank=%d\n", blank); 

   switch (blank) { 
   case FB_BLANK_POWERDOWN: 
   case FB_BLANK_VSYNC_SUSPEND: 
   case FB_BLANK_HSYNC_SUSPEND: 
   case FB_BLANK_NORMAL: 
         myfb_disable_controller(fbi); 
         break; 

   case FB_BLANK_UNBLANK: 
         myfb_enable_controller(fbi); 
         break; 
   } 
   return 0; 
} 

消隐操作应禁用控制器,停止其时钟并断电。取消冻结应该执行相反的操作。

加速方法

用户视频操作,如混合、拉伸、移动位图或动态渐变生成都是繁重的任务。它们需要图形加速来获得可接受的性能。可以使用struct fp_ops结构的以下字段实现帧缓冲加速方法:

  • .fb_imageblit():这个方法在显示器上画一个图像,非常有用
  • .fb_copyarea():此方法将一个矩形区域从一个屏幕区域复制到另一个屏幕区域
  • .fb_fillrect():该方法以优化的方式用像素线填充矩形

因此,内核开发者想到了没有硬件加速的控制器,并提供了软件优化的方法。这使得加速实现成为可选的,因为存在软件回退。也就是说,如果帧缓冲控制器不提供任何加速机制,那么必须使用内核通用例程来填充这些方法。

这些分别是:

  • cfb_imageblit():这是 imageblit 的内核提供的回退。内核使用它在启动时向屏幕输出一个徽标。
  • cfb_copyarea():这是区域复制操作。
  • cfb_fillrect():这是实现同名操作的 framebuffer 核心非加速方法。

把它们放在一起

在这一节中,让我们总结一下上一节讨论的内容。为了编写 framebuffer 驱动,必须:

  • 填充一个struct fb_var_screeninfo结构,以便提供有关 framebuffer 变量属性的信息。用户空间可以更改这些属性。
  • 填充一个struct fb_fix_screeninfo结构,提供固定的参数。
  • 建立struct fb_ops结构,提供必要的回调函数,帧缓冲子系统将使用这些函数来响应用户的动作。
  • 仍然在struct fb_ops结构中,如果设备支持,必须提供加速函数回调。
  • 设置一个struct fb_info结构,给它填充上一步填充的结构,并在上面调用register_framebuffer(),以便在内核中注册。

关于编写简单的帧缓冲驱动的想法,可以看看drivers/video/fbdev/vfb.c,这是一个内核中的虚拟帧缓冲驱动。您可以通过CONGIF_FB_VIRTUAL选项在内核中启用此功能。

来自用户空间的帧缓冲区

人们通常通过mmap()命令访问帧缓冲存储器,以便将帧缓冲存储器映射到系统内存的一部分,从而在屏幕上绘制像素成为影响内存值的简单事情。屏幕参数(可变和固定)通过 ioctl 命令提取,尤其是FBIOGET_VSCREENINFOFBIOGET_FSCREENINFO。完整列表可在内核源代码中的include/uapi/linux/fb.h处获得。

下面是在 framebuffer 上绘制 300*300 正方形的示例代码:

#include <stdlib.h> 
#include <unistd.h> 
#include <stdio.h> 
#include <fcntl.h> 
#include <linux/fb.h> 
#include <sys/mman.h> 
#include <sys/ioctl.h> 

#define FBCTL(_fd, _cmd, _arg)         \ 
    if(ioctl(_fd, _cmd, _arg) == -1) { \ 
        ERROR("ioctl failed");         \ 
        exit(1); } 

int main() 
{ 
    int fd; 
    int x, y, pos; 
    int r, g, b; 
    unsigned short color; 
    void *fbmem; 

    struct fb_var_screeninfo var_info; 
    struct fb_fix_screeninfo fix_info; 

    fd = open(FBVIDEO, O_RDWR); 
    if (tfd == -1 || vfd == -1) { 
        exit(-1); 
    } 

    /* Gather variable screen info (virtual and visible) */ 
    FBCTL(fd, FBIOGET_VSCREENINFO, &var_info); 

    /* Gather fixed screen info */ 
    FBCTL(fd, FBIOGET_FSCREENINFO, &fix_info); 

    printf("****** Frame Buffer Info ******\n"); 
    printf("Visible: %d,%d  \nvirtual: %d,%d \n  line_len %d\n", 
           var_info.xres, this->var_info.yres, 
           var_info.xres_virtual, var_info.yres_virtual, 
           fix_info.line_length); 
    printf("dim %d,%d\n\n", var_info.width, var_info.height); 

    /* Let's mmap frame buffer memory */ 
    fbmem = mmap(0, v_var.yres_virtual * v_fix.line_length, \ 
                     PROT_WRITE | PROT_READ, \ 
                     MAP_SHARED, fd, 0); 

    if (fbmem == MAP_FAILED) { 
        perror("Video or Text frame bufer mmap failed"); 
        exit(1); 
    } 

    /* upper left corner (100,100). The square is 300px width */ 
    for (y = 100; y < 400; y++) { 
        for (x = 100; x < 400; x++) { 
            pos = (x + vinfo.xoffset) * (vinfo.bits_per_pixel / 8) 
                   +   (y + vinfo.yoffset) * finfo.line_length; 

            /* if 32 bits per pixel */ 
            if (vinfo.bits_per_pixel == 32) { 
                /* We prepare some blue color */ 
                *(fbmem + pos) = 100; 

                /* adding a little green */ 
                *(fbmem + pos + 1) = 15+(x-100)/2; 

                /* With lot of read */ 
                *(fbmem + pos + 2) = 200-(y-100)/5; 

                /* And no transparency */ 
                *(fbmem + pos + 3) = 0; 
            } else  { /* This assume 16bpp */ 
                r = 31-(y-100)/16; 
                g = (x-100)/6; 
                b = 10; 

                /* Compute color */  
                color = r << 11 | g << 5 | b; 
                *((unsigned short int*)(fbmem + pos)) = color; 
            } 
        } 
    } 

    munmap(fbp, screensize); 
    close(fbfd); 
    return 0; 
} 

还可以使用catdd命令将帧缓冲存储器转储为原始图像:

 # cat /dev/fb0 > my_image 

使用以下方法写回:

 # cat my_image > /dev/fb0 

可以通过特殊的/sys/class/img/fb<N>/blank sysfs文件来清空/取消屏幕,其中<N>是帧缓冲区索引。写 1 会使屏幕空白,而写 0 会使屏幕不空白:

 # echo 0 > /sys/class/img/fb0/blank
    # echo 1 > /sys/class/img/fb0/blank

摘要

帧缓冲驱动是 Linux 图形驱动的最简单形式,几乎不需要实现工作。他们大量抽象硬件。在这个阶段,您应该能够增强现有的驱动(例如图形加速功能),或者从头开始编写一个新的驱动。但是,建议依赖于现有的驱动,该驱动的硬件与您需要为其编写驱动的硬件共享尽可能多的特性。让我们跳到下一章,也是最后一章,讨论网络设备。