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

紧接上一博文 ,实现一个最基本功能的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有没有实现相应的功能函数,如有实现,则直接调用,如没有实现,则用里面写好的功能代码来代替.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值