原文出处: http://liu1227787871.blog.163.com/blog/static/205363197201242254810976/
在写lcd驱动程序之前,我们必须先要明白lcd驱动程序编写的框架,这点韦东山老师分析的相当详细,让我们来学习一下吧!首先从fbmem.c文件开始。
(1)我们先找到入口函数:
static int __init fbmem_init(void)
{
proc_create("fb", 0, NULL, &fb_proc_fops);
if (register_chrdev(FB_MAJOR,"fb",&fb_fops))//
注册字符设备,FB_MAJOR为主设备号,fb_fops为操作函数集
printk("unable to get major %d for fb devs\n", FB_MAJOR);
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;
}
(2)接下来当然要看操作函数集
fb_fops
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read,
.write = fb_write,
.unlocked_ioctl = fb_ioctl,
..............................................................
.mmap = fb_mmap,
.open = fb_open,
.release = fb_release,
..............................................................
};
我们选两个来分析:open和read
首先是open函数:
static int
fb_open(struct inode *inode, struct file *file)
__acquires(&info->lock)
__releases(&info->lock)
{
int fbidx = iminor(inode);//
根据次设备号得到fbidx
struct fb_info *info;
int res = 0;
if (fbidx >= FB_MAX)
return -ENODEV;
info = registered_fb[fbidx];//
这个东西很重要,从下面的注释中我们可以看到,帧缓冲区操作函数集定义在这个结构体里,我们将在注释1里来 //具体分析
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;
if (info->fbops->fb_open)//
如果在在帧缓冲区操作函数集里定义了open函数,就会调用open函数,否则就算了
{
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);
#endif
out:
mutex_unlock(&info->lock);
return res;
}
接下来我们看看read函数
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];//
熟悉吧,我们又看到这个数组了,看来它很重要,待会一定好好款待一下它
u32 *buffer, *dst;
u32 __iomem *src;
int c, i, 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_read)//
如果操作函数集里定义了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)
count = total_size;
if (count + p > total_size)
count = total_size - p;
buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,//
开辟一个buffer
GFP_KERNEL);
if (!buffer)
return -ENOMEM;
src = (u32 __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;//
目的指针指向一个buffer(我们在上面开辟的)
for (i = c >> 2; i--; )
*dst++ = fb_readl(src++);//
从基地址读取数据放进buffer中
if (c & 3) {
u8 *dst8 = (u8 *) dst;
u8 __iomem *src8 = (u8 __iomem *) src;
for (i = c & 3; i--;)
*dst8++ = fb_readb(src8++);
src = (u32 __iomem *) src8;
}
if (copy_to_user(buf, buffer, c))//
将buffer中的数据拷贝到用户空间,这样在用户空间调用read函数时就把显存内容读出来了
{
err = -EFAULT;
break;
}
*ppos += c;
buf += c;
cnt += c;
count -= c;
}
kfree(buffer);
return (err) ? err : cnt;
}
注释1:registered_fb[]这个东东哪里来的呢?我们收索了一下,并在本文件的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)
return -ENXIO;
if (fb_check_foreignness(fb_info))
return -ENOSYS;
num_registered_fb++;
for (i = 0 ; i < FB_MAX; i++)
if (!registered_fb[i])//
寻找registered_fb数组中空的一项
break;
fb_info->node = i;
mutex_init(&fb_info->lock);
fb_info->dev = device_create(fb_class, fb_info->device,
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);
fb_var_to_videomode(&mode, &fb_info->var);
fb_add_videomode(&mode, &fb_info->modelist);
registered_fb[i] = fb_info;//我们在这里找到了它
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);
return 0;
}
结合之前的知识我们就可以将整个框架大致搭建起来了,我们在驱动程序中调用register_framebuffer(struct fb_info *fb_info)这个函数,并将帧缓冲设备描述结构体struct fb_info *fb_info传递了进来,在这个结构体里就定义了帧缓冲操作函数集,以及各种跟硬件相关的东西。现在我们就清楚了
registered_fb[]这个数组只是起到了一个中间变量的作用,在注册函数中将
struct fb_info结构体存进去,然后在操作函数中再取出来。
(3)
现在框架就搭建起来了,不过为了进一步理解,我们在内核里查看一下有哪些文件调用了
register_framebuffer(
struct fb_info *fb_info
),并适当分析其各种硬件相关设置。其实我们搜索到的这些文件就是我们编写驱动程序应当编写的东西,下面我们以/driver/video/arcfb.c来分析一下:
platform_driver_register(&arcfb_driver);
int __init arcfb_probe(struct platform_device *dev)
register_framebuffer(info);
//struct fb_info *info;这个结构体里有各种东东的,包括file_operations结构体,注册前已经设置了
这样一注册的话,
fb_info
里设置的东东就都传过去了,内核就可以调用我们自己写的东东了。
我们稍微总结一下驱动程序的流程:
分配fb_info
设置
注册
硬件相关设置
关于驱动程序的编写我们在接下来程序中会涉及。