經過三年的發展, Android 已經從最初的智慧型手機領域,逐漸進...

download 經過三年的發展, Android 已經從最初的智慧型手機領域,逐漸進 …epaper.gotop.com.tw/pdf/ACL033500.pdf · 系統底層相關的應用程式,這將給開發者帶來大量的機會,尤其是系統層級應用

If you can't read please download the document

Transcript of 經過三年的發展, Android 已經從最初的智慧型手機領域,逐漸進...

  • 前 言

    經過三年的發展,Android 已經從最初的智慧型手機領域,逐漸進入教育、醫療、軍事、汽車、家居等重要領域。它一路披荊斬棘、攻城掠地,發展勢頭有目共睹,

    已經成為行動平台領域當之無愧的王者。目前,已有眾多設備開始選擇使用

    Android 系統,例如智慧型手機、智慧型電視、平板電腦、小筆電、MP3、MP4、智慧型相機等;相信在不久的未來,將會有更多採用 Android 系統的高科技產品進入我們的生活。這些設備將產生各式各樣的應用程式需求,尤其是與 Android系統底層相關的應用程式,這將給開發者帶來大量的機會,尤其是系統層級應用

    程式的開發工程師。

    Android 基於 Linux 核心,但並不是標準的 Linux。因為 Google 為了讓 Android更適合行動手持設備,對 Linux 核心進行了各種最佳化和增強,這些增強的部分也正是從事 Android 系統開發的嵌入式系統工程師所急需瞭解的內容;同時Android 的原始碼不僅複雜,而且程式碼數量龐大,各模組之間聯繫緊密。這讓大多數 Android 應用程式開發者不知從何處著手,他們都希望能夠有一本系統化且全面的,針對 Android 核心的架構和實作原理進行分析的書,而國內目前分析Android 底層實作的書籍甚少。因此,筆者對自身的實戰經驗進行了總結和整理,編寫了本書,希望能夠幫助眾多 Android 應用程式開發者更快、更深入地理解Android 各個部分的具體實作,從而為開發各種系統層級的應用程式做好準備。

    Android 的五層架構可以分為兩個部分,即系統和應用程式。本書主要分析Android 系統部分的實作,包括下面三層:Linux 核心層、硬體抽象層、系統執行階段程式庫層;其餘的上面兩層(包括應用程式框架層和應用程式層)則不在本

    書的範疇。如圖 1 所示:

  • Android 技術內幕-探索 Android 核心原理與系統開發

    iv

    圖 1 Android系統架構

    圖中「Linux 核心」(Linux Kernel)部分是本書第一部分的內容,主要分析 Android核心驅動程式的實作,包括驅動程式的系統架構、原理和實作。掌握這部分內容

    之後,讀者將能夠修改和編寫 Android 的各個設備驅動程式。緊接著上面則是「硬體抽象層」(Hardware Abstraction Layer,HAL),本書第 7 章透過大量篇幅深入分析了 Android 中各個模組的硬體抽象層實作,使讀者在掌握 Android 中既有的硬體設備介面實作的同時,能夠獨立編寫適合自己的硬體設備驅動程式的抽象

    層介面。圖中的「程式庫」(Libraries)部分即本書的第 6 章,分析了 Android的系統程式庫、執行階段程式庫和功能程式庫的具體實作,能讓讀者在理解

    Android 的各種功能的底層實作的同時,還能按照功能需求來進行擴充和最佳化。最後,圖中的「Android 執行階段」(Android Runtime)部分又分為 Dalvik虛擬機器和核心程式庫兩部分,分別在本書的第 8 章和第 9 章介紹,剖析了 Dalvik虛擬機器的架構與實作,以及 Android 核心程式庫和 API 的運作機制,使讀者能夠完成 Android 執行階段程式庫的移植和修改。

    1 一般而言,Android 系統在構架上分為 4 層,分別為:應用程式層、應用程式框架層、系統執行階段程式庫層和 Linux 核心層;為了使分析更加深入透徹,本書將系統執行階段程式庫層和 Linux 核心層之間與硬體及其驅動程式相關的內容單獨劃分為另一層—— 硬體抽象層。

  • 前 言

    v

    8-v

    本書的目標讀者

    本書主要分析了 Android 系統底層的架構與實作原理,從原始碼的取得和系統開發環境的建立,到 Android Kernel 的核心實作,再到硬體抽象層和 Android 執行階段程式庫等各個模組的細部實作,讓讀者可以從更深的層次去理解 Android 的系統架構,並對 Android 系統進行移植和二次開發。閱讀本書的一個必要條件是對 Linux 核心有一定的瞭解,因此本書非常適合以下人員閱讀:

    Android 系統開發 /移植工程師

    Android 驅動程式開發 /移植工程師

    Android 系統架構師

    嵌入式系統工程師

    如何閱讀本書

    在編寫本書之前,筆者收到很多《Android 應用程式開發揭秘》一書的讀者所寄來的郵件,他們都希望有一本能深入講解 Android 實作原理的書籍,因此筆者編寫了本書,旨在幫助眾多開發者晉級。如果是進行系統層級開發,非常建議閱讀

    本書。

    本書是以 Android 原始碼為基礎來進行分析的,因為原始碼內容很多,無法全部列出來,因此筆者建議在閱讀本書的同時,最好能對照查看 Android 的原始碼實作(本書的所有程式碼列表都指明了它在原始碼中的路徑,以方便大家查看);

    另外,本書中有多處標示為“注意”、“延伸學習”的內容,都是一些實戰經驗。

    最後,雖然 Android 的各部分聯繫緊密,但各個部分的講解都非常完整,大家仍然可以根據需要調整閱讀的順序。

  • 209

    隨著技術的不斷進步,Linux系統的拓撲結構變得越來越複雜,對智慧型電源管理、熱插拔等的要求也

    越來越高,於是Linux核心提供了統一的核心設備模型。Linux設備模型是一個複雜的資料結構,與設備模型的直接互動通常由匯流排層級的邏輯和其他核

    心子系統來完成。所以本章主要將分析如何在這個

    設備模型的基礎上實作特定的設備驅動程式。Linux系統將設備驅動程式分成三類型:字元設備、區塊

    設備、網路設備。這些內容大家可能已經很清楚

    5.1 顯示驅動程式(Framebuffer)

    了,這一章我們將目標放在Android和標準Linux設備驅動程式上,分析每個驅動程式的工作原理和實作

    機制。

    由於 Linux 是在保護模式下工作的,所以使用者模式處理程序無法像 DOS 那樣使用顯示卡 BIOS 裡提供的中斷呼叫來實作直接寫入螢幕,Linux 抽象出Framebuffer 這個設備來供使用者模式處理程序實作直接寫入螢幕,也就是我們所說的顯示驅動程式。在嵌入式系統的 SOC 處理器(包括 Android 系統)中,Framebuffer 通常被作為 LCD 控制器或者其他顯示設備驅動程式。LCD(Liquid

    1 如果不清楚,建議查看 Linux 設備驅動程式的相關資料,例如 sysfs 檔案系統和 kobject 核心物件等都是我們在實作驅動程式時經常會使用到的,前面在分析 Android 的專屬驅動程式時也曾提及它們。

    驅動程式的工作原理及實作機制

    本章主要內容

    Android 系統有哪些設備驅動程式?

    在移植設備驅動程式時需要注意

    什麼?

    Android 的各種設備驅動程式的工作

    機制是什麼?

    這些設備驅動程式的實作原理是什

    麼?

    如何操作這些設備驅動程式?

    Android 使用了哪些標準 Linux 設備

    驅動程式?

    Android 中哪些是字元設備,哪些是

    網路設備?

  • Android 技術內幕-探索 Android 核心原理與系統開發

    210

    Crystal Display,液晶顯示器)是一種數位顯示技術,可以透過液晶和彩色濾波器來過濾光源,並在面板上產生影像,其因具有體積小、輻射低、功耗低等特點

    而被廣泛應用。液晶本身不能發光,只能透過對光線的穿透和反射來實現顯示功

    能,因此所有的液晶顯示器都會有背光來照亮液晶,這樣我們才能看到螢幕上的

    內容。常見的背光有 EL、CCFL 和 LED 等。通常,LCD 中都整合了背光模組。

    5.1.1 Framebuffer 工作原理

    Framebuffer 的機制就是模仿顯示卡的功能,直接透過 Framebuffer 的讀寫就能對顯示卡記憶體進行操作。對於使用者而言,Framebuffer 和/dev 下面的其他設備沒有什麼區別,使用者可以將 Framebuffer 看成是顯示卡記憶體的一個映像,將其映射到處理程序位址空間之後,就可以直接執行讀寫操作,寫入操作可以立即反映在

    螢幕上。這種操作是抽象的和統一的,使用者不必關心實體顯示卡記憶體的位置

    和換頁機制等具體細節,這些都是由 Framebuffer 設備驅動程式來完成的,下面我們將針對這個設備驅動程式的實作進行介紹。Framebuffer 本身不具備任何資料運算的能力,它就好比是一個暫時存放水的水池,CPU 將運算後的結果放到這個水池,水池再將結果流到顯示器,中間不會對資料做處理。應用程式也可以直接讀

    寫這個水池的內容。在這種機制下,儘管 Framebuffer 需要真正的顯示卡驅動程式的支援,但是所有顯示任務都由 CPU 完成,因此 CPU 的負擔很重。

    PCI 設備可以將自己的控制暫存器映射到實體記憶體空間,然後對這些控制暫存器的存取就變成了對實體記憶體的存取。因此,這些暫存器又被稱為 memio。一旦被映射到實體記憶體空間,Linux 的普通處理程序就可以透過 mmap 將這些記憶體的 I/O 映射到處理程序位址空間,這樣就可以直接存取這些暫存器了。畫面緩衝區設備(Framebuffer)屬於字元設備,它採用了“檔案層—驅動程式層”的介面方式。Linux 為畫面緩衝區設備定義的驅動程式層介面為 fb_info 結構。在檔案階層上,使用者呼叫 file_operations 結構的函式操作,其中,間接呼叫 fb_ops結構的函式來操作硬體。在向核心註冊 FB 設備的時候,也註冊了 struct fb_ops的指標。當開啟 FB 設備時,先呼叫 fb_drivers[]的 xxxfb_init()來初始化設備。

    通常,每個系統都可以擁有多個顯示設備,它們用“ /dev/fbX”來表示,其中的X 表示設備的序號。例如,第一個被註冊的 Framebuffer 的 minor 等於 0,第二個被註冊的 Framebuffer 的 minor 等於 1,那麼就應該分別寫成 /dev/fb0 和 /dev/fb1,以此類推。

  • 驅動程式的工作原理及實作機制

    211

    8-211

    1 2 3 4 5 6 7 8 9

    5.1.2 Framebuffer 架構

    根據我們對 Framebuffer 原理的瞭解,首先,在使用者空間中可以透過 iotcl 和 mmap 等檔案系統的介面對“ /dev/fbX”進行操作;然後,透過字元設備驅動程式呼叫 Framebuffer 驅動程式,該驅動程式又會呼叫具體的 Framebuffer 驅動程式(struct fb_info);最後,由 fb_info 操作不同的硬體顯示設備。Framebuffer 的註冊流程則正好與此相反,圖 5-1 展示了 Framebuffer 的系統架構。

    圖 5-1 Framebuffer 系統架構

    因為不同的顯示晶片具有不同的加速能力,對 memio 的使用和定義也各不相同,這時,就需要針對加速晶片的不同類型來實作不同的加速功能。例如,大多數晶

    片都提供了對矩形填充的硬體加速支援,但不同的晶片實現的方式不同,這時,

    就需要針對不同的晶片類型編寫不同的用來完成填充矩形的函式。

    5.1.3 Framebuffer 驅動程式的實作

    對 Framebuffer 的原理和架構有所瞭解之後,現在我們來分析該驅動程式的具體實作過程。Framebuffer 對應的原始碼檔案在 linux/drivers/video/目錄下,抽象設備檔案為 fbcon.c,在這個目錄下還有與各種顯示卡驅動程式相關的原始碼檔案。Framebuffer 設備驅動程式基於如下三個檔案:

    linux/include/linux/fb.h

    linux/drivers/video/fbmem.c

    linux/drivers/video/xxxfb.c

    其中,fb.h 主要定義了一些結構和巨集;fbmem.c 實作了設備初始化、卸載和檔案操作介面;xxxfb.c 為自己新增的設備驅動程式檔案(如 struct fb_info)實作了入口點函式 xxxfb_init、xxxfb_setup。下面我們先分析 fb.h 中定義的一些資料結

  • Android 技術內幕-探索 Android 核心原理與系統開發

    212

    構。首先,struct fb_var_screeninfo 描述了顯示卡的特性,這通常是由使用者設定的,具體定義如程式碼列表 5-1 所示。

    程式碼列表 5-1 struct fb_var_screeninfo 的定義

    struct fb_var_screeninfo { __u32 xres; /* visible resolution */ __u32 yres; __u32 xres_virtual; /* virtual resolution */ __u32 yres_virtual; __u32 xoffset; /* offset from virtual to visible */ __u32 yoffset; /* resolution */ __u32 bits_per_pixel; /* guess what */ __u32 grayscale; /* != 0 Graylevels instead of colors */ struct fb_bitfield red; /* bitfield in fb mem if true color, */ struct fb_bitfield green; /* else only length is significant */ struct fb_bitfield blue; struct fb_bitfield transp; /* transparency */ __u32 nonstd; /* != 0 Non standard pixel format */ __u32 activate; /* see FB_ACTIVATE_**/ __u32 height; /* height of picture in mm */ __u32 width; /* width of picture in mm */ __u32 accel_flags; /* (OBSOLETE) see fb_info.flags */ /* Timing: All values in pixclocks, except pixclock (of course) */ __u32 pixclock; /* pixel clock in ps (pico seconds) */ __u32 left_margin; /* time from sync to picture */ __u32 right_margin; /* time from picture to sync */ __u32 upper_margin; /* time from sync to picture */ __u32 lower_margin; __u32 hsync_len; /* length of horizontal sync */ __u32 vsync_len; /* length of vertical sync */ __u32 sync; /* see FB_SYNC_* */ __u32 vmode; /* see FB_VMODE_* */ __u32 rotate; /* angle we rotate counter clockwise */ __u32 reserved[5]; /* Reserved for future compatibility */ }; /*在像素所占位元組內,各個色彩的位元配置,例如 RGB=888、565、555等*/ struct fb_bitfield { __u32 offset; /* beginning of bitfield */ __u32 length; /* length of bitfield */ __u32 msb_right; /* != 0 : Most significant bit is */ /* right */ };

    該結構成員用來記錄使用者可修改的顯示控制器參數,包括螢幕解析度、每個像

    素點的位元數、fb_var_screeninfo 中的 xres 定義螢幕一列有多少個點、yres 定義螢幕一行有多少個點、fb_bitfield 結構描述每一個像素顯示緩衝區的組織方式,以及位元欄位長度和 MSB 指示等。bits_per_pixel 定義每個點用多少個位元組來

  • 驅動程式的工作原理及實作機制

    213

    8-213

    1 2 3 4 5 6 7 8 9

    表示,將 bits_per_pixel 設為 1、2、4、8、16、24 或 32 可以改變色彩深度(color depth)。不是所有的顯示卡和驅動程式都支援全部的色彩深度。當色彩深度改變時,驅動程式將自動改變 fb_bitfields。如果 bits_per_pixel 小於 8,則 fb_bitfields將無定義,色彩映射將被啟用。fb_var_screeninfo 結尾的頻率設定在選擇一個新的解析度時用來設定顯示的頻率。

    struct fb_fix_screeninfo 結構定義了顯示卡的硬體功能,它們是不能修改的,定義如程式碼列表 5-2 所示。

    程式碼列表 5-2 struct fb_fix_screeninfo 的定義

    struct fb_fix_screeninfo { char id[16]; unsigned long smem_start; __u32 smem_len; __u32 type; __u32 type_aux; __u32 visual; __u16 xpanstep; __u16 ypanstep; __u16 ywrapstep; __u32 line_length; unsigned long mmio_start; __u32 mmio_len; __u32 accel; __u16 reserved[3]; };

    該結構比 struct fb_var_screeninfo 簡單,其中記錄了使用者不能修改的顯示控制器的參數,例如螢幕緩衝區的實體位址和長度。在對畫面緩衝區設備執行映射操

    作時,就是從 fb_fix_screeninfo 中取得緩衝區的實體位址,這些資料成員都需要在驅動程式初始化時設定。在這裡,非常重要的欄位是 smem_len 和 line_length。smem_len 欄位告訴我們 Framebuffer 設備的大小,而 line_length 欄位則告訴我們指標應該前進多少位元組以得到下一列的資料。struct fb_cmap 描述與設備無關的色彩映射資訊,其定義如程式碼列表 5-3 所示。

    程式碼列表 5-3 struct fb_cmap 的定義

    struct fb_cmap { __u32 start; __u32 len; __u16 *red; __u16 *green; __u16 *blue; __u16 *transp; /* 透明度,允許為 NULL */ };

  • Android 技術內幕-探索 Android 核心原理與系統開發

    214

    可以透過 FBIOGETCMAP 和 FBIOPUTCMAP 對應的 ioctl 操作,設定或取得色彩映射資訊,稍後在分析 struct fb_ops 結構時會介紹其設定(fb_set_cmap)和取得

    (fb_get_cmap)色彩表的操作方法。

    畫面緩衝區設備最關鍵的一個資料結構是 fb_info,FBI 中包括了關於畫面緩衝區設備的屬性和操作的完整描述,每一個畫面緩衝區設備都必須對應到一個 FBI,其定義如程式碼列表 5-4 所示。

    程式碼列表 5-4 struct fb_info 的定義

    struct fb_info { int node; int flags; struct mutex lock; /* Lock for open/release/ioctl funcs */ struct fb_var_screeninfo var; /* Current var */ struct fb_fix_screeninfo fix; /* Current fix */ struct fb_monspecs monspecs; /* Current Monitor specs */ struct work_struct queue; /* Framebuffer event queue */ struct fb_pixmap pixmap; /* Image hardware mapper */ struct fb_pixmap sprite; /* Cursor hardware mapper */ struct fb_cmap cmap; /* Current cmap */ struct list_head modelist; /* mode list */ struct fb_videomode *mode; /* current mode */ #ifdef CONFIG_FB_BACKLIGHT /* assigned backlight device */ /* set before framebuffer registration, remove after unregister */ struct backlight_device *bl_dev; /* Backlight level curve */ struct mutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS]; #endif #ifdef CONFIG_FB_DEFERRED_IO struct delayed_work deferred_work; struct fb_deferred_io *fbdefio; #endif struct fb_ops *fbops; //畫面緩衝區操作 struct device *device; /* This is the parent */ struct device *dev; /* This is this fb device */ int class_flag; /* private sysfs flags */ #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */ #endif char __iomem *screen_base; /* Virtual address */ unsigned long screen_size; /* Amount of ioremapped VRAM or 0 */ void *pseudo_palette; /* Fake palette of 16 colors */ #define FBINFO_STATE_RUNNING 0 #define FBINFO_STATE_SUSPENDED 1 u32 state; /* Hardware state i.e suspend */ void *fbcon_par; /* fbcon use-only private area */

  • 驅動程式的工作原理及實作機制

    215

    8-215

    1 2 3 4 5 6 7 8 9

    /* From here on everything is device dependent */ void *par; };

    其中 node 成員欄位識別了特定的 Framebuffer,實際上也就是一個 Framebuffer設備的次設備編號;fb_info 中記錄了畫面緩衝區設備的全部資訊,包括設備的設定參數、狀態以及操作函式指標。每一個畫面緩衝區設備都必須對應到一個

    fb_info 結構,由於 fb_info 定義了目前顯示卡 Framebuffer 設備的狀態,一張顯示卡可能有兩個 Framebuffer,因此在這種情況下就需要兩個 fb_info 結構。這個結構是核心空間中唯一可見的結構。在這個結構中有一個 fb_ops 指標,指向驅動設備工作所需的函式集。關於 struct fb_ops 的定義,如程式碼列表 5-5 所示。

    程式碼列表 5-5 struct fb_ops 的定義

    struct fb_ops { struct module *owner; int (*fb_open)(struct fb_info *info, int user); int (*fb_release)(struct fb_info *info, int user); 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_check_var)(struct fb_var_screeninfo *var, struct fb_info *info); int (*fb_set_par)(struct fb_info *info); int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green, unsigned

    blue, unsigned transp, struct fb_info *info); int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info); int (*fb_blank)(int blank, struct fb_info *info); int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info); void (*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect); void (*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region); void (*fb_imageblit) (struct fb_info *info, const struct fb_image *image); int (*fb_cursor) (struct fb_info *info, struct fb_cursor *cursor); void (*fb_rotate)(struct fb_info *info, int angle); int (*fb_sync)(struct fb_info *info); int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg); int (*fb_compat_ioctl)(struct fb_info *info, unsigned cmd, unsigned long arg); int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma); void (*fb_save_state)(struct fb_info *info); void (*fb_restore_state)(struct fb_info *info); void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,struct fb_var_screeninfo *var); };

    其中,成員函式 fb_check_var 用來檢查可以修改的螢幕參數並將其調整到合適的數值,而 fb_set_par 則用來讓使用者設定的螢幕參數在硬體上生效。使用者應用程式層可以使用 ioctl 系統呼叫來操作設備,這個結構就是用來支援 ioctl 的這些操作。

  • Android 技術內幕-探索 Android 核心原理與系統開發

    216

    注意

    這裡的 fb_ops 結構與之前的 file_operations 結構不同。 fb_ops 是底層操作的抽象,而 file_operations 是提供給上層系統呼叫的介面,可以直接呼叫,定義在 kernel/include/linux/fs.h 中,這裡我們就不再貼出程式碼了。

    對於 LCD 來說,該結構的填充位於 kernel/drivers/vedio/fbmem.c 中,定義如程式碼列表 5-6 所示。

    程式碼列表 5-6 struct file_operations 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 };

    file_operations 是提供給上層系統呼叫的介面,大家可以看出,這裡提供了一些包括 read、write、mmap、 ioctl 在內的常見操作。

    使用者應用程式透過 ioctl 系統呼叫來操作硬體,fb_ops 中的函式就是用來支援這些操作。 ioctl 系統呼叫在檔案 fbmem.c 中實作,透過觀察可以發現 ioctl 命令與 fb_ops 中函式的關係如下:

    FBIOGET_VSCREENINFO fb_get_var

    FBIOPUT_VSCREENINFO fb_set_var

    FBIOGET_FSCREENINFO fb_get_fix

    FBIOPUTCMAP fb_set_cmap

    FBIOGETCMAP fb_get_cmap

    FBIOPAN_DISPLAY fb_pan_display

  • 驅動程式的工作原理及實作機制

    217

    8-217

    1 2 3 4 5 6 7 8 9

    如果我們定義了 fb_XXX_XXX 方法,使用者程式就可以使用 FBIOXXXX 巨集的ioctl 來操作硬體。fbmem.c 實作了 Linux Framebuffer 的中間層,如圖 5-2 所示。任何一個 Framebuffer 驅動程式在系統初始化時都必須向 fbmem.c 註冊,圖 5-3描述了它們之間的關係。

    圖 5-2 Framebuffer 的中間層 fbmem

    圖 5-3 Framebuffer 關係流程

    在分析 fbmem.c 的操作之前先來瞭解以下兩個變數的意義:

    struct fb_info *registered_fb[FB_MAX]; int num_registered_fb;

    核心

    register_chrdev

    fb_open

    return return

    xxx fb open

    register_framebuffer Fbmein.c 使用者驅動程式

    把驅動程式傳過來的 fb_info 放入 register ed_fb[]陣列

    使用者呼叫 open 函式,核心呼叫Fromebuffer 的 open

    根據 inode,尋找 register ed_fb[]陣列, 呼叫真正驅動程式的 open 函式

    Linux 核心

    Framebuffer 中間層 (fbmem.c)

    操作 fb_open fb_write fb_mmap …………

    struct fb_info xregistered_fb[FB_MAX_]=

    設備 1 fb_info 設備 2 fb_info

    …………

    設備 n fb_info 設備 n 驅動程式

    設備 2 驅動程式

    設備 1 驅動程式

    intnode; //次設備編號

    struct fb_info

    structfb_var_screeninfo var; //可設定的螢幕資訊 structfb_fix_screeninfo fix; //固定的螢幕資訊 structfb_ops *fbops; //對應的 file_ops …………

  • Android 技術內幕-探索 Android 核心原理與系統開發

    218

    這兩個變數記錄了所有 fb_info 結構的實體,fb_info 結構描述顯示卡的目前狀態,所有設備對應的 fb_info 結構都儲存在這個陣列中。當一個 Framebuffer 設備驅動程式向系統註冊自己時,其對應的 fb_info 結構就會新增到這個結構中,同時 num_registered_fb 也會自動加 1。從圖 5-2 中我們也看到了 fbmem.c 所實作的功能,下面就來分析 fbmem.c 的具體實作。同樣地,首先從初始化和結束模組開始,fbmem.c 的初始化和結束操作的實作如程式碼列表 5-7 所示。

    程式碼列表 5-7 fbmem_init 和 fbmem_exit 的實作

    //初始化 static int __init fbmem_init(void) { proc_create("fb", 0, NULL, &fb_proc_fops); if (register_chrdev(FB_MAJOR,"fb",&fb_fops)) printk("unable to get major %d for fb devs\n", FB_MAJOR); 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; } #ifdef MODULE module_init(fbmem_init); //結束和卸載 static void __exit fbmem_exit(void) { remove_proc_entry("fb", NULL); class_destroy(fb_class); unregister_chrdev(FB_MAJOR, "fb"); } module_exit(fbmem_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Framebuffer base"); #else subsys_initcall(fbmem_init); #endif

    該 部 分 實 作 很 簡 單 , 在 初 始 化 函 式 時 判 斷 : 是 使 用 subsys_initcall 還是module_init。由於 LCD 控制器經常被整合在 SOC 上,作為一個獨立的硬體模組而存在著(成為 platform_device),因此 LCD 驅動程式中經常包含平台驅動程式。這樣,在畫面緩衝區設備驅動程式的模組載入函式中完成的工作就是註冊平

    台驅動程式,至於初始化 FBI 結構中的固定參數和可變參數、LCD 控制器硬體的初始化、申請畫面緩衝區設備的顯示緩衝區空間,以及註冊畫面緩衝區設備的

    工作,則移交到平台驅動程式的 prob 函式中完成。從 fbmem_init 中可以看出,

  • 驅動程式的工作原理及實作機制

    219

    8-219

    1 2 3 4 5 6 7 8 9

    在註冊該字元設備時,透過 register_chrdev 註冊了 fb_fops 的操作介面,將這些設備檔案(也就是 /dev 目錄下的檔案)和 file_operation 關聯起來了。關於這些操作介面,我們在前幾章分析了很多,由於篇幅有限這裡就不再重複了。

    下面的重點在於 Framebuffer 驅動程式是如何註冊到 Linux Framebuffer 的中間層(fbmem.c)。仔細查看後會發現,fbmem.c 為我們提供了 register_framebuffer 和

    unregister_framebuffer 函式,專門用來註冊和卸載具體的 Framebuffer 驅動程式,註冊過程的實作如程式碼列表 5-8 所示。

    程式碼列表 5-8 register_framebuffer 的實作

    int register_framebuffer(struct fb_info *fb_info) { int i; struct fb_event event; struct fb_videomode mode; //檢查陣列是否已經滿了,即是否超過最大註冊數量 if (num_registered_fb == FB_MAX) return -ENXIO; if (fb_check_foreignness(fb_info)) return -ENOSYS; num_registered_fb++; for (i = 0 ; i < FB_MAX; i++) if (!registered_fb[i])//找到閒置的空間 break; fb_info→node = i; mutex_init(&fb_info→lock); fb_info→dev = device_create(fb_class, fb_info→device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i); //建立的設備節點的次設備編號就是該驅動程式資訊在registered_fb存放的位置,即陣列的下標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;

  • Android 技術內幕-探索 Android 核心原理與系統開發

    220

    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_var_to_videomode(&mode, &fb_info→var); fb_add_videomode(&mode, &fb_info→modelist); registered_fb[i] = fb_info; event.info = fb_info; fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); return 0; }

    我們可以透過呼叫 register_framebuffer 函式來註冊 Framebuffer 驅動程式。在這個過程中,設備驅動程式的資訊將會存放在名為 registered_fb 的陣列中,這個陣列定義為 struct fb_info *registered_ fb;它是類型為 fb_info 的陣列。另外,num_register_fb 存放了註冊過的設備數量;當 Framebuffer 驅動程式在進行註冊的時候,它將驅動程式的 fb_info 結構記錄到全域陣列 registered_fb 中,並動態建立設備節點以進行設備的初始化。卸載函式 unregister_framebuffer 則正好相反,這裡我們就不再貼出程式碼了。

    需要註冊的 Framebuffer 驅動程式的具體實作位於 linux/drivers/video/xxxfb.c,其中 xxx 表示具體的 LCD,這裡以 s3c2410fb.c 為例進行分析。s3c2410fb.c 中定義的 fb_ops 如程式碼列表 5-9 所示。

    程式碼列表 5-9 static struct fb_ops s3c2410fb_ops 的定義

    static struct fb_ops s3c2410fb_ops = { .owner = THIS_MODULE, .fb_check_var = s3c2410fb_check_var, .fb_set_par = s3c2410fb_set_par, .fb_blank = s3c2410fb_blank, .fb_setcolreg = s3c2410fb_setcolreg, .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, };

    該驅動程式的初始化函式和結束函式為 s3c2410fb_init 和 s3c2410fb_cleanup,其設備驅動程式的定義如程式碼列表 5-10 所示。

  • 驅動程式的工作原理及實作機制

    221

    8-221

    1 2 3 4 5 6 7 8 9

    程式碼列表 5-10 s3c2410fb_driver 的定義

    static struct platform_driver s3c2410fb_driver = { .probe = s3c2410fb_probe, .remove = s3c2410fb_remove, .suspend = s3c2410fb_suspend, .resume = s3c2410fb_resume, .driver = { .name = "s3c2410-lcd", .owner = THIS_MODULE, }, };

    和前幾章一樣,其中指定了設備名稱,以及 probe、remove、suspend、resume 等操作,該驅動程式在 probe 和 remove 中分別呼叫了 fbmem.c 所提供的register_framebuffer 和 unregister_framebuffer 函式,用來註冊和卸載該驅動程式。

    至此,我們完整地分析了整個 Framebuffer 的架構、原理和實作, Framebuffer架構可以描述為如圖 5-4 所示。

    使用者空間

    註冊 註銷

    硬體

    fb_info fb_info

    fb_info register_framebuffer unregister_framebuffer

    Fbmem.c

    xxxfb.c

    fb_ioctl fb_mmap fb_write fb_read

    file_operations

    fb_ops

    fb_set_par fb_check_var

    應用程式

    LCD 控制器

    圖 5-4 Framebuffer 架構

    5.1 顯示驅動程式(Framebuffer)5.1.1 Framebuffer工作原理

    前 言本書的目標讀者如何閱讀本書

    /ColorImageDict > /JPEG2000ColorACSImageDict > /JPEG2000ColorImageDict > /AntiAliasGrayImages false /CropGrayImages true /GrayImageMinResolution 300 /GrayImageMinResolutionPolicy /OK /DownsampleGrayImages true /GrayImageDownsampleType /Bicubic /GrayImageResolution 300 /GrayImageDepth -1 /GrayImageMinDownsampleDepth 2 /GrayImageDownsampleThreshold 1.50000 /EncodeGrayImages true /GrayImageFilter /DCTEncode /AutoFilterGrayImages true /GrayImageAutoFilterStrategy /JPEG /GrayACSImageDict > /GrayImageDict > /JPEG2000GrayACSImageDict > /JPEG2000GrayImageDict > /AntiAliasMonoImages false /CropMonoImages true /MonoImageMinResolution 1200 /MonoImageMinResolutionPolicy /OK /DownsampleMonoImages true /MonoImageDownsampleType /Bicubic /MonoImageResolution 1200 /MonoImageDepth -1 /MonoImageDownsampleThreshold 1.50000 /EncodeMonoImages true /MonoImageFilter /CCITTFaxEncode /MonoImageDict > /AllowPSXObjects false /CheckCompliance [ /None ] /PDFX1aCheck false /PDFX3Check false /PDFXCompliantPDFOnly false /PDFXNoTrimBoxError true /PDFXTrimBoxToMediaBoxOffset [ 0.00000 0.00000 0.00000 0.00000 ] /PDFXSetBleedBoxToMediaBox true /PDFXBleedBoxToTrimBoxOffset [ 0.00000 0.00000 0.00000 0.00000 ] /PDFXOutputIntentProfile () /PDFXOutputConditionIdentifier () /PDFXOutputCondition () /PDFXRegistryName () /PDFXTrapped /False

    /CreateJDFFile false /Description > /Namespace [ (Adobe) (Common) (1.0) ] /OtherNamespaces [ > /FormElements false /GenerateStructure false /IncludeBookmarks false /IncludeHyperlinks false /IncludeInteractive false /IncludeLayers false /IncludeProfiles false /MultimediaHandling /UseObjectSettings /Namespace [ (Adobe) (CreativeSuite) (2.0) ] /PDFXOutputIntentProfileSelector /DocumentCMYK /PreserveEditing true /UntaggedCMYKHandling /LeaveUntagged /UntaggedRGBHandling /UseDocumentProfile /UseDocumentBleed false >> ]>> setdistillerparams> setpagedevice