FrameBuffer浅谈

来源:互联网 发布:卡通人物软件 编辑:程序博客网 时间:2024/06/04 01:04

FrameBuffer通常作为LCD控制器或者其他显示设备的驱动,FrameBuffer驱动是一个字符设备,设备节点是/dev/fbX,主设备号为29,次设备号递增,用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。Framebuffer设备为上层应用程序提供系统调用,也为下一层的特定硬件驱动提供接口;那些底层硬件驱动需要用到这儿的接口来向系统内核注册它们自己。

Framebuffer数据结构

kernel\include\linux\fb.h

fb_info是Linux为帧缓冲设备定义的驱动层接口。它不仅包含了底层函数,而且还有记录设备状态的数据。每个帧缓冲设备都与一个fb_info结构相对应。

struct fb_info {  atomic_t count;  int node;  /*一个FrameBuffer设备的次设备号*/  int flags;  struct mutex lock;/* Lock for open/release/ioctl funcs */  struct mutex mm_lock;/* Lock for fb_mmap and smem_* fields */  struct fb_var_screeninfo var;/* Current var */  struct fb_fix_screeninfo fix;/* Current fix */  struct fb_monspecs monspecs;/* Current Monitor specs */  struct work_struct queue;/* Framebuffer event queue */  struct fb_pixmap pixmap;/* Image hardware mapper */  struct fb_pixmap sprite;/* Cursor hardware mapper */  struct fb_cmap cmap;/* Current cmap */  struct list_head modelist;  /* mode list */  struct fb_videomode *mode;/* current mode */#ifdef CONFIG_FB_BACKLIGHT  struct backlight_device *bl_dev;  /* Backlight level curve */  struct mutex bl_curve_mutex;  u8 bl_curve[FB_BACKLIGHT_LEVELS];#endif#ifdef CONFIG_FB_DEFERRED_IO  struct delayed_work deferred_work;  struct fb_deferred_io *fbdefio;#endif  struct fb_ops *fbops;  struct device *device;/* This is the parent */  struct device *dev;/* This is this fb device */  int class_flag;         /* private sysfs flags */#ifdef CONFIG_FB_TILEBLITTING  struct fb_tile_ops *tileops;/* Tile Blitting */#endif  char __iomem *screen_base;/* Virtual address */  unsigned long screen_size;/* Amount of ioremapped VRAM or 0 */   void *pseudo_palette;/* Fake palette of 16 colors */ #define FBINFO_STATE_RUNNING0#define FBINFO_STATE_SUSPENDED1  u32 state;/* Hardware state i.e suspend */  void *fbcon_par;    /* fbcon use-only private area */  void *par;  struct apertures_struct {    unsigned int count;    struct aperture {      resource_size_t base;      resource_size_t size;    } ranges[0];  } *apertures;};

fb_var_screeninfo:用于记录用户可修改的显示控制器参数,包括屏幕分辨率、每个像素点的比特数等

struct fb_var_screeninfo {  __u32 xres;/* 行可见像素*/  __u32 yres;         /* 列可见像素*/  __u32 xres_virtual;/* 行虚拟像素*/  __u32 yres_virtual; /* 列虚拟像素*/  __u32 xoffset;/* 水平偏移量*/  __u32 yoffset;/* 垂直偏移量*/  __u32 bits_per_pixel;/*每个像素所占bit位数*/  __u32 grayscale;/* 灰色刻度*/  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;/* 图像高度*/  __u32 width;/* 图像宽度*/  __u32 accel_flags;/* (OBSOLETE) see fb_info.flags */  __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 rotate;/* angle we rotate counter clockwise */  __u32 reserved[5];/* Reserved for future compatibility */};

fb_fix_screeninfo:记录了用户不能修改的显示控制器的参数,这些参数是在驱动初始化时设置的

struct fb_fix_screeninfo {  char id[16];/* identification string eg "TT Builtin" */  unsigned long smem_start;/* Start of frame buffer mem */  __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   */  __u32 mmio_len;/* Length of Memory Mapped I/O  */  __u32 accel;/* Indicate to driver which*/  __u16 reserved[3];/* Reserved for future compatibility */};

fb_ops是提供给底层设备驱动的一个接口。当我们编写一个FrameBuffer的时候,就要依照Linux FrameBuffer编程的套路,填写fb_ops结构体。

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);  /* For framebuffers with strange non linear layouts or that do not   * work with normal memory mapped access   */  ssize_t (*fb_read)(struct fb_info *info, char __user *buf,         size_t count, loff_t *ppos);  ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,          size_t count, loff_t *ppos);  /* checks var and eventually tweaks it to something supported,   * DO NOT MODIFY PAR */  int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);  /* set the video mode according to info->var */  int (*fb_set_par)(struct fb_info *info);  /* set color register */  int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,          unsigned blue, unsigned transp, struct fb_info *info);  /* set color registers in batch */  int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);  /* blank display */  int (*fb_blank)(int blank, struct fb_info *info);  /* pan display */  int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);  /* Draws a rectangle */  void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);  /* Copy data from area to another */  void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);  /* Draws a image to the display */  void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image);  /* Draws cursor */  int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor);  /* Rotates the display */  void (*fb_rotate)(struct fb_info *info, int angle);  /* wait for blit idle, optional */  int (*fb_sync)(struct fb_info *info);  /* perform fb specific ioctl (optional) */  int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,      unsigned long arg);  /* Handle 32bit compat ioctl (optional) */  int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,      unsigned long arg);  /* perform fb specific mmap */  int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);  /* get capability given var */  void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,          struct fb_var_screeninfo *var);  /* teardown any resources to do with this framebuffer */  void (*fb_destroy)(struct fb_info *info);  /* called at KDB enter and leave time to prepare the console */  int (*fb_debug_enter)(struct fb_info *info);  int (*fb_debug_leave)(struct fb_info *info);};

 Framebuffer模块初始化过程

