生活随笔
收集整理的這篇文章主要介紹了
Linux platform总线(1):总体框架
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
PlatForm設備驅動:??一、platform總線、設備與驅動??1.一個現實的Linux設備和驅動通常都需要掛接在一種總線上,對于本身依附于PCI、USB、I2?C、SPI等的設備而言,這自然不是問題,??但是在嵌入式系統里面,SoC系統中集成的獨立的外設控制器、掛接在SoC內存空間的外設等確不依附于此類總線。??基于這一背景,Linux發明了一種虛擬的總線,稱為platform總線,相應的設備稱為platform_device,而驅動成為?platform_driver。??2.注意,所謂的platform_device并不是與字符設備、塊設備和網絡設備并列的概念,而是Linux系統提供的一種附加手段,??例如,在?S3C6410處理器中,把內部集成的I2C、RTC、SPI、LCD、看門狗等控制器都歸納為platform_device,而它們本身就是字符設備。??3.基于Platform總線的驅動開發流程如下:??(1)定義初始化platform?bus??(2)定義各種platform?devices??(3)注冊各種platform?devices??(4)定義相關platform?driver??(5)注冊相關platform?driver??(6)操作相關設備??4.平臺相關結構????struct?platform_device?{???const?char?*?name;???u32?id;???struct?device?dev;???u32?num_resources;????struct?resource?*?resource;??};????struct?resource?{???resource_size_t?start;????resource_size_t?end;????const?char?*name;????unsigned?long?flags;????struct?resource?*parent,?*sibling,?*child;??};????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?(*suspend_late)(struct?platform_device?*,?pm_message_t?state);???int?(*resume_early)(struct?platform_device?*);???int?(*resume)(struct?platform_device?*);???struct?pm_ext_ops?*pm;???struct?device_driver?driver;??};???????struct?bus_type?platform_bus_type?=?{???.name?=?“platform”,???.dev_attrs?=?platform_dev_attrs,???.match?=?platform_match,???.uevent?=?platform_uevent,???.pm?=?PLATFORM_PM_OPS_PTR,??};??EXPORT_SYMBOL_GPL(platform_bus_type);???????static?int?platform_match(struct?device?*dev,?struct?device_driver?*drv)??{???struct?platform_device?*pdev;???pdev?=?container_of(dev,?struct?platform_device,?dev);???return?(strncmp(pdev->name,?drv->name,?BUS_ID_SIZE)?==?0);??}????????int?platform_add_devices(struct?platform_device?**devs,?int?num);????1.?platform?bus總線先被kenrel注冊。??2.?系統初始化過程中調用platform_add_devices或者platform_device_register,將平臺設備(platform?devices)注冊到平臺總線中(platform?bus)??3.?平臺驅動(platform?driver)與平臺設備(platform?device)的關聯是在platform_driver_register或者driver_register中實現,一般這個函數在驅動的初始化過程調用。??通過這三步,就將平臺總線,設備,驅動關聯起來。??二.Platform初始化??系統啟動時初始化時創建了platform_bus總線設備和platform_bus_type總線,platform總線是在內核初始化的時候就注冊進了內核。??內核初始化函數kernel_init()中調用了do_basic_setup()?,該函數中調用driver_init(),該函數中調用platform_bus_init(),我們看看platform_bus_init()函數:????????????????????????????????????????????????????????????????????????????????????????????????????????????????????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;??}??????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);??}??static?const?struct?platform_device_id?*platform_match_id(struct?platform_device_id?*id,struct?platform_device?*pdev)??{?????????while?(id->name[0])?{????????????????if?(strcmp(pdev->name,?id->name)?==?0)?{???????????????????????pdev->id_entry?=?id;???????????????????????return?id;????????????????}????????????????id++;?????????}?????????return?NULL;??}????????static?int?platform_uevent(struct?device?*dev,?struct?kobj_uevent_env?*env)??{?????????struct?platform_device???*pdev?=?to_platform_device(dev);?????????add_uevent_var(env,?"MODALIAS=%s%s",?PLATFORM_MODULE_PREFIX,?(pdev->id_entry)???pdev->id_entry->name?:?pdev->name);?????????return?0;??}????static?struct?kset_uevent_ops?device_uevent_ops?=?{?????????.filter?=????dev_uevent_filter,?????????.name?=??????????dev_uevent_name,?????????.uevent?=?dev_uevent,??};????三.Platform設備的注冊??我們在設備模型的分析中知道了把設備添加到系統要調用device_initialize()和platform_device_add(pdev)函數。??Platform設備的注冊分兩種方式:??1.對于platform設備的初注冊,內核源碼提供了platform_device_add()函數,輸入參數platform_device可以是靜態的全局設備,它是進行一系列的操作后調用device_add()將設備注冊到相應的總線(platform總線)上,??內核代碼中platform設備的其他注冊函數都是基于這個函數,如platform_device_register()、platform_device_register_simple()、platform_device_register_data()等。??2.另外一種機制就是動態申請platform_device_alloc()一個platform_device設備,然后通過platform_device_add_resources及platform_device_add_data等添加相關資源和屬性。??無論哪一種platform_device,最終都將通過platform_device_add這冊到platform總線上。??區別在于第二步:其實platform_device_add()包括device_add(),不過要先注冊resources,然后將設備掛接到特定的platform總線。??3.第一種平臺設備注冊方式??????????????int?platform_device_register(struct?platform_device?*pdev)??{???device_initialize(&pdev->dev);???return?platform_device_add(pdev);??}??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;??}??4.第二種平臺設備注冊方式??????struct?platform_device?*?platform_device_alloc(const?char?*name,?int?id)??{???struct?platform_object?*pa;???????????pa?=?kzalloc(sizeof(struct?platform_object)?+?strlen(name),?GFP_KERNEL);???if?(pa)?{????strcpy(pa->name,?name);????pa->pdev.name?=?pa->name;????pa->pdev.id?=?id;????device_initialize(&pa->pdev.dev);????pa->pdev.dev.release?=?platform_device_release;???}???return?pa???&pa->pdev?:?NULL;??}????struct?platform_device?*platform_device_register_simple(const?char?*name,int?id,struct?resource?*res,unsigned?int?num)??{?????????struct?platform_device?*pdev;?????????int?retval;?????????pdev?=?platform_device_alloc(name,?id);?????????if?(!pdev)?{????????????????retval?=?-ENOMEM;????????????????goto?error;?????????}?????????if?(num)?{????????????????retval?=?platform_device_add_resources(pdev,?res,?num);????????????????if?(retval)???????????????????????goto?error;?????????}?????????retval?=?platform_device_add(pdev);?????????if?(retval)????????????????goto?error;?????????????????????????return?pdev;??error:?????????platform_device_put(pdev);?????????return?ERR_PTR(retval);??}????int?platform_device_add_resources(struct?platform_device?*pdev,struct?resource?*res,?unsigned?int?num)??{?????????struct?resource?*r;?????????r?=?kmalloc(sizeof(struct?resource)?*?num,?GFP_KERNEL);?????????if?(r)?{????????????????memcpy(r,?res,?sizeof(struct?resource)?*?num);????????????????pdev->resource?=?r;????????????????????????pdev->?num_resources?=?num;?????????}?????????return?r???0?:?-ENOMEM;??}????四.Platform設備驅動的注冊??我們在設備驅動模型的分析中已經知道驅動在注冊要調用driver_register(),??platform?driver的注冊函數platform_driver_register()同樣也是進行其它的一些初始化后調用driver_register()將驅動注冊到platform_bus_type總線上.??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);??}????static?int?platform_drv_probe(struct?device?*_dev)??{?????????struct?platform_driver?*drv?=?to_platform_driver(_dev->driver);?????????struct?platform_device?*dev?=?to_platform_device(_dev);?????????return?drv->probe(dev);????????????????????????????????????????????}??static?int?platform_drv_remove(struct?device?*_dev)??{?????????struct?platform_driver?*drv?=?to_platform_driver(_dev->driver);?????????struct?platform_device?*dev?=?to_platform_device(_dev);?????????return?drv->remove(dev);??}??static?void?platform_drv_shutdown(struct?device?*_dev)??{?????????struct?platform_driver?*drv?=?to_platform_driver(_dev->driver);?????????struct?platform_device?*dev?=?to_platform_device(_dev);?????????drv->shutdown(dev);??}????1.從這三個函數的代碼可以看到,又找到了相應的platform_driver和platform_device,然后調用platform_driver的probe、remove、shutdown函數。這是一種高明的做法:??在不針對某個驅動具體的probe、remove、shutdown指向的函數,而通過上三個過度函數來找到platform_driver,然后調用probe、remove、shutdown接口。??如果設備和驅動都注冊了,就可以通過bus?->match、bus->probe或driver->probe進行設備驅動匹配了。??2.驅動注冊的時候platform_driver_register()->driver_register()->bus_add_driver()->driver_attach()->bus_for_each_dev(),??對每個掛在虛擬的platform?bus的設備作__driver_attach()->driver_probe_device()->drv->bus->match()==platform_match()->比較strncmp(pdev->name,?drv->name,?BUS_ID_SIZE),??如果相符就調用platform_drv_probe()->driver->probe(),如果probe成功則綁定該設備到該驅動。 ?
總結
以上是生活随笔為你收集整理的Linux platform总线(1):总体框架的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。