Device Tree
Введение
Данная статья посвящена механизму описания структуры периферии, использующемуся в частности в ядре Linux для архитектур armh и aarch64.
Одноплатные компьютеры семейств RaspberryPi и OrangePi и многие прочие подобные используют в качестве процессора микросхему - систему на кристалле (SoC). Микросхема SoC нуждается только во внешнем ОЗУ, при этом она реализует в одном чипе множество возможных, часто взаимоисключающих конфигураций периферии интерфейсов ввода-вывода.
Таким образом должен быть механизм, позволяющий передать ядру, какая из конфигураций соответствует тому, что распаяно на печатной плате, а также какое поведение инженер-разработчик ожидает от внешних универсальных контактов (штырьков) ввода-вывода платы (GPIO). Данным механизмом и является описываемое здесь дерево устройств - Device Tree.
Форматы файлов
*.dtb файл
Device Tree Binary - иерархическое описание структуры периферии в бинарном формате. Именно в таком формате оно подргужается в ядро Linux в качестве аргумента через загрузчик. Ядро использует структуру, чтобы узнать о периферийных устройствах - какие включены, какие модули ядра задействовать, символические имена.
*.dts файл
Device Tree Source (aka Strings) - представление дерева устройств в человекочитаемом формате, в виде исходного кода. Синтаксис языка похож на JSON. Для связи с описанием в *.dtb формате используется утилита dtc.
Компилятор/декомпилятор dtc
Для работы с описанием дерева устройств предназначен компилятор dtc (пакет dtc).
Для конвертации бинарного дерева устройств в текстовое представление:
dtc $NAME.dtb -o $NAME.dts
Для компиляции текстового описания дерева устройств обратно в бинарное представление:
dtc $NAME.dts -o $NAME.dtb
Оверлеи *.dtbo
В специализированном дистрибутиве Raspbian реализован механизм, позволяющий во время загрузки накладывать на *.dtb файлы патчи. Такой патч, включающий какую-то аппаратную возможность платы, называется оверлеем. Формат файла - такой же, как у .dtb - файл можно прочитать при помощи утилиты dtc.
Файлы находятся в папке /boot/dtb/overlay системы Raspbian. Исследование их помогает понять, как исправить дерево устройств, чтобы включить какую-либо нужную фичу.
Вроде бы, загрузчик U-Boot умеет их применять. Данный вопрос требует дальнейшего исследования.
Настройки
Ванильные *.dtb файлы ядра
Вместе с ядром Linux (проверено для ядер для 5.*) поставляется библиотека *.dtb файлов под разные одноплатные компьютеры, модули и системы. При установке ядра файлы оказываются в папке
/lib/devicetree/`uname -r`
Для каждого чипа и платы имеется свой файл - одна и та же микросхема может оказаться на разных платах разных производителей.
Настройка устройств под себя
Конфигурации *.dtb, поставляемые вместе с ядром для каждой платы, описывают некоторую базовую конфигурацию, которая подойдёт всем. Учитывая настраиваемость платы, однако же, может потребоваться эту конфигурацию изменить. Для этого следует вскрыть *.dtb файл утилитой dtc, отредактировать его в *.dts формате и сохранить обратно. Какой из многих файлов загружается - можно посмотреть в выводе загрузчика U-Boot во время старта системы, этот файл будет единственный с именем, соответствующем модели платы.
Обновление ядра
Очевидна проблема - при обновлении ядра исправления будут утеряны. Это не является проблемой, если сборка подготавливается для встраиваемой системы, которую не будут обновлять. Иначе следовало бы написать скрипт, делающий исправления автоматически, и прописать его как-то в механизм /etc/initrd.mk. Эта секция требует уточнения.
Синтаксис
Синтаксис *.dts файла достаточно понятен (очевидно!) =) Лучший способ исследования - вскрыть файл и посмотреть, что там внутри.
Приглашаю всех практикующих инженеров писать сюда свои примеры ;)
Включение и отключение устройств
В исходном, "ванильном" дереве многие устройства описаны, но отключены. Примеры таких устройств - контроллеры шины i2c, контроллер uart (serial). Для включения устройства достаточно поправить один параметр.
Отключённое устройство имеет в своём описании поле:
{ status = "disabled"; }
Для включения устройства следует исправить:
{ status = "okay"; }