您的位置:首页 > 运维架构 > Linux

67 linux内核里的framebuffer设备驱动模型

2017-07-06 01:24 447 查看
紧接上一博文 ,实现一个最基本功能的fb设备驱动,需实现如下步聚:

1) 从内存里分配出禁用数据缓存功能的缓冲区,用于作显存.

2) 动态分配struct fb_info对象空间, 每个fb_info对象表示一个fb设备.

3) 初始化fb_info对象里的fb_var_screeninfo成员里的分辨率, 位色,每个像素的rgb位域等信息.

4) 初始化fb_info对象里的fb_fix_screeninfo成员里的显存物理地址,line_length等成员信息.

5) 初始化fb_info对象里的fbops成员指针,此指针不可以为NULL, 必须指向一个struct fb_ops对象的地址.

如果指向的fb_ops对象里功能函数没有实现,则会调用fbmem.c里实现功能函数.

struct fb_ops {
...
int (*fb_open)(struct fb_info *info, int user); //打开/dev/fb*设备文件时触发调用
int (*fb_release)(struct fb_info *info, int user); //关闭/dev/fb*设备文件时触发调用
...
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_ioctl)(struct fb_info *info, unsigned int cmd,   //对设备文件ioctl时触发
unsigned long arg);
...
};


6) 初始化fb_info对象里的screen_base成员,记录显存的虚拟地址. 最后注册fb设备

fbi->screen_base = v_addr; // 显示缓冲区的虚拟地址

fbi->screen_size = X*Y*4; //显存大小

register_framebuffer(fbi); //注册fb设备

//////////////////////////////////////////////////////////////////////

framebuffer设备驱动的主要功能及入口在内核源码里的”drivers/video/fbmem.c”里

过程分析:

46 struct fb_info *registered_fb[FB_MAX] __read_mostly; //此全局fb_info指针数组用于记录所有的fb_info对象的地址. FB_MAX的值是32
47 int num_registered_fb __read_mostly; //此全局变量记录已注册的fb_info对象的个数


1802 #ifdef MODULE  //如fbmem.c编成模块,则此宏成立
1803 module_init(fbmem_init);
..
1815 #else  //编进内核镜像,则这里成立.  总之fbmem_init函数会在内核初始化后被调用的.
1816 subsys_initcall(fbmem_init);
1817 #endif


1786 static int __init
1787 fbmem_init(void)
1788 {
1789     proc_create("fb", 0, NULL, &fb_proc_fops); //会创建"/proc/fb"属性文件
1790
1791     if (register_chrdev(FB_MAJOR,"fb",&fb_fops))  //实现字符设备驱动接口,当操作所有的fb设备文件时都会先使用fb_fops文件操作对象里的功能函数
...
1794     fb_class = class_create(THIS_MODULE, "graphics"); //创建"/sys/class/graphics"子目录
...
1799     return 0;
1800 }

//注意看哦, fb_fops文件操作对象里所有文件操作的功能函数全部实现了哦,这也就是为什么我们前面的fb设备驱动里什么文件操作函数都没有实现也可以正常工作的原因.
1461 static const struct file_operations fb_fops = {
1462     .owner =    THIS_MODULE,
1463     .read =     fb_read,
1464     .write =    fb_write,
1465     .unlocked_ioctl = fb_ioctl,
1466 #ifdef CONFIG_COMPAT
1467     .compat_ioctl = fb_compat_ioctl,
1468 #endif
1469     .mmap =     fb_mmap,
1470     .open =     fb_open,
1471     .release =  fb_release,
1472 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1473     .get_unmapped_area = get_fb_unmapped_area,
1474 #endif
1475 #ifdef CONFIG_FB_DEFERRED_IO
1476     .fsync =    fb_deferred_io_fsync,
1477 #endif
1478     .llseek =   default_llseek,
1479 };


//注册fb_info对象时的代码过程
1714 int
1715 register_framebuffer(struct fb_info *fb_info)
1716 {
...
1720     ret = do_register_framebuffer(fb_info);
...
1723     return ret;
1724 }

