linux核心device生成流程
阿新 • • 發佈:2018-12-10
一、前言
在驅動模型的框架下,裝置驅動的開發主要包含以下兩個步驟: 步驟1:分配一個struct device型別的變數,填充必要的資訊後,把它註冊到核心中。 步驟2:分配一個struct device_driver型別的變數,填充必要的資訊後,把它註冊到核心中。 上述兩個步驟完成後,核心會在合適的時機(註冊device、註冊device_driver等)執行probe等回撥函式,那麼每個裝置對應的struct device結構是何時建立的呢?本文主要針對這個問題分析。 注:本文涉及的程式碼基於linux 3.10版本
二、device初始化流程
對於平臺裝置等無法熱插拔的裝置,必須在核心初始化的時候就生成對應的struct device結構體。而對於掛在真正匯流排(如i2c、usb等)上的裝置,struct device結構體由對應的匯流排驅動在初始化或裝置熱插拔時生成。本文只對前一種裝置做介紹。 核心啟動過程中,會呼叫arm64_device_init函式,程式碼如下:
const struct of_device_id of_default_bus_match_table[] = { { .compatible = "simple-bus", }, #ifdef CONFIG_ARM_AMBA { .compatible = "arm,amba-bus", }, #endif /* CONFIG_ARM_AMBA */ {} /* Empty terminated list */ }; static int __init arm64_device_init(void) { of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); return 0; } arch_initcall_sync(arm64_device_init);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
從arch_initcall_sync的巨集定義我們可以知道,arch_initcall_sync的優先順序是比較高的,相比於驅動的初始化要早,也就是說在驅動的初始化前,核心已經對平臺裝置進行了初始化
int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, struct device *parent) { struct device_node *child; int rc = 0; root = root ? of_node_get(root) : of_find_node_by_path("/"); ---(1) if (!root) return -EINVAL; for_each_child_of_node(root, child) { ---(2) rc = of_platform_bus_create(child, matches, lookup, parent, true); ---(3) if (rc) break; } of_node_put(root); return rc; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
程式碼(1)解析:
從前面傳入的引數可知,root=NULL,因此執行流程of_find_node_by_path,of_find_node_by_path函式程式碼如下:
struct device_node *of_find_node_by_path(const char *path)
{
struct device_node *np = of_allnodes;
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
for (; np; np = np->allnext) {
if (np->full_name && (of_node_cmp(np->full_name, path) == 0)
&& of_node_get(np))
break;
}
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return np;
}
程式碼(2)解析:
遍歷dts中根節點下面的所有子節點。
程式碼(3)解析:
of_platform_bus_create函式程式碼如下:
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, "compatible", NULL))) { //忽略沒有compatible屬性的節點
pr_debug("%s() - skipping %s, no compatible prop\n",
__func__, bus->full_name);
return 0;
}
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata->name;
platform_data = auxdata->platform_data;
}
if (of_device_is_compatible(bus, "arm,primecell")) { //特殊節點處理,不深入
of_amba_device_create(bus, bus_id, platform_data, parent);
return 0;
}
/*這個函式是真正生成struct device的地方*/
dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
/*如果compatible屬性不是"simple-bus"和"arm,amba-bus"則在返回,不繼續遍歷子節點。這裡我的理解是"simple-bus"和"arm,amba-bus"這兩種裝置不具備熱插拔能力,因此在這裡就先建立了struct device*/
if (!dev || !of_match_node(matches, bus))
return 0;
/*對於"simple-bus"和"arm,amba-bus"裝置要繼續遍歷子節點,並建立對應的 struct device*/
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;
}
}
return rc;
}
具體建立device的程式碼在of_platform_device_create_pdata中,程式碼如下:
struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
{
struct platform_device *dev;
if (!of_device_is_available(np))
return NULL;
/* of_device_alloc除了分配struct platform_device的記憶體,還分配了該platform device需要的resource的記憶體(參考struct platform_device 中的resource成員)。當然,這就需要解析該device node的interrupt資源以及memory address資源,這些資源的原始資料都來自dtb中。*/
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
return NULL;
#if defined(CONFIG_MICROBLAZE)
dev->archdata.dma_mask = 0xffffffffUL;
#endif
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
if (!dev->dev.dma_mask)
dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
dev->dev.bus = &platform_bus_type;
dev->dev.platform_data = platform_data;
of_reserved_mem_device_init(&dev->dev);
if (of_device_add(dev) != 0) {//把這個device加入到裝置模型中,後續驅動註冊的時候就可以匹配到了
platform_device_put(dev);
of_reserved_mem_device_release(&dev->dev);
return NULL;
}
return dev;
}