Принцип наложения дерева устройств ядра Linux и сценарии использования
Принцип наложения дерева устройств ядра Linux и сценарии использования

Начало работы с наложением дерева устройств ядра Linux

Предисловие

Эта статья основана на ядре Linux версии 5.15 и призвана проанализировать принцип работы и сценарии применения Linux Device Tree Overlay (DTO).

Введение в принцип наложения дерева устройств (DTO)

Так называемое «наложение дерева устройств» относится к процессу динамического изменения текущего активного дерева устройств (живого дерева устройств), которое включает добавление или удаление подустройств, а также расширение атрибутов узла устройства. Этот процесс в основном включает в себя два ключевых этапа:

  1. Как загрузить и применить DTBO:первый,Файл наложения дерева устройств (.dtbo) необходимо загрузить в систему.,и примените его к окончательному дереву устройств.
  2. Как уведомить компонент о создании устройства:Во-вторых,Соответствующие компоненты необходимо уведомить о создании конкретных экземпляров устройств на основе обновленных узлов дерева устройств.,Наконец, вызывает сопоставление устройства и драйвера устройства.

Зачем мне нужно наложение дерева устройств?

При разработке драйверов устройств ядра Linux традиционный подход заключается в изменении исходного файла дерева устройств (DTS) и кода драйвера, затем компиляции и генерации нового образа ядра, записи его на SSD или другой носитель и, наконец, перезагрузки системы. проверьте корректность драйвера.

Технология наложения дерева устройств позволяет нам динамически изменять активное дерево устройств во время выполнения, что означает, что нет необходимости перекомпилировать дерево устройств или перезапускать систему. Такой механизм значительно повышает гибкость и эффективность разработки и отладки.

Примеры сценариев применения

Предположим, аппаратная платформа использует устройство A версии V1, а в версии V2 устройство обновляется до устройства B. Если группа разработчиков программного обеспечения платформы поддерживает только один набор баз кода Linux, то с помощью наложения дерева устройств соответствующий файл .dtbo может быть динамически выбран в соответствии с версией оборудования, так что один и тот же набор кода может удовлетворить потребности как V1, так и версии V1. Версии оборудования V2, значительно повышающие гибкость и удобство обслуживания проекта.

Короче говоря, наложение дерева устройств обеспечивает эффективный и гибкий механизм управления устройствами для ядра Linux, который особенно подходит для сценариев, требующих динамической настройки конфигурации оборудования или поддержки нескольких версий оборудования.

Как это работает:

Целью наложения дерева устройств является изменение живого дерева ядра и влияние на состояние ядра таким образом, чтобы оно отражало изменения. Поскольку ядро ​​имеет дело в первую очередь с устройствами, любые новые узлы устройств, которые вызывают активацию устройства, должны быть созданы, тогда как если узел устройства отключен или полностью удален, затронутое устройство должно быть отменено.

Если взять в качестве примера доску foo, то базовое дерево выглядит следующим образом:

Язык кода:dts
копировать
    ---- foo.dts ---------------------------------------------------------------
	/* FOO platform */
	/dts-v1/;
	/ {
		compatible = "corp,foo";

		/* shared resources */
		res: res {
		};

		/* On chip peripherals */
		ocp: ocp {
			/* peripherals that are always instantiated */
			peripheral1 { ... };
		};
	};
    ---- foo.dts ---------------------------------------------------------------

Фрагмент наложения bar.dts выглядит следующим образом:

Язык кода:dts
копировать
    ---- bar.dts - overlay target location by label ----------------------------
	/dts-v1/;
	/plugin/;
	&ocp {
		/* bar peripheral */
		bar {
			compatible = "corp,bar";
			... /* various properties and child nodes */
		};
	};
    ---- bar.dts ---------------------------------------------------------------

После слияния должно быть

Язык кода:dts
копировать
    ---- foo+bar.dts -----------------------------------------------------------
	/* FOO platform + bar peripheral */
	/ {
		compatible = "corp,foo";

		/* shared resources */
		res: res {
		};

		/* On chip peripherals */
		ocp: ocp {
			/* peripherals that are always instantiated */
			peripheral1 { ... };

			/* bar peripheral */
			bar {
				compatible = "corp,bar";
				... /* various properties and child nodes */
			};
		};
	};
    ---- foo+bar.dts -----------------------------------------------------------

В результате переопределения создается новый узел устройства (стержень), поэтому устройство платформы стержня будет зарегистрировано, и если соответствующий драйвер устройства загружен, устройство будет создано, как ожидалось.

Если базовое дерево устройств не было скомпилировано с опцией -@,Так"&ocp"Метки не будут использоваться для сопоставления узла наложения с правильным местоположением в базовом дереве устройств.。в этом случае,Можно указать путь назначения. Поскольку переопределение можно применить к любому базовому дереву устройств, содержащему тег,Независимо от того, где в дереве устройств появляется метка,Поэтому для указания целевого местоположения предпочтительнее использовать синтаксис тегов.