1574 static int do_register_framebuffer(struct fb_info *fb_info)
1575 {
1576     int i;
...
1586     if (num_registered_fb == FB_MAX) //如果已有32个fb设备了,则失败返回。因全局数组registered_fb元素个数只有32个.
1587         return -ENXIO;
1588
1589     num_registered_fb++; //fb设备的计数加1
1590     for (i = 0 ; i < FB_MAX; i++) //顺序找出registered_fb数组里最前面的空的指针
1591         if (!registered_fb[i])
1592             break;
1593     fb_info->node = i; // i表示registered_fb数组里的下标,也就是需要注册的fb设备的编号
...
//创建出fb设备文件,注意设备文件的次设备号为fb_info对象在registered_fb数组里的下标
1598     fb_info->dev = device_create(fb_class, fb_info->device,
1599                      MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
...

1630     registered_fb[i] = fb_info; //注册的fb_info对象的地址存放在registered_fb数组里了,这就是注册.
//注意通过创建的设备文件的次设备号,就可以在registered_fb数组里找到fb_info对象的地址了
...
1639     return 0;
1640 }


////////////////////////////////////////////////////////////////////////////

当应用程序打开/dev/fb*设备文件时,先触发调用fb_ops里的fb_open

1404 static int
1405 fb_open(struct inode *inode, struct file *file)
1406 __acquires(&info->lock)
1407 __releases(&info->lock)
1408 {
1409     int fbidx = iminor(inode); //获取设备文件的次设备号,也就获取出fb_info对象在registered_fb数组里的下标
1410     struct fb_info *info;
1411     int res = 0;
1412
1413     info = get_fb_info(fbidx); //根据次设备号,获取到相应的fb_info对象的地址
...
1428     file->private_data = info;
1429     if (info->fbops->fb_open) { //如果fb_info对象的fbops成员有实现fb_open函数
1430         res = info->fbops->fb_open(info,1);  //则会在这里被调用(这是用C语言实现C++的多态功能)
...
1433     }
1442     return res;
1443 }


当应用程序读设备文件时,也就是读显存里的内容时, 触发fb_ops里的fb_read

739 static ssize_t
740 fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
741 {
742     unsigned long p = *ppos; //文件描述符的偏移
743     struct fb_info *info = file_fb_info(file); //通过文件描述符对象获取到次设备号,再根据次设备获取到fb_info对象的地址
744     u8 *buffer, *dst;
745     u8 __iomem *src;
746     int c, cnt = 0, err = 0;
747     unsigned long total_size;
...
755     if (info->fbops->fb_read) //如果fb_info对象里的fbops有实现fb_read功能函数,则直接调用
756         return info->fbops->fb_read(info, buf, count, ppos);

757    //如果fb_info对象里的fbopsi没有实现fb_read函数,则使用下面的代码(这是用C语言实现C++的多态功能)

758     total_size = info->screen_size; //记录显存的大小
...
772     buffer = kmalloc((count > PAGE_SIZE) ? PAGE_SIZE : count,
773              GFP_KERNEL); //动态分配出一个缓冲区
...
777     src = (u8 __iomem *) (info->screen_base + p); //src指针指向显存的虚拟地址加上文件描述符的偏移
...
782     while (count) {
783         c  = (count > PAGE_SIZE) ? PAGE_SIZE : count;
784         dst = buffer;
785         fb_memcpy_fromfb(dst, src, c);//先把显存里的内容复制到buffer指向的缓冲区里
786         dst += c;
787         src += c;
788
789         if (copy_to_user(buf, buffer, c)) { //再把buffer指向的缓冲里内容复制到用户进程的缓冲区里
790             err = -EFAULT;
791             break;
792         }
793         *ppos += c;
794         buf += c;
795         cnt += c;
796         count -= c;
797     }
798
799     kfree(buffer);
800
801     return (err) ? err : cnt;
802 }


////////////////

其它函数基本也是这套路,根据设备文件的次设备号在registered_fb数组里找到对应的fb_info对象,

然后判断fb_info对象里的fbops有没有实现相应的功能函数,如有实现,则直接调用,如没有实现,则用里面写好的功能代码来代替.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息