1. 程式人生 > >驅動程式分層、分隔思想

驅動程式分層、分隔思想

文章來源:http://blog.csdn.net/zqixiao_09/article/details/51088887

 前面我們學習I2C、USB、SD驅動時,有沒有發現一個共性,就是在驅動開發時,每個驅動都分層三部分,由上到下分別是:

1、XXX 裝置驅動

2、XXX 核心層

3、XXX 主機控制器驅動

      而需要我們編寫的主要是裝置驅動部分,主機控制器驅動部分也有少量編寫,二者進行互動主要時由核心層提供的介面來實現;這樣結構清晰,大大地有利於我們的驅動開發,這其中就是利用了Linux裝置驅動開發中兩個重要思想,下面來一一解析

一、裝置驅動的分層思想

       在面向物件的程式設計中,可以為某一類相似的事物定義一個基類,而具體的事物可以繼承這個基類中的函式。如果對於繼承的這個事物而言,其某函式的實現與基類一致,那它就可以直接繼承基類的函式;相反,它可以過載之。這種面向物件的設計思想極大地提高了程式碼的可重用能力,是對現實世界事物間關係的一種良好呈現。

      Linux核心完全由C語言和組合語言寫成,但是卻頻繁用到了面向物件的設計思想。在裝置驅動方面,往往為同類的裝置設計了一個框架,而框架中的核心層則實現了該裝置通用的一些功能。同樣的,如果具體的裝置不想使用核心層的函式,它可以過載之。舉個例子:

return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)  
{  
    if (bottom_dev->funca)  
    return bottom_dev->funca(param1, param2);  
    /* 核心層通用的funca程式碼 */  
    ...  
}  

上述core_funca的實現中,會檢查底層裝置是否過載了funca(),如果過載了,就呼叫底層的程式碼,否則,直接使用通用層的。這樣做的好處是,核心層的程式碼可以處理絕大多數該類裝置的funca()對應的功能,只有少數特殊裝置需要重新實現funca()

再看一個例子:

return_type core_funca(xxx_device * bottom_dev, param1_type param1, param1_type param2)  
{  
    /*通用的步驟程式碼A */  
    ...  
    bottom_dev->funca_ops1();  
    /*通用的步驟程式碼B */  
    ...  
    bottom_dev->funca_ops2();  
    /*通用的步驟程式碼C */  
    ...  
    bottom_dev->funca_ops3();  
}  

上述程式碼假定為了實現funca(),對於同類裝置而言,操作流程一致,都要經過“通用程式碼A、底層ops1、通用程式碼B、底層ops2、通用程式碼C、底層ops3”這幾步,分層設計明顯帶來的好處是,對於通用程式碼A、B、C,具體的底層驅動不需要再實現,而僅僅只關心其底層的操作ops1、ops2、ops3。圖1明確反映了裝置驅動的核心層與具體裝置驅動的關係,實際上,這種分層可能只有2層(圖1的a),也可能是多層的(圖1的b)。


 這樣的分層化設計在Linux的input、RTC、MTD、I2 C、SPI、TTY、USB等諸多裝置驅動型別中屢見不鮮。

二、主機驅動和外設驅動分離思想

主機、外設驅動分離的意義

       在Linux裝置驅動框架的設計中,除了有分層設計實現以外,還有分隔的思想。舉一個簡單的例子,假設我們要通過SPI匯流排訪問某外設,在這個訪問過程中,要通過操作CPU XXX上的SPI控制器的暫存器來達到訪問SPI外設YYY的目的,最簡單的方法是:

return_type xxx_write_spi_yyy(...)  
{  
    xxx_write_spi_host_ctrl_reg(ctrl);  
    xxx_ write_spi_host_data_reg(buf);  
    while(!(xxx_spi_host_status_reg()&SPI_DATA_TRANSFER_DONE));  
    ...  
}  

如果按照這種方式來設計驅動,結果是對於任何一個SPI外設來講,它的驅動程式碼都是CPU相關的。也就是說,當然用在CPU XXX上的時候,它訪問XXX的SPI主機控制暫存器,當用在XXX1的時候,它訪問XXX1的SPI主機控制暫存器:
return_type xxx1_write_spi_yyy(...)  
{  
    xxx1_write_spi_host_ctrl_reg(ctrl);  
    xxx1_ write_spi_host_data_reg(buf);  
    while(!(xxx1_spi_host_status_reg()&SPI_DATA_TRANSFER_DONE));  
    ...  
}  
這顯然是不能接受的,因為這意味著外設YYY用在不同的CPU XXX和XXX1上的時候需要不同的驅動。那麼,我們可以用如圖的思想對主機控制器驅動和外設驅動進行分離。這樣的結構是,外設a、b、c的驅動與主機控制器A、B、C的驅動不相關,主機控制器驅動不關心外設,而外設驅動也不關心主機,外設只是訪問核心層的通用的API進行資料傳輸,主機和外設之間可以進行任意的組合



如果我們不進行上圖的主機和外設分離,外設a、b、c和主機A、B、C進行組合的時候,需要9個不同的驅動。設想一共有m個主機控制器,n個外設,分離的結果是需要m+n個驅動,不分離則需要m*n個驅動。

      Linux SPI、I2C、USB、ASoC(ALSA SoC)等子系統都典型地利用了這種分離的設計思想。