记录kernel lcd驱动 register_framebuffer中的一个bug

linux驱动 专栏收录该内容
99 篇文章 2 订阅

/**
    向核心层注册一个framebuffer设备
*/
int register_framebuffer(struct fb_info * fb_info)
{
    /**
        设备的次设备号
    */
    int i;  
    /**
        struct fb_event
        {
            struct fb_info *info;
            void *data;
        };
    */
    struct fb_event event;

/*
    struct fb_videomode {
        const char *name;   
        u32 refresh;        
        u32 xres;       X方向上的分辨率
        u32 yres;       Y方向上的分辨率
        u32 pixclock;   像素时钟

        一些时序参数
        u32 left_margin;
        u32 right_margin;
        u32 upper_margin;
        u32 lower_margin;
        ....
    };

    下面会在这个函数中 fb_var_to_videomode() 进行初始化 
*/
    struct fb_videomode mode;

    /**
        FB_MAX : 32
        每注册一个framebuffer设备,num_registered_fb就递增一次
        下面会有num_registered_fb++
        只能注册32个设备
        /dev/fb0  /dev/fb1  /dev/fb2 ..... /dev/fb31

        struct fb_info *registered_fb[32];
    */
    if (num_registered_fb = FB_MAX)
    {
        return -ENXIO;
    }
    for ( i = 0; i < FB_MAX; i++)
    {
        if (!registreed_fb[i])
        {
            continue;
        }
        if (registered_fb[i]->flags & FBINFO_MISC_FIRMWARE) {
            if (fb_do_apertures_overlap(registered_fb[i], fb_info)) {
                printk(KERN_ERR "fb: conflicting fb hw usage "
                       "%s vs %s - removing generic driver\n",
                       fb_info->fix.id,
                       registered_fb[i]->fix.id);
                unregister_framebuffer(registered_fb[i]);
                break;
            }
        }
    }
    //注册一个framebuffer设备就递增一次
    num_registered_fb++;
    /**
        从registered_fb[]中找到第一个空缺项
        下面会把设置好的fb_info放到里面去
        这个空缺项目的索引值 i 就是设备的次设备号

    */
    for (i = 0 ; i < FB_MAX; i++)
    {
        if (!registered_fb[i])
        {
            break;
        }
    }

    /**
        【这一步很重要】。
        把设备的次设备号赋给了node,即把存放这个fb_info的数组索引值赋值给了node

        像open()函数:
        static int fb_open(struct inode *inode, struct file *file)
        {
            int fbidx = iminor(inode);//获取次设备号
            struct fb_info *info;
            ...
            info = registered_fb[fbidx];//根据次设备号从registered_fb[]获取对应的fb_info    
        }
    */
    fb_info->node = i;
    /*
        初始化互斥锁
    */
    mutex_init(&fb_info->lock);
    mutex_init(&fb_info->mm_lock);

    /**
        在类下创建设备。
        创建类是再fbmem_init()函数中创建的。
        static int __init fbmem_init(void)
        {
            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;
        }
    */
    fb_info->dev = device_create(fb_class,fb_info->device,MKDEV(FB_MAJOR,i),NULL,"fb%d",i);
    if (IS_ERR(fb_info->dev))
    {
        printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
        fb_info->dev = NULL;
    }else{
        /**
            会调用device_create_file()来创建设备文件
        */
        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_videomode 进行初始化赋值
        mode->name = NULL;
        mode->xres = var->xres;
        mode->yres = var->yres;
        mode->pixclock = var->pixclock;
        mode->hsync_len = var->hsync_len;
        mode->vsync_len = var->vsync_len;

        其中有一项很重要
        if (!var->pixclock)
        {
            return; 
        }

        pixclock = PICOS2KHZ(var->pixclock) * 1000;
        ....
    */
    fb_var_to_videomode(&mode, &fb_info->var);

    /**
        把设备驱动层设置好的fb_info放入到registered_fb[]数组中
    */
    registered_fb[i] = fb_info;

    /**
        将fb_videomode添加到modelist链表上
    */
    fb_add_videomode(&mode, &fb_info->modelist);

    /**
        把设备驱动层设置好的fb_info放入到registered_fb[]数组中
        这边没有使用互斥机制来保护临界区。
        而且在调用register_framebuffer的函数在调用register_framebuffer
        的时候,也没有进行加锁
        但是在3.0以上的内核中 改进了这一点
        一下是 3.0内核中的代码

        int register_framebuffer(struct fb_info *fb_info)
        {
            int ret;

            mutex_lock(&registration_lock);
            ret = do_register_framebuffer(fb_info)
            {
                ...
                    registered_fb[i] = fb_info;
                ...
            }
            mutex_unlock(&registration_lock);

            return ret;
        }

    */
    registered_fb[i] = fb_info;
    /**
        将fb_info赋值给evdev中的info
    */
    event.info = fb_info;

    /**
        这个函数 
            1 : 是判断fb_info有没有提供fbops,
            2 : 封装了mutex_lock,用来对临界区进行互斥保护

        它用一个互斥锁.
        可能会问:
        临界区这么短小,为什么不用自旋锁。
        而且临界区中没有能引起阻塞的代码呀

        为什么不一开始 在函数的开头 就判断有没有提供呢,
        int lock_fb_info(struct fb_info *info)
        {
            mutex_lock(&info->lock);
            if (!info->fbops) {
                mutex_unlock(&info->lock);
                return 0;
            }
            return 1;
        }
        这是因为:
        它最终要锁定的临界区是这个:

        fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);

        lock_fb_info 要锁的是 这个临界区。所以 不适合使用自旋锁。
    */
    if (!lock_fb_info(fb_info))
    {
        return -ENODEV;
    }

    fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);

    /**
        mutex_unlock(&info->lock);
    */
    unlock_fb_info(fb_info);

    return 0;

}

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论
请先登录 后发表评论~
©️2021 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页

打赏作者

leesagacious

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值