FrameBuffer驱动是以模块的形式注册到系统中,在模块初始化时,创建FrameBuffer对应的设备文件及proc文件,并注册FrameBuffer设备操作接口函数。  
kernel\drivers\video\Fbmem.c

module_init(fbmem_init);static int __init fbmem_init(void){  //在proc文件系统中创建/proc/fb文件,并注册proc接口函数  proc_create("fb", 0, NULL, &fb_proc_fops);  //注册字符设备fb,并注册fb设备文件的操作接口函数,主设备号为29,  //#define FB_MAJOR29   if (register_chrdev(FB_MAJOR,"fb",&fb_fops))    printk("unable to get major %d for fb devs\n", FB_MAJOR);  //创建sys/class/graphics目录  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;}

首先在proc文件系统中创建fb文件,同时注册操作该文件的接口函数:

static const struct file_operations fb_proc_fops = {  .owner= THIS_MODULE,  .open= proc_fb_open,  .read= seq_read,  .llseek= seq_lseek,  .release= seq_release,};

因此可以对/proc/fb文件进行打开,读写操作。然后注册一个主设备号为29的字符设备,fbmem_init函数中注册了字符设备的文件操作接口函数fb_fops,定义如下:

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,};

Framebuffer驱动注册过程

变量定义:

//保存注册的所有Framebuffer驱动extern struct fb_info *registered_fb[FB_MAX];//已注册的Framebuffer驱动的个数extern int num_registered_fb;

任何一个特定硬件Framebuffer驱动在初始化时都必须向fbmem.c注册,FrameBuffer模块提供了驱动注册接口函数register_framebuffer:

int register_framebuffer(struct fb_info *fb_info){  int ret;  mutex_lock(®istration_lock);  ret = do_register_framebuffer(fb_info);  mutex_unlock(®istration_lock);  return ret;}

参数fb_info描述特定硬件的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;  //根据当前注册的fb_info的apertures属性从FrameBuffer驱动数组registered_fb中查询是否存在冲突  do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,           fb_is_primary_device(fb_info));  //判断已注册的驱动是否超过32个FrameBuffer驱动  if (num_registered_fb == FB_MAX)    return -ENXIO;  //增加已注册的驱动个数  num_registered_fb++;  //从数组registered_fb中查找空闲元素,用于存储当前注册的fb_info  for (i = 0 ; i < FB_MAX; i++)    if (!registered_fb[i])      break;  //将当前注册的fb_info在数组registered_fb中的索引位置保存到fb_info->node  fb_info->node = i;  //初始化当前注册的fb_info的成员信息  atomic_set(&fb_info->count, 1);  mutex_init(&fb_info->lock);  mutex_init(&fb_info->mm_lock);    //在/dev目录下创建一个fbx的设备文件,次设备号就是该fb_info在数组registered_fb中的索引  fb_info->dev = device_create(fb_class, fb_info->device,MKDEV(FB_MAJOR, i), NULL, "fb%d", i);  if (IS_ERR(fb_info->dev)) {    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_info    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);  //将特定硬件对应的fb_info注册到registered_fb数组中  registered_fb[i] = fb_info;  event.info = fb_info;  if (!lock_fb_info(fb_info))    return -ENODEV;  //使用Linux事件通知机制发送一个FrameBuffer注册事件FB_EVENT_FB_REGISTERED  fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);  unlock_fb_info(fb_info);  return 0;}

