4.3 显示BSP的结构

Android显示部分的BSP支持,主要是硬件抽象层的grallloc模块和内核中的Framebuffer等驱动程序。

gralloc的含义为Graphics Allocation(图形分配),它位于框架层的通用部分和显示设备的驱动程序之间,作为显示硬件部分对上层的唯一接口。

gralloc有一个不同于其他硬件模块的地方,系统中必须有一个grallloc,否则系统无法正常启动。Android开源代码中已经实现的默认的gralloc模块名称为gralloc.default.so。它不仅是仿真器的实现,也适用于使用标准Framebuffer驱动的平台。

在某个硬件平台上,如果实现特定gralloc模块,其下层的显示设备就可以是各种类型的驱动程序。例如,对一个标准Framebuffer驱动程序实现带有优化的改动,增加一些ioctl命令来获得额外的控制;通过Android的pmem驱动等方式获得加速的效果。

↘4.3.1 Framebuffer驱动程序

在Linux中,Framebuffer驱动是标准的显示设备的驱动;对于PC系统,Framebuffer驱动是显卡的驱动;对于嵌入式系统的SOC处理器,Framebuffer通常作为其LCD控制器或者其他显示设备的驱动。

Framebuffer驱动是一个字符设备,这个驱动在文件系统中的设备节点通常是/dev/fbX。每个系统可以有多个显示设备,使用/dev/fb0、/dev/fb1等来表示。

提示:在Android中,Framebuffer驱动的设备节点在/dev/graphics目录中。

Framebuffer驱动是一个字符设备,主设备号为29,次设备号递增生成(由每个Framebuffer程序的注册顺序决定)。

Framebuffer显示驱动的架构如图4-2所示。

图4-2 Framebuffer显示驱动的架构

Framebuffer驱动在用户空间大多使用ioctl、mmap等文件系统的接口进行操作,ioctl用于获得和设置信息,mmap可以将Framebuffer的内存映射到用户空间。Framebuffer驱动也可以直接支持write操作,直接用写的方式输出显示内容。

Framebuffer驱动的主要头文件:include/linux/fb.h。

Framebuffer驱动核心实现:drivers/video/fbmem.c。

Framebuffer驱动中核心的数据接口是fb_info,在fb.h中定义:

    struct fb_info {
        int node;
        int flags;
        struct fb_var_screeninfo var;     // 显示屏变的信息
        struct fb_fix_screeninfo fix;     // 显示屏固定信息
        // 省略部分成员
        struct fb_ops *fbops;            // framebuffer的操作指针集合
        // 省略部分成员
    };

struct fb_info包含了Framebuffer驱动的主要信息,struct fb_var_screeninfo和struct fb_fix_screeninfo是两个相关的数据结构。通常对应FBIOGET_VSCREENINFO和FBIOGET_FSCREENINFO这两个ioctl命令从用户空间获得的显示信息。fb_ops表示对Framebuffer的操作,由一系列函数指针组成。

在具体的Framebuffer驱动的实现中,通常通过以下函数进行注册和注销:

    extern int register_framebuffer(struct fb_info *fb_info);
    extern int unregister_framebuffer(struct fb_info *fb_info);

Framebuffer的ioctl命令在include/linux/目录的fb.h文件中定义,如下所示:

    #define FBIOGET_VSCREENINFO 0x4600        // 获得变化屏幕信息
    #define FBIOPUT_VSCREENINFO 0x460l        // 设置变化屏幕信息
    #define FBIOGET_FSCREENINFO 0x4602        // 获得固定屏幕信息
    #define FBIOGETCMAP              0x4604   // 获得映射内容
    #define FBIOPUTCMAP              0x4605   // 设置映射内容
    #define FBIOPAN_DISPLAY     0x4606        // 调整显示区域(虚拟显示-实际显示)

具体的Framebuffer驱动需要定义一个实现fb_info结构、实现fb_ops中的各个函数指针。从驱动程序的用户空间进行ioctl调用时,会转换成调用其中的函数。具体的Framebuffer驱动注册后,将会自动递增获得一个次设备号。

在配置Linux系统时,Framebuffer驱动的配置选项是:Device Drivers=>Graphics support。Framebuffer驱动中也包含了文本模式、控制台、启动图标(Bootup Logo)等子选项支持,具体的Framebuffer驱动由每一个平台支持。

Framebuffer驱动程序通常也支持用户空间的写操作,在系统中调试Framebuffer驱动程序,可以使用直接写驱动程序设备节点的方式,这样可以改变基本的显示情况。

例如,在Android中,可以使用如下的方式在Framebuffer中获得输出效果:

    # cat init > /dev/graphics/fb0

如果屏幕大小是QVGA(640×480)的,按照以上操作,由于init文件的大小大约占到一个屏幕的显示缓冲的1/3,而init文件中的内容对于显示是杂乱的,因此可以观察到屏幕上1/3的花屏区域。

提示:如果当前Framebuffer驱动程序使用双缓冲显示方式,将会看到屏幕中交替显示的带有花屏和不带有花屏的内容,交替时间就是双缓冲刷新间隔时间。

