生活随笔
收集整理的這篇文章主要介紹了
linux platform 驱动模型分析
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
一. 概述
? ? platform設(shè)備和驅(qū)動與linux設(shè)備模型密切相關(guān)。platform在linux設(shè)備模型中,其實(shí)就是一種虛擬總線沒有對應(yīng)的硬件結(jié)構(gòu)。它的主要作用就是管理系統(tǒng)的外設(shè)資源,比如io內(nèi)存,中斷信號線。現(xiàn)在大多數(shù)處理器芯片都是soc,如s3c2440,它包括處理器內(nèi)核(arm920t)和系統(tǒng)的外設(shè)(lcd接口,nandflash接口等)。linux在引入了platform機(jī)制之后,內(nèi)核假設(shè)所有的這些外設(shè)都掛載在platform虛擬總線上,以便進(jìn)行統(tǒng)一管理。
二. platform 總線
? ?1. 在系統(tǒng)中platform對應(yīng)的文件drivers/base/platform.c,它不是作為一個模塊注冊到內(nèi)核的,關(guān)鍵的注冊總線的函數(shù)由系統(tǒng)初始化部分,對應(yīng)/init/main.c中的do_basic_setup函數(shù)間接調(diào)用。這里可以看出platform非常重要,要在系統(tǒng)其他驅(qū)動加載之前注冊。下面分析platform總線注冊函數(shù)
[cpp]?view plaincopy
int?__init?platform_bus_init(void)?? {?? ????int?error;?? ????early_platform_cleanup();?? ????error?=?device_register(&platform_bus);?? ?????????? ????if?(error)?? ????????return?error;?? ????error?=??bus_register(&platform_bus_type);?? ?????????? ????if?(error)?? ????????device_unregister(&platform_bus);?? ????return?error;?? }??
? ? 這個函數(shù)向內(nèi)核注冊了一種總線。他首先由/drivers/base/init.c中的driver_init函數(shù)調(diào)用,driver_init函數(shù)由/init/main.c中的do_basic_setup函數(shù)調(diào)用,do_basic_setup這個函數(shù)由kernel_init調(diào)用,所以platform總線是在內(nèi)核初始化的時候就注冊進(jìn)了內(nèi)核。
? ?2. platform_bus_type 總線結(jié)構(gòu)與設(shè)備結(jié)構(gòu)
? ? (1) platform總線 設(shè)備結(jié)構(gòu)
[cpp]?view plaincopy
struct?device?platform_bus?=?{?? ????.init_name??=?"platform",?? };??
? ? platform總線也是一種設(shè)備,這里初始化一個device結(jié)構(gòu),設(shè)備名稱platform,因?yàn)闆]有指定父設(shè)備,所以注冊后將會在/sys/device/下出現(xiàn)platform目錄。
? ? (2) platform總線 總線結(jié)構(gòu)
[cpp]?view plaincopy
struct?bus_type?platform_bus_type?=?{?? ????.name???????=?"platform",?? ????.dev_attrs??=?platform_dev_attrs,??? ????.match??????=?platform_match,?? ????.uevent?????=?platform_uevent,?? ????.pm?????=?&platform_dev_pm_ops,?? };??
? ? platform_dev_attrs ? ?設(shè)備屬性
? ? platform_match ? ? ? ?match函數(shù),這個函數(shù)在當(dāng)屬于platform的設(shè)備或者驅(qū)動注冊到內(nèi)核時就會調(diào)用,完成設(shè)備與驅(qū)動的匹配工作。
? ? platform_uevent ? ? ? 熱插拔操作函數(shù)
三. platform 設(shè)備
? ?1. platform_device 結(jié)構(gòu)
[cpp]?view plaincopy
struct?platform_device?{?? ????const?char??*?name;?? ????int?????id;?? ????struct?device???dev;?? ????u32?????num_resources;?? ????struct?resource?*?resource;?? ????struct?platform_device_id???*id_entry;?? ?????? ????struct?pdev_archdata????archdata;?? };??
? ? (1)platform_device結(jié)構(gòu)體中有一個struct resource結(jié)構(gòu),是設(shè)備占用系統(tǒng)的資源,定義在ioport.h中,如下
[cpp]?view plaincopy
struct?resource?{?? ????resource_size_t?start;?? ????resource_size_t?end;?? ????const?char?*name;?? ????unsigned?long?flags;?? ????struct?resource?*parent,?*sibling,?*child;?? };??
? ? (2) num_resources 占用系統(tǒng)資源的數(shù)目,一般設(shè)備都占用兩種資源,io內(nèi)存和中斷信號線。這個為兩種資源的總和。
? ?2. 設(shè)備注冊函數(shù) platform_device_register
[cpp]?view plaincopy
int?platform_device_register(struct?platform_device?*pdev)?? {?? ????device_initialize(&pdev->dev);?? ????return?platform_device_add(pdev);?? }??
? ? 這個函數(shù)首先初始化了platform_device的device結(jié)構(gòu),然后調(diào)用platform_device_add,這個是注冊函數(shù)的關(guān)鍵,下面分析platform_device_add:
[cpp]?view plaincopy
int?platform_device_add(struct?platform_device?*pdev)?? {?? ????int?i,?ret?=?0;?? ?? ????if?(!pdev)?? ????????return?-EINVAL;?? ?? ????if?(!pdev->dev.parent)?? ????????pdev->dev.parent?=?&platform_bus;?? ?????????? ????pdev->dev.bus?=?&platform_bus_type;?? ?????????? ????if?(pdev->id?!=?-1)?? ????????dev_set_name(&pdev->dev,?"%s.%d",?pdev->name,??pdev->id);?? ????else?? ????????dev_set_name(&pdev->dev,?"%s",?pdev->name);?? ?????????? ????for?(i?=?0;?i?<?pdev->num_resources;?i++)?{??? ????????struct?resource?*p,?*r?=?&pdev->resource[i];?? ?? ????????if?(r->name?==?NULL)?? ????????????r->name?=?dev_name(&pdev->dev);?? ?? ????????p?=?r->parent;?? ????????if?(!p)?{?? ????????????if?(resource_type(r)?==?IORESOURCE_MEM)?? ????????????????p?=?&iomem_resource;?? ????????????else?if?(resource_type(r)?==?IORESOURCE_IO)?? ????????????????p?=?&ioport_resource;?? ????????}?? ?? ????????if?(p?&&?insert_resource(p,?r))?{?? ????????????printk(KERN_ERR?? ???????????????????"%s:?failed?to?claim?resource?%d\n",?? ???????????????????dev_name(&pdev->dev),?i);?? ????????????ret?=?-EBUSY;?? ????????????goto?failed;?? ????????}?? ????}?? ????????? ????????? ????pr_debug("Registering?platform?device?'%s'.?Parent?at?%s\n",?? ?????????dev_name(&pdev->dev),?dev_name(pdev->dev.parent));?? ?? ????ret?=?device_add(&pdev->dev);?? ?????????? ????if?(ret?==?0)?? ????????return?ret;?? ?failed:?? ????while?(--i?>=?0)?{?? ????????struct?resource?*r?=?&pdev->resource[i];?? ????????unsigned?long?type?=?resource_type(r);?? ????????if?(type?==?IORESOURCE_MEM?||?type?==?IORESOURCE_IO)?? ????????????release_resource(r);?? ????}?? ????return?ret;?? }??
? ?3. mini2440內(nèi)核注冊platform設(shè)備過程
? ? 因?yàn)橐环Nsoc確定之后,其外設(shè)模塊就已經(jīng)確定了,所以注冊platform設(shè)備就由板級初始化代碼來完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函數(shù)中調(diào)用platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))來完成注冊。這個函數(shù)完成mini2440的所有platform設(shè)備的注冊:
? ? (1) platform_add_devices函數(shù)是platform_device_register的簡單封裝,它向內(nèi)核注冊一組platform設(shè)備
? ? (2) mini2440_devices是一個platform_device指針數(shù)組,定義如下:
[cpp]?view plaincopy
static?struct?platform_device?*mini2440_devices[]?__initdata?=?{?? ????&s3c_device_usb,?? ????&s3c_device_rtc,?? ????&s3c_device_lcd,?? ????&s3c_device_wdt,?? ????&s3c_device_i2c0,?? ????&s3c_device_iis,?? ????&mini2440_device_eth,?? ????&s3c24xx_uda134x,?? ????&s3c_device_nand,?? ????&s3c_device_sdi,?? ????&s3c_device_usbgadget,?? };??
? ? 這個就是mini2440的所有外設(shè)資源了,每個外設(shè)的具體定義在/arch/arm/plat-s3c24xx/devs.c,下面以s3c_device_lcd為例說明,其他的類似。s3c_device_lcd在devs.c中它定義為:
[cpp]?view plaincopy
struct?platform_device?s3c_device_lcd?=?{?? ????.name?????????=?"s3c2410-lcd",?? ????.id???????=?-1,?? ????.num_resources????=?ARRAY_SIZE(s3c_lcd_resource),?? ????.resource?????=?s3c_lcd_resource,?? ????.dev??????????????=?{?? ????????.dma_mask???????=?&s3c_device_lcd_dmamask,?? ????????.coherent_dma_mask??=?0xffffffffUL?? ????}?? };??
? ? 可以看出,它占用的資源s3c_lcd_resource,定義如下:
[cpp]?view plaincopy
static?struct?resource?s3c_lcd_resource[]?=?{?? ????[0]?=?{?? ????????.start?=?S3C24XX_PA_LCD,?? ????????.end???=?S3C24XX_PA_LCD?+?S3C24XX_SZ_LCD?-?1,?? ????????.flags?=?IORESOURCE_MEM,?? ????},?? ????[1]?=?{?? ????????.start?=?IRQ_LCD,?? ????????.end???=?IRQ_LCD,?? ????????.flags?=?IORESOURCE_IRQ,?? ????}?? };??
? ?這是一個數(shù)組,有兩個元素,說明lcd占用了系統(tǒng)兩個資源,一個資源類型是IORESOURCE_MEM代表io內(nèi)存,起使地址S3C24XX_PA_LCD,這個是LCDCON1寄存器的地址。另外一個資源是中斷信號線。
四. platform設(shè)備驅(qū)動
? ?如果要將所寫的驅(qū)動程序注冊成platform驅(qū)動,那么所做的工作就是初始化一個platform_driver,然后調(diào)用platform_driver_register進(jìn)行注冊。
? ?1. 基本數(shù)據(jù)機(jī)構(gòu)platform_driver
[cpp]?view plaincopy
struct?platform_driver?{?? ????int?(*probe)(struct?platform_device?*);?? ????int?(*remove)(struct?platform_device?*);?? ????void?(*shutdown)(struct?platform_device?*);?? ????int?(*suspend)(struct?platform_device?*,?pm_message_t?state);?? ????int?(*resume)(struct?platform_device?*);?? ????struct?device_driver?driver;?? ????struct?platform_device_id?*id_table;?? };??
? ? 這是platform驅(qū)動基本的數(shù)據(jù)結(jié)構(gòu),在驅(qū)動程序中我們要做的就是聲明一個這樣的結(jié)構(gòu)并初始化。下面是lcd驅(qū)動程序?qū)λ某跏蓟?#xff1a;
[cpp]?view plaincopy
static?struct?platform_driver?s3c2412fb_driver?=?{?? ????.probe??????=?s3c2412fb_probe,?? ????.remove?????=?s3c2410fb_remove,?? ????.suspend????=?s3c2410fb_suspend,?? ????.resume?????=?s3c2410fb_resume,?? ????.driver?????=?{?? ????????.name???=?"s3c2412-lcd",?? ????????.owner??=?THIS_MODULE,?? ????},?? };??
? ? 上面幾個函數(shù)是我們要實(shí)現(xiàn)的,它將賦值給device_driver中的相關(guān)成員,probe函數(shù)是用來查詢特定設(shè)備是夠真正存在的函數(shù)。當(dāng)設(shè)備從系統(tǒng)刪除的時候調(diào)用remove函數(shù)。
? ?2. 注冊函數(shù)platform_driver_register
[cpp]?view plaincopy
int?platform_driver_register(struct?platform_driver?*drv)?? {?? ????drv->driver.bus?=?&platform_bus_type;?? ????if?(drv->probe)?? ????????drv->driver.probe?=?platform_drv_probe;?? ????if?(drv->remove)?? ????????drv->driver.remove?=?platform_drv_remove;???? ????if?(drv->shutdown)?? ????????drv->driver.shutdown?=?platform_drv_shutdown;?? ????return?driver_register(&drv->driver);?? }??
? ? 這個函數(shù)首先使驅(qū)動屬于platform_bus_type總線,將platform_driver結(jié)構(gòu)中的定義的probe,remove,shutdown賦值給device_driver結(jié)構(gòu)中的相應(yīng)成員,以供linux設(shè)備模型核心調(diào)用,然后調(diào)用driver_regster將設(shè)備驅(qū)動注冊到linux設(shè)備模型核心中。
五. 各環(huán)節(jié)的整合
? ? 前面提到mini2440板級初始化程序?qū)⑺械膒latform設(shè)備注冊到了linux設(shè)備模型核心中,在/sys/devices/platform目錄中都有相應(yīng)的目錄表示。platform驅(qū)動則是由各個驅(qū)動程序模塊分別注冊到系統(tǒng)中的。但是他們是如何聯(lián)系起來的呢,這就跟linux設(shè)備模型核心有關(guān)系了。在ldd3中的linux設(shè)備模型的各環(huán)節(jié)的整合中有詳細(xì)的論述。這里簡要說明一下platform實(shí)現(xiàn)的方法。每當(dāng)注冊一個platform驅(qū)動的時候就會調(diào)用driver_register,這個函數(shù)的調(diào)用會遍歷設(shè)備驅(qū)動所屬總線上的所有設(shè)備,并對每個設(shè)備調(diào)用總線的match函數(shù)。platform驅(qū)動是屬于platform_bus_type總線,所以調(diào)用platform_match函數(shù)。這個函數(shù)實(shí)現(xiàn)如下:
[cpp]?view plaincopy
static?int?platform_match(struct?device?*dev,?struct?device_driver?*drv)?? {?? ????struct?platform_device?*pdev?=?to_platform_device(dev);?? ????struct?platform_driver?*pdrv?=?to_platform_driver(drv);?? ?? ?????? ????if?(pdrv->id_table)?? ????????return?platform_match_id(pdrv->id_table,?pdev)?!=?NULL;?? ?????? ????return?(strcmp(pdev->name,?drv->name)?==?0);?? }??
? ? 這個函數(shù)將device結(jié)構(gòu)轉(zhuǎn)換為platform_devcie結(jié)構(gòu),將device_driver結(jié)構(gòu)轉(zhuǎn)換為platform_driver結(jié)構(gòu),并調(diào)用platform_match_id對設(shè)備與驅(qū)動相關(guān)信息進(jìn)行比較。如果沒有比較成功會返回0,以便進(jìn)行下一個設(shè)備的比較,如果比較成功就會返回1,并且將device結(jié)構(gòu)中的driver指針指向這個驅(qū)動。然后調(diào)用device_driver中的probe函數(shù),在lcd驅(qū)動中就是s3c2412fb_probe。這個函數(shù)是我們要編寫的函數(shù)。這個函數(shù)檢測驅(qū)動的狀態(tài),并且測試能否真正驅(qū)動設(shè)備,并且做一些初始化工作。
總結(jié)
以上是生活随笔為你收集整理的linux platform 驱动模型分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。