注册过程就是将指定的设备驱动信息fb_info存放到registered_fb数组中。因此在注册具体的fb_info时,首先要构造一个fb_info数据结构,并初始化该数据结构,该结构用于描述一个特定的FrameBuffer驱动。

fbX设备文件的打开过程

open("/dev/fb0")打开设备文件fb0对应的操作过程如下:

static int fb_open(struct inode *inode, struct file *file)__acquires(&info->lock)__releases(&info->lock){  //从文件节点中取得次设备号  int fbidx = iminor(inode);  struct fb_info *info;  int res = 0;  //根据次设备号从registered_fb数组中取出对应的fb_info  info = get_fb_info(fbidx);  if (!info) {    request_module("fb%d", fbidx);    info = get_fb_info(fbidx);    if (!info)      return -ENODEV;  }  if (IS_ERR(info))    return PTR_ERR(info);  mutex_lock(&info->lock);  if (!try_module_get(info->fbops->owner)) {    res = -ENODEV;    goto out;  }  //将当前的fb_info保存到/dev/fbx设备文件的private_data成员中  file->private_data = info;  if (info->fbops->fb_open) {    //调用当前fb_info的fb_open函数打开当前FrameBuffer设备    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);#endifout:  mutex_unlock(&info->lock);  if (res)    put_fb_info(info);  return res;}
打开过程很简单,首先从文件节点中取出要打开FrameBuffer的fb_info数据信息,并保存到设备文件file的private_data变量中,然后调用当前fb_info中的fb_open函数完成设备打开过程。该函数在构造具体的fb_info并注册FrameBuffer时,就已注册了对应的打开操作函数指针。


fbX设备文件的映射过程

static int fb_mmap(struct file *file, struct vm_area_struct * vma){  //从文件节点中取出fb_info,并且判断是否和private_data变量中的fb_info相同  struct fb_info *info = file_fb_info(file);  struct fb_ops *fb;  unsigned long off;  unsigned long start;  u32 len;  if (!info)    return -ENODEV;  if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))    return -EINVAL;  off = vma->vm_pgoff << PAGE_SHIFT;  fb = info->fbops;  if (!fb)    return -ENODEV;  mutex_lock(&info->mm_lock);  //如果fb_info中注册了fb_mmap函数,则调用fb_info中的fb_mmap来完成地址空间映射  if (fb->fb_mmap) {    int res;    res = fb->fb_mmap(info, vma);    mutex_unlock(&info->mm_lock);    return res;  }  //如果具体的fb_info没有实现fb_mmap  start = info->fix.smem_start;  len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);  if (off >= len) {    /* memory mapped io */    off -= len;    if (info->var.accel_flags) {      mutex_unlock(&info->mm_lock);      return -EINVAL;    }    start = info->fix.mmio_start;    len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);  }  mutex_unlock(&info->mm_lock);  start &= PAGE_MASK;  if ((vma->vm_end - vma->vm_start + off) > len)    return -EINVAL;  off += start;  vma->vm_pgoff = off >> PAGE_SHIFT;  /* This is an IO map - tell maydump to skip this VMA */  vma->vm_flags |= VM_IO | VM_RESERVED;  vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);  fb_pgprotect(file, vma, off);  if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,           vma->vm_end - vma->vm_start, vma->vm_page_prot))    return -EAGAIN;  return 0;}
这里和fb打开过程类似,仍然是调用具体的fb_info的映射函数来完成地址空间映射过程,但是也有区别,就是在具体的fb_info没有实现地址空间映射时,就在FrameBuffer这一层完成映射过程。

fbX设备文件的命令控制过程

FrameBuffer

Fb_info

FBIOGET_VSCREENINFO

 

FBIOPUT_VSCREENINFO

 

FBIOGET_FSCREENINFO

 

FBIOPUTCMAP

 

FBIOGETCMAP

 

FBIOPAN_DISPLAY

 

FBIO_CURSOR

 

FBIOGET_CON2FBMAP

 

FBIOPUT_CON2FBMAP

 

FBIOBLANK

  

fb->fb_ioctl()

 

static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,unsigned long arg){  struct fb_ops *fb;  struct fb_var_screeninfo var;  struct fb_fix_screeninfo fix;  struct fb_con2fbmap con2fb;  struct fb_cmap cmap_from;  struct fb_cmap_user cmap;  struct fb_event event;  void __user *argp = (void __user *)arg;  long ret = 0;  switch (cmd) {  case FBIOGET_VSCREENINFO:    if (!lock_fb_info(info))      return -ENODEV;    var = info->var;    unlock_fb_info(info);    ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;    break;  case FBIOPUT_VSCREENINFO:    if (copy_from_user(&var, argp, sizeof(var)))      return -EFAULT;    if (!lock_fb_info(info))      return -ENODEV;    console_lock();    info->flags |= FBINFO_MISC_USEREVENT;    ret = fb_set_var(info, &var);    info->flags &= ~FBINFO_MISC_USEREVENT;    console_unlock();    unlock_fb_info(info);    if (!ret && copy_to_user(argp, &var, sizeof(var)))      ret = -EFAULT;    break;  case FBIOGET_FSCREENINFO:    if (!lock_fb_info(info))      return -ENODEV;    fix = info->fix;    unlock_fb_info(info);    ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;    break;  case FBIOPUTCMAP:    if (copy_from_user(&cmap, argp, sizeof(cmap)))      return -EFAULT;    ret = fb_set_user_cmap(&cmap, info);    break;  case FBIOGETCMAP:    if (copy_from_user(&cmap, argp, sizeof(cmap)))      return -EFAULT;    if (!lock_fb_info(info))      return -ENODEV;    cmap_from = info->cmap;    unlock_fb_info(info);    ret = fb_cmap_to_user(&cmap_from, &cmap);    break;  case FBIOPAN_DISPLAY:    if (copy_from_user(&var, argp, sizeof(var)))      return -EFAULT;    if (!lock_fb_info(info))      return -ENODEV;    console_lock();    ret = fb_pan_display(info, &var);    console_unlock();    unlock_fb_info(info);    if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))      return -EFAULT;    break;  case FBIO_CURSOR:    ret = -EINVAL;    break;  case FBIOGET_CON2FBMAP:    if (copy_from_user(&con2fb, argp, sizeof(con2fb)))      return -EFAULT;    if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)      return -EINVAL;    con2fb.framebuffer = -1;    event.data = &con2fb;    if (!lock_fb_info(info))      return -ENODEV;    event.info = info;    fb_notifier_call_chain(FB_EVENT_GET_CONSOLE_MAP, &event);    unlock_fb_info(info);    ret = copy_to_user(argp, &con2fb, sizeof(con2fb)) ? -EFAULT : 0;    break;  case FBIOPUT_CON2FBMAP:    if (copy_from_user(&con2fb, argp, sizeof(con2fb)))      return -EFAULT;    if (con2fb.console < 1 || con2fb.console > MAX_NR_CONSOLES)      return -EINVAL;    if (con2fb.framebuffer < 0 || con2fb.framebuffer >= FB_MAX)      return -EINVAL;    if (!registered_fb[con2fb.framebuffer])      request_module("fb%d", con2fb.framebuffer);    if (!registered_fb[con2fb.framebuffer]) {      ret = -EINVAL;      break;    }    event.data = &con2fb;    if (!lock_fb_info(info))      return -ENODEV;    event.info = info;    ret = fb_notifier_call_chain(FB_EVENT_SET_CONSOLE_MAP, &event);    unlock_fb_info(info);    break;  case FBIOBLANK:    if (!lock_fb_info(info))      return -ENODEV;    console_lock();    info->flags |= FBINFO_MISC_USEREVENT;    ret = fb_blank(info, arg);    info->flags &= ~FBINFO_MISC_USEREVENT;    console_unlock();    unlock_fb_info(info);    break;  default:    if (!lock_fb_info(info))      return -ENODEV;    fb = info->fbops;    if (fb->fb_ioctl)      ret = fb->fb_ioctl(info, cmd, arg);    else      ret = -ENOTTY;    unlock_fb_info(info);  }  return ret;}
FrameBuffer驱动的框架就介绍到这来,总结一下:

1)构建一个fb_info数据结构,用来描述帧缓冲设备;

2)调用FrameBuffer驱动模块提供的接口函数register_framebuffer来注册帧缓冲设备;

3)对FrameBuffer设备文件的操作过程是,首先执行FrameBuffer驱动函数,然后根据注册的帧缓冲设备的次设备号得到注册的fb_info,最后调用具体的帧缓冲设备的操作函数;

原创粉丝点击