Framebuffer 驅動學習總結(二)---- Framebuffer模塊初始化


---恢復內容開始---

Framebuffer模塊初始化過程:--driver\video\fbmem.c

1、  初始化Framebuffer

FrameBuffer驅動是以模塊的形式注冊到系統中,在模塊初始化時,創建FrameBuffer對應的設備文件及proc文件,並注冊FrameBuffer設備操作接口函數fb_fops。

static int __init  fbmem_init(void)
{
    proc_create("fb", 0, NULL, &fb_proc_fops);///向 proc 文件系統報告驅動狀態和參數
    if (register_chrdev(FB_MAJOR,"fb",&fb_fops))///注冊字符設備驅動,主設備號是29 
        printk("unable to get major %d for fb devs\n", FB_MAJOR);
    fb_class = class_create(THIS_MODULE, "graphics");///創建 /sys/class/graphics 設備類,配合 mdev生成設備文件
    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;
}

Framebuffer作為一個子系統,在fbmem_init中通過register_chrdev接口向系統注冊一個主設備號位29的字符設備驅動。通過class_create創建graphics設備類,配合mdev機制生成供用戶訪問的設備文件(位於/dev目錄)。

2、 Framebuffer設備驅動的接口集fb_fops的定義為:

static const struct file_operations fb_fops = {
    .owner =    THIS_MODULE,
    .read =        fb_read,
    .write =    fb_write,//二次拷貝
    .unlocked_ioctl = fb_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = fb_compat_ioctl,
#endif
    .mmap =        fb_mmap,///映射,一次拷貝
    .open =        fb_open,
    .release =    fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA
    .get_unmapped_area = get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO
    .fsync =    fb_deferred_io_fsync,
#endif
    .llseek =    default_llseek,
};

在linux設備驅動中,所有的顯示緩存設備均由framebuffer子系統內部管理,即linux設備驅動框架只認識一個主設備號為29的framebuffer設備。應用層所有針對顯示緩存(最多32個)的訪問均會推送給fb_fops進行進一步分發操作。

 

3、 注冊Framebuffer

linux 提供了register_framebuffer()和unregister_framebuffer()函數分別作為注冊和注銷幀緩沖設備,這兩個函數都接受fb_info指針為參數,原型為:

int register_framebuffer(struct fb_info  *fb_info);

int Unregister_framebuffer(struct fb_info  *fb_info);

對於register_framebuffer()而言,如果注冊的幀緩沖設備超過了FB_MAX(目前定義為32),則返回為-ENXIO,注冊成功則返回為0。

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

    mutex_lock(&registration_lock);
    ret = do_register_framebuffer(fb_info);
    mutex_unlock(&registration_lock);

    return ret;
}
EXPORT_SYMBOL(register_framebuffer);// 在內核的啟動過程會被調用,以便執行注冊幀緩沖區硬件設備的操作
static int do_register_framebuffer(struct fb_info *fb_info)
{
    int i, ret;
    struct fb_event event;
    struct fb_videomode mode;

    if (fb_check_foreignness(fb_info))
        return -ENOSYS;

    ret = do_remove_conflicting_framebuffers(fb_info->apertures,
                         fb_info->fix.id,
                         fb_is_primary_device(fb_info));
    if (ret)
        return ret;

    if (num_registered_fb == FB_MAX)//如果注冊的幀緩沖設備超過了FB_MAX(目前定義為32),則返回為-ENXIO,注冊成功則返回為0。
        return -ENXIO;

    num_registered_fb++;///已經注冊了的幀緩沖區硬件設備個數
    for (i = 0 ; i < FB_MAX; i++)
        if (!registered_fb[i])///registered_fb[i]保存所有已經注冊了的幀緩沖區硬件設備
            break;
    fb_info->node = i;
    atomic_set(&fb_info->count, 1);
    mutex_init(&fb_info->lock);
    mutex_init(&fb_info->mm_lock);

    fb_info->dev = device_create(fb_class, fb_info->device,///在/sys/grapics/下創建 fbx設備,用於設備文件的創建
                     MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
    if (IS_ERR(fb_info->dev)) {
        /* Not fatal */
        printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
        fb_info->dev = NULL;
    } else
        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);

    if (fb_info->skip_vt_switch)
        pm_vt_switch_required(fb_info->dev, false);
    else
        pm_vt_switch_required(fb_info->dev, true);

    fb_var_to_videomode(&mode, &fb_info->var);
    fb_add_videomode(&mode, &fb_info->modelist);
    registered_fb[i] = fb_info;

    event.info = fb_info;
    console_lock();
    if (!lock_fb_info(fb_info)) {
        console_unlock();
        return -ENODEV;
    }

    fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event);///通知幀緩沖區控制台,有一個新的幀緩沖區設備被注冊到內核中來了
    unlock_fb_info(fb_info);
    console_unlock();
    return 0;
}

每個從設備都需要傳遞一個fb_info的數據結構指針,其即代表單個顯示緩存設備。從中,可以看到fb_info最終會存儲到全局數組struct fb_info*registered_fb[FB_MAX]中,FB_MAX是32,從這里我們也可以看出,framebuffer最多支持32個從設備。另外,每個從設備注冊還會在/sys/class/graphics/設備類中創建一個設備,最終由mdev在/dev/目錄中生成對應的設備文件。假設M個從設備調用register_framebuffer接口,即會在/dev中生成M個設備文件,如/dev/fb0、/dev/fb1、/dev/fb2等等。這M個設備的主設備號都是29,從設備則是0、1、2等等。

     每一個被注冊的幀緩沖區硬件設備在/dev/graphics目錄下都有一個對應的設備文件fb<minor>,其中,<minor>表示一個從設備號。例如,第一個被注冊的幀緩沖區硬件設備在/dev/graphics目錄下都有一個對應的設備文件fb0。用戶空間的應用程序通過這個設備文件就可以操作幀緩沖區硬件設備了,即將要顯示的畫面渲染到幀緩沖區硬件設備上去。

 幀緩沖區控制台在內核中對應的驅動程序模塊為fbcon:  (drivers\video\console\Fbcon.c)

初始化:

static struct notifier_block fbcon_event_notifier = {
    .notifier_call    = fbcon_event_notify,
};
...... static int __init fb_console_init(void)//幀緩沖區控制台初始化
{
    int i;

    console_lock();
    fb_register_client(&fbcon_event_notifier);//調用fb_register_client來監聽幀緩沖區硬件設備的注冊事件,fbcon_event_notifier--->實現
    fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL,
                     "fbcon");

    if (IS_ERR(fbcon_device)) {
        printk(KERN_WARNING "Unable to create device "
               "for fbcon; errno = %ld\n",
               PTR_ERR(fbcon_device));
        fbcon_device = NULL;
    } else
        fbcon_init_device();

    for (i = 0; i < MAX_NR_CONSOLES; i++)
        con2fb_map[i] = -1;

    console_unlock();
    fbcon_start();
    return 0;
}

這個函數除了會調用函數device_create來創建一個類別為graphics的設備fbcon之外,還會調用函數fb_register_client來監聽幀緩沖區硬件設備的注冊事件,這是由函數fbcon_event_notify來實現的,如下所示:

static int fbcon_event_notify(struct notifier_block *self,
                  unsigned long action, void *data)
{
    struct fb_event *event = data;
    struct fb_info *info = event->info;
    struct fb_videomode *mode;
    struct fb_con2fbmap *con2fb;
    struct fb_blit_caps *caps;
    int idx, ret = 0;

    switch(action) {
        
        ......

    case FB_EVENT_FB_REGISTERED:
        ret = fbcon_fb_registered(info);///---> 幀緩沖區硬件設備的注冊事件最終是由函數fbcon_fb_registered來處理的
        break;

        .......
    }
done:
    return ret;
}        

幀緩沖區硬件設備的注冊事件最終是由函數fbcon_fb_registered來處理的,它的實現如下所示:

static int fbcon_fb_registered(struct fb_info *info)
{
    int ret = 0, i, idx;

    idx = info->node;
    fbcon_select_primary(info);///檢查當前注冊的幀緩沖區硬件設備是否是一個主幀緩沖區硬件設備,如果是的話,那么就將它的信息記錄下來

    if (info_idx == -1) {///如果是的話,那么就將它的信息記錄下來
        for (i = first_fb_vc; i <= last_fb_vc; i++) {
            if (con2fb_map_boot[i] == idx) {
                info_idx = idx;
                break;
            }
        }

        if (info_idx != -1)
            ret = do_fbcon_takeover(1);
    } else {
        for (i = first_fb_vc; i <= last_fb_vc; i++) {
            if (con2fb_map_boot[i] == idx)
                set_con2fb_map(i, idx, 0);
        }
    }

    return ret;
}

 函數fbcon_select_primary用來檢查當前注冊的幀緩沖區硬件設備是否是一個主幀緩沖區硬件設備。如果是的話,那么就將它的信息記錄下來。這個函數只有當指定了CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY編譯選項時才有效,否則的話,它是一個空函數。

在Linux內核中,每一個控制台和每一個幀緩沖區硬件設備都有一個從0開始的編號,它們的初始對應關系保存在全局數組con2fb_map_boot中。控制台和幀緩沖區硬件設備的初始對應關系是可以通過設置內核啟動參數來初始化的。在模塊fbcon中,還有另外一個全局數組con2fb_map,也是用來映射控制台和幀緩沖區硬件設備的對應關系,不過它映射的是控制台和幀緩沖區硬件設備的實際對應關系。

 

 

---恢復內容結束---


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM