Linux Platform Device and Driver,platform_add_devices()->platform_driver_register()

來源:互聯網
上載者:User
  Linux Platform Device and Driver,platform_add_devices()->platform_driver_register() 收藏 http://blog.csdn.net/lanmanck/archive/2009/08/17/4455692.aspx

來自: http://www.diybl.com/course/6_system/linux/Linuxjs/200871/129585.html

從 Linux 2.6 起引入了一套新的驅動管理和註冊機制 :Platform_device 和 Platform_driver 。

     Linux 中大部分的裝置驅動,都可以使用這套機制 , 裝置用 Platform_device 表示,驅動用 Platform_driver 進行註冊。

    Linux platform driver 機制和傳統的 device driver 機制 ( 通過 driver_register 函數進行註冊 ) 相比,一個十分明顯的優勢在於 platform 機制將裝置本身的資源註冊進核心,由核心統一管理,在驅動程式中使用這些資源時通過 platform device 提供的標準介面進行申請並使用。這樣提高了驅動和資源管理的獨立性,並且擁有較好的可移植性和安全性 ( 這些標準介面是安全的 ) 。

    Platform 機制的本身使用並不複雜,由兩部分組成: platform_device 和 platfrom_driver 。

    通過 Platform 機制開發發底層驅動的大致流程為 : 定義 platform_device -> 註冊 platform_device-> 定義 platform_driver-> 註冊 platform_driver 。

    首先要確認的就是裝置的資源資訊,例如裝置的地址,中斷號等。

在 2.6 核心中 platform 裝置用結構體 platform_device 來描述,該結構體定義在 kernel\include\linux\platform_device.h 中,

struct platform_device {

const char * name;

u32 id;

struct device dev;

u32 num_resources;

struct resource * resource;

};

    該結構一個重要的元素是 resource ,該元素存入了最為重要的裝置資源資訊,定義在 kernel\include\linux\ioport.h 中,

struct resource {

const char *name;

unsigned long start, end;

unsigned long flags;

struct resource *parent, *sibling, *child;

};

下面舉 s3c2410 平台的 i2c 驅動作為例子來說明:

/* arch/arm/mach-s3c2410/devs.c */
/* I2C */
static struct resource s3c_i2c_resource[ ] = {
[ 0] = {
. start = S3C24XX_PA_IIC,
. end = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
. flags = IORESOURCE_MEM,
} ,
[ 1] = {
. start = IRQ_IIC, //S3C2410_IRQ(27)
. end = IRQ_IIC,
. flags = IORESOURCE_IRQ,
}
} ;

    這裡定義了兩組 resource ,它描述了一個 I2C 裝置的資源,第 1 組描述了這個 I2C 裝置所佔用的匯流排位址範圍, IORESOURCE_MEM 表示第 1 組描述的是記憶體類型的資源資訊,第 2 組描述了這個 I2C 裝置的中斷號, IORESOURCE_IRQ 表示第 2 組描述的是中斷資源資訊。裝置驅動會根據 flags 來擷取相應的資源資訊。

有了 resource 資訊,就可以定義 platform_device 了:

struct platform_device s3c_device_i2c = {
. name = "s3c2410-i2c" ,
. id = - 1,
. num_resources = ARRAY_SIZE( s3c_i2c_resource) ,
. resource = s3c_i2c_resource,
} ;

      定義好了 platform_device 結構體後就可以調用函數 platform_add_devices 向系統中添加該裝置了,之後可以調用 platform_device_register() 進行裝置註冊。要注意的是,這裡的 platform_device 裝置的註冊過程必須在相應裝置驅動載入之前被調用,即執行 platform_driver_register 之前 , 原因是因為驅動註冊時需要匹配核心中所以登入的裝置名稱。

    s3c2410-i2c 的 platform_device 是在系統啟動時,在 cpu.c 裡的 s3c_arch_init() 函數裡進行註冊的,這個函數申明為 arch_initcall(s3c_arch_init); 會在系統初始化階段被調用 。

arch_initcall 的優先順序高於 module_init 。所以會在 Platform 驅動註冊之前調用。 ( 詳細參考 include/linux/init.h)

s3c_arch_init 函數如下:

/* arch/arm/mach-3sc2410/cpu.c */
static int __init s3c_arch_init( void )
{
int ret;
     ……
/* 這裡board指標指向在mach-smdk2410.c裡的定義的smdk2410_board,裡麵包含了預先定義的I2C Platform_device等. */
if ( board ! = NULL ) {
struct platform_device * * ptr = board- > devices;
int i;
for ( i = 0; i < board- > devices_count; i+ + , ptr+ + ) {
             ret = platform_device_register( * ptr) ; //在這裡進行註冊
if ( ret) {
                 printk( KERN_ERR "s3c24xx: failed to add board device %s (%d) @%p\n" , ( * ptr) - > name,
ret, * ptr) ;
}
}
/* mask any error, we may not need all these board
         * devices */
         ret = 0;
}
return ret;
}

同時被註冊還有很多其他平台的 platform_device ,詳細查看 arch/arm/mach-s3c2410/mach-smdk2410.c 裡的 smdk2410_devices 結構體。

