LinuxカーネルDTSによるデバイス作成プロセス分析(Android 5.1)

6634 ワード

私たちはMACHINEからSTART分析開始.マクロ定義MACHINEの検索STARTのすべてのファイル発見:このマクロ定義は「kernelarcharmincludeasmmach」ディレクトリの「arch.h」であり、それ以外のファイルはこのマクロ定義の使用ファイルであり、例えばディレクトリ「kernelarcharmmach-sc」の下で「board-sp 7731 gea_hd.c」というファイルである.
static const char *sprd_boards_compat[] __initdata = {
	"sprd,sp8835eb",
	NULL,
};
MACHINE_START(SCPHONE, "sc8830")
	.smp		= smp_ops(sprd_smp_ops),
	.reserve	= sci_reserve,
	.map_io		= sci_map_io,
	.init_irq	= sci_init_irq,
	.init_time	= sprd_init_time,
	.init_machine	= sc8830_init_machine,
	.init_late	= sc8830_init_late,
	.dt_compat	= sprd_boards_compat,
MACHINE_END

そのうちdt_compatという値はdtsのcompatibleと一致します.デバイスを作成したのはsc 8830_です.init_machineという関数、この関数で呼び出されたof_を見てみましょう.platform_populate関数.
static void __init sc8830_init_machine(void)
{
	printk("sci get chip id = 0x%x
",__sci_get_chip_id()); //sci_adc_init((void __iomem *)ADC_BASE); sci_adc_init(); sci_regulator_init(); of_sprd_default_bus_lookup[0].phys_addr = 0x20300000; of_sprd_default_bus_lookup[1].phys_addr = 0x20400000; of_sprd_default_bus_lookup[2].phys_addr = 0x20500000; of_sprd_default_bus_lookup[3].phys_addr = 0x20600000; of_platform_populate(NULL, of_sprd_default_bus_match_table, of_sprd_default_bus_lookup, NULL); sprd_sr2351_vddwpa_ctrl_power_register(); }

コアワークはfor_each_child_of_Node関数は、ルートデバイスノードの下にあるすべてのサブノードを巡り、of_を実行します.platform_bus_create関数は、各サブノードに対してプラットフォームバスデバイスの作成を行う
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("/");
	if (!root)
		return -EINVAL;

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc)
			break;
	}

	of_node_put(root);
	return rc;
}
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))) {
		pr_debug("%s() - skipping %s, no compatible prop
", __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; } dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent); if (!dev || !of_match_node(matches, bus)) return 0; for_each_child_of_node(bus, child) { pr_debug(" create child: %s
", child->full_name); rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict); if (rc) { of_node_put(child); break; } } return rc; }

プラットフォームバスデバイスが作成する主なワークフローは、まずof_を通過することです.get_property関数このプラットフォームバスデバイスノードがcompatible属性を持っているかどうかを確認し、通過後にof_の実行を開始するplatform_device_create_pdata関数は本当にプラットフォームのデバイスコンテンツを作成して、ここまで実はすでに1つのデバイスノードがマッチングから最終的にデバイスコンテンツを作成する完全な流れを完成したと言えるが、DTCファイルを知っている友达は、このデバイスノードの下に他のデバイスのサブノードがあることを知っていて、それからサブノードのサブノードとデバイスノードがあることを知っている......など、このようにして構成された巨大な木の枝の設備の木の構造は、どうすればいいのでしょうか.下を見るとわかりますが、実は簡単で、どれだけのデバイスのノードがあっても、私たちのところに呼び出してデバイスの内容を本当に作成すれば解決するのではないでしょうか.はい、そうです.これからfor_を使います.each_child_of_Node関数は現在のプラットフォームバスデバイスノードのすべてのサブノードを遍歴し、再帰的な実行of_platform_bus_create関数は、各プラットフォームバスデバイスの作成を行い、その作成が完了するたびに、of_を呼び出す.node_put関数は、現在のノードのメモリを解放する.ここまで分析すると、私は再帰的な使い方をもっと深く理解しています.
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;

	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);
	dev->dev.bus = &platform_bus_type;
	dev->dev.platform_data = platform_data;

	/* We do not fill the DMA ops for platform devices by default.
	 * This is currently the responsibility of the platform code
	 * to do such, possibly using a device notifier
	 */

	if (of_device_add(dev) != 0) {
		platform_device_put(dev);
		return NULL;
	}

	return dev;
}

主に、of_device_is_AVailable関数は、デバイスノードが有効かどうかを確認し、of_を実行します.device_alloc関数はデバイスメモリを割り当て、デバイスがあれば、デバイスの一部の内容を初期化してバラバラを初期化し、最終的にof_を呼び出す.device_add関数はカーネルにデバイスを追加する.
デバイスのコンテンツをどのように割り当てるかを見てみましょうof_device_alloc関数:
struct platform_device *of_device_alloc(struct device_node *np,
				  const char *bus_id,
				  struct device *parent)
{
	struct platform_device *dev;
	int rc, i, num_reg = 0, num_irq;
	struct resource *res, temp_res;

	dev = platform_device_alloc("", -1);
	if (!dev)
		return NULL;

	/* count the io and irq resources */
	if (of_can_translate_address(np))
		while (of_address_to_resource(np, num_reg, &temp_res) == 0)
			num_reg++;
	num_irq = of_irq_count(np);

	/* Populate the resource table */
	if (num_irq || num_reg) {
		res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
		if (!res) {
			platform_device_put(dev);
			return NULL;
		}

		dev->num_resources = num_reg + num_irq;
		dev->resource = res;
		for (i = 0; i < num_reg; i++, res++) {
			rc = of_address_to_resource(np, i, res);
			WARN_ON(rc);
		}
		WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
	}

	dev->dev.of_node = of_node_get(np);
#if defined(CONFIG_MICROBLAZE)
	dev->dev.dma_mask = &dev->archdata.dma_mask;
#endif
	dev->dev.parent = parent;

	if (bus_id)
		dev_set_name(&dev->dev, "%s", bus_id);
	else
		of_device_make_bus_id(&dev->dev);

	return dev;
}

まずplatform_を呼び出すdevice_alloc関数は定義されたplatformです.デバイス構造体タイプのデバイスの割り当てメモリ、次にof_をそれぞれ呼び出すaddress_to_义齿irq_countの2つの関数は、デバイスが使用するアドレスと割り込みリソースの数を統計し、上記のリソースが存在する場合、それぞれof_を呼び出す.address_to_义齿irq_to_resource_tableの2つの関数は、デバイスにアドレスとリソース割り当てを中断し、上記のデバイスの初期化作業が完了すると、of_を呼び出すなど、さらに初期化される.node_get関数は、デバイスが属するデバイスツリーノードを初期化し、最終的にdev_を実行します.set_nameまたはof_device_make_bus_id関数は、デバイスの名前を設定したり、デバイスバスIDを設定したりします.