了解framebuffer

把png图片资源转换为framebuffer可显示数据     我们都知道,我们是通过framebuffer来显示图片的,但是我们有不能直接把png图片显示到framebuffer,因为framebuffer是不能直接识别png图片的。     我们如何把一个1.png图片转换为framebuffer需要的格式呢? 1. 了解framebuffer的大小 # cat /sys/class/graphics/fb0/window_axis ... 阅读详情
Framebuffer的配置及应用

*一、FrameBuffer的原理*
FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。
Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这 个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过Framebuffer的读写直接对显存进行操 作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操 作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。
    但Framebuffer本身不具备任何运算数据的能力,就只好比是一个暂时存放水的水池.CPU将运算后的结果放到这个水池,水池再将结果流到显示器.中间不会对数据做处理. 应用程序也可以直接读写这个水池的内容.在这种机制下,尽管Framebuffer需要真正的显卡驱动的支持,但所有显示任务都有CPU完成,因此CPU负担很重.
framebuffer的设备文件一般是 /dev/fb0、/dev/fb1 等等。
可以用命令: #dd if=/dev/zero of=/dev/fb 清空屏幕.
如果显示模式是 1024x768-8 位色,用命令:$ dd if=/dev/zero of=/dev/fb0 bs=1024 count=768 清空屏幕;
用命令: #dd if=/dev/fb of=fbfile  可以将fb中的内容保存下来;
可以重新写回屏幕: #dd if=fbfile of=/dev/fb;
在使用Framebuffer时,Linux是将显卡置于图形模式下的.

    在应用程序中,一般通过将 FrameBuffer 设备映射到进程地址空间的方式使用,比如下面的程序就打开 /dev/fb0 设备,并通过mmap 系统调用进行地址映射,随后用 memset 将屏幕清空(这里假设显示模式是 1024x768-8 位色模式,线性内存模式):
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*76;

    FrameBuffer 设备还提供了若干ioctl命令,通过这些命令,可以获得显示设备的一些固定信息(比如显示内存大小)、与显示模式相关的可变信息(比如分辨率、象素结构、每扫描线的字节宽度),以及伪彩 色模式下的调色板信息等等。
    通过 FrameBuffer设备,还可以获得当前内核所支持的加速显示卡的类型(通过固定信息得到),这种类型通常是和特定显示芯片相关的。比如目前最新的内核(2.4.9)中,就包含有 对S3、Matrox、nVidia、3Dfx 等等流行显示芯片的加速支持。在获得了加速芯片类型之后,应用程序就可以将 PCI设备的内存I/O(memio)映射到进程的地址空间。这些memio
一般是用来控制显示卡的寄存器,通过对这些寄存器的操作,应用程序就可以控制特定显卡的加速功能。
    PCI设备可以将自己的控制寄存器映射到物理内存空间,而后,对这些控制寄存器的访问,给变成了对物理内存的访问。因此,这些寄存器又被称为"memio"。一旦被映 射到物理内存,Linux的普通进程就可以通过 mmap 将这些内存 I/O 映射到进程地址空间,这样就可以直接访问这些寄存器了。
    当然,因为不同的显示芯片具有不同的加速能力,对memio的使用和定义也各自不同,这时,就需要针对加速芯片的不同类型来编写实现不同的加速功能。比如大多数芯片都提供了对矩形填充的硬件加速支持,但不同的芯片实现方 式不同,这时,就需要针对不同的芯片类型编写不同的用来完成填充矩形的函数。
    FrameBuffer 只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备。所以,对于应用程序而言,如果希望在FrameBuffer 之上进行图形编程,还需要自己动手完成其他许多工作。

*二、FrameBuffer在Linux中的实现和机制*
Framebuffer对应的源文件在linux/drivers/video/目录下。总的抽象设备文件为fbcon.c,在这个目录下还有与各种显卡驱动相关的源文件。
(一)、分析Framebuffer设备驱动
    需要特别提出的是在INTEL平台上,老式的VESA1.2卡,如CGA/EGA卡,是不能支持Framebuffer的,因为Framebuffer要求显卡支持线性帧缓冲,即CPU可以访问显缓冲中的每一位, 但是VESA1.2 卡只能允许CPU一次访问64K的地址空间。
FrameBuffer设备驱动基于如下两个文件:
1) linux/include/linux/fb.h
2) linux/drivers/video/fbmem.c 下面分析这两个文件。
1、fb.h
   几乎主要的结构都是在这个中文件定义的。这些结构包括:
1)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; /* 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
这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如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 */
};


3) fb_cmap
描述设备无关的颜色映射信息。可以通过FBIOGETCMAP 和 FBIOPUTCMAP 对应的ioctl操作设定或获取颜色映射信息.
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
定义当显卡的当前状态;fb_info结构仅在内核中可见,在这个结构中有一个fb_ops指针, 指向驱动设备工作所需的函数集。
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
用户应用可以使用ioctl()系统调用来操作设备,这个结构就是用一支持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
[编排有点困难,第一行的第一条竖线和下面的第一列竖线对齐,第一行的第二条竖线和下面的第二列竖线对齐就可以了]
这个结构 fbgen_hwswitch抽象了硬件的操作.虽然它不是必需的,但有时候很有用.
2、 fbmem.c
fbmem.c 处于Framebuffer设备驱动技术的中心位置.它为上层应用程序提供系统调用也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向 系统内核注册它们自己.
fbmem.c 为所有支持FrameBuffer的设备驱动提供了通用的接口,避免重复工作.
1) 全局变量
struct fb_info *registered_fb[FB_MAX];
int num_registered_fb;
这两变量记录了所有fb_info 结构的实例,fb_info 结构描述显卡的当前状态,所有设备对应的fb_info结构都保存在这个数组中,当一个FrameBuffer设备驱动向系统注册自己时,其对应的fb_info结构就会添加到这个结构中,同时num_registered_fb 为自动加1. static struct {
const char *name;
int (*init)(void);
int (*setup)(void);
} fb_drivers[] __initdata= { ....};


如果FrameBuffer设备被静态链接到内核,其对应的入口就会添加到这个表中;如果是动态加载的,即使用insmod/rmmod,就不需要关心这个表。
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
};


这是一个提供给应用程序的接口.
2)fbmem.c 实现了如下函数.
register_framebuffer(struct fb_info *fb_info);
unregister_framebuffer(struct fb_info *fb_info);
这两个是提供给下层FrameBuffer设备驱动的接口,设备驱动通过这两函数向系统注册或注销自己。几乎底层设备驱动所要做的所有事情就是填充fb_inf o结构然后向系统注册或注销它。
(二)一个LCD显示芯片的驱动实例
    以Skeleton LCD控制器驱动为例,在LINUX中存有一个/fb/skeleton.c的skeleton的Framebuffer驱动程序,很简单,仅仅是填充了fb_info结构,并且注册/注销自己。设备驱动是向用户程序提供系统调用接口,所以我们需要实现底层硬件操作并且定义file_operations结构来向系统提供系统调用接口,从而实现更有效的LCD控制器驱动程序。
