linux framebuffer设备驱动

一. 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

linux framebuffer设备驱动_第1张图片

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


 

你可能感兴趣的:(linux framebuffer设备驱动)