生活随笔
收集整理的這篇文章主要介紹了
linux platform 驱动模型分析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
一. 概述
? ? platform設備和驅動與linux設備模型密切相關。platform在linux設備模型中,其實就是一種虛擬總線沒有對應的硬件結構。它的主要作用就是管理系統的外設資源,比如io內存,中斷信號線。現在大多數處理器芯片都是soc,如s3c2440,它包括處理器內核(arm920t)和系統的外設(lcd接口,nandflash接口等)。linux在引入了platform機制之后,內核假設所有的這些外設都掛載在platform虛擬總線上,以便進行統一管理。
二. platform 總線
? ?1. 在系統中platform對應的文件drivers/base/platform.c,它不是作為一個模塊注冊到內核的,關鍵的注冊總線的函數由系統初始化部分,對應/init/main.c中的do_basic_setup函數間接調用。這里可以看出platform非常重要,要在系統其他驅動加載之前注冊。下面分析platform總線注冊函數
[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;?? }??
? ? 這個函數向內核注冊了一種總線。他首先由/drivers/base/init.c中的driver_init函數調用,driver_init函數由/init/main.c中的do_basic_setup函數調用,do_basic_setup這個函數由kernel_init調用,所以platform總線是在內核初始化的時候就注冊進了內核。
? ?2. platform_bus_type 總線結構與設備結構
? ? (1) platform總線 設備結構
[cpp]?view plaincopy
struct?device?platform_bus?=?{?? ????.init_name??=?"platform",?? };??
? ? platform總線也是一種設備,這里初始化一個device結構,設備名稱platform,因為沒有指定父設備,所以注冊后將會在/sys/device/下出現platform目錄。
? ? (2) platform總線 總線結構
[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 ? ?設備屬性
? ? platform_match ? ? ? ?match函數,這個函數在當屬于platform的設備或者驅動注冊到內核時就會調用,完成設備與驅動的匹配工作。
? ? platform_uevent ? ? ? 熱插拔操作函數
三. platform 設備
? ?1. platform_device 結構
[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結構體中有一個struct resource結構,是設備占用系統的資源,定義在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 占用系統資源的數目,一般設備都占用兩種資源,io內存和中斷信號線。這個為兩種資源的總和。
? ?2. 設備注冊函數 platform_device_register
[cpp]?view plaincopy
int?platform_device_register(struct?platform_device?*pdev)?? {?? ????device_initialize(&pdev->dev);?? ????return?platform_device_add(pdev);?? }??
? ? 這個函數首先初始化了platform_device的device結構,然后調用platform_device_add,這個是注冊函數的關鍵,下面分析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內核注冊platform設備過程
? ? 因為一種soc確定之后,其外設模塊就已經確定了,所以注冊platform設備就由板級初始化代碼來完成,在mini2440中是mach-mini2440.c的mini2440_machine_init函數中調用platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))來完成注冊。這個函數完成mini2440的所有platform設備的注冊:
? ? (1) platform_add_devices函數是platform_device_register的簡單封裝,它向內核注冊一組platform設備
? ? (2) mini2440_devices是一個platform_device指針數組,定義如下:
[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的所有外設資源了,每個外設的具體定義在/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,?? ????}?? };??
? ?這是一個數組,有兩個元素,說明lcd占用了系統兩個資源,一個資源類型是IORESOURCE_MEM代表io內存,起使地址S3C24XX_PA_LCD,這個是LCDCON1寄存器的地址。另外一個資源是中斷信號線。
四. platform設備驅動
? ?如果要將所寫的驅動程序注冊成platform驅動,那么所做的工作就是初始化一個platform_driver,然后調用platform_driver_register進行注冊。
? ?1. 基本數據機構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驅動基本的數據結構,在驅動程序中我們要做的就是聲明一個這樣的結構并初始化。下面是lcd驅動程序對它的初始化:
[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,?? ????},?? };??
? ? 上面幾個函數是我們要實現的,它將賦值給device_driver中的相關成員,probe函數是用來查詢特定設備是夠真正存在的函數。當設備從系統刪除的時候調用remove函數。
? ?2. 注冊函數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);?? }??
? ? 這個函數首先使驅動屬于platform_bus_type總線,將platform_driver結構中的定義的probe,remove,shutdown賦值給device_driver結構中的相應成員,以供linux設備模型核心調用,然后調用driver_regster將設備驅動注冊到linux設備模型核心中。
五. 各環節的整合
? ? 前面提到mini2440板級初始化程序將它所有的platform設備注冊到了linux設備模型核心中,在/sys/devices/platform目錄中都有相應的目錄表示。platform驅動則是由各個驅動程序模塊分別注冊到系統中的。但是他們是如何聯系起來的呢,這就跟linux設備模型核心有關系了。在ldd3中的linux設備模型的各環節的整合中有詳細的論述。這里簡要說明一下platform實現的方法。每當注冊一個platform驅動的時候就會調用driver_register,這個函數的調用會遍歷設備驅動所屬總線上的所有設備,并對每個設備調用總線的match函數。platform驅動是屬于platform_bus_type總線,所以調用platform_match函數。這個函數實現如下:
[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);?? }??
? ? 這個函數將device結構轉換為platform_devcie結構,將device_driver結構轉換為platform_driver結構,并調用platform_match_id對設備與驅動相關信息進行比較。如果沒有比較成功會返回0,以便進行下一個設備的比較,如果比較成功就會返回1,并且將device結構中的driver指針指向這個驅動。然后調用device_driver中的probe函數,在lcd驅動中就是s3c2412fb_probe。這個函數是我們要編寫的函數。這個函數檢測驅動的狀態,并且測試能否真正驅動設備,并且做一些初始化工作。
總結
以上是生活随笔為你收集整理的linux platform 驱动模型分析的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。