Пример приведенного выше файла bar.dts, модифицированного для использования синтаксиса целевого пути:

Язык кода:dts
копировать
---- bar.dts - Переопределить целевое местоположение с помощью явного указания пути --------------------------------
/dts-v1/;
/plugin/;
&{/ocp} {
	/* периферийные устройства для бара */
	bar {
		compatible = "corp,bar";
		... /* Различные атрибуты и подузлы */
	}
};
---- bar.dts ---------------------------------------------------------------

Основной API наложения в основном вызывает следующие два интерфейса для реализации наложения.

Язык кода:C
копировать
int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size,
			 int *ret_ovcs_id)
int of_overlay_remove(int *ovcs_id)

of_overlay_fdt_applyЭта функция,Код в основном разделен на два ключевых этапа.,первый,Это содержимое файла dtbo,Вставить в текущее дерево устройств,затем отправить уведомление,Уведомите каждый компонент Device Tree об изменении платформы, spi, i2c и других основных уровней, подписавшихся на это сообщение.,То есть они осознали свои соответствующиеnotifier_callфункция обратного вызова。

Язык кода:C
копировать
int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, int *ret_ovcs_id)
		overlay_mem = of_fdt_unflatten_tree(new_fdt_align, NULL, &ovcs->overlay_root);
		ret = of_overlay_apply(ovcs);
			ret = of_resolve_phandles(ovcs->overlay_root);
			ret = init_overlay_changeset(ovcs);
			ret = overlay_notify(ovcs, OF_OVERLAY_PRE_APPLY);
			ret = build_changeset(ovcs);
			ret = __of_changeset_apply_entries(&ovcs->cset, &ret_revert);
			ret = __of_changeset_apply_notify(&ovcs->cset);
				ret_tmp = __of_changeset_entry_notify(ce, 0);
					ret = of_reconfig_notify(ce->action, &rd);
						rc = blocking_notifier_call_chain(&of_reconfig_chain, action, p)
							ret = nb->notifier_call(nb, val, v);
					ret = of_property_notify(ce->action, ce->np, ce->prop, ce->old_prop);
			ret_tmp = overlay_notify(ovcs, OF_OVERLAY_POST_APPLY);

Overlay использует технологию цепочки уведомлений ядра. Платформа, spi, i2c и другие шины подписываются на соответствующие события при инициализации инфраструктуры драйвера. При применении dt overlay будет выдано уведомление, и все компоненты, которые подписываются на это уведомление, смогут его получить. к этому сообщению и обработать его соответствующим образом. Если будет обнаружено, что обновление узла не связано с самим собой, оно вернется напрямую. Как этот компонент связан сам с собой?

Соответствующее устройство будет зарегистрировано и будет запущено сопоставление устройства и драйвера устройства.

Язык кода:C
копировать
gpiolib.c	4395 WARN_ON(of_reconfig_notifier_register(&gpio_of_notifier)); in gpiolib_dev_init()
spi.c	4344 WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); in spi_init()
platform.c	730 WARN_ON(of_reconfig_notifier_register(&platform_of_notifier)); in of_platform_register_reconfig_notifier()
i2c-core-base.c	1985 WARN_ON(of_reconfig_notifier_register(&i2c_of_notifier)); in i2c_init()

Обратный вызов Platform_of_notifier выглядит следующим образом. Обратите внимание на 14 строк кода. Если он вам не нужен, он будет возвращен напрямую.

Язык кода:C
копировать
#ifdef CONFIG_OF_DYNAMIC
static int of_platform_notify(struct notifier_block *nb,
				unsigned long action, void *arg)
{
	struct of_reconfig_data *rd = arg;
	struct platform_device *pdev_parent, *pdev;
	bool children_left;
	int ret;

	switch (of_reconfig_get_state_change(action, rd)) {
	case OF_RECONFIG_CHANGE_ADD:
		/* verify that the parent is a bus */
		if (!of_node_check_flag(rd->dn->parent, OF_POPULATED_BUS))
			return NOTIFY_OK;	/* not for us */

		/* already populated? (driver using of_populate manually) */
		if (of_node_check_flag(rd->dn, OF_POPULATED))
			return NOTIFY_OK;

		/* pdev_parent may be NULL when no bus platform device */
		pdev_parent = of_find_device_by_node(rd->dn->parent);
		ret = of_platform_bus_create(rd->dn, of_default_bus_match_table,
					     NULL, pdev_parent ?
					     &pdev_parent->dev : NULL, true);
		platform_device_put(pdev_parent);

		if (ret) {
			pr_err("%s: failed to create for '%pOF'\n",
					__func__, rd->dn);
			/* of_platform_device_create tosses the error code */
			return notifier_from_errno(ret);
		}
		break;

	case OF_RECONFIG_CHANGE_REMOVE:

		/* already depopulated? */
		if (!of_node_check_flag(rd->dn, OF_POPULATED))
			return NOTIFY_OK;

		/* find our device by node */
		pdev = of_find_device_by_node(rd->dn);
		if (pdev == NULL)
			return NOTIFY_OK;	/* no? not meant for us */

		/* unregister takes one ref away */
		of_platform_device_destroy(&pdev->dev, &children_left);

		/* and put the reference of the find */
		platform_device_put(pdev);
		break;
	}

	return NOTIFY_OK;
}

