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

来源:互联网 发布:windows黑体字体下载 编辑:程序博客网 时间:2024/06/05 19:21

紧接上一博文 ,实现一个最基本功能的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 __init1787 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_COMPAT1467     .compat_ioctl = fb_compat_ioctl,1468 #endif1469     .mmap =     fb_mmap,1470     .open =     fb_open,1471     .release =  fb_release,1472 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA1473     .get_unmapped_area = get_fb_unmapped_area,1474 #endif1475 #ifdef CONFIG_FB_DEFERRED_IO1476     .fsync =    fb_deferred_io_fsync,1477 #endif1478     .llseek =   default_llseek,1479 };
//注册fb_info对象时的代码过程1714 int1715 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设备的计数加11590     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 int1405 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有没有实现相应的功能函数,如有实现,则直接调用,如没有实现,则用里面写好的功能代码来代替.

阅读全文
0 0
原创粉丝点击