1.LCD驱动概念

  LCD是Liquid Crystal Display的简称,也就是经常所说的液晶显示器。LCD能够支持彩色图像的显示和视频的播放,是一种非常重要的输出设备。如果我们的系统要用GUI(图形界面接口),比如minigui,MicroWindows。这时LCD设备驱动程序就应该编写成frambuffer接口,而不是编写成仅仅操作底层的LCD控制器接口。

  framebuffer是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行操作。framebuffer又叫帧缓冲,是Linux为操作显示设备提供的一个用户接口。用户应用程序可以通过framebuffer透明地访问不同类型的显示设备。从这个方面来说,framebuffer是硬件设备显示缓冲区的抽象。Linux抽象出framebuffer这个帧缓冲区可以供用户应用程序直接读写,通过更改framebuffer中的内容,就可以立刻显示在LCD显示屏上。

  framebuffer是一个标准的字符设备,主设备号是29,次设备号根据缓冲区的数目而定。framebuffer对应/dev/fb%d设备文件。根据显卡的多少,设备文件可能是/dev/fb0、/dev/fb1等。缓冲区设备也是一种普通的内存设备,可以直接对其进行读写。对用户程序而言,它和/dev下面的其他设备没有什么区别,用户可以把frameBuffer看成一块内存,既可以写,又可以读。显示器将根据内存数据显示对应的图像界面。这一切都由LCD控制器和响应的驱动程序来完成。

 

2、fb与应用程序的交互

  对于用户程序而言,它和其他的设备并没有什么区别,用户可以把fb看成是一块内存,既可以向内存中写数据,也可以读数据。fb的显示缓冲区位于内核空间,应用程序可以把此空间映射到自己的用户空间,在进行操作。

  在应用程序中,操作/dev/fbn的一般步骤如下:
  (1)打开/dev/fbn设备文件。
  (2)用ioctl()操作取得当前显示屏幕的参数,如屏幕分辨率、每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。
  (3)用mmap()函数,将屏幕缓冲区映射到用户空间。
  (4)映射后就可以直接读/写屏幕缓冲区,进行绘图和图片显示了。

 

3、LCD驱动框架

  在linux中,fb设备驱动的源码主要在Fb.h (\include\linux)和Fbmem.c (\drivers\video)两个文件中,它们是fb设备驱动的中间层,为上层提供系统调用,为底层驱动提供接口。

  

Android lcd 驱动程序 lcd驱动器_应用程序

Android lcd 驱动程序 lcd驱动器_Android lcd 驱动程序_02

  其中结构体成员因为太多就不列出来。具体可查看源码。

 

4.LCD驱动核心层(fbmem.c)源码分析

  一般分析一个驱动,先从他得入口函数开始分析fbmem_init()

static int __init fbmem_init(void)
{
    proc_create("fb", 0, NULL, &fb_proc_fops);

    if (register_chrdev(FB_MAJOR,"fb",&fb_fops))  
        printk("unable to get major %d for fb devs\n", FB_MAJOR);

    fb_class = class_create(THIS_MODULE, "graphics");
    if (IS_ERR(fb_class)) {
        printk(KERN_WARNING "Unable to create fb class; errno = %ld\n", PTR_ERR(fb_class));
        fb_class = NULL;
    }
    return 0;
}

#define FB_MAJOR  29   /* /dev/fb* framebuffers */

  register_chrdev(FB_MAJOR,"fb",&fb_fops)向内核注册了一个主设备号为29,名字叫“fb”,fb_fops的file_operations的结构体变量。

  通过cat /proc/devices 也能找到这个字符设备:  

  

Android lcd 驱动程序 lcd驱动器_设备节点_03

   和我们之前的驱动程序一样,但是没有使用创建设备节点,为什么?

  因为需要注册了LCD驱动后,才会有设备节点,所以这里的代码没有 ,后面会分析哪里有。

  

  应用程序要使用某个设备,就要先open这个设备。所以接下来分析一下fb_fops中的fb_open

 

extern struct fb_info *registered_fb[FB_MAX];
static int fb_open(struct inode *inode, struct file *file)

{
    int fbidx = iminor(inode);
    struct fb_info *info;
    int res = 0;

    if (fbidx >= FB_MAX)
        return -ENODEV;
    info = registered_fb[fbidx];
    if (!info)
        request_module("fb%d", fbidx);
    info = registered_fb[fbidx];
    if (!info)
        return -ENODEV;
    mutex_lock(&info->lock);
    if (!try_module_get(info->fbops->owner)) {
        res = -ENODEV;
        goto out;
    }
    file->private_data = info;
    if (info->fbops->fb_open) {
        res = info->fbops->fb_open(info,1);
        if (res)
            module_put(info->fbops->owner);
    }
#ifdef CONFIG_FB_DEFERRED_IO
    if (info->fbdefio)
        fb_deferred_io_open(info, inode, file);
#endif
out:
    mutex_unlock(&info->lock);
    return res;
}

   从上面的三行红色代码大概可以看出,把次设备号作为下标,在registered_fb数组中找到保存着的LCD驱动信息,再赋值给info,然后去调用具体LCD的open函数。

  所以接下来就要了解一下,怎么把LCD驱动信息放到registered_fb数组中(查看在哪里对registered_fb进行赋值)。

  通过查找,只有一条registered_fb[i] = fb_info(register_framebuffer()函数中)

  所以驱动设备是通过register_framebuffer()函数进行一个注册的,而且还在这里创建了设备节点device_create()。

 

  

  现在就可以通过查看哪些函数调用了register_framebuffer()函数,然后分析一下LCD设备是怎么向驱动核心层注册的。

  

Android lcd 驱动程序 lcd驱动器_设备号_04

 

   可以看到有很多设备都已经注册了。就拿我们熟悉的s3c24xx来分析一下吧。

 

static int __init s3c24xxfb_probe(struct platform_device *pdev,
                  enum s3c_drv_type drv_type)
{
  /*略*/  
    ret = register_framebuffer(fbinfo);
  /*略*/  
}

  probe函数,很熟悉。就是驱动设备总线模型了。和我们上节分析的platform机制一样,当与设备匹配成功,就进入probe函数,初始化驱动设备。

 

5.总结

  1.当我们加载某个lcd硬件设备模块,就会通过platform机制一样,当与设备匹配成功,就进入probe函数,初始化驱动设备。

  2.probe函数中调用了registered_fb[i] = fb_infodevice_create(),这两个分别注册了LCD硬件信息和创建了设备节点。

  3.应用程序通过open函数打开某个LCD设备的时候,会调用fb_open。

  4.fb_open中通过获取次设备号,在registered_fb数组中找到相对应LCD设备信息。最后调用LCD设备信息中open函数。