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里实现功能函数.
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”里
过程分析:
////////////////////////////////////////////////////////////////////////////
当应用程序打开/dev/fb*设备文件时,先触发调用fb_ops里的fb_open
当应用程序读设备文件时,也就是读显存里的内容时, 触发fb_ops里的fb_read
////////////////
其它函数基本也是这套路,根据设备文件的次设备号在registered_fb数组里找到对应的fb_info对象,
然后判断fb_info对象里的fbops有没有实现相应的功能函数,如有实现,则直接调用,如没有实现,则用里面写好的功能代码来代替.
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有没有实现相应的功能函数,如有实现,则直接调用,如没有实现,则用里面写好的功能代码来代替.
相关文章推荐
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(4)
- linux内核部件分析(五)——设备驱动模型的基石kobject
- Linux内核部件分析--设备驱动模型之device
- Linux内核部件分析--设备驱动模型之bus
- linux内核部件分析(七)——设备驱动模型之driver
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(6)
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(1)
- linux内核部件分析(五)——设备驱动模型的基石kobject
- linux内核部件分析之——设备驱动模型之class
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(7)
- 【转】Linux内核大讲堂 (一) 设备驱动的基石驱动模型(3)
- linux内核部件分析(十)——设备驱动模型之class
- 【转】Linux内核大讲堂 (一) 设备驱动的基石驱动模型(2)
- linux内核组件分析之--设备驱动模型之bus
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(2)
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(6)
- linux内核部件分析(七)——设备驱动模型之driver
- Linux内核大讲堂 (一) 设备驱动的基石驱动模型(7)
- Linux内核大讲堂之设备驱动的基石驱动模型(5)
- Linux内核部件分析<5> 设备驱动模型的基石kobject