Принцип наложения дерева устройств ядра 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
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода