標籤:
---恢複內容開始---
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(®istration_lock); ret = do_register_framebuffer(fb_info); mutex_unlock(®istration_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,也是用來映射控制台和框架緩衝區硬體裝置的對應關係,不過它映射的是控制台和框架緩衝區硬體裝置的實際對應關係。
---恢複內容結束---
Framebuffer 驅動學習總結(二)---- Framebuffer模組初始化