天天看点

LCD驱动及framebuffer子系统解析

之前实现了lcd上显示图片,现在来看看framebuffer子系统的主要结构

LCD驱动及framebuffer子系统解析

与input设备相同,lcd设备也有自己的子系统framebuffer子系统,内核空间部分分为核心层和驱动层,核心层负责提供API给应用调用,提供API给驱动层,每个帧缓冲设备都对应一个fb_info的结构体,而驱动层就进行fb_info结构体的初始化,并注册到内核中

核心层

fbmem.c

fbmem.c 处于Framebuffer设备驱动技术的中心位置.它为上层应用程序提供系统调用也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己. fbmem.c 为所有支持FrameBuffer的设备驱动提供了通用的接口,避免重复工作.

fb_info结构体 linux/include/fb.h 这个结构体贯穿整个子系统,非常重要。

LCD驱动及framebuffer子系统解析

fb_info中重要的三个结构体fb_var_screeninfo、fb_fix_screeninfo、fb_ops。fb_var_screeninfo结构体记录了用户可以修改的显示参数,比如屏幕分辨率,屏幕颜色位域等,比如画图的时候需要得知屏幕的参数的时候就需要用到它,fb_fix_screeninfo结构体则记录了用户不可修改的参数,比如屏幕缓冲区的物理地址和缓存的长度等。fb_ops结构体记录了对底层硬件操作的函数指针

在probe函数中分配一个fbinfo进行初始化,framebuffer_alloc位于driver/video/fbmem.c

fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);

初始化,其中将s3c2410fb_ops传给了fbops

LCD驱动及framebuffer子系统解析

驱动层给出的fb_ops,可以看道并没又给出read,write,open等字符操作,可以断定核心层一定还有fb_ops方法。

LCD驱动及framebuffer子系统解析

然后就是注册,定义了API,并在驱动层调用

ret = register_framebuffer(fbinfo);
           

register_framebuffer会调用do_register_framebuffer,在这个函数中将fb_info添加到register_fb[]中,他们都是在fbmem.c中定义的

LCD驱动及framebuffer子系统解析
static int do_register_framebuffer(struct fb_info *fb_info)
{
    int i;
    struct fb_event event;
    struct fb_videomode mode;

    if (fb_check_foreignness(fb_info))
        return -ENOSYS;

    do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
                     fb_is_primary_device(fb_info));

    if (num_registered_fb == FB_MAX) //最大32个fb_info
        return -ENXIO;

    num_registered_fb++;
    for (i = 0 ; i < FB_MAX; i++)
        if (!registered_fb[i])
            break;
    fb_info->node = i;
    atomic_set(&fb_info->count, 1);
    mutex_init(&fb_info->lock);
    mutex_init(&fb_info->mm_lock);

    fb_info->dev = device_create(fb_class, fb_info->device,
                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    if (IS_ERR(fb_info->dev)) {
        /* Not fatal */
        printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
        fb_info->dev = NULL;
    } else
        fb_init_device(fb_info);

    if (fb_info->pixmap.addr == NULL) {
        fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL);
        if (fb_info->pixmap.addr) {
            fb_info->pixmap.size = FBPIXMAPSIZE;
            fb_info->pixmap.buf_align = 1;
            fb_info->pixmap.scan_align = 1;
            fb_info->pixmap.access_align = 32;
            fb_info->pixmap.flags = FB_PIXMAP_DEFAULT;
        }
    }
    fb_info->pixmap.offset = 0;

    if (!fb_info->pixmap.blit_x)
        fb_info->pixmap.blit_x = ~(u32)0;

    if (!fb_info->pixmap.blit_y)
        fb_info->pixmap.blit_y = ~(u32)0;

    if (!fb_info->modelist.prev || !fb_info->modelist.next)
        INIT_LIST_HEAD(&fb_info->modelist);

    fb_var_to_videomode(&mode, &fb_info->var);
    fb_add_videomode(&mode, &fb_info->modelist);
    registered_fb[i] = fb_info; //将fb结构体放进一个数组中

    event.info = fb_info;
    if (!lock_fb_info(fb_info))
        return -ENODEV;
    fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
    unlock_fb_info(fb_info);
    return 0;
}
           

驱动层

在核心层里面的fb_ops,果然定义了fb_read等操作方法,那驱动层的ops和核心层的ops有什么联系呢,是怎么交互的呢

LCD驱动及framebuffer子系统解析

先看看fb_read的实现

LCD驱动及framebuffer子系统解析

上图中的file_fb_info将驱动层已注册的fb赋值给核心层的fb_info

LCD驱动及framebuffer子系统解析
LCD驱动及framebuffer子系统解析

在fd_read里面他会查看已注册的fb_info中的fops是否定义了fd_read函数,如果没有,就用copy_to_user来操作。相应的其他的操作函数中也是一样的。

继续阅读