进一步还可以使用dd命令进行指定大小的操作,如下所示:

    # dd if=/dev/zero of=/dev/graphics/fb0 count=l bs=256k

命令表示向/dev/graphics/fb0设备中写入256k字节为0x0的内容,使用这个命令可以看到屏幕中的部分黑屏。

↘4.3.2 gralloc硬件抽象层

gralloc模块作为显示系统硬件抽象层,其接口的内容在gralloc.h文件中定义。此内容由实际的硬件抽象层实现,由UI库调用。

gralloc.h首先定义了一个硬件模块和两个子设备的名称,内容如下所示:

    #define GRALLOC_HARDWARE_MODULE_ID "gralloc"       // 硬件模块的名称
    #define GRALLOC_HARDWARE_FB0 "fb0"                 // framebuffer设备和
    #define GRALLOC_HARDWARE_GPU0 "gpu0"               // GPU设备

其中,“gralloc”为这个硬件模块的名称,“fb0”和“gpu0”分别表示Framebuffer设备和GPU(Graphics Process Unit,图形处理单元)硬件设备。

gralloc硬件模块是扩展定义hw_module_t来完成的,定义如下所示:

    typedef struct gralloc_module_t {
        struct hw_module_t common;
        int (*registerBuffer)(struct gralloc_module_t const* module,
                                buffer_handle_t handle);
        int (*unregisterBuffer)(struct gralloc_module_t const* module,
                                buffer_handle_t handle);
        int (*lock)(struct gralloc_module_t const* module, buffer_handle_t handle,
                int usage, int l, int t, int w, int h, void** vaddr);
        int (*unlock)(struct gralloc_module_t const* module, buffer_handle_t handle);
        int (*perform)(struct gralloc_module_t const* module, int operation, ... );
        void* reserved_proc[7];
    } gralloc_module_t;

gralloc_module_t是模块的核心,其中的几个函数指针的功能如下所示。

·registerBuffer需要在alloc_device_t::alloc之前被调用,其参数类型buffer_handle_t是内存指针;unregisterBuffer用于注销此内存。

·lock用于访问特定访问缓冲区,在调用这个接口时,硬件设备需要结束渲染或者完成同步,其参数指定一个区域。unlock用于所有buffer改变之后被调用。

·perform用于可扩展的功能,Surflinger会调用它,但是其实现是可选的。

gralloc和Framebuffer两种设备的打开和关闭使用几个静态函数表示,如下所示:

    static inline int gralloc_open(const struct hw_module_t* module,
            struct alloc_device_t** device) {
        return module->methods->open(module,
                GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);
    }
    static inline int gralloc_close(struct alloc_device_t* device) {
        return device->common.close(&device->common);
    }
    static inline int framebuffer_open(const struct hw_module_t* module,
            struct framebuffer_device_t** device) {
        return module->methods->open(module,
                GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);
    }
    static inline int framebuffer_close(struct framebuffer_device_t* device) {
        return device->common.close(&device->common);
    }

gralloc_open()、gralloc_close ()、framebuffer_open ()和framebuffer_close ()这4个函数是gralloc模块特定的API。调用它们分别用于打开和关闭GRALLOC_HARDWARE_GPU0和GRALLOC_HARDWARE_FB0这两个设备。前者对应的设备为alloc_device_t结构体,后者对应的设备为framebuffer_device_t结构体。它们都“继承自”hw_device_t结构体。

alloc_device_t设备的定义如下所示:

    typedef struct alloc_device_t {
        struct hw_device_t common;
        int (*alloc)(struct alloc_device_t* dev,           // 分配,以宽、高、颜色格式为参数
                int w, int h, int format, int usage,
                buffer_handle_t* handle, int* stride);
        int (*free)(struct alloc_device_t* dev,            // 释放
                buffer_handle_t handle);
    } alloc_device_t;

alloc函数指针用于分配一块显示内存,其参数包括宽、高、颜色格式和buffer_handle_t类型的句柄,free函数指针用于释放显示内存。

framebuffer_device_t结构的定义如下所示:

    typedef struct framebuffer_device_t {
        struct hw_device_t common;
        const uint32_t  flags;
        const uint32_t  width;              // 宽
        const uint32_t  height;             // 高
        const int      stride;              // 每行内容
        const int      format;              // 颜色格式
        const float     xdpi;               // X方向像素密度
        const float     ydpi;               // Y方向像素密度
        const float     fps;                // 帧率
        const int      minSwapInterval;
        const int      maxSwapInterval;
        int reserved[8];
        int (*setSwapInterval)(struct framebuffer_device_t* window, int interval);
        int (*setUpdateRect)(struct framebuffer_device_t* window,
                int left, int top, int width, int height);
        int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
        int (*compositionComplete)(struct framebuffer_device_t* dev);
        void* reserved_proc[8];
    } framebuffer_device_t;

framebuffer_device_t设备中定义了几个通用的显示内存描述和几个函数指针,setSwapInterval用于交换显示区域;setUpdateRect用于更新指定的区域,这个函数是可选的;post用于发送某一个Buffer到屏幕上。