1)在系统内存中分配显存
在fbmem.c文件中可以看到,file_operations结构中的open()和release()操作不需底层支持,但read()、write()和 mmap()操作需要函数fb_get_fix()的支持.因此需要重新实现函数fb_get_fix()。另外还需要在系统内存中分配显存空间,大多数的LCD控制器都没有自己的显存空间,被分配的地址空间的起 始地址与长度将会被填充到fb_fix_screeninfo结构的smem_start 和smem_len 的两个变量中.被分配的空间必须是物理连续的。
2)实现 fb_ops 中的函数
用户应用程序通过ioctl()系统调用操作硬件,fb_ops 中的函数就用于支持这些操作。(注: fb_ops结构与file_operations结构不同,fb_ops是底层操作的抽象,而file_operations是提供给上层系统调用的接口,可以直接调用ioctl()系统调用在文件fbmem.c中实现,通过观察可以发现ioctl()命令与fb_ops's 中函数的关系:
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目录下的其它设备驱动是比较好的参考资料。在所有的这 些函数中fb_set_var()是最重要的,它用于设定显示卡的模式和其它属性,下面是函数fb_set_var()的执行步骤:
1)检测是否必须设定模式
2)设定模式
3)设定颜色映射
4) 根据以前的设定重新设置LCD控制器的各寄存器。

第四步表明了底层操作到底放置在何处。在系统内存中分配显存后,显存的起始地址及长度将被设定到LCD控制器的各寄存器中(一般通过fb_set_var()函数),显存中的内容将自动被LCD控制器输出到屏幕上。另一方面,用户程序通过函数mmap()将显存映射到用户进程地址空间中,然后用户进程向映射空间发送 的所有数据都将会被显示到LCD显示器上。
*三、FrameBuffer的应用*
(一)、一个使用FrameBuffer的例子
   1. FrameBuffer主要是根据VESA标准的实现的,所以只能实现最简单的功能。
   2. 由于涉及内核的问题,FrameBuffer是不允许在系统起来后修改显示模式等一系列操作。(好象很多人都想要这样干,这是不被允许的,当然如果你自己写驱动 的话,是可以实现的).
   3. 对FrameBuffer的操作,会直接影响到本机的所有控制台的输出,包括XWIN的图形界面。
好,现在可以让我们开始实现直接写屏:
1、打开一个FrameBuffer设备
2、通过mmap调用把显卡的物理内存空间映射到用户空间
3、直接写内存。
/********************************
File name : fbtools.h
*/
#ifndef _FBTOOLS_H_
#define _FBTOOLS_H_
#include <linux/fb.h>
//a framebuffer device structure;
typedef struct fbdev{
       int fb;
       unsigned long fb_mem_offset;
       unsigned long fb_mem;
       struct fb_fix_screeninfo fb_fix;
       struct fb_var_screeninfo fb_var;
       char dev[20];
} FBDEV, *PFBDEV;


