framebuffer设备驱动的几个连接关键点
framebuffer设备驱动的几个连接关键点
framebuffer的架构
见到的宋宝华老师总结的一个图表,觉得很清晰,直接移过来了
关键点点析
- 文件
driver/video/fbmem.c
中的file_operations结构体(framebuffer是一个字符设备) - 文件
xxxfb.c
,对应于各具体厂商的framebuffer驱动,我这边看的是fsl的驱动,名字是drivers/video/mxc/mxc_ipuv3_fb.c
- 在文件
xxxxfb.c
中的重要结构体为fb_info
,用来连接到文件driver/video/fbmem.c
中,连接的函数就是register_framerbuffer
和unregister_framerbuffer
。所以在xxxxfb.c
中找入口,连接上下层的东西就在这个结构体和两个函数入口。 - 应用层的操作入口,例如读、写、控制等,在文件
xxxxfb.c
中实现,通过结构体fb_info
的fbops
(类型为struct fb_ops
)成员来连接。所以驱动最主要的是要实现结构体struct fb_ops
成员的填充。以下根据实例代码,描述连接。
实例代码
文件drivers/video/mxc/mxc_ipuv3_fb.c
static struct fb_ops mxcfb_ops = { /* 注册关键的操作函数 11111111111 */
.owner = THIS_MODULE,
.fb_set_par = mxcfb_set_par,
.fb_check_var = mxcfb_check_var,
.fb_setcolreg = mxcfb_setcolreg,
.fb_pan_display = mxcfb_pan_display,
.fb_ioctl = mxcfb_ioctl,
.fb_mmap = mxcfb_mmap,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_blank = mxcfb_blank,
};
......
static struct fb_info *mxcfb_init_fbinfo(struct device *dev, struct fb_ops *ops) /* 333333333 */
{
struct fb_info *fbi;
struct mxcfb_info *mxcfbi;
/*
* Allocate sufficient memory for the fb structure
*/
fbi = framebuffer_alloc(sizeof(struct mxcfb_info), dev);
if (!fbi)
return NULL;
mxcfbi = (struct mxcfb_info *)fbi->par;
fbi->var.activate = FB_ACTIVATE_NOW;
fbi->fbops = ops; /*************将fb_ops 注册到 fb_info 中****************/
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->pseudo_palette = mxcfbi->pseudo_palette;
/*
* Allocate colormap
*/
fb_alloc_cmap(&fbi->cmap, 16, 0);
return fbi;
}
......
static int mxcfb_probe(struct platform_device *pdev)
{
......
struct fb_info *fbi;
......
fbi = mxcfb_init_fbinfo(&pdev->dev, &mxcfb_ops); /* 注册11111111111,详见333333333 。 2222222222222 */
......
mxcfb_register(fbi); /* 最终调用 register_framebuffer(fbi); 进行注册,将fb_info 注册到驱动架构中 */
/* 继续往下个文件看 */
......
}
文件driver/video/fbmem.c
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;
}
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;
do_remove_conflicting_framebuffers(fb_info->apertures, fb_info->fix.id,
fb_is_primary_device(fb_info));
if (num_registered_fb == FB_MAX)
return -ENXIO;
num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++) /* 找到第一个可用的fb */
if (!registered_fb[i])
break;
fb_info->node = i;
atomic_set(&fb_info->count, 1); /* 设置原子操作数为 1 */
mutex_init(&fb_info->lock); /* 初始化互斥体 lock Lock for open/release/ioctl funcs **/
mutex_init(&fb_info->mm_lock); /* 初始化互斥体 mm_lock Lock for fb_mmap and smem_* fields */
fb_info->dev = device_create(fb_class, fb_info->device, /* 在/dev/ 目录下创建一个fb* 的设备结点 */
MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
if (IS_ERR(fb_info->dev)) {
/* Not fatal */
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); /* 里面构建所有的属性文件,赋予用户空间操作 */
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);
if (fb_info->skip_vt_switch)
pm_vt_switch_required(fb_info->dev, false);
else
pm_vt_switch_required(fb_info->dev, true);
fb_var_to_videomode(&mode, &fb_info->var);
fb_add_videomode(&mode, &fb_info->modelist);
registered_fb[i] = fb_info; /**************** 连接关键点,下面的围绕它来找出 fb_info中的操作 和
file_operation 中的操作的连接关系 ***********/
event.info = fb_info;
if (!lock_fb_info(fb_info))
return -ENODEV;
console_lock();
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
console_unlock();
unlock_fb_info(fb_info);
return 0;
}
extern struct fb_info *registered_fb[FB_MAX]; /********** 就是一个数组 (文件include/linux/fb.h中)**********/
static const struct file_operations fb_fops = { /********** 注意,这是file_operations 相关函数的注册 */
.owner = THIS_MODULE,
.read = fb_read, /* 下面以它为契机,去1111111111 */
.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,
};
static ssize_t
fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) /* 1111111111 */
{
unsigned long p = *ppos;
struct fb_info *info = file_fb_info(file); /* 连接点,继续往里面看,去 222222222222 */
......
if (info->fbops->fb_read) /* 之前在文件 mxc_ipuv3_fb.c 中注册的 fb_read */
return info->fbops->fb_read(info, buf, count, ppos);
......
}
static struct fb_info *file_fb_info(struct file *file) /* 222222222222 */
{
struct inode *inode = file_inode(file);
int fbidx = iminor(inode);
struct fb_info *info = registered_fb[fbidx]; /***** 熟悉的背影:registered_fb,
通过它将之前注册的fb_info 找到! */
if (info != file->private_data)
info = NULL;
return info;
}
重要结构体
struct fb_info
struct fb_info {
......
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
......
#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 */
......
};
struct 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 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);
......
/* perform fb specific mmap */
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
......
};
总结
一个简单的驱动,重要的是摸清脉络,搞清调用关系,这样有问题或者想灵活操作(例如更改等)可以找准下手点。本文主要目的是弄清软件流程,至于里面的具体实施跟具体的芯片有关,例如本文提到的例子,用的fsl的片子,而这里面糅合了ipu来进行framebuffer来处理,它的ipu操作并没有在这里进行分析,做显示的东西比较多,对他的ipu也有一定的了解,有时间会另外写出来。本文留做快速回忆或者稍微帮大家理理,希望有用。