從?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_driver_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?執行,下面看代碼:
?
?
/* 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 Platform Device and Driver 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。