//open & init a frame buffer
//to use this function,
//you must set FBDEV.dev="/dev/fb0"
//or "/dev/fbX"
//it's your frame buffer.
int fb_open(PFBDEV pFbdev);
//close a frame buffer
int fb_close(PFBDEV pFbdev);
//get display depth
int get_display_depth(PFBDEV pFbdev);
//full screen clear
void fb_memset(void *addr, int c, size_t len);
#endif
/******************
File name : fbtools.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <asm/page.h>
#include "fbtools.h"
#define TRUE        1
#define FALSE       0
#define MAX(x,y)        ((x)>(y)?(x)y))
#define MIN(x,y)        ((x)<(y)?(x)y))
//open & init a frame buffer
int fb_open(PFBDEV pFbdev)
{
       pFbdev->fb = open(pFbdev->dev, O_RDWR);
       if(pFbdev->fb < 0)
       {
              printf("Error opening %s: %m. Check kernel config\n",
pFbdev->dev);
              return FALSE;
       }
       if (-1 == ioctl(pFbdev->fb,FBIOGET_VSCREENINFO,&(pFbdev->fb_var)))
       {
              printf("ioctl FBIOGET_VSCREENINFO\n");
              return FALSE;
       }
       if (-1 == ioctl(pFbdev->fb,FBIOGET_FSCREENINFO,&(pFbdev->fb_fix)))
       {
              printf("ioctl FBIOGET_FSCREENINFO\n");
              return FALSE;
       }
       //map physics address to virtual address
       pFbdev->fb_mem_offset = (unsigned long)(pFbdev->fb_fix.smem_start) &
(~PAGE_MASK);
       pFbdev->fb_mem = (unsigned long int)mmap(NULL,
pFbdev->fb_fix.smem_len + pFbdev->fb_mem_offset,              PROT_READ |
PROT_WRITE, MAP_SHARED, pFbdev->fb, 0);
       if (-1L == (long) pFbdev->fb_mem)
       {
              printf("mmap error! mem:%d offset:%d\n", pFbdev->fb_mem,
pFbdev->fb_mem_offset);
              return FALSE;
       }
       return TRUE;
}


//close frame buffer
int fb_close(PFBDEV pFbdev)
{
       close(pFbdev->fb);
       pFbdev->fb=-1;
}


//get display depth
int get_display_depth(PFBDEV pFbdev);
{
       if(pFbdev->fb<=0)
       {
              printf("fb device not open, open it first\n");
              return FALSE;
       }
       return pFbdev->fb_var.bits_per_pixel;
}


//full screen clear
void fb_memset (void *addr, int c, size_t len)
{
    memset(addr, c, len);
}


//use by test
#define DEBUG
#ifdef DEBUG
main()
{
       FBDEV fbdev;
       memset(&fbdev, 0, sizeof(FBDEV));
       strcpy(fbdev.dev, "/dev/fb0");
       if(fb_open(&fbdev)==FALSE)
       {
              printf("open frame buffer error\n");
              return;
       }
       fb_memset(fbdev.fb_mem + fbdev.fb_mem_offset, 0,
fbdev.fb_fix.smem_len);
              fb_close(&fbdev);
}


(二)基于Linux核心的汉字显示的尝试
我们以一个简单的例子来说明字符显示的过程。我们假设是在虚拟终端1(/dev/tty1)下运行一个如下的简单程序。
main ( )
{
puts("hello, world.\n");
}


puts 函数向缺省输出文件(/dev/tty1)发出写的系统调用write(2)。系统调用到linux核心里面对应的核心函数是console.c中的con_w rite(),con_write()最终会调用do_con_write()。在do_con_write( )中负责把"hello,world.\n"这个字符串放到tty1对应的缓冲区中去。
do_con_write( )还负责处理控制字符和光标的位置。让我们来看一下do_con_write()这个函数的声明。
static int do_con_write(struct tty_struct * tty, int from_user, const
unsigned char *buf, int count)
    其中tty是指向tty_struct结构的指针,这个结构里面存放着关于这个tty的所有信息(请参照
linux/include/linux/tty.h)。Tty_struct结构中定义了通用(或高层)tty的属性(例如宽度和高度等)。在do_con_ write()函数中用到了tty_struct结构中的driver_data变量。driver_data是一个vt_struct指针。在vt_struct结构中包 含这个tty的序列号(我们正使用tty1,所以这个序号为1)。Vt_struct结构中有一个vc结构的数组vc_cons,这个数组就是各虚拟终端的私有 数据。
static int do_con_write(struct tty_struct * tty, int from_user,const
unsigned char *buf, int count)
{
struct vt_struct *vt = (struct vt_struct
*)tty->driver_data;//我们用到了driver_data变量
. . . . .
currcons = vt->vc_num; file://我们在这里的vc_nums就是1
. . . . .
}


要访问虚拟终端的私有数据,需使用vc_cons〔currcons〕.d指针。这个指针指向的结构含有当前虚拟终端上光标的位置、缓冲区的起始地址、缓冲区大 小等等。
    "hello, world.\n"中的每一个字符都要经过conv_uni_to_pc()这个函数转换成8位的显示字符。这要做的主要目的是使不同语言的国家能把16位的UniCode码映射到8位的显示字符集上,目前还是主要针对欧洲国家的语言 ,映射结果为8位,不包含对双字节(double byte)的范围。
这种UNICODE到显示字符的映射关系可以由用户自行定义。在缺省的映射表上,会把中文的字符映射到其他的字符上,这是我们不希望看到也是不需要的。所以我们 有两个选择∶
   1. 不进行conv_uni_to_pc( )的转换。
   2. 加载符合双字节处理的映射关系,即对非控制字符进行1对1的不变映射。
   我们自己定制的符合这种映射关系的UNICODE码表是direct.uni。要想查看/装载当前系统的unicode映射表,可使外部命令loadunima p。
经过conv_uni_to_pc( )转换之后,"hello,world.\n"中的字符被一个一个地填写到tty1的缓冲区中。然后do_con_write()调用下层的驱动,把缓冲区中的内容输出到显示器上(也就相当于把缓冲区的内容拷贝到VGA显存中去)。 sw->con_putcs(vc_cons〔currcons〕.d, (u16 *)draw_from, (u16*)draw_to-(u16 *)draw_from, y, draw_x);
之所以要调用底层驱动,是因为存在不同的显示设备,其对应VGA显存的存取方式也不一样。
上面的Sw->con_putcs()就会调用到fbcon.c中的fbcon_putcs()函数(con_putcs是一个函数的指针,在Framebuffer模式下指向fbcon_putcs()函数)。也就是说在do_con_write()函数中是直接调用了fbcon_putcs()函数来进行字符的绘制。比如说在256色模式下,真正负责输出的函数是void fbcon_cfb8_putcs(struct vc_data *conp, struct display *p,const unsigned short *s, int count, int yy, int xx)
显示中文
    比如说我们试图输出一句中文∶putcs(你好\n);(你好的内码为0xc4,0xe3,0xba,0xc3)。这时候会怎么样呢,有一点可以肯定,"你好"肯定不会出现在屏幕上,国为核心中没有汉字字库,中 文显示就是无米之炊了.
    1 在负责字符显示的void fbcon_cfb8_putcs()函数中,原有操作如下∶对于每个要显示的字符,依次从虚拟终端缓冲区中以WORD为单位读取(低位字节是ASCII码,高8位是字符的属性),由于汉字是双字 节编码方式,所以这种操作是不可能显示出汉字的,只能显示出xxxx_putcs()是一个一个VGA字符.
要解决的问题∶
确保在do_con_write( )时uni□pc转换不会改变原有编码。一个很直接的实现方式就是加载一个我们自己定制的UNICODE映射表,loadunimapdirect.uni,或者直接把direct.uni置为核心的缺省映射表。 针对如上问题,我们要做的第一个尝试方案是如下。
首先需要在核心中加载汉字字库,然后修改fbcon_cfb8_putcs()函数,在fbcon_cfb8_putcs()中一次读两个WORD,检查这两个WORD的低位字节是否能拼成一个汉字,如果发现能拼成一个汉字,就算出这个汉字在汉字字库中的偏移,然后把它当成一个16
x 16的VGA字符来显示。
试验的结果表明∶
   1. 能够输出汉字,但仍有许多不理想的地方,比如说,输出以半个汉字开始的一串汉字,则这半个汉字后面的汉字都会是乱码。这是半个汉字的问题。
   2. 光标移动会破坏汉字的显示。表现为,光标移动过的汉字会变成乱码。这是因为光标的更新是通过xxxx_putc( )函数来完成的。
xxxx_putc( )函数与xxxx_putcs()函数实现的功能类似,但是xxxx_putc()函数只刷新一个字符而不是一个字符串,因而xxxx_putc()的输入参数是一个整数,而不是一个字符串的 地址。Xxxx_putc()函数的声明如下∶void fbcon_cfb8_putc(struct vc_data *conp, struct display *p, int c, int yy, int xx)
    下一个尝试方案就是同时修改xxxx_putcs()函数和xxxx_putc()函数。为了解决半个汉字的问题,每一次输出之前,都从屏幕当前行的起始位置开始扫描,以确定要输出的字符是否落在半个汉字的位置 上。如果是半个汉字的位置,则进行相应的调整,即从向前移动一个字节的位置开始输出。
    这个方案有一个困难,即xxxx_putc( )函数不用缓冲区的地址,而是用一个整数作为参数。所以xxxx_putc()无法直接利用相邻的字符来判别该定符是否是汉字。
    解决方案是,利用xxxx_putc( )的光标位置参数(yy,xx),可以逆推出该字符在缓冲区中的位置。但仍有一些小麻烦,在Linux的虚拟终端下,用户可能会上卷该屏幕(shift +
pageup),导致光标的y座标和相应字符在缓冲区的行数不一致。相应的解决方案是,在逆推的过程中,考虑卷屏的参量。
这样一来,我们就又进了一步,得到了一个相对更好的版本。但仍有问题没有解决。敲入turbonetcfg,会发现菜单的边框字符也被当成汉字显示。这是因为, 这种边框字符是扩展字符,也使用了字符的第8位,因而被当作汉字来显示。例如,单线一的制表符内码为0xC4,当连成一条长线就是由一连串0xC4组成,而0x C4C4正是汉字哪。于是水平的制表符被一连串的哪字替代了。要解决这个问题就非常不容易了,因为制表符的种类比较多,而且垂直制表符与其后面字符的组合型式又 多种多样,因而很难判断出相应位置的字符是不是制表符,从理论上说,无论采取什么样的排除算法,都必然存在误判的情况,因为总存在二义性,没有充足的条件来推断 出当前字符究竟是制表符还是汉字。
我们一方面寻找更好的排除组合算法,一方面试图寻找其它的解决方案。要想从根本上解决定个问题,必须利用其它的辅助信息,仅仅从缓冲区的字符来判断是不够的。
    经过一番努力,我们发现,在UNIX中使用扩展字符时,都要先输出字符转义序列(Escape sequence)来切换当前字符集。字符转义序列是以控制字符Esc为首的控制命令,在UNIX的虚拟终端中完成终端控制命令,这种命令包括,移动光标座标、 卷屏、删除、切换字符集等等。也就是说在输出代表制表符的字符串之前,通常是要先输出特定的字符转义序列。
在console.c里,有根据字符转义序列命令来记录字符状态的变量。结合该变量提供的信息,就可以非常干净地把制表符与汉字区别开来。
    在如上思路的指引下,我们又产生了新的解决方案。经过改动得到了另一各版本.
在这个新版本上,turbonetcfg在初次绘制的时候,制表符与汉字被清晰地区分开来,结果是非常正确的。但还有新的问题存在∶turbonetcfg在重绘的时候(如切换虚拟终端或是移动鼠标光标的时候),制表符还是变成了汉字,因为重绘完全依赖于缓冲区,而这时用来记录字符集状态的变量并不反映当前字符集 状态。问题还是没有最终解决。我们又回到了起点。∶(
看来问题的最终解决手段必须是把字符集的状态伴随每一个字符存在缓冲区中。让我们来研究一下缓冲区的结构。每一个字符占用16bit的缓冲区,低8位是ASCI I值,完全被利用,高8位包含前景颜色和背景颜色的属性,也没有多余的空间可以利用。因而只能另外开辟新的缓冲区。为了保持一致性,我们决定在原来的缓冲区后面 添加相同大小的缓冲区,用来存放是否是汉字的信息。
也许有读者会问,我们只需要为每个字符添加一bit的信息来标志是否是汉字就足够了,为什么还要开辟与原缓冲区大小相同的双倍缓冲区,是不是太浪费呢?我们先放 下这个问题,稍后再作回答。
其实,如果再添加一bit来标志是当前字符是汉字的左半边还是右半边的话,就会省去扫描屏幕上当前整行字符串的工作,这样一来,编程会更简单。但是有读者会问, 即使是这样,使用8bit总够用了吧?为什么还要使用16bit呢?
    我们的作法是∶用低8位来存放汉字另外一半的内码,用高8位中的2 bit来存放上面所讲的辅助信息,高8位的剩余6位可以用来存放汉字或其它编码方式(如BIG5或日文、韩文)的信息,从而使我们可以实现同屏显示多种双字节语 言的字符而不会有相互干扰。另外,在编程时,双倍缓冲也比较容易计算。这样我们就回答了如上的两个问题。
    迄今为止,我们有了一套彻底解决汉字和制表符相互干扰、半个汉字的刷新、重绘等问题的方案。剩下的就是具体编程实现的问题了。
    但是,由于Framebuffer的驱动很多,修改每一个驱动的xxxx_putc()函数和xxxx_putcs()函数会是一项不小的工作,而且,改动驱动程序后,每种驱动的测试也是很麻烦的,尤其是对于有硬件加速的显卡,修改和测试会更不容易。那么,存不存在一种不需要 修改显卡驱动程序的方法呢?
    经过努力,我们发现,可以在调用xxxx_putcs()或xxxx_putc()函数输出汉字之前,修改vga字库的指针使其指向所需显示的汉字在汉字字库中的位置,即把一个汉字当成两个vga
ASCII字符输出。也就是说,在内核中存在两个字库,一个是原有的vga字符字库,另一个是汉字字库,当我们需要输出汉字的时候,就把vga字库的指针指向汉 字字库的相应位置,汉字输出完之后,再把该指针指向vga字库的原有位置。
   这样一来,我们只需要修改fbcon.c和console.c,其中console.c负责维护双倍缓冲区,把每一个字符的信息存入附加的缓冲区;
而fbcon.c负责利用双倍缓冲区中附加的信息,调整vga字库的指针,调用底层的显示驱动程序。这里还有几个需要注意的地方∶
   1. 由于屏幕重绘等原因,调用底层驱动xxxx_putc()和xxxx_putcs()的地方有多处。我们作了两个函数分别包装这两个调用,完成替换字库、调用xxxx_putcs( )或xxxx_putc()、恢复字库等功能。
   2. 为了实现向上滚屏(shift + pageup)时也能看到汉字,我们需要作另外的修改。
       Linux 在设计虚拟终端的时候,提供了回顾被卷出屏幕以外的信息的功能,这就是用热键来向上滚屏(shift + pageup)。当前被使用的虚拟终端拥有一个公共的缓冲区(soft back),用来存放被滚出屏幕以外的信息。当切换虚拟终端的时候,公共缓冲区的内容会被清除而被新的虚拟终端使用。向上滚屏的时候,显示的是公共缓冲区中的内 容。因此,如果我们想在向上滚屏的时候看到汉字,公共缓冲区也必须加倍,以确保没有信息丢失。当滚出屏幕的信息向公共缓冲区填写的时候,必须把相应的附加信息也 填写进公共缓冲区的附加区域。
   这就要求fbcon.c必须懂得利用公共缓冲区的附加信息。
   当然,有另外一种偷懒的方法,那就是不允许用户向上滚屏,从而避免对公区缓冲区的处理。
   3. 把不同的编码方式(GB、BIG5、日文和韩文)写成不同的module,以实现动态加载,从而使得扩展新的编码方式不需要重新编译核心。
测试
本文实现的Kernel Patch文件(patch.kernel.chinese)可以从http://www.turbolinux.com.cn下载。Cd /usr/src/(该目录下应有Linux核心源程序所在的目录linux/) patch -p0 -b <patch.kernel.chinesemake menuconfig 请选择Console drivers选项中的 〔*〕 Double Byte Character Display Support(EXPERIMENTAL)
〔*〕 Double Byte GB encode (module only)
〔*〕 VESA VGA graphics console
<*> Virtual Frame Buffer support (ONLY FOR TESTING!)
<*> 8 bpp packed pixels support
<*> 16 bpp packed pixels support
<*> VGA characters/attributes support
〔*〕 Select compiled-in fonts
〔*〕VGA 8x8 font
〔*〕VGA 8x16 font
make dep
make bzImage
make modules
make install
make modules_install
然后用新的核心启动。

Insmod encode-gb.o
*四、其它*
(一)   设置FrameBuffer
    FrameBuffer,可以译作"帧缓冲",有时简称为fbdrv,基于fbdrv的console也被称之为fbcon。这是一种独立于硬件的抽象图形设备。FrameBuffer的优点在于其高度的可移植
性、易使用性、稳定性。使用Linux内核的FrameBuffer驱动(vesafb),可以轻松支持到1024X768X32bpp以上的分辩率。而且目前可得到的绝大多数linux版本所发行的内核中,已经预编译了FrameBuffer支持,通常不需要重新编译内核就可以使用。所以FrameBuffer也是zhcon推荐使用的驱动方式。
进入FrameBuffer可以简单地在系统启动时向kernel传送vga=mode-number的参数来激活FrameBuffer设备,如:
lilo:linux vga=305
将会启动1024x768x8bpp模式。
           640x480    800x600    1024x768    1280x1024
  8 bpp      769          771       773        775
  16 bpp     785          788       791        794
  32 bpp     786          789       792        795
(二)   要使linux缺省进入FrameBuffer,可以修改/etc/lilo.conf,加入一下语句:
vga=0x303退出编辑,执行:
lilo -v
重新启动linux,可以使其进入800x600的256色模式。
grub也是一样,在grub.conf中的kernel行后面写上vga=xxx就行了,也可以用vga=ask,让系统启动的时候询问你用多大的分辨率

(三)我编译内核时,选择framebuffer模式,启动时屏幕上有一企鹅图片,不知这是如何造成的这个图片可以去掉或改动吗?
可以将drivers/video/fbcon.c: fbcon_setup()中if (logo) { } 代码去掉。


Framebuffer的配置及应用


借助于framebuffer,我们能够在console下面作很多事情。首先下载framebuffer的配置工具fbset:
# apt-get install fbset 下载完毕后,配置文件/etc/fb.modes随之产生。

比较简单的作法是用万能的vesafb,如果它被编译进了内核,如:
Device Drivers -> Graphics support ->

  • VESA VGA graphics support
    那么在grub内核引导那一行的后面加上vga=791 它的含义是VESA framebuffer console @ 1024x768x64k,进入系统后可以直接使用framebuffer,看一下这种情况下的各项数据:
    # fbset -s
    mode "1024x768-76"
        # D: 78.653 MHz, H: 59.949 kHz, V: 75.694 Hz
        geometry 1024 768 1024 768 16
        timings 12714 128 32 16 4 128 4
        rgba 5/11,6/5,5/0,0/0
    endmode

    用具体显卡的framebuffer驱动是另一种选择,拿Nvidia显卡为例,Nvidia显卡的xorg驱动模块与其framebuffer的驱动模块是互相排斥的,如果要用一个就必须清除另一个:
    # rmmod nvidia
    装载nvidia的framebuffer驱动:
    # modprobe nvidiafb
    装载成功的时候,会产生/dev/fb0设备,console屏幕上的字体会有变化。
    看一下当前的配置:
    # fbset -s
    mode "1024x768-85"
        # D: 94.500 MHz, H: 68.677 kHz, V: 84.997 Hz
        geometry 1024 768 1024 32767 8
        timings 10582 208 48 36 1 96 3
        hsync high
        vsync high
        accel true
        rgba 8/0,8/0,8/0,0/0
    endmode

    需要改变一下geometry及色深:
    # fbset -g 1024 768 1024 768 32
    # fbset -s
    mode "1024x768-85"
        # D: 94.500 MHz, H: 68.677 kHz, V: 84.997 Hz
        geometry 1024 768 1024 768 32
        timings 10582 208 48 36 1 96 3
        hsync high
        vsync high
        accel true
        rgba 8/16,8/8,8/0,8/24
    endmode

    我们把它与使用VESA ramebuffer后的数据比较一下,显然,根据具体的显卡来驱动framebuffer可以在颜色上达到最佳值,好,现在我们在console下面能够作的事情:
    一、视频播放,可以用mplayer 或者 fbxine:
    # mplayer -vo fbdev -vf scale=1024:768 video_file.avi
    -vo fbdev 是告诉mplayer用framebuffer作视频驱动.
    -vf scale=1024:768 是全屏的方法,可按屏幕的具体情况作调整
    用fbxine的话需要下载:
    # apt-get install xine-console
    二、图片文件与pdf文件浏览:
    # apt-get install fbi
    用这个软件包里的fbi可以浏览图片,fbgs可以观看pdf文件:
    # fbi -a *jpg
    # fbgs -c *pdf
    三、中文显示:
    # apt-get install jfbterm
    # jfbterm
    中文显示的效果完美。

    文章出处:飞诺网(http://www.diybl.com/course/6_system/linux/Linuxjs/2008721/133651.html)

    ------------------------------------------------------------------------
    在内核Documentation/fb/vesafb.txt文件中,有如下vesa-framebuffer的说明
    Switching modes is done using the vga=... boot parameter. Read
    Documentation/svga.txt for details.
    You should compile in both vgacon (for text mode) and vesafb (for
    graphics mode). Which of them takes over the console depends on
    whenever the specified mode is text or graphics.
    The graphic modes are NOT in the list which you get if you boot with
    vga=ask and hit return. The mode you wish to use is derived from the
    VESA mode number. Here are those VESA mode numbers:
    | 640x480 800x600 1024x768 1280x1024
    ----+-------------------------------------
    256 | 0x101 0x103 0x105 0x107 8位色
    32k | 0x110 0x113 0x116 0x119 15位色
    64k | 0x111 0x114 0x117 0x11A 16位色
    16M | 0x112 0x115 0x118 0x11B 24位色
    The video mode number of the Linux kernel is the VESA mode number plus
    0x200.
    Linux_kernel_mode_number = VESA_mode_number + 0x200
    So the table for the Kernel mode numbers are:
    | 640x480 800x600 1024x768 1280x1024
    ----+-------------------------------------
    256 | 0x301 0x303 0x305 0x307 8位色
    32k | 0x310 0x313 0x316 0x319 15位色
    64k | 0x311 0x314 0x317 0x31A 16位色
    16M | 0x312 0x315 0x318 0x31B 24位色
    To enable one of those modes you have to specify "vga=ask" in the
    lilo.conf file and rerun LILO. Then you can type in the desired
    mode at the "vga=ask" prompt. For example if you like to use
    1024x768x256 colors you have to say "305" at this prompt.
    If this does not work, this might be because your BIOS does not support
    linear framebuffers or because it does not support this mode at all.
    Even if your board does, it might be the BIOS which does not. VESA BIOS
    Extensions v2.0 are required, 1.2 is NOT sufficient. You will get a
    "bad mode number" message if something goes wrong.
    1. Note: LILO cannot handle hex, for booting directly with
    "vga=mode-number" you have to transform the numbers to decimal.
    2. Note: Some newer versions of LILO appear to work with those hex values,
    if you set the 0x in front of the numbers.
    Framebuffer使用测试

    这两天拾起以前做过的Framebuffer,不相同的是以前在嵌入式上做的,现在在自己电脑上Debian上进行测试,不过都类似罢了,嵌入式里要初始化很多东西。下面具体列一下步骤。至于Framebuffer的原理,就我的理解是比较简单的,无非往mmap好的fb上填写显示数据罢了,不对这些数据进行处理,FrameBuffer 只是一个提供显示内存和显示芯片寄存器从物理内存映射到进程地址空间中的设备,它需要真正的显卡驱动的支持。在这次测试中,我用了默认就安装的vesafb,好像又被称为万能Fb驱动。

    1、   首先在系统Grub启动时按e进入命令启动行的编辑模式,改为:kernel /boot/vmlinuz-2.6.18-5-686 root=/dev/sda6 ro vga=791(vga=791表示fb用1024 * 768 * 16bpp,其他模式的参数可以上网查查);

    2、   进入系统的命令行模式,编译fb测试例子:gcc fb_test.c;

    3、   允许测试例子:sudo ./a.out >> fb.txt(必须要用超级用户权限,>>将屏幕打印写到fb.txt中),效果如下:

    打印如下:


    Fixed screen info:
        id: VESA VGA
        smem_start: 0xf0000000
        smem_len: 3145728
        type: 0
        type_aux: 0
        visual: 2
        xpanstep: 0
        ypanstep: 0
        ywrapstep: 0
        line_length: 2048
        mmio_start: 0x0
        mmio_len: 0
        accel: 0

    Variable screen info:
        xres: 1024
        yres: 768
        xres_virtual: 1024
        yres_virtual: 768
        yoffset: 0
        xoffset: 0
        bits_per_pixel: 16
        grayscale: 0
        red: offset: 11, length: 5, msb_right: 0
        green: offset: 5, length: 6, msb_right: 0
        blue: offset: 0, length: 5, msb_right: 0
        transp: offset: 0, length: 0, msb_right: 0
        nonstd: 0
        activate: 0
        height: -1
        width: -1
        accel_flags: 0x0
        pixclock: 12714
        left_margin: 128
        right_margin: 32
        upper_margin: 16
        lower_margin: 4
        hsync_len: 128
        vsync_len: 4
        sync: 0
        vmode: 0

    Frame Buffer Performance test...
            Average: 2508 usecs
            Bandwidth: 1196.172 MByte/Sec
            Max. FPS: 398.724 fps


    4、还可以通过fb在命令行模式下看视频,如:sudo mplayer –vo fbdev ./air_nessesity.mpg。


    1. #include <stdlib.h>
    2. #include <unistd.h>
    3. #include <stdio.h>
    4. #include <fcntl.h>
    5. #include <linux/fb.h>
    6. #include <linux/kd.h>
    7. #include <sys/mman.h>
    8. #include <sys/ioctl.h>
    9. #include <sys/time.h>
    10. #include <string.h>
    11. #include <errno.h>

    12. struct fb_var_screeninfo vinfo;
    13. struct fb_fix_screeninfo finfo;
    14. char *frameBuffer = 0;

    15. //打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。
    16. void
    17. printFixedInfo ()
    18. {
    19.     printf ("Fixed screen info:\n"
    20.             "\tid: %s\n"
    21.             "\tsmem_start: 0x%lx\n"
    22.             "\tsmem_len: %d\n"
    23.             "\ttype: %d\n"
    24.             "\ttype_aux: %d\n"
    25.             "\tvisual: %d\n"
    26.             "\txpanstep: %d\n"
    27.             "\typanstep: %d\n"
    28.             "\tywrapstep: %d\n"
    29.             "\tline_length: %d\n"
    30.             "\tmmio_start: 0x%lx\n"
    31.             "\tmmio_len: %d\n"
    32.             "\taccel: %d\n"
    33.             "\n",
    34.             finfo.id, finfo.smem_start, finfo.smem_len, finfo.type,
    35.             finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep,
    36.             finfo.ywrapstep, finfo.line_length, finfo.mmio_start,
    37.             finfo.mmio_len, finfo.accel);
    38. }

    39. //打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置
    40. void
    41. printVariableInfo ()
    42. {
    43.     printf ("Variable screen info:\n"
    44.             "\txres: %d\n"
    45.             "\tyres: %d\n"
    46.             "\txres_virtual: %d\n"
    47.             "\tyres_virtual: %d\n"
    48.             "\tyoffset: %d\n"
    49.             "\txoffset: %d\n"
    50.             "\tbits_per_pixel: %d\n"
    51.             "\tgrayscale: %d\n"
    52.             "\tred: offset: %2d, length: %2d, msb_right: %2d\n"
    53.             "\tgreen: offset: %2d, length: %2d, msb_right: %2d\n"
    54.             "\tblue: offset: %2d, length: %2d, msb_right: %2d\n"
    55.             "\ttransp: offset: %2d, length: %2d, msb_right: %2d\n"
    56.             "\tnonstd: %d\n"
    57.             "\tactivate: %d\n"
    58.             "\theight: %d\n"
    59.             "\twidth: %d\n"
    60.             "\taccel_flags: 0x%x\n"
    61.             "\tpixclock: %d\n"
    62.             "\tleft_margin: %d\n"
    63.             "\tright_margin: %d\n"
    64.             "\tupper_margin: %d\n"
    65.             "\tlower_margin: %d\n"
    66.             "\thsync_len: %d\n"
    67.             "\tvsync_len: %d\n"
    68.             "\tsync: %d\n"
    69.             "\tvmode: %d\n"
    70.             "\n",
    71.             vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual,
    72.             vinfo.xoffset, vinfo.yoffset, vinfo.bits_per_pixel,
    73.             vinfo.grayscale, vinfo.red.offset, vinfo.red.length,
    74.             vinfo.red.msb_right, vinfo.green.offset, vinfo.green.length,
    75.             vinfo.green.msb_right, vinfo.blue.offset, vinfo.blue.length,
    76.             vinfo.blue.msb_right, vinfo.transp.offset, vinfo.transp.length,
    77.             vinfo.transp.msb_right, vinfo.nonstd, vinfo.activate,
    78.             vinfo.height, vinfo.width, vinfo.accel_flags, vinfo.pixclock,
    79.             vinfo.left_margin, vinfo.right_margin, vinfo.upper_margin,
    80.             vinfo.lower_margin, vinfo.hsync_len, vinfo.vsync_len,
    81.             vinfo.sync, vinfo.vmode);
    82. }

    83. //画大小为width*height的同色矩阵,8alpha+8reds+8greens+8blues
    84. void
    85. drawRect_rgb32 (int x0, int y0, int width, int height, int color)
    86. {
    87.     const int bytesPerPixel = 4;
    88.     const int stride = finfo.line_length / bytesPerPixel;

    89.     int *dest = (int *) (frameBuffer)
    90.         + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);

    91.     int x, y;
    92.     for (y = 0; y < height; ++y)
    93.     {
    94.         for (x = 0; x < width; ++x)
    95.         {
    96.             dest[x] = color;
    97.         }
    98.         dest += stride;
    99.     }
    100. }

    101. //画大小为width*height的同色矩阵,5reds+6greens+5blues
    102. void
    103. drawRect_rgb16 (int x0, int y0, int width, int height, int color)
    104. {
    105.     const int bytesPerPixel = 2;
    106.     const int stride = finfo.line_length / bytesPerPixel;
    107.     const int red = (color & 0xff0000) >> (16 + 3);
    108.     const int green = (color & 0xff00) >> (8 + 2);
    109.     const int blue = (color & 0xff) >> 3;
    110.     const short color16 = blue | (green << 5) | (red << (5 + 6));

    111.     short *dest = (short *) (frameBuffer)
    112.         + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);

    113.     int x, y;
    114.     for (y = 0; y < height; ++y)
    115.     {
    116.         for (x = 0; x < width; ++x)
    117.         {
    118.             dest[x] = color16;
    119.         }
    120.         dest += stride;
    121.     }
    122. }

    123. //画大小为width*height的同色矩阵,5reds+5greens+5blues
    124. void
    125. drawRect_rgb15 (int x0, int y0, int width, int height, int color)
    126. {
    127.     const int bytesPerPixel = 2;
    128.     const int stride = finfo.line_length / bytesPerPixel;
    129.     const int red = (color & 0xff0000) >> (16 + 3);
    130.     const int green = (color & 0xff00) >> (8 + 3);
    131.     const int blue = (color & 0xff) >> 3;
    132.     const short color15 = blue | (green << 5) | (red << (5 + 5)) | 0x8000;

    133.     short *dest = (short *) (frameBuffer)
    134.         + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset);

    135.     int x, y;
    136.     for (y = 0; y < height; ++y)
    137.     {
    138.         for (x = 0; x < width; ++x)
    139.         {
    140.             dest[x] = color15;
    141.         }
    142.         dest += stride;
    143.     }
    144. }

    145. void
    146. drawRect (int x0, int y0, int width, int height, int color)
    147. {
    148.     switch (vinfo.bits_per_pixel)
    149.     {
    150.     case 32:
    151.         drawRect_rgb32 (x0, y0, width, height, color);
    152.         break;
    153.     case 16:
    154.         drawRect_rgb16 (x0, y0, width, height, color);
    155.         break;
    156.     case 15:
    157.         drawRect_rgb15 (x0, y0, width, height, color);
    158.         break;
    159.     default:
    160.         printf ("Warning: drawRect() not implemented for color depth %i\n",
    161.                 vinfo.bits_per_pixel);
    162.         break;
    163.     }
    164. }

    165. #define PERFORMANCE_RUN_COUNT 5
    166. void
    167. performSpeedTest (void *fb, int fbSize)
    168. {
    169.     int i, j, run;
    170.     struct timeval startTime, endTime;
    171.     unsigned long long results[PERFORMANCE_RUN_COUNT];
    172.     unsigned long long average;
    173.     unsigned int *testImage;

    174.     unsigned int randData[17] = {
    175.         0x3A428472, 0x724B84D3, 0x26B898AB, 0x7D980E3C, 0x5345A084,
    176.         0x6779B66B, 0x791EE4B4, 0x6E8EE3CC, 0x63AF504A, 0x18A21B33,
    177.         0x0E26EB73, 0x022F708E, 0x1740F3B0, 0x7E2C699D, 0x0E8A570B,
    178.         0x5F2C22FB, 0x6A742130
    179.     };

    180.     printf ("Frame Buffer Performance test...\n");
    181.     for (run = 0; run < PERFORMANCE_RUN_COUNT; ++run)
    182.     {
    183.         /* Generate test image with random(ish) data: */
    184.         testImage = (unsigned int *) malloc (fbSize);
    185.         j = run;
    186.         for (i = 0; i < (int) (fbSize / sizeof (int)); ++i)
    187.         {
    188.             testImage[i] = randData[j];
    189.             j++;
    190.             if (j >= 17)
    191.                 j = 0;
    192.         }

    193.         gettimeofday (&startTime, NULL);
    194.         memcpy (fb, testImage, fbSize);
    195.         gettimeofday (&endTime, NULL);

    196.         long secsDiff = endTime.tv_sec - startTime.tv_sec;
    197.         results[run] =
    198.             secsDiff * 1000000 + (endTime.tv_usec - startTime.tv_usec);

    199.         free (testImage);
    200.     }

    201.     average = 0;
    202.     for (i = 0; i < PERFORMANCE_RUN_COUNT; ++i)
    203.         average += results[i];
    204.     average = average / PERFORMANCE_RUN_COUNT;

    205.     printf (" Average: %llu usecs\n", average);
    206.     printf (" Bandwidth: %.03f MByte/Sec\n",
    207.             (fbSize / 1048576.0) / ((double) average / 1000000.0));
    208.     printf (" Max. FPS: %.03f fps\n\n",
    209.             1000000.0 / (double) average);

    210.     /* Clear the framebuffer back to black again: */
    211.     memset (fb, 0, fbSize);
    212. }

    213. int
    214. main (int argc, char **argv)
    215. {
    216.     const char *devfile = "/dev/fb0";
    217.     long int screensize = 0;
    218.     int fbFd = 0;


    219.     /* Open the file for reading and writing */
    220.     fbFd = open (devfile, O_RDWR);
    221.     if (fbFd == -1)
    222.     {
    223.         perror ("Error: cannot open framebuffer device");
    224.         exit (1);
    225.     }

    226.     //获取finfo信息并显示
    227.     if (ioctl (fbFd, FBIOGET_FSCREENINFO, &finfo) == -1)
    228.     {
    229.         perror ("Error reading fixed information");
    230.         exit (2);
    231.     }
    232.     printFixedInfo ();
    233.     //获取vinfo信息并显示
    234.     if (ioctl (fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1)
    235.     {
    236.         perror ("Error reading variable information");
    237.         exit (3);
    238.     }
    239.     printVariableInfo ();

    240.     /* Figure out the size of the screen in bytes */
    241.     screensize = finfo.smem_len;

    242.     /* Map the device to memory */
    243.     frameBuffer =
    244.         (char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,
    245.                      fbFd, 0);
    246.     if (frameBuffer == MAP_FAILED)
    247.     {
    248.         perror ("Error: Failed to map framebuffer device to memory");
    249.         exit (4);
    250.     }

    251.     //测试virt fb的性能
    252.     performSpeedTest (frameBuffer, screensize);

    253.     printf ("Will draw 3 rectangles on the screen,\n"
    254.             "they should be colored red, green and blue (in that order).\n");
    255.     drawRect (vinfo.xres / 8, vinfo.yres / 8,
    256.              vinfo.xres / 4, vinfo.yres / 4, 0xffff0000);
    257.     drawRect (vinfo.xres * 3 / 8, vinfo.yres * 3 / 8,
    258.              vinfo.xres / 4, vinfo.yres / 4, 0xff00ff00);
    259.     drawRect (vinfo.xres * 5 / 8, vinfo.yres * 5 / 8,
    260.              vinfo.xres / 4, vinfo.yres / 4, 0xff0000ff);

    261.     sleep (5);
    262.     printf (" Done.\n");

    263.     munmap (frameBuffer, screensize);    //解除内存映射,与mmap对应

    264.     close (fbFd);
    265.     return 0;
    266. }

ubuntu 上framebuffer使用之描点函数 【代码】ubuntu 上framebuffer使用之描点函数。 阅读详情

相关推荐

手把手教你用8位单片机IO口直接驱动WS2812灯带(附完整代码和避坑指南)

本文详细介绍了如何使用8位单片机IO口直接驱动WS2812灯带,包括硬件准备、时序生成、色彩引擎与特效算法的实现,以及调试技巧与性能优化。通过汇编语言优化和精确时序控制,实现高效的LED驱动和炫酷的彩虹灯效,适合嵌入式开发者和硬件爱好者参考。

weixin_42696271的博客 304

Framebuffer, 原理

上一篇文章居然加精了。现在继续将自己收集的一些资料和工作中的心得总结贴出来,我很多工作可能和大家没多大的通用性,所以只能挑有限的一些文章。如果有同学是做音视频编解码或图像处理的,可以交流下,我现在业余做这个。上年做过fb的驱动,收集了不少这方面的文章,不过建议大家还是要多看内核驱动代码,read the fucking code是真理。代码为主,其他为辅。首先第一篇是《Framebuffer原理、

1387

InSAR_ISCE软件数据处理流程之:利用哨兵数据获取2015年尼泊尔地震同震形变场

ISCE软件数据处理流程之:利用哨兵数据获取2015年尼泊尔地震同震形变场 ISCE软件做干涉只需要将数据准备好,并配置好insarApp.xml文件即可进行。 一、数据准备 1、SAR数据获取 本利中使用的数据为欧空局提供的sentinel-1A/B数据,可从阿拉斯加数据服务器上下载,具体下载方法可参考: 哨兵数据下载方法. 2、哨兵卫星轨道文件下载 哨兵数据处理除了需要SAR影像外,还需提供轨道参数文件,可从https://qc.sentinel1.eo.esa.int/aux_poeorb/下载。下载

weixin_42128764的博客 1万+

Framebuffer原理、使用、测试

Framebuffer的配置及应用 *一、FrameBuffer的原理*      FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。  Linux是工作在保护模式下,所以用户态进程是无法象DOS那样使用显卡BIOS里提供的中断调用来实现直接写屏,Linux抽象出FrameBuffer这 个设备来供用户态进程实现直接写屏。Framebuffer机制模仿显卡的功能,将

Rain 4404

Windows 游戏 EXCEPTION_ACCESS_VIOLATION 修复:3类硬件问题与5步软件排查法

本文提供Windows游戏EXCEPTION_ACCESS_VIOLATION错误的终极修复指南,涵盖3类硬件问题(内存故障、显卡问题、超频不稳定)和5步软件排查法(文件验证、驱动更新、DEP调整等),帮助玩家彻底解决0xFFFFFFFFFFFFFFFF内存访问冲突问题。

weixin_31856057的博客 2429

使用 Simulink 进行电力电子系统的小信号模型仿真

电力电子系统(Power Electronics System)包括各种功率转换器(如 DC-DC 转换器、逆变器等)。这些设备中的关键组件(如 MOSFET、IGBT、电容器、电感器等)不仅影响系统的性能,还直接关系到系统的稳定性、效率和可靠性。因此,在设计阶段进行详细的建模和仿真至关重要。小信号模型仿真的目的是通过线性化非线性系统来评估其动态特性,包括增益、相位裕度、带宽等指标。通过本教程,我们介绍了如何使用 Simulink 进行电力电子系统的小信号模型仿真。背景介绍。

xiaoheshang_123的博客 480

使用fbset设定framebuffer相关参数的问题

原文地址:http://bbs.chinaunix.net/thread-1414658-1-1.html 我使用fbset来设定framebuffer的参数,为什么命令执行了以后啥都没反应???不知道是什么问题。 fbset -fb /dev/fb0 -xres 720 -yres 480 -vxres 720 -vyres 960 -depth 4 -nonstd 1

Android/Linux的专栏 8531

【考研高数-高等数学-基础】第一章 函数、极限、连续

第一章 函数、极限、连续 第二章 导数与微分 第三章 微分中值定理及导数应用 第四章 不定积分 第五章 定积分与反常积分 第六章 定积分的应用 第七章 微分方程 第八章 多元函数微分学 第九章 二重积分 第十章 无穷级数 第十一章 向量代数与空间解析几何及多元微分学在几何上的应用 第十二章 多元积分学及其应用 难题: 极限的递推 微分中值定理的证明题 定积分的不等式和等式的证明 重积分的不等式和等式的证明 线面积分的不等式和等...

刘鑫磊 2034

Framebuffer的配置及应用

<br /><br />借助于framebuffer,我们能够在console下面作很多事情。首先下载framebuffer的配置工具fbset:<br /># apt-get install fbset 下载完毕后,配置文件/etc/fb.modes随之产生。<br /><br />比较简单的作法是用万能的vesafb,如果它被编译进了内核,如:<br />Device Drivers -> Graphics support -> VESA VGA graphics support<br />那么在gru

myth_liu的专栏 1178

出现 Error:Unable to access jarfile xxxx\target\nacos-server.jar 解决方法

Nacos中的 2.x执行步骤如上,如果需要安装下载1.x版本,执行该步骤之后 自动更新下载的是2.x版本 对此如果需要下载安装1.x的版本,可通过如下连接(windows版本)

码农研究僧的博客 5699

10、高阶ΣΔ调制器设计与优化

本文深入探讨了高阶ΣΔ调制器的设计与优化技术,重点介绍了NTF零点优化方法,通过分散单位圆上的零点分布提升信噪失真比(SQNR)。文章分析了量化噪声耦合技术如何有效提高噪声整形阶数,并对比了CIFB、CIFF、CRFB和CRFF等不同架构的性能特点。同时,详细阐述了基于稳定性与性能权衡的设计流程,结合Schreier的Delta-Sigma Toolbox实现NTF综合与仿真验证。内容涵盖从理论推导到实际设计步骤,为高阶ΣΔ调制器的稳定性提升与性能优化提供了系统性指导。

tgb345678的博客 134

linux设置终端缓冲区,使用linux的fbset命令设置景框缓冲区

服务器本篇文章和大家了解一下使用linux的fbset命令设置景框缓冲区。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。fbset命令为“framebuffer set”的简写,该命令可用于设置景框缓冲区的大小,还能调整画面之分辨率,位置,高低宽窄,色彩深度,并可决定是否启动先卡之各项硬件特性。语法格式:fbset [参数] [显示模式]常用参数:-a改变所有使用该设备之虚拟终端...

weixin_32829077的博客 625

Android功耗系列专题理论之十三:MTK平台待机功耗问题分析方法

摘要:本文详细分析了MTK平台待机功耗问题的调试方法,重点介绍了SPM电源管理机制、26M时钟控制逻辑以及系统深睡眠状态的判断标准。文章提供了完整的Debug流程,包括日志抓取、唤醒源分析、SCP睡眠状态检查以及时钟/LDO状态排查等实用技巧,并给出硬件漏电点和软件配置异常的排查方案。针对不同模块(Modem/Conn/SSPM等)提供了具体的电源管理绕过命令,适用于功耗问题调试和低功耗模式验证场景。

559

推荐阅读 GPU Framebuffer Memory: Understanding Tiling 这篇文章,更加直观的了解不同架构的绘制过程。

GPU Framebuffer Memory: Understanding Tiling

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 160

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

linux framebuffer 例子

  昨天做的一个极简单的framebuffer的例子,用来学习怎样操作fb设备。  这段代码是在picogl的vesafb backend部分的基础上简出来的,所以变量名还保留着。流程如下:1 打开framebuffer设备;2 通过ioctl取得fixed screen information;3 通过ioctl取得variable screen information;

-第二月-的专栏 3453

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

ubuntu上ffmpeg使用framebuffer显示video

总结一下,虽然使用FFmpeg和framebuffer在Ubuntu上播放视频看起来有些复杂,但是一旦你理解了每个步骤的意义,就会发现其实并不难。需要注意的是,这个命令只能在命令行环境下运行,如果你在图形环境下运行,可能会看不到任何效果,因为图形环境通常会接管framebuffer设备。这些命令会更新你的包列表,然后安装FFmpeg和fbset,fbset是一个用来显示和修改framebuffer设备设置的工具。这个命令会开始播放视频,并将视频显示到framebuffer设备上。

高性价比服务器就选:蓝易云 556

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

framebuffer驱动全篇(一)

以下关于framebuffer驱动全篇几篇文章均来自于:http://hi.baidu.com/excellentderek/blog/item/2e1d2003233e2f703812bb2d.html2008-12-31 21:20在后续的几篇里面会详细介绍如何编写一个显卡的驱动程序。framebuffer device在内核里面作为显卡驱动模型,许多函数和数据结构

阶廷成长日记 779

framebuffer显示JPEG图片

#include  #include  #include  #include  #include  #include  #include  #include  #include  #include  #define        FB_DEV        "/dev/fb0" /***************** function declaration *********

eilianlau 1393

什么是framebuffer

FrameBuffer FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。这种接口将显示设 备抽象为帧缓冲区。用户可以将它看成是显示内存的一个映像,将其映射到进程地 址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。该驱动 程序的设备文件一般是 /dev/fb0、/dev/fb1 等等。比如,假设现在的显示模式是 1024x768-8 位色,则可

yarsen的专栏 898

]基于FrameBuffer的直接写屏

