第12章 Linux设备驱动的软件架构思想之设备驱动的分层思想(Framebuffer设备驱动)

12.3.4 Framebuffer设备驱动

    Framebuffer(帧缓冲)是Linux系统为显示设备提供的一个接口,Framebuffer将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。对于帧缓冲设备来说,只要在显示缓冲区中与显示点对应的区域内写入颜色值,对应的颜色会自动在屏幕上显示。

    图12.8所示为Linux帧缓冲设备驱动的主要结构,帧缓冲设备提供给用户空间的file_operations结构体,由drivers/video/fbdev/core/fbmem.c中的file_operations提供。

static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = fb_compat_ioctl,
#endif
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
.get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
.fsync = fb_deferred_io_fsync,
#endif
.llseek = default_llseek,

};

而特定帧缓冲设备struct fb_info结构体的注册、注销以及其中成员的维护,尤其是struct fb_ops中成员函数的实现则由对应的xxxfb.c文件实现,fb_ops中的成员函数最终会操作LCD控制其硬件寄存器。

例如:drivers/video/msm/mdss/mdss_fb.c

static struct fb_ops mdss_fb_ops = {
.owner = THIS_MODULE,
.fb_open = mdss_fb_open,
.fb_release = mdss_fb_release,
.fb_check_var = mdss_fb_check_var, /* vinfo check */
.fb_set_par = mdss_fb_set_par, /* set the video mode */
.fb_blank = mdss_fb_blank, /* blank display */
.fb_pan_display = mdss_fb_pan_display, /* pan display */
.fb_ioctl_v2 = mdss_fb_ioctl, /* perform fb specific ioctl */
#ifdef CONFIG_COMPAT
.fb_compat_ioctl_v2 = mdss_fb_compat_ioctl,
#endif
.fb_mmap = mdss_fb_mmap,
};


图12.8 Linux帧缓冲设备驱动的程序结构

多数显存的操作方法都是规范的,可以按照像素点格式的要求顺序写帧缓冲区。但是有少量LCD的显存写法可能比较特殊,这时,在核心层drivers/video/fbdev/core/fbmem.c实现的fb_write()中,实际上可以给底层提供一个重写自己的机会,如代码清单12.16所示:

代码清单12.16 LCD的framebuffer write()函数

static ssize_t
fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file);
u8 *buffer, *src;
u8 __iomem *dst;
int c, cnt = 0, err = 0;
unsigned long total_size;

if (!info || !info->screen_base)
return -ENODEV;

if (info->state != FBINFO_STATE_RUNNING)
return -EPERM;

if (info->fbops->fb_write)
return info->fbops->fb_write(info, buf, count, ppos);


total_size = info->screen_size;

if (total_size == 0)
total_size = info->fix.smem_len;

if (p > total_size)
return -EFBIG;

if (count > total_size) {
err = -EFBIG;
count = total_size;
}

if (count + p > total_size) {
if (!err)
err = -ENOSPC;

count = total_size - p;
}

buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
GFP_KERNEL);
if (!buffer)
return -ENOMEM;

dst = (u8 __iomem *) (info->screen_base + p);

if (info->fbops->fb_sync)
info->fbops->fb_sync(info);

while (count) {
c = (count > PAGE_SIZE) ? PAGE_SIZE : count;
src = buffer;

if (copy_from_user(src, buf, c)) {
err = -EFAULT;
break;
}

fb_memcpy_tofb(dst, src, c);
dst += c;
src += c;
*ppos += c;
buf += c;
cnt += c;
count -= c;
}

kfree(buffer);

return (cnt) ? cnt : err;

}

备注:

if (info->fbops->fb_write)

         return info->fbops->fb_write(info, buf, count, ppos);

这段代码,是检查底层LCD有没有实现自己特殊显存写法的代码,如果有,直接调底层的;如果
没有,用中间层标准的显存写法就搞定了底层的那个不特殊的LCD。

猜你喜欢

转载自blog.csdn.net/xiezhi123456/article/details/80475612