驅動程式需要實現結構體 struct platform_driver ,參考 drivers/i2c/busses

/* device driver for platform bus bits */


static struct platform_driver s3c2410_i2c_driver = {
. probe = s3c24xx_i2c_probe,
. remove = s3c24xx_i2c_remove,
. resume = s3c24xx_i2c_resume,
. driver = {
. owner = THIS_MODULE,
. name = "s3c2410-i2c" ,
} ,
} ;

在驅動初始化函數中調用函數 platform_driver_register() 註冊 platform_driver ,需要注意的是 s3c_device_i2c 結構中 name 元素和 s3c2410_i2c_driver 結構中 driver.name 必須是相同的, 這樣在 platform_driver_register() 註冊時會對所有登入的所有 platform_device 中的 name 和當前註冊的 platform_driver 的 driver.name 進行比較,只有找到相同的名稱的 platfomr_device 才能註冊成功,當註冊成功時會調用 platform_driver 結構元素 probe 函數指標, 這裡就是 s3c24xx_i2c_probe, 當進入 probe 函數後,需要擷取裝置的資源資訊 ,常用擷取資源的函數主要是:

struct resource * platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num);

根據參數 type 所指定類型,例如 IORESOURCE_MEM ,來擷取指定的資源。

struct int platform_get_irq(struct platform_device *dev, unsigned int num);

擷取資源中的中斷號。

下面舉 s3c24xx_i2c_probe 函數分析 , 看看這些介面是怎麼用的。

前面已經講了, s3c2410_i2c_driver 註冊成功後會調用 s3c24xx_i2c_probe 執行,下面看代碼:

/* s3c24xx_i2c_probe
*
* called by the bus driver when a suitable device is found
*/

/* drivers/i2c/busses/i2c-s3c2410.c */
static int s3c24xx_i2c_probe( struct platform_device * pdev)
{
struct s3c24xx_i2c * i2c = & s3c24xx_i2c;
struct resource * res;
int ret;
/* find the clock and enable it */
     i2c- > dev = & pdev- > dev;
     i2c- > clk = clk_get( & pdev- > dev, "i2c" ) ;
if ( IS_ERR( i2c- > clk) ) {
     dev_err( & pdev- > dev, "cannot get clock\n" ) ;
     ret = - ENOENT;
goto out;
}
     dev_dbg( & pdev- > dev, "clock source %p\n" , i2c- > clk) ;
     clk_enable( i2c- > clk) ;


/* map the registers */
     res = platform_get_resource( pdev, IORESOURCE_MEM, 0) ; /* 擷取裝置的IO資源地址 */
if ( res = = NULL ) {
     dev_err( & pdev- > dev, "cannot find IO resource\n" ) ;
     ret = - ENOENT;
goto out;
}
     i2c- > ioarea = request_mem_region( res- > start, ( res- > end- res- > start) + 1, pdev- > name) ; /* 申請這塊IO Region */
if ( i2c- > ioarea = = NULL ) {
     dev_err( & pdev- > dev, "cannot request IO\n" ) ;
     ret = - ENXIO;
goto out;
}
     i2c- > regs = ioremap( res- > start, ( res- > end- res- > start) + 1) ; /* 映射至核心虛擬空間 */
if ( i2c- > regs = = NULL ) {
     dev_err( & pdev- > dev, "cannot map IO\n" ) ;
     ret = - ENXIO;
goto out;
}
     dev_dbg( & pdev- > dev, "registers %p (%p, %p)\n" , i2c- > regs, i2c- > ioarea, res) ;
/* setup info block for the i2c core */
     i2c- > adap. algo_data = i2c;
     i2c- > adap. dev. parent = & pdev- > dev;
/* initialise the i2c controller */
     ret = s3c24xx_i2c_init( i2c) ;
if ( ret ! = 0)
goto out;
/* find the IRQ for this unit (note, this relies on the init call to ensure no current IRQs pending */
     res = platform_get_resource( pdev, IORESOURCE_IRQ, 0) ; /* 擷取裝置IRQ中斷號 */
if ( res = = NULL ) {
     dev_err( & pdev- > dev, "cannot find IRQ\n" ) ;
     ret = - ENOENT;
goto out;
}
     ret = request_irq( res- > start, s3c24xx_i2c_irq, IRQF_DISABLED, /* 申請IRQ */
     pdev- > name, i2c) ;
     ……
return ret;
}

小思考:

那什麼情況可以使用 platform driver 機制編寫驅動呢?

       我的理解是只要和核心本身運行依賴性不大的外圍裝置 ( 換句話說只要不在核心運行所需的一個最小系統之內的裝置 ), 相對獨立的 , 擁有各自獨自的資源 (addresses and IRQs) , 都可以用 platform_driver 實現。如: lcd,usb,uart 等,都可以用 platfrom_driver 寫,而 timer,irq 等最小系統之內的裝置則最好不用 platfrom_driver 機制,實際上核心實現也是這樣的。

參考資料:

linux-2.6.24/Documentation/driver-model/platform.txt

《platform _device 和 platform_driver 註冊過程》

http://blog.chinaunix.net/u2/60011/showart.php?id=1018999

http://www.eetop.cn/blog/html/45/11145-676.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.