引自:http://www.blogjava.net/lihao336/archive/2011/03/03/345547.html 1.将FrameBuffer配置成800x600x16色的显示模式 在/boot/grub/menu.lst中的kernel加入vga=0x314 //test.c #include stdio.h> #include unistd.h> #

zcg1041的专栏 1236

/dev/fb0入门练习(linux FrameBuffer)

大家都知道Unix/Linux系统是由命令驱动的。那么最基本的系统是命令行的(就是想DOS一样的界面)。X-Window-System是Unix/Linux上的图形系统,它是通过X-Server来控制硬件的。但有一些Linux的发行版在引导的时候就会在屏幕上出现图形,这时的图形是不可能由X来完成的,那是什么机制呢?答案是FrameBuffer。 FrameBuffer不是一个图形系统,更不是窗口

zgrjkflmkyc的专栏 3万+

framebuffer相关知识

什么是framebuffer 设备 framebuffer 是一种能够提取图形的硬件设备,是用户进入图形界面很好的接口。有了framebuffer,用户的应用程序不需要对底层的驱动的深入了解就能够做出很好的图形。 对于用户而言,它和/dev 下面的其他设备没有什么区别,用户可以把framebuffer 看成一块内存,既可以向这块内存中写入数据,也可以从这块内存中读取数据。 第一个...

