一. framebuffer结构体
1. fb_info
struct fb_info { int node; //次设备号 int flags; struct mutex lock; struct mutex mm_lock; struct fb_var_screeninfo var; //可变参数 struct fb_fix_screeninfo fix; //不可变参数 struct fb_monspecs monspecs; struct work_struct queue; struct fb_pixmap pixmap; struct fb_pixmap sprite; struct fb_cmap cmap; struct list_head modelist; struct fb_videomode *mode; //显示模式 #ifdef CONFIG_FB_BACKLIGHT struct backlight_device *bl_dev;//背光设备 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; //fb操作函数集 struct device *device; //设备文件 struct device *dev; //设备文件 int class_flag; #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; #endif char __iomem *screen_base; //虚拟基地址 unsigned long screen_size; //虚拟显存大小 void *pseudo_palette; //调色板 一般设置为PALETTE_BUFF_CLEAR #define FBINFO_STATE_RUNNING 0 #define FBINFO_STATE_SUSPENDED 1 u32 state; //状态 状态值为上面两个宏 void *fbcon_par; void *par; struct apertures_struct { unsigned int count; struct aperture { resource_size_t base; resource_size_t size; } ranges[0]; } *apertures; };
2. 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; //红色位域 offset=11 length=5 (RGB565) struct fb_bitfield green; //绿色位域 offset=5 length=6 struct fb_bitfield blue; //蓝色位域 offset=0 length=5 struct fb_bitfield transp; //透明度位域 __u32 nonstd; //非标准标志 __u32 activate; //激活标志 __u32 height; //物理高度 __u32 width; //物理宽度 __u32 accel_flags;//硬件加速标志 __u32 pixclock; __u32 left_margin; //左边缘宽度 __u32 right_margin; //右边缘宽度 __u32 upper_margin; //上边缘宽度 __u32 lower_margin; //下边缘宽度 __u32 hsync_len; //水平同步长度 __u32 vsync_len; //垂直同步长度 __u32 sync; __u32 vmode; __u32 rotate;//旋转 __u32 reserved[5]; };
2.1 位域 fb_bitfield
struct fb_bitfield { __u32 offset; //偏移量 __u32 length; //长度 __u32 msb_right; //高位有效?对齐方式 };
3.fb_fix_screeninfo
struct fb_fix_screeninfo { char id[16]; unsigned long smem_start; //物理显存起始地址 __u32 smem_len; //显示长度(xres*yres*bits_per_pixel/8) __u32 type; //显示类型(FB_TYPE_PACKED_PIXELS) __u32 type_aux; __u32 visual; //颜色模式(FB_VISUAL_TRUECOLOR) __u16 xpanstep; __u16 ypanstep; __u16 ywrapstep; __u32 line_length; //一行像素的字节数(xres*bits_per_pixel/8) unsigned long mmio_start; __u32 mmio_len; __u32 accel;//加速类型 __u16 reserved[3]; };
4. 操作函数集
struct fb_ops { struct module *owner; int (*fb_open)(struct fb_info *info, int user); //打开 int (*fb_release)(struct fb_info *info, int user); //释放 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); //写 int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); //检测参数 int (*fb_set_par)(struct fb_info *info); //设置参数 int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,unsigned blue, unsigned transp, struct fb_info *info); //设置调色板 int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); int (*fb_blank)(int blank, struct fb_info *info); //清屏 int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); // void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); //填充区域 void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); //复制区域 void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); //图形填充 int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); //绘制光标 void (*fb_rotate)(struct fb_info *info, int angle); //旋转屏幕 int (*fb_sync)(struct fb_info *info); //同步 等待blit空闲 int (*fb_ioctl)(struct fb_info *info, unsigned int cmd,unsigned long arg); //控制 int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd,unsigned long arg); //32位兼容控制 int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); //映射 void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var); void (*fb_destroy)(struct fb_info *info); //销毁 int (*fb_debug_enter)(struct fb_info *info); int (*fb_debug_leave)(struct fb_info *info); };
二. fb系统初始化
1. 主设备号
#define FB_MAJOR 29
2. fbmem_init
static int __init fbmem_init(void) { proc_create("fb", 0, NULL, &fb_proc_fops); //创建"/proc/fb"文件 if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) //注册所有的fb字符设备,并捆绑fb_fops操作函数集合 printk("unable to get major %d for fb devs\n", FB_MAJOR); fb_class = class_create(THIS_MODULE, "graphics"); //创建"/sys/class/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; }
3. /proc下的接口fb_proc_fops
3.1 操作函数集
static const struct file_operations fb_proc_fops = { .owner = THIS_MODULE, .open = proc_fb_open, //打开 .read = seq_read, //读取 .llseek = seq_lseek, //读写指针 .release = seq_release, };
3.2 cat /prco/fb
cat /proc/fb 0 ti81xxfb 1 ti81xxfb 2 ti81xxfb
先调用open方法
static int proc_fb_open(struct inode *inode, struct file *file) { return seq_open(file, &proc_fb_seq_ops); //主要做了file->private_data->op=proc_fb_seq_ops }
proc_fb_seq_ops
static const struct seq_operations proc_fb_seq_ops = { .start = fb_seq_start, //读写位置开头 .next = fb_seq_next, //下一个读写位置 .stop = fb_seq_stop, //无操作 .show = fb_seq_show, //显示 };
fb_seq_show函数
static int fb_seq_show(struct seq_file *m, void *v) { int i = *(loff_t *)v; struct fb_info *fi = registered_fb[i]; //获取fb_info结构体 if (fi) seq_printf(m, "%d %s\n", fi->node, fi->fix.id); //打印其设备编号及设备信息到seq_file文件 return 0; }
接着调用read方法
seq_read方法调用proc_fb_seq_ops的各个方法,最终将seq_file文件里的打印信息,复制到用户空间的buf里面
4. 字符设备捆绑的fb_fops
4.1 操作函数集
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, //读写指针 };
4.1.1 打开
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; if (fbidx >= FB_MAX) return -ENODEV; info = registered_fb[fbidx]; //从全局registered_fb数组中获取fb_info 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; //将fb_info数据存放在文件的私有数据里 if (info->fbops->fb_open) { //若fb_info的fb操作函数集有实现open方法 res = info->fbops->fb_open(info,1); //则调用其open方法 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; }
4.1.2 读
static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); //通过节点算出次设备号 struct fb_info *info = registered_fb[fbidx]; //从全局数组获取fb_info u8 *buffer, *dst; u8 __iomem *src; int c, cnt = 0, err = 0; unsigned long total_size; if (!info || ! info->screen_base) //fb_info为空或者虚拟地址不存在 return -ENODEV; //错误 if (info->state != FBINFO_STATE_RUNNING) //处于运行态 return -EPERM; //错误 if (info->fbops->fb_read) //fb操作函数集存在read方法 return info->fbops->fb_read(info, buf, count, ppos); //则调用 否则继续运行 total_size = info->screen_size; //获取屏幕尺寸 if (total_size == 0) //屏幕尺寸没设置 total_size = info->fix.smem_len; //则获取显存长度 if (p >= total_size) //读写位置已经超出 return 0; if (count >= total_size) //读数据长度限制在total_size范围你 count = total_size; if (count + p > total_size) //有效读取的数据个数 count = total_size - p; buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,GFP_KERNEL); //分配临时缓冲区 if (!buffer) return -ENOMEM; src = (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; //按页读数据 dst = buffer; //设置目的地址 fb_memcpy_fromfb(dst, src, c); //复制数据 dst += c; //重置目的地址 src += c; //重置目的地址 if (copy_to_user(buf, buffer, c)) { //内核空间拷贝到用户空间 err = -EFAULT; break; } *ppos += c; //移动读写指针 buf += c; //用户空间缓冲区指针 cnt += c; //已读数据计数 count -= c; //待读数据计数 } kfree(buffer); //释放临时缓冲区 return (err) ? err : cnt; //返回读取数据大小 }
4.1.3 写
static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); //根据节点计算次设备号 struct fb_info *info = registered_fb[fbidx]; //全局数组中获取fb_info 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) //fb函数集存在写方法 return info->fbops->fb_write(info, buf, count, ppos); //则调用其方法 total_size = info->screen_size; //获取屏幕尺寸 if (total_size == 0) //没设置screen_size参数 total_size = info->fix.smem_len; //则获取显存长度 if (p > total_size) //读写指针已超过 return -EFBIG; if (count > total_size) { //控制写长度在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; //返回已经写了的数据长度 }
4.1.4 控制
static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct inode *inode = file->f_path.dentry->d_inode; int fbidx = iminor(inode); //根据节点获取次设备号 struct fb_info *info = registered_fb[fbidx]; //在全局数组中获取fb_info return do_fb_ioctl(info, cmd, arg); //调用do_fb_ioctl函数 }
4.1.4.1 do_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; acquire_console_sem(); //锁定fbcon info->flags |= FBINFO_MISC_USEREVENT; ret = fb_set_var(info, &var); info->flags &= ~FBINFO_MISC_USEREVENT; release_console_sem(); //释放fbcon 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; acquire_console_sem(); ret = fb_pan_display(info, &var); release_console_sem(); 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; acquire_console_sem(); //锁定fbcon info->flags |= FBINFO_MISC_USEREVENT; ret = fb_blank(info, arg); info->flags &= ~FBINFO_MISC_USEREVENT; release_console_sem(); //释放fbcon unlock_fb_info(info); break; default: if (!lock_fb_info(info)) return -ENODEV; fb = info->fbops; if (fb->fb_ioctl)//若fb操作函数集存在操作方法 ret = fb->fb_ioctl(info, cmd, arg);//则调用其方法,这样就可以针对具体设备添加自定义的命令 else ret = -ENOTTY; unlock_fb_info(info); } return ret; }
4.1.5 映射
static int fb_mmap(struct file *file, struct vm_area_struct * vma) { int fbidx = iminor(file->f_path.dentry->d_inode); struct fb_info *info = registered_fb[fbidx]; struct fb_ops *fb = info->fbops; unsigned long off; unsigned long start; u32 len; if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; off = vma->vm_pgoff << PAGE_SHIFT; if (!fb) return -ENODEV; mutex_lock(&info->mm_lock); if (fb->fb_mmap) { //fb操作函数集存在mmap方法 int res; res = fb->fb_mmap(info, vma); //则调用其方法 mutex_unlock(&info->mm_lock); return res; } /* frame buffer memory */ 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_info
struct fb_info *framebuffer_alloc(size_t size, struct device *dev) { #define BYTES_PER_LONG (BITS_PER_LONG/8) #define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) int fb_info_size = sizeof(struct fb_info); //fb_info的大小 struct fb_info *info; char *p; if (size) fb_info_size += PADDING; p = kzalloc(fb_info_size + size, GFP_KERNEL); //分配内存 if (!p) return NULL; info = (struct fb_info *) p; if (size) info->par = p + fb_info_size; info->device = dev; //设备文件 #ifdef CONFIG_FB_BACKLIGHT mutex_init(&info->bl_curve_mutex); #endif return info; #undef PADDING #undef BYTES_PER_LONG }
四. 注册与注销framebuffer
1.注册framebuffer
1.1 register_framebuffer
int register_framebuffer(struct fb_info *fb_info) { int i; struct fb_event event; struct fb_videomode mode; if (num_registered_fb == FB_MAX)//最多32个 return -ENXIO; if (fb_check_foreignness(fb_info)) return -ENOSYS; remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,fb_is_primary_device(fb_info)); num_registered_fb++;//注册的fb设备计数加1 for (i = 0 ; i < FB_MAX; i++)//查找分配次设备号 if (!registered_fb[i]) break; fb_info->node = i;//设置次设备号 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);//创建"/sys/class/graphics/fb%d" 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_init_device(fb_info);//创建"sys/class/graphics/fbX/"下的属性文件 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;//填充registered全局数组 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; }
1.2 fb_init_device
int fb_init_device(struct fb_info *fb_info) { int i, error = 0; dev_set_drvdata(fb_info->dev, fb_info); fb_info->class_flag |= FB_SYSFS_FLAG_ATTR; for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { error = device_create_file(fb_info->dev, &device_attrs[i]);//"/sys/class/graphics/fbX/"下属性文件 if (error) break; } if (error) { while (--i >= 0) device_remove_file(fb_info->dev, &device_attrs[i]); fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR; } return 0; }1.2.1 属性文件device_attrs
static struct device_attribute device_attrs[] = { __ATTR(bits_per_pixel, S_IRUGO|S_IWUSR, show_bpp, store_bpp), __ATTR(blank, S_IRUGO|S_IWUSR, show_blank, store_blank), __ATTR(console, S_IRUGO|S_IWUSR, show_console, store_console), __ATTR(cursor, S_IRUGO|S_IWUSR, show_cursor, store_cursor), __ATTR(mode, S_IRUGO|S_IWUSR, show_mode, store_mode), __ATTR(modes, S_IRUGO|S_IWUSR, show_modes, store_modes), __ATTR(pan, S_IRUGO|S_IWUSR, show_pan, store_pan), __ATTR(virtual_size, S_IRUGO|S_IWUSR, show_virtual, store_virtual), __ATTR(name, S_IRUGO, show_name, NULL), __ATTR(stride, S_IRUGO, show_stride, NULL), __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), __ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate), #ifdef CONFIG_FB_BACKLIGHT __ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve), #endif };
2.注销framebuffer
int unregister_framebuffer(struct fb_info *fb_info) { struct fb_event event; int i, ret = 0; i = fb_info->node;//获取次设备号 if (!registered_fb[i]) { ret = -EINVAL; goto done; } if (!lock_fb_info(fb_info)) return -ENODEV; event.info = fb_info; ret = fb_notifier_call_chain(FB_EVENT_FB_UNBIND, &event);//发送通知 unlock_fb_info(fb_info); if (ret) { ret = -EINVAL; goto done; } if (fb_info->pixmap.addr &&(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); fb_destroy_modelist(&fb_info->modelist); registered_fb[i]=NULL;//清空全局数组 num_registered_fb--;//注册了的fb计数减1 fb_cleanup_device(fb_info); device_destroy(fb_class, MKDEV(FB_MAJOR, i));移除"/sys/class/graphics/fbX" event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);//发送通知 if (fb_info->fbops->fb_destroy)//若fb操作函数集存在销毁方法 fb_info->fbops->fb_destroy(fb_info);//则调用其方法 done: return ret; }
五. 编写framebuffer驱动步骤
1.framebuffer_alloc分配fb_info,设置其重要成员
2.填充可变fb_var_screeninfo和不可变参数fb_fix_screeninfo
3.定义并实现fb_ops并与fb_info捆绑
4.注册设备register_framebuffer
六. 应用层的一个测试实例
如果不是使用fb0那么修改下open函数啊,显示结果是rgb三个方块
//gcc fb_test.c #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <linux/kd.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <sys/time.h> #include <string.h> #include <errno.h> struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; char *frameBuffer = 0; //打印fb驱动中fix结构信息,注:在fb驱动加载后,fix结构不可被修改。 void printFixedInfo () { printf ("Fixed screen info:\n" "\tid: %s\n" "\tsmem_start: 0x%lx\n" "\tsmem_len: %d\n" "\ttype: %d\n" "\ttype_aux: %d\n" "\tvisual: %d\n" "\txpanstep: %d\n" "\typanstep: %d\n" "\tywrapstep: %d\n" "\tline_length: %d\n" "\tmmio_start: 0x%lx\n" "\tmmio_len: %d\n" "\taccel: %d\n" "\n", finfo.id, finfo.smem_start, finfo.smem_len, finfo.type, finfo.type_aux, finfo.visual, finfo.xpanstep, finfo.ypanstep, finfo.ywrapstep, finfo.line_length, finfo.mmio_start, finfo.mmio_len, finfo.accel); } //打印fb驱动中var结构信息,注:fb驱动加载后,var结构可根据实际需要被重置 void printVariableInfo () { printf ("Variable screen info:\n" "\txres: %d\n" "\tyres: %d\n" "\txres_virtual: %d\n" "\tyres_virtual: %d\n" "\tyoffset: %d\n" "\txoffset: %d\n" "\tbits_per_pixel: %d\n" "\tgrayscale: %d\n" "\tred: offset: %2d, length: %2d, msb_right: %2d\n" "\tgreen: offset: %2d, length: %2d, msb_right: %2d\n" "\tblue: offset: %2d, length: %2d, msb_right: %2d\n" "\ttransp: offset: %2d, length: %2d, msb_right: %2d\n" "\tnonstd: %d\n" "\tactivate: %d\n" "\theight: %d\n" "\twidth: %d\n" "\taccel_flags: 0x%x\n" "\tpixclock: %d\n" "\tleft_margin: %d\n" "\tright_margin: %d\n" "\tupper_margin: %d\n" "\tlower_margin: %d\n" "\thsync_len: %d\n" "\tvsync_len: %d\n" "\tsync: %d\n" "\tvmode: %d\n" "\n", vinfo.xres, vinfo.yres, vinfo.xres_virtual, vinfo.yres_virtual, vinfo.xoffset, vinfo.yoffset, vinfo.bits_per_pixel, vinfo.grayscale, vinfo.red.offset, vinfo.red.length, vinfo.red.msb_right, vinfo.green.offset, vinfo.green.length, vinfo.green.msb_right, vinfo.blue.offset, vinfo.blue.length, vinfo.blue.msb_right, vinfo.transp.offset, vinfo.transp.length, vinfo.transp.msb_right, vinfo.nonstd, vinfo.activate, vinfo.height, vinfo.width, vinfo.accel_flags, vinfo.pixclock, vinfo.left_margin, vinfo.right_margin, vinfo.upper_margin, vinfo.lower_margin, vinfo.hsync_len, vinfo.vsync_len, vinfo.sync, vinfo.vmode); } //画大小为width*height的同色矩阵,8alpha+8reds+8greens+8blues void drawRect_rgb32 (int x0, int y0, int width, int height, int color) { const int bytesPerPixel = 4; const int stride = finfo.line_length / bytesPerPixel; int *dest = (int *) (frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); int x, y; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { dest[x] = color; } dest += stride; } } //画大小为width*height的同色矩阵,5reds+6greens+5blues void drawRect_rgb16 (int x0, int y0, int width, int height, int color) { const int bytesPerPixel = 2; const int stride = finfo.line_length / bytesPerPixel; const int red = (color & 0xff0000) >> (16 + 3); const int green = (color & 0xff00) >> (8 + 2); const int blue = (color & 0xff) >> 3; const short color16 = blue | (green << 5) | (red << (5 + 6)); short *dest = (short *) (frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); int x, y; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { dest[x] = color16; } dest += stride; } } //画大小为width*height的同色矩阵,5reds+5greens+5blues void drawRect_rgb15 (int x0, int y0, int width, int height, int color) { const int bytesPerPixel = 2; const int stride = finfo.line_length / bytesPerPixel; const int red = (color & 0xff0000) >> (16 + 3); const int green = (color & 0xff00) >> (8 + 3); const int blue = (color & 0xff) >> 3; const short color15 = blue | (green << 5) | (red << (5 + 5)) | 0x8000; short *dest = (short *) (frameBuffer) + (y0 + vinfo.yoffset) * stride + (x0 + vinfo.xoffset); int x, y; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { dest[x] = color15; } dest += stride; } } void drawRect (int x0, int y0, int width, int height, int color) { switch (vinfo.bits_per_pixel) { case 32: drawRect_rgb32 (x0, y0, width, height, color); break; case 16: drawRect_rgb16 (x0, y0, width, height, color); break; case 15: drawRect_rgb15 (x0, y0, width, height, color); break; default: printf ("Warning: drawRect() not implemented for color depth %i\n", vinfo.bits_per_pixel); break; } } #define PERFORMANCE_RUN_COUNT 5 void performSpeedTest (void *fb, int fbSize) { int i, j, run; struct timeval startTime, endTime; unsigned long long results[PERFORMANCE_RUN_COUNT]; unsigned long long average; unsigned int *testImage; unsigned int randData[17] = { 0x3A428472, 0x724B84D3, 0x26B898AB, 0x7D980E3C, 0x5345A084, 0x6779B66B, 0x791EE4B4, 0x6E8EE3CC, 0x63AF504A, 0x18A21B33, 0x0E26EB73, 0x022F708E, 0x1740F3B0, 0x7E2C699D, 0x0E8A570B, 0x5F2C22FB, 0x6A742130 }; printf ("Frame Buffer Performance test...\n"); for (run = 0; run < PERFORMANCE_RUN_COUNT; ++run) { /* Generate test image with random(ish) data: */ testImage = (unsigned int *) malloc (fbSize); j = run; for (i = 0; i < (int) (fbSize / sizeof (int)); ++i) { testImage[i] = randData[j]; j++; if (j >= 17) j = 0; } gettimeofday (&startTime, NULL); memcpy (fb, testImage, fbSize); gettimeofday (&endTime, NULL); long secsDiff = endTime.tv_sec - startTime.tv_sec; results[run] = secsDiff * 1000000 + (endTime.tv_usec - startTime.tv_usec); free (testImage); } average = 0; for (i = 0; i < PERFORMANCE_RUN_COUNT; ++i) average += results[i]; average = average / PERFORMANCE_RUN_COUNT; printf (" Average: %llu usecs\n", average); printf (" Bandwidth: %.03f MByte/Sec\n", (fbSize / 1048576.0) / ((double) average / 1000000.0)); printf (" Max. FPS: %.03f fps\n\n", 1000000.0 / (double) average); /* Clear the framebuffer back to black again: */ memset (fb, 0, fbSize); } int main (int argc, char **argv) { const char *devfile = "/dev/fb0"; long int screensize = 0; int fbFd = 0; /* Open the file for reading and writing */ fbFd = open (devfile, O_RDWR); if (fbFd == -1) { perror ("Error: cannot open framebuffer device"); exit (1); } //获取finfo信息并显示 if (ioctl (fbFd, FBIOGET_FSCREENINFO, &finfo) == -1) { perror ("Error reading fixed information"); exit (2); } printFixedInfo (); //获取vinfo信息并显示 if (ioctl (fbFd, FBIOGET_VSCREENINFO, &vinfo) == -1) { perror ("Error reading variable information"); exit (3); } printVariableInfo (); /* Figure out the size of the screen in bytes */ screensize = finfo.smem_len; /* Map the device to memory */ frameBuffer = (char *) mmap (0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fbFd, 0); if (frameBuffer == MAP_FAILED) { perror ("Error: Failed to map framebuffer device to memory"); exit (4); } //测试virt fb的性能 performSpeedTest (frameBuffer, screensize); printf ("Will draw 3 rectangles on the screen,\n" "they should be colored red, green and blue (in that order).\n"); drawRect (vinfo.xres / 8, vinfo.yres / 8, vinfo.xres / 4, vinfo.yres / 4, 0xffff0000); drawRect (vinfo.xres * 3 / 8, vinfo.yres * 3 / 8, vinfo.xres / 4, vinfo.yres / 4, 0xff00ff00); drawRect (vinfo.xres * 5 / 8, vinfo.yres * 5 / 8, vinfo.xres / 4, vinfo.yres / 4, 0xff0000ff); sleep (5); printf (" Done.\n"); munmap (frameBuffer, screensize); //解除内存映射,与mmap对应 close (fbFd); return 0; }
七. 截图方法
cat /dev/fb0 >0.raw ./ffmpeg -vcodec rawvideo -pix_fmt rgb565 -s 1920*1080 -i 0.raw -f image2 -vcodec mjpeg 0.jpg
借助ffmpeg工具
或者用qt的demo有个screenshot
八. 显示图片方法(针对565 A8平台)
先编译ljpeg库,然后交叉编译下面的c代码 编译时候加上 -ljpeg
#include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <linux/fb.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <jpeglib.h> #include <jerror.h> #define FB_DEV "/dev/fb0" /***************** function declaration ******************/ void usage(char *msg); unsigned short RGB888toRGB565(unsigned char red, unsigned char green, unsigned char blue); int fb_open(char *fb_device); int fb_close(int fd); int fb_stat(int fd, int *width, int *height, int *depth); void *fb_mmap(int fd, unsigned int screensize); int fb_munmap(void *start, size_t length); int fb_pixel(void *fbmem, int width, int height, int x, int y, unsigned short color); /************ function implementation ********************/ int main(int argc, char *argv[]) { /* * declaration for jpeg decompression */ struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE *infile; unsigned char *buffer; /* * declaration for framebuffer device */ int fbdev; char *fb_device; unsigned char *fbmem; unsigned int screensize; unsigned int fb_width; unsigned int fb_height; unsigned int fb_depth; unsigned int x; unsigned int y; /* * check auguments */ if (argc != 2) { usage("insuffient auguments"); exit(-1); } /* * open framebuffer device */ if ((fb_device = getenv("FRAMEBUFFER")) == NULL) fb_device = FB_DEV; fbdev = fb_open(fb_device); /* * get status of framebuffer device */ fb_stat(fbdev, &fb_width, &fb_height, &fb_depth); /* * map framebuffer device to shared memory */ screensize = fb_width * fb_height * fb_depth / 8; fbmem = fb_mmap(fbdev, screensize); /* * open input jpeg file */ if ((infile = fopen(argv[1], "rb")) == NULL) { fprintf(stderr, "open %s failed\n", argv[1]); exit(-1); } /* * init jpeg decompress object error handler */ cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); /* * bind jpeg decompress object to infile */ jpeg_stdio_src(&cinfo, infile); /* * read jpeg header */ jpeg_read_header(&cinfo, TRUE); /* * decompress process. * note: after jpeg_start_decompress() is called * the dimension infomation will be known, * so allocate memory buffer for scanline immediately */ jpeg_start_decompress(&cinfo); if ((cinfo.output_width > fb_width) || (cinfo.output_height > fb_height)) { printf("too large JPEG file,cannot display\n"); return (-1); } buffer = (unsigned char *) malloc(cinfo.output_width * cinfo.output_components); y = 0; while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, &buffer, 1); if (fb_depth == 16) { unsigned short color; for (x = 0; x < cinfo.output_width; x++) { color = RGB888toRGB565(buffer[x * 3], buffer[x * 3 + 1], buffer[x * 3 + 2]); fb_pixel(fbmem, fb_width, fb_height, x, y, color); } } else if (fb_depth == 24) { memcpy((unsigned char *) fbmem + y * fb_width * 3, buffer, cinfo.output_width * cinfo.output_components); } y++; // next scanline } /* * finish decompress, destroy decompress object */ jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); /* * release memory buffer */ free(buffer); /* * close jpeg inputing file */ fclose(infile); /* * unmap framebuffer's shared memory */ fb_munmap(fbmem, screensize); /* * close framebuffer device */ fb_close(fbdev); return (0); } void usage(char *msg) { fprintf(stderr, "%s\n", msg); printf("Usage: fv some-jpeg-file.jpg\n"); } /* * convert 24bit RGB888 to 16bit RGB565 color format */ unsigned short RGB888toRGB565(unsigned char red, unsigned char green, unsigned char blue) { unsigned short B = (blue >> 3) & 0x001F; unsigned short G = ((green >> 2) << 5) & 0x07E0; unsigned short R = ((red >> 3) << 11) & 0xF800; return (unsigned short) (R | G | B); } /* * open framebuffer device. * return positive file descriptor if success, * else return -1. */ int fb_open(char *fb_device) { int fd; if ((fd = open(fb_device, O_RDWR)) < 0) { perror(__func__); return (-1); } return (fd); } /* * get framebuffer's width,height,and depth. * return 0 if success, else return -1. */ int fb_stat(int fd, int *width, int *height, int *depth) { struct fb_fix_screeninfo fb_finfo; struct fb_var_screeninfo fb_vinfo; if (ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) { perror(__func__); return (-1); } if (ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) { perror(__func__); return (-1); } *width = fb_vinfo.xres; *height = fb_vinfo.yres; *depth = fb_vinfo.bits_per_pixel; return (0); } /* * map shared memory to framebuffer device. * return maped memory if success, * else return -1, as mmap dose. */ void * fb_mmap(int fd, unsigned int screensize) { caddr_t fbmem; if ((fbmem = mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { perror(__func__); return (void *) (-1); } return (fbmem); } /* * unmap map memory for framebuffer device. */ int fb_munmap(void *start, size_t length) { return (munmap(start, length)); } /* * close framebuffer device */ int fb_close(int fd) { return (close(fd)); } /* * display a pixel on the framebuffer device. * fbmem is the starting memory of framebuffer, * width and height are dimension of framebuffer, * x and y are the coordinates to display, * color is the pixel's color value. * return 0 if success, otherwise return -1. */ int fb_pixel(void *fbmem, int width, int height, int x, int y, unsigned short color) { if ((x > width) || (y > height)) return (-1); unsigned short *dst = ((unsigned short *) fbmem + y * width + x); *dst = color; return 0; }