1. 程式人生 > >Linux Framebuffer驅動剖析之一—軟體需求

Linux Framebuffer驅動剖析之一—軟體需求

嵌入式企鵝圈將以本文作為2015年的終結篇,以迴應第一篇《Linux字元裝置驅動剖析》。嵌入式企鵝圈一直專注於嵌入式Linux和物聯網IOT兩方面的原創技術分享,稍後會發布嵌入式企鵝圈的2015年的年終總結和2016年的分享計劃。

       本系列文章將分析Linux Framebuffer驅動的作用(需求)、框架、介面實現和使用。按筆者一直倡導的Linux學習理念—從軟體需求的角度去理解Linux,對於Linux各個子系統,我們首先要理解其軟體需求,從中自然會清楚其存在的價值和作用;接下來是理解子系統在Linux整個驅動框架中的層次、角色和如何互動;最後是理解驅動的介面如何實現軟體需求,明確介面如何在各種場景中使用。

一、Linux裝置驅動和裸裝置驅動的關係

理解framebuffer的軟體需求之前,我們先理解Linux裝置驅動和裸裝置驅動的關係:

例如,對於LCD液晶屏,其可以由三星研發的SOC S5PV210(Cortex A8 arm核)的多媒體硬體模組所支援。而對於具體某種LCD液晶屏,涉及到解析度、時延引數等不同,需要通過軟體來設定相應的SOC多媒體硬體暫存器,以達到控制顯示的目的。這個軟體設定就是SOC程式設計,其不管是SOC上執行的是Linux,還是Windows,或者是IOS,軟體設定的最終結果體現到暫存器上都是一樣的。一般地,嵌入式都是C語言開發,而高階處理器的暫存器是統一編址的,因此裸裝置驅動外圍裝置的C語言程式碼基本是一樣的。

在帶作業系統執行時,為了安全考慮,系統一般分為使用者態和核心態。那麼,SOC程式設計是硬體程式設計,只能在核心態完成,並且需要向用戶態程式提供一個介面以進行呼叫。對於不同的作業系統而言,從使用者態的介面開始到進行最終的SOC程式設計介面呼叫的過程中,會經過不同的軟體層次。對於Linux作業系統,裝置驅動的介面呼叫過程就是Linux裝置驅動框架所決定的。詳見《Linux字元裝置驅動剖析》、《Linux 裝置檔案的建立和mdev》、《匯流排、裝置和驅動》和《字元裝置驅動、裝置驅動模型、sysfs、平臺裝置驅動的關係》。

從上面分析可以看出,任何Linux裝置驅動都有兩個層次,一個是偏底層硬體的SOC暫存器程式設計,一個是偏上層應用的Linux子系統軟體介面,前者負責和硬體的互動,後者負責跟上層應用互動。Linux為了給使用者提供統一的程式設計介面,在所有的裝置驅動之上再架設一層公共介面層,如所有驅動都可以通過open、read、write來進行操作,其是Linux裝置驅動框架的組成部分。因此,一般地,Linux裝置驅動都有三個層次。

當然,各個子系統內部還會通過分層來解耦內部的需求和實現。

二、LinuxFramebuffer的軟體需求

Linux Framebuffer的需求就是驅動LCD屏顯示。所以其自然也有兩個層次,偏底層硬體的SOC暫存器程式設計和偏上層應用的寫屏介面。本文的重點是為了分析Linux framebuffer驅動的偏上層應用的介面實現,而不是闡述如何進行SOC程式設計,因為SOC程式設計是針對某個具體的SOC暫存器來進行的。

1.     LCD屏的驅動需求

SOC和LCD屏的連線示意圖如下:

1) SOC程式設計是為了支援多種不同的LCD屏,以使該SOC的應用場景最大化。因此SOC的多媒體Display模組需要考慮不同的螢幕解析度、點陣圖深度、行切換和幀切換的時延等等。這些引數的設定最終使得LCD控制器(硬體引擎)產生匹配的時鐘和資料輸出到LCD引腳上。這些設定的程式設計方式和其他字元裝置(如滑鼠、串列埠等)都是差不多的。所以,LCD的驅動需求是通過暫存器設定支援各種不同的LCD屏。

2)唯一有一點不同的就是,LCD屏驅動器內部有個大的fifo(跟解析度有關,可能是幾百K位元組,甚至M位元組級)。Fifo中存放LCD屏的顯示資料,LCD驅動器內部顯示電路會自動將FIFO的資料重新整理到LCD屏上。由於FIFIO很大,通過CPU寫匯流排的方式來將記憶體資料寫到fifo的方式是不可行的,這樣會加重CPU負擔。現代高階SOC處理器都使用DMA的方式,由DMA直接將記憶體資料搬到FIFO。DMA可以理解為一個專職搬運工,與CPU、GPU一樣是獨立工作的,只需要告訴它源地址、目的地址和長度就可以了。源地址就是實體記憶體地址,目的地址就是FIFO對映地址。DMA工作不經過MMU,所以它只認實體記憶體地址。

那麼,LCD的另一個驅動需求是CPU將使用者資料寫到DMA所認的實體記憶體地址上。CPU寫好使用者資料到實體記憶體地址上,顯示專用DMA就自動搬運到顯示FIFO上。

2.     LCD屏驅動的實現思路

從上面分析可以看出,LCD屏的裝置驅動可以是一個字元裝置驅動。第一個需求通過暫存器設定支援各種不同的LCD屏是很容易實現的。而第二個需求通過write介面也是很容易實現的。

       寫介面就是將使用者影象資料(0-3G程序虛擬空間buffer, 對應實際的實體記憶體地址1)拷貝到核心虛擬地址空間(對應實際的實體記憶體地址2)。

       一般地,我們在使用者程序中是申請一塊物理連續的記憶體塊(返回地址是0-3G的程序虛擬地址空間),並將多個影象資源資料(如文字,影象等)放到這個記憶體中。我們把這次資源資料拷貝到記憶體塊稱為一次拷貝。然後通過寫介面拷貝到實際的顯示實體記憶體,稱為二次拷貝。

       虛擬地址空間和實體地址空間的對映是通過MMU(記憶體管理單元)來進行對映和管理的。MMU機制請看《SoC嵌入式軟體架構設計之二:記憶體管理單元的軟、硬體協同設計》。簡單的理解就是程式執行的空間是4G虛擬地址空間,而實際的實體記憶體可能是1G記憶體,程式碼和資料是真正儲存在實際的實體記憶體上的。如何通過虛擬地址找到對應的實際實體記憶體地址就是MMU的作用。

3.     Framebuffer的軟體需求

從上面分析,影象資料顯示要經歷兩次拷貝。那麼,有沒有方法做到一次拷貝就可以顯示了呢?很好,Framebuffer就是利用MMU機制來實現一次拷貝即可顯示。

它的顯示示意圖是:

可見,當用戶影象資料buffer和核心虛擬地址空間buffer對應的都是同一塊實體記憶體。當資源資料拷貝到使用者影象資料buffer時,即是直接拷貝到顯示實體記憶體了。

所以,framebuffer驅動最重要的功能就是給使用者提供一個程序空間對映到實際的顯示實體記憶體的介面(mmap)。它跟程序間通訊的共享變數的原理是一致的。

       另外,考慮到一臺裝置可能要支援多個輸出,例如HDMI介面、VGA,或者類似視訊監控需求,一個螢幕上有好多個監控視窗。如何更好地管理多個顯示快取。Framebuffer在內部進行了抽象,即其向上層應用統一抽象為一個字元主裝置,而不同的視窗顯示快取即視為不同的字元從裝置。Framebuffer支援多達32個從裝置。

       接下來會從程式碼級詳細分析Linux Framebuffer驅動的框架組成、mmap和其他介面實現、介面使用場景。透徹理解以上framebuffer驅動的軟體需求,再來跟蹤分析Linux Framebuffer驅動是不難的。     

敬請關注後續的分析文章!

更多嵌入式Linux和物聯網原創技術分享請關注微信公眾號:嵌入式企鵝圈