weixin_30401605的博客 95

LCD & framebuffer开发心得

原文转载自:http://blog.youkuaiyun.com/gqwang2005/archive/2009/01/11/3752207.aspx Linux内核:2.6.24 帧缓冲设备属于字符设备,采用了“文件层-驱动层”的接口方式。在驱动层上,Linux为帧缓冲设备定义的接口为struct fb_info结构。在文件层次上,Linux为其定义了下面的操作函数:stru

364

android中 如何操作framebuffer

一般在 linux 中,操作 framebuffer 是透過 /dev/fb0、/dev/fb1 …等設備文件,在 android 中則稍微有點不同,是透過 /dev/graphics/fb0、/dev/graphics/fb1 …等設備文件去讀寫系統的 framebuffer。假設目前的顯示模式為 1024x768 8 bit,則可以透過以下的命令來清空 framebuffer: # dd i

康林工作室 6597

/LGC图形渲染/基于 framebuffer 在屏幕上画一个点

基于 framebuffer 在屏幕上画一个点 作者: liuuupen 日期: 2008-09-15 简介<br />本文介绍了一个通过 framebuffer 在屏幕上绘制一个点

uunubt的专栏 798

Linux驱动之Framebuffer子系统

其注册位置在fbmem_init函数里,其实现逻辑是,对于fb设备驱动程序,驱动开发人员的工作就是分配注册一个fb_info结构体,每个结构体有不同的次设备号,以fb_open为例,这个函数会通过次设备号获得fb_info结构体,然后调用该fb_info结构体的fb_ops的fb_open函数,其他函数的实现与此类似。这是一个有趣的选择。每个颜色部分(红色、绿色、蓝色、透明度)使用 16 位进行通信,以支持所有现有硬件,驱动程序执行将其应用于硬件所需的所有计算(将其舍入到更少的位,可能会丢弃透明度)。

Android系统攻城狮 3029
上一篇: Android 驱动组成
下一篇: Qt应用程序跑在Android上
linux_eve
博客等级 码龄17年 4粉丝 10原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值