如同Linux Kernel(15.3)- The Linux usage model for device tree data所描述,init_machine()透過呼叫of_platform_populate()建構platform device。
kernel v3.5 |-->> msm8x60_dt_init(void) @arch/arm/mach-msm/board-msm8x60.c |--> of_platform_populate() @drivers/of/platform.c |--> of_platform_bus_create() @drivers/of/platform.c /* Make sure it has a compatible property */ 那些具有compatible的node都會被轉成platform device if (strict && (!of_get_property(bus, "compatible", NULL))) { pr_debug("%s() - skipping %s, no compatible prop\n", __func__, bus->full_name); return 0; } ... for_each_child_of_node(bus, child) { pr_debug("create child: %s\n", child->full_name); rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); if (rc) { of_node_put(child); break; } }
接著會在註冊driver時, 呼叫of_driver_match_device()進行match
kernel v3.5 |--> platform_driver_register() @drivers/base/platform.c |--> driver_register() @drivers/base/driver.c |--> bus_add_driver() @drivers/base/bus.c /* 建立sysfs file node 與 attr */ |--> driver_attach() @drivers/base/dd.c |--> bus_for_each_dev(drv->bus, NULL, drv, __driver_attach) @bus.c |--> __driver_attach @drivers/base/dd.c |--> driver_match_device(drv, dev) @base.h |--> platform_match(); @drivers/base/platform.c if (of_driver_match_device(dev, drv)) return 1; |--> __driver_attach @drivers/base/dd.c if (!dev->driver) driver_probe_device(drv, dev); |--> driver_probe_device @drivers/base/dd.c ret = really_probe(dev, drv); |--> really_probe @drivers/base/dd.c if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) goto probe_failed; } else if (drv->probe) { ret = drv->probe(dev); if (ret) goto probe_failed; } ----- @include/linux/of_device.h ----- of_driver_match_device(struct device *dev, const struct device_driver *drv) { return of_match_device(drv->of_match_table, dev) != NULL; } ----- @drivers/of/device.c ----- const struct of_device_id *of_match_device(const struct of_device_id *matches, const struct device *dev) { if ((!matches) || (!dev->of_node)) return NULL; return of_match_node(matches, dev->of_node); } ----- @drivers/of/base.c ----- const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) { if (!matches) return NULL; while (matches->name[0] || matches->type[0] || matches->compatible[0]) { int match = 1; if (matches->name[0]) match &= node->name && !strcmp(matches->name, node->name); if (matches->type[0]) match &= node->type && !strcmp(matches->type, node->type); if (matches->compatible[0]) match &= of_device_is_compatible(node, matches->compatible); if (match) return matches; matches++; } return NULL; }
修改一下DTS驗證一下platform device
/ { node1 { compatible = "brook,dts1"; a-string-property = "A string"; a-string-list-property = "first string", "second string"; // hex is implied in byte arrays. no '0x' prefix is required a-byte-data-property = [01 23 34 56]; child-node1 { first-child-property; second-child-property = <1>; a-string-property = "Hello, world"; }; child-node2 { }; }; };
/ # ls /sys/bus/platform/devices 10000000.sysreg 10002000.i2c ... node1 ... / # cd /sys/bus/platform/devices/node1/ /sys/devices/platform/node1 # ls driver_override of_node subsystem modalias power uevent /sys/devices/platform/node1 # ls of_node/ a-byte-data-property child-node1 name a-string-list-property child-node2 a-string-property compatible /sys/devices/platform/node1 # ftpget 192.168.1.1 /tmp/brook_modules.ko /home/brook/my_driver/brook_modules.ko /sys/devices/platform/node1 # insmod /tmp/brook_modules.ko brook_modules: loading out-of-tree module taints kernel. brook_init(#55) brook_probe(#21)
基本上與傳統的platform device driver的差異是:
- device由DTS的compatible產生,無須呼叫platform_device_register()註冊device
- 需在platform_driver.driver.of_match_table中掛上of_device_id[],裡面的compatible會與DTS的compatible進行比對
#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_device.h> MODULE_AUTHOR("Brook"); MODULE_DESCRIPTION("Kernel module for demo"); MODULE_LICENSE("GPL"); #define DEVNAME "brook" static struct platform_device brook_device = { .name = DEVNAME, }; static int brook_probe(struct platform_device *pdev) { pr_info("%s(#%d)\n", __func__, __LINE__); return 0; } static int brook_remove(struct platform_device *pdev) { pr_info("%s(#%d)\n", __func__, __LINE__); return 0; } static struct of_device_id brook_dt_ids[] = { { .compatible = "brook,dts1", }, { .compatible = "brook,dts2", }, { } }; MODULE_DEVICE_TABLE(of, brook_dt_ids); static struct platform_driver brook_driver = { .driver = { .name = DEVNAME, .owner = THIS_MODULE, .of_match_table = brook_dt_ids, }, .probe = brook_probe, .remove = brook_remove, }; static int __init brook_init(void) { int ret; pr_info("%s(#%d)\n", __func__, __LINE__); ret = platform_driver_register(&brook_driver); if (ret) { dev_err(&(brook_device.dev), "%s(#%d): platform_driver_register fail(%d)\n", __func__, __LINE__, ret); } return ret; } module_init(brook_init); static void __exit brook_exit(void) { dev_info(&(brook_device.dev), "%s(#%d)\n", __func__, __LINE__); platform_driver_unregister(&brook_driver); } module_exit(brook_exit);
-
參考資料:
- http://wiki.dreamrunner.org/public_html/Embedded-System/Linux-Device-Tree.html, Linux Device tree
- Linux Kernel(15.3)- The Linux usage model for device tree data
- http://wiki.100ask.org/%E7%AC%AC%E4%B8%89%E8%AF%BE:%E5%86%85%E6%A0%B8%E5%AF%B9%E8%AE%BE%E5%A4%87%E6%A0%91%E7%9A%84%E5%A4%84%E7%90%86, 内核对设备树的处理