static struct notifier_block platform_of_notifier = {
	.notifier_call = of_platform_notify,
};

Реализация spi_of_notifier выглядит следующим образом:

Язык кода:C
копировать
static int of_spi_notify(struct notifier_block *nb, unsigned long action,
			 void *arg)
{
	struct of_reconfig_data *rd = arg;
	struct spi_controller *ctlr;
	struct spi_device *spi;

	switch (of_reconfig_get_state_change(action, arg)) {
	case OF_RECONFIG_CHANGE_ADD:
		ctlr = of_find_spi_controller_by_node(rd->dn->parent);
		if (ctlr == NULL)
			return NOTIFY_OK;	/* not for us */

		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
			put_device(&ctlr->dev);
			return NOTIFY_OK;
		}

		spi = of_register_spi_device(ctlr, rd->dn);
		put_device(&ctlr->dev);

		if (IS_ERR(spi)) {
			pr_err("%s: failed to create for '%pOF'\n",
					__func__, rd->dn);
			of_node_clear_flag(rd->dn, OF_POPULATED);
			return notifier_from_errno(PTR_ERR(spi));
		}
		break;

	case OF_RECONFIG_CHANGE_REMOVE:
		/* already depopulated? */
		if (!of_node_check_flag(rd->dn, OF_POPULATED))
			return NOTIFY_OK;

		/* find our device by node */
		spi = of_find_spi_device_by_node(rd->dn);
		if (spi == NULL)
			return NOTIFY_OK;	/* no? not meant for us */

		/* unregister takes one ref away */
		spi_unregister_device(spi);

		/* and put the reference of the find */
		put_device(&spi->dev);
		break;
	}

	return NOTIFY_OK;
}

static struct notifier_block spi_of_notifier = {
	.notifier_call = of_spi_notify,
};

Реализация i2c_of_notifier выглядит следующим образом:

Язык кода:C
копировать
#if IS_ENABLED(CONFIG_OF_DYNAMIC)
static int of_i2c_notify(struct notifier_block *nb, unsigned long action,
			 void *arg)
{
	struct of_reconfig_data *rd = arg;
	struct i2c_adapter *adap;
	struct i2c_client *client;

	switch (of_reconfig_get_state_change(action, rd)) {
	case OF_RECONFIG_CHANGE_ADD:
		adap = of_find_i2c_adapter_by_node(rd->dn->parent);
		if (adap == NULL)
			return NOTIFY_OK;	/* not for us */

		if (of_node_test_and_set_flag(rd->dn, OF_POPULATED)) {
			put_device(&adap->dev);
			return NOTIFY_OK;
		}

		client = of_i2c_register_device(adap, rd->dn);
		if (IS_ERR(client)) {
			dev_err(&adap->dev, "failed to create client for '%pOF'\n",
				 rd->dn);
			put_device(&adap->dev);
			of_node_clear_flag(rd->dn, OF_POPULATED);
			return notifier_from_errno(PTR_ERR(client));
		}
		put_device(&adap->dev);
		break;
	case OF_RECONFIG_CHANGE_REMOVE:
		/* already depopulated? */
		if (!of_node_check_flag(rd->dn, OF_POPULATED))
			return NOTIFY_OK;

		/* find our device by node */
		client = of_find_i2c_device_by_node(rd->dn);
		if (client == NULL)
			return NOTIFY_OK;	/* no? not meant for us */

		/* unregister takes one ref away */
		i2c_unregister_device(client);

		/* and put the reference of the find */
		put_device(&client->dev);
		break;
	}

	return NOTIFY_OK;
}

struct notifier_block i2c_of_notifier = {
	.notifier_call = of_i2c_notify,
};

of_platform_bus_create, of_register_spi_device, of_i2c_register_device Эти три функции отвечают за создание устройства, соответствующего устройству, тем самым запуская сопоставление устройства и драйвера. Эти три функции связаны с моделью устройства Linux и будут представлены в последующих статьях.

При фактическом использовании наложения дерева устройств основной API наложения может быть инкапсулирован в системный узел, а наложение дерева устройств может быть реализовано путем управления sys узлом.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose