Linux驅動修鍊之道-platform
首先看一下我的系統中都有什麼裝置掛在了platform虛擬匯流排上:
hacker@hacker:~/linux-2.6.30.4$ cd /sys/bus/platform/<br />hacker@hacker:/sys/bus/platform$ tree<br />.<br />|-- devices<br />| |-- Fixed MDIO bus.0 -> ../../../devices/platform/Fixed MDIO bus.0<br />| |-- eisa.0 -> ../../../devices/platform/eisa.0<br />| |-- i8042 -> ../../../devices/platform/i8042<br />| |-- pcspkr -> ../../../devices/platform/pcspkr<br />| |-- rtc_cmos -> ../../../devices/platform/rtc_cmos<br />| `-- serial8250 -> ../../../devices/platform/serial8250<br />|-- drivers<br />| |-- dsa<br />| | |-- bind<br />| | |-- uevent<br />| | `-- unbind<br />| |-- i8042<br />| | |-- bind<br />| | |-- i8042 -> ../../../../devices/platform/i8042<br />| | |-- uevent<br />| | `-- unbind<br />| |-- mdio-gpio<br />| | |-- bind<br />| | |-- uevent<br />| | `-- unbind<br />| |-- parport_pc<br />| | |-- bind<br />| | |-- module -> ../../../../module/parport_pc<br />| | |-- uevent<br />| | `-- unbind<br />| |-- rtc_cmos<br />| | |-- bind<br />| | |-- rtc_cmos -> ../../../../devices/platform/rtc_cmos<br />| | |-- uevent<br />| | `-- unbind<br />| |-- serial8250<br />| | |-- bind<br />| | |-- serial8250 -> ../../../../devices/platform/serial8250<br />| | |-- uevent<br />| | `-- unbind<br />| `-- twl4030_reg<br />| |-- bind<br />| |-- uevent<br />| `-- unbind<br />|-- drivers_autoprobe<br />|-- drivers_probe<br />`-- uevent</p><p>19 directories, 24 files
platform的初始化:首先系統啟動的時候會調用platform_bus_init來初始化這個虛擬匯流排,讓後向虛擬匯流排註冊即將掛載這條匯流排上的裝置。platform_bus_type部分是核心為我們實現好的,我們只關係platform_device與platform_driver就行了。
struct bus_type platform_bus_type = {<br />.name= "platform",<br />.dev_attrs= platform_dev_attrs,<br />.match= platform_match,<br />.uevent= platform_uevent,<br />.pm= PLATFORM_PM_OPS_PTR,<br />};<br />EXPORT_SYMBOL_GPL(platform_bus_type);</p><p>int __init platform_bus_init(void)<br />{<br />int error;</p><p>early_platform_cleanup();</p><p>error = device_register(&platform_bus);<br />if (error)<br />return error;<br />error = bus_register(&platform_bus_type);<br />if (error)<br />device_unregister(&platform_bus);<br />return error;<br />}
記住匯流排也是一種裝置,所以首先註冊匯流排裝置,然後註冊匯流排。
static struct platform_device *smdk2410_devices[] __initdata = {<br />&s3c_device_usb,<br />&s3c_device_lcd,<br />&s3c_device_wdt,<br />&s3c_device_i2c0,<br />&s3c_device_iis,<br />};
把裝置掛到platform匯流排上:
static void __init smdk2410_init(void)<br />{<br />s3c_i2c0_set_platdata(NULL);<br />platform_add_devices(smdk2410_devices, ARRAY_SIZE(smdk2410_devices));<br />smdk_machine_init();<br />}
首先來看一個重要的資料結構:
struct resource {<br />resource_size_t start; /*資源的起始物理地址*/<br />resource_size_t end; /*資源的結束物理地址*/<br />const char *name; /*資源的名稱*/<br />unsigned long flags; /*資源的類型*/<br />struct resource *parent, *sibling, *child; /*資源的鏈表指標*/<br />};</p><p>struct platform_device {<br />const char* name; /*裝置名稱*/<br />intid; /*裝置編號,配合裝置名稱使用*/<br />struct devicedev;<br />u32num_resources;<br />struct resource* resource; /*裝置資源*/</p><p>struct platform_device_id*id_entry;<br />};</p><p>struct platform_driver {<br />int (*probe)(struct platform_device *);<br />int (*remove)(struct platform_device *);<br />void (*shutdown)(struct platform_device *);<br />int (*suspend)(struct platform_device *, pm_message_t state);<br />int (*suspend_late)(struct platform_device *, pm_message_t state);<br />int (*resume_early)(struct platform_device *);<br />int (*resume)(struct platform_device *);<br />struct device_driver driver;<br />struct platform_device_id *id_table;<br />};
裝置 的分配:
struct platform_device *platform_device_alloc(const char *name, int id); //name:裝置名稱,id:裝置id,一般為-1
裝置的註冊:
int platform_device_add(struct platform_device *pdev);
擷取資源:
int platform_device_add(struct platform_device *pdev);
/*dev:資源所屬的裝置,type:擷取的資源類型,num:擷取的資源數*/
這裡詳述platform_device與platform_driver是怎樣匹配上的,這裡跟蹤函數的執行過程,首先是platform_driver_register:
int platform_driver_register(struct platform_driver *drv)<br />{<br />。。。。。。。。。。<br />return driver_register(&drv->driver);<br />}<br />int driver_register(struct device_driver *drv)<br />{<br />。。。。。。。。。。。<br />ret = bus_add_driver(drv);<br />。。。。。。。。。。。<br />}<br />int bus_add_driver(struct device_driver *drv)<br />{<br />。。。。。。。。。。。。<br />if (drv->bus->p->drivers_autoprobe) {<br />error = driver_attach(drv);<br />if (error)<br />goto out_unregister;<br />}<br />。。。。。。。。。。。。<br />}<br />int driver_attach(struct device_driver *drv)<br />{<br />return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);<br />}
這裡來看__driver_attach這個函數,其中分別調用了driver_match_device,driver_probe_device函數。如果匹配成果調用probe函數,否則返回。
static int __driver_attach(struct device *dev, void *data)<br />{<br />struct device_driver *drv = data;</p><p>/*<br /> * Lock device and try to bind to it. We drop the error<br /> * here and always return 0, because we need to keep trying<br /> * to bind to devices and some drivers will return an error<br /> * simply if it didn't support the device.<br /> *<br /> * driver_probe_device() will spit a warning if there<br /> * is an error.<br /> */</p><p>if (!driver_match_device(drv, dev))<br />return 0;</p><p>if (dev->parent)/* Needed for USB */<br />down(&dev->parent->sem);<br />down(&dev->sem);<br />if (!dev->driver)<br />driver_probe_device(drv, dev);<br />up(&dev->sem);<br />if (dev->parent)<br />up(&dev->parent->sem);</p><p>return 0;<br />}
匹配的時候調用的bus的match函數。
struct bus_type platform_bus_type = {<br />.name= "platform",<br />.dev_attrs= platform_dev_attrs,<br />.match= platform_match,<br />.uevent= platform_uevent,<br />.pm= PLATFORM_PM_OPS_PTR,<br />};
找到platform_match:
static int platform_match(struct device *dev, struct device_driver *drv)<br />{<br />struct platform_device *pdev = to_platform_device(dev);<br />struct platform_driver *pdrv = to_platform_driver(drv);</p><p>/* match against the id table first */<br />if (pdrv->id_table)<br />return platform_match_id(pdrv->id_table, pdev) != NULL;</p><p>/* fall-back to driver name match */<br />return (strcmp(pdev->name, drv->name) == 0);<br />}
最後一行可以看到通過pdev->name與drv->name進行匹配,也就是說是通過裝置與驅動的名字進行匹配。匹配成功後調用驅動的probe函數。
int driver_probe_device(struct device_driver *drv, struct device *dev)<br />{<br />。。。。。。。。。<br />ret = really_probe(dev, drv);<br />。。。。。。。。<br />}<br />static int really_probe(struct device *dev, struct device_driver *drv)<br />{<br />。。。。。。。。<br />if (dev->bus->probe) {<br />ret = dev->bus->probe(dev);<br />if (ret)<br />goto probe_failed;<br />} else if (drv->probe) {<br />ret = drv->probe(dev);<br />if (ret)<br />goto probe_failed;<br />}<br />。。。。。。。。<br />}
由relly_probe函數可以看出,如果bus定義了probe函數,則調用bus的probe函數;如果bus,沒有定義而driver定義了probe函數,則調用driver的probe函數。由上邊的platform_bus_type可以看出bus並沒有定義probe函數,所以調用driver的probe函數。
測試程式:
device.c
#include <linux/module.h><br />#include <linux/init.h><br />#include <linux/device.h><br />#include <linux/string.h><br />#include <linux/platform_device.h></p><p>static struct platform_device *my_device;</p><p>static int __init platform_dev_init(void) {<br />int ret;</p><p>//分配結構<br />my_device = platform_device_alloc("my_dev", -1);<br />//註冊裝置<br />ret = platform_device_add(my_device);</p><p>if(ret)<br />printk("platform_device_add failed!/n");</p><p>return ret;<br />}</p><p>static void __exit platform_dev_exit(void) {<br />platform_device_unregister(my_device);//卸載裝置<br />}</p><p>module_init(platform_dev_init);<br />module_exit(platform_dev_exit);<br />MODULE_LICENSE("GPL");
driver.c
#include <linux/module.h><br />#include <linux/kernel.h><br />#include <linux/init.h><br />#include <linux/device.h><br />#include <linux/string.h><br />#include <linux/platform_device.h></p><p>static int my_probe(struct device *dev) {<br />printk("Driver found device!/n");<br />return 0;<br />}</p><p>static int my_remove(struct device *dev) {<br />printk("Driver found device unpluged!/n");<br />return 0;<br />}<br />//定義platform_driver結構體<br />static struct platform_driver my_driver = {<br />.probe= my_probe,<br />.remove = my_remove,<br />.driver = {<br />.owner = THIS_MODULE,<br />.name = "my_dev",<br />},<br />};</p><p>static int __init my_driver_init(void) {<br />return platform_driver_register(&my_driver);<br />}</p><p>static void __exit my_driver_exit(void) {<br />platform_driver_unregister(&my_driver);<br />}</p><p>module_init(my_driver_init);<br />module_exit(my_driver_exit);<br />MODULE_LICENSE("GPL");
測試效果:
root@hacker:/home/hacker/platform# insmod driver.ko<br />root@hacker:/home/hacker/platform# insmod device.ko<br />root@hacker:/home/hacker/platform# dmesg<br />[ 4499.724439] Driver found device!<br />root@hacker:/home/hacker/platform# rmmod driver.ko<br />root@hacker:/home/hacker/platform# dmesg<br />[ 4499.724439] Driver found device!<br />[ 4513.368712] Driver found device unpluged!<br />root@hacker:/home/hacker/platform# rmmod device.ko</p><p>root@hacker:/home/hacker/platform# insmod device.ko<br />root@hacker:/home/hacker/platform# insmod driver.ko<br />root@hacker:/home/hacker/platform# dmesg<br />[ 4540.509227] Driver found device!<br />root@hacker:/home/hacker/platform# rmmod device.ko<br />root@hacker:/home/hacker/platform# dmesg<br />[ 4540.509227] Driver found device!<br />[ 4545.786076] Driver found device unpluged!<br />root@hacker:/home/hacker/platform# rmmod driver.ko<br />root@hacker:/home/hacker/platform# dmesg<br />[ 4540.509227] Driver found device!<br />[ 4545.786076] Driver found device unpluged!
轉載請標明出處
http://blog.csdn.net/woshixingaaa/archive/2011/05/21/6436172.aspx