歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux基礎 >> 關於Linux

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和物聯網原創技術分享請關注微信公眾號:嵌入式企鵝圈

\

Copyright © Linux教程網 All Rights Reserved