Les derniers noyaux et firmware du Raspberry Pi, y compris Raspbian et NOOBS, utilisent maintenant par défaut Device Tree (Arborescence Matérielle) pour gérer certaines allocations des ressources et les chargements de modules. L’objectif de ce changement est de diminuer le problème d’accès concurrent aux ressources du système par plusieurs pilotes, et de permettre la configuration automatique des modules HAT.
Les Device Tree et le Raspberry Pi
Ce billet s’adresse plutôt à des utilisateurs avancés
qui sont amenés à intervenir au niveau du système,
et à utiliser les bus externes I2C, I2C et SPI.
<== C’est une traduction d’un article paru sur raspberrypi.org ==>
Depuis quelques années, un important changement a eu lieu dans le support de l’architecture ARM du noyau Linux : le passage à une description du matériel appelée le Device Tree. Tout développeur souhaitant aujourd’hui porter le noyau Linux sur une plateforme ARM (que ce soit une nouvelle carte ou un nouveau processeur) doit désormais comprendre ce nouveau mécanisme et l’utiliser: c’est devenu une connaissance indispensable du développeur Linux embarqué.
L’implémentation actuelle n’est pas un système d’arborescence des périphériques pur – il y a encore du code qui s’occupe des composants présents sur la carte – mais les interfaces externes (I2C, I2S spi) et les périphériques audio qui les utilisent doivent maintenant être créés à l’aide d’un Blob d’Arborescence Matérielle (DTB = Device Tree Blob) transmis au noyau par le loader (start.elf). (voir la page 11 de Device Tree for the Dummies)
Le principal impact de l’utilisation de l’Arborescence Matérielle est de changer d’approche. On part d’une situation où tout est validé, une liste noire (blacklist) permettant d’éliminer les modules inutiles. Ici rien n’est validé, sauf les modules indiqués par la DTB. Si vous voulez continuer à utiliser des interfaces externes ainsi que les périphériques qui s’y rattachent, vous allez devoir ajouter de nouveaux paramètres à votre fichier config.txt. La partie 3 de cet article donne des informations plus détaillées, en attendant voici quelques exemples :
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
# Uncomment one of these lines to enable an audio interface
#dtoverlay=hifiberry-amp
#dtoverlay=hifiberry-dac
#dtoverlay=hifiberry-dacplus
#dtoverlay=hifiberry-digi
#dtoverlay=iqaudio-dac
#dtoverlay=iqaudio-dacplus
# Uncomment this to enable the lirc-rpi module
#dtoverlay=lirc-rpi
# Uncomment this to override the defaults for the lirc-rpi module
#dtparam=gpio_out_pin=16
#dtparam=gpio_in_pin=17
#dtparam=gpio_in_pull=down
Partie 1 : Les Arborescences Matérielles (DT)
Une Arborscence Matérielle (DT = Device Tree) est une façon de décrire le matériel présent dans un système. Il doit inclure le nom de la CPU (microprocesseur) de base, sa configuration mémoire et les périphériques (internes et externes) qui y sont reliés. Une DT ne doit pas être utilisée pour décrire le logiciel, même si en énumérant les modules matériels elle provoque habituellement le chargement des pilotes pour ces modules. Il peut être utile de rappeler que les DT sont censées être neutres par rapport à l’OS, donc tout ce qui est spécifique à Linux n’a probablement pas lieu de s’y trouver…
Les DT représentent la configuration matérielle sous la forme d’une hiérarchie de nœuds. Chaque nœud peut contenir des propriétés ainsi que des sous-noeuds. Les propriétés sont appelées tableaux d’octets, et peuvent contenir des chaînes, des nombres (big-endian), des séquences arbitraires d’octets… Par analogie avec un système de fichiers, les nœuds seraient les répertoires et les propriétés seraient les fichiers. Les emplacements des nœuds et des propriétés au sein de l’arborescence peuvent être décrits à l’aide d’un chemin, avec des barres obliques comme séparateurs et une seule barre oblique (/) pour indiquer la racine.
Syntaxe de base des DT
[Cette section emprunte beaucoup à devicetree.org]
Les Arborescences Matérielles sont généralement écrites sous forme de texte, appelés Source de l’Arborscence Matérielle (DTS = Device Tree Source) et stockées dans des fichiers ayant un suffixe .dts.
DTS est une syntaxe C-like, avec des accolades pour le regroupement et des points-virgules à la fin de chaque ligne. ATTENTION /!\. DTS nécessite des points-virgules après les accolades fermantes – pensez plutôt aux structures du C plutôt qu’aux fonctions.
Le format binaire compilé est appelé Arborescence Matérielle Aplatie (FDT = Flattened Device Tree) ou Blob d’Arborescence Matérielle (DTB = Device Tree Blob), et est stocké dans des fichiers .dtb.
Voici un exemple d’arborescence simple au format .dts :
/dts-v1/;
/include/ "common.dtsi";
/ {
node1 {
a-string-property = "A string";
a-string-list-property = "first string", "second string";
a-byte-data-property = [0x01 0x23 0x34 0x56];
cousin: child-node1 {
first-child-property;
second-child-property = ;
a-string-property = "Hello, world";
};
child-node2 {
};
};
node2 {
an-empty-property;
a-cell-property = < 1 2 3 4>; /* chaque nombre (cell) est un uint32 */
child-node1 {
my-cousin = <&cousin>;
};
};
};
/node2 {
another-property-for-node2;
};
Cet arbre contient
- Une liste d’Item
- Un en-tête obligatoire — / dts-v1 /.
- L’inclusion d’un autre fichier DTS, classiquement appelé *.dtsi, analogue à un fichier d’en-tête .h en C – voir ci dessous « Un aparté sur /include/ ».
- Un seul nœud racine : /
- Deux nœuds enfants : node1 et node2 .
- Des enfants pour node1: child-node1 et child-node2 .
- Un label (étiquette) ( cousin ) et une référence à ce label ( &cousin ) – voir les labels et références ci-dessous.
- Un tas de propriétés dispersés à travers l’arborescence.
- Un nœud répété ( /node2 ) – voir ci dessous « Un aparté sur /include/ ».
Les propriétés sont de simples paires clé – valeur où valeur peut être vide ou contenir un flux d’octets quelconque. Bien que les types de données ne soient pas encodées dans la structure de données, il y a quelques types de données fondamentales qui peuvent être utilisés dans un fichier source d’Arborescence Matérielle.
Les chaînes de caractères (terminées par NUL) sont indiqués par des guillemets :
string-property = "a string";
Les «cellules» sont des entiers sur 32 bits délimités par < et > :
cell-property = <0xbeef 123 0xabcd1234>;
Des données sous forme d’octets arbitraires sont délimitée par des crochets, et saisies en hexadécimal :
binary-property = [01 23 45 67 89 ab cd ef];
Les données de types différentes peuvent être concaténées en utilisant une virgule :
mixed-property = "a string", [01 23 45 67], <0x12345678>;
Les virgules sont également utilisé pour créer des listes de chaînes :
string-list = "red fish", "blue fish";
Un aparté sur /include/
La directive /include/ provoque une inclusion de texte simple, un peu comme la directive #include de C, mais une caractéristique du compilateur d’Arborescence Matérielle conduit à différents modes d’utilisation. Étant donné que les nœuds sont nommés, éventuellement avec des chemins absolus, il est possible pour le même nœud apparaisse deux fois dans un fichier DTS (et ses inclusions). Lorsque cela arrive, les nœuds et les propriétés sont combinés, écrasant les propriétés (les dernières valeurs remplacent les valeurs précédentes).
Dans l’exemple ci-dessus, la deuxième apparition de /node2 fait qu’une nouvelle propriété va être ajoutée à l’original :
/node2 {
an-empty-property;
a-cell-property = <1 2 3 4>; /* chaque nombre (cell) est un uint32 */
another-property-for-node2;
child-node1 {
my-cousin = <&cousin>;
};
};
Il est ainsi possible pour un .dtsi de remplacer (ou de fournir des valeurs par défaut) à différents endroits d’une arborescence.
Labels et Références
Il est souvent nécessaire pour une partie de l’arbre de se référer à une autre partie, et il y a quatre façons de le faire :
Chemin sous forme de chaîne de caractères
Les chemins doivent être explicites, par analogie à un système de fichiers – /soc/i2S@7e203000 est le chemin complet vers le périphérique I2S dans un BCM2835 et un BCM2836. Notez que même s’il est facile de construire un chemin d’accès à une propriété (/soc/i2S@7e203000/état – voilà, c’est fait), les API standard ne le font pas ; vous trouvez tout d’abord un nœud, puis vous choisissez les propriétés de ce nœud.
phandles
Un phandle est un entier de 32 bits unique attribué à un nœud dans sa propriété phandle. (Pour des raisons historiques, vous verrez aussi un doublon, correspondant à linux,phandle). Les phandle sont numérotés de manière séquentielle à partir de 1 – 0 est pas un phandle valable – et sont généralement attribués par le compilateur DT quand il rencontre une référence à un nœud dans un contexte d’entiers, généralement sous la forme d’un label (voir ci-dessous). Les références aux nœuds à l’aide phandles sont simplement encodées en tant que la valeur de cet entier (cell) ; il n’y a pas de balisage pour indiquer qu’ils doivent être interprétés comme phandles – c’est défini au niveau de l’application.
Labels
Tout comme une étiquette en C donne un nom à un endroit dans le code, un label – ou étiquette – de DT attribue un nom à un noeud dans la hiérarchie. Le compilateur prend les références aux labels et les convertit en chemins lorsqu’il est utilisé dans un contexte de chaîne ( &node ) et en phandles un contexte d’entiers ( <&node> ) ; les étiquettes d’origine ne figurent pas dans la sortie compilée. Notez que les étiquettes ne contiennent pas de structure – ce ne sont que des repères dans un espace de noms global et plat.
Alias
Les Alias sont similaires aux étiquettes, sauf qu’ils apparaissent dans la sortie FDT comme une espèce d’index. Ils sont stockés en tant que propriétés du nœud /aliases avec chaque propriété reliant un nom d’alias à un chemin sous forme de chaîne de caractères. Bien que le nœud aliases apparaisse dans la source, les chaînes de chemin apparaissent généralement comme des références aux labels ( &node ) plutôt que d’être écrites en toutes lettres. Les APIs DT qui résolvent une chaîne de chemin en nœud lisent généralement le premier caractère du chemin, et traitent les chemins qui ne commencent pas par un slash comme les alias qui doivent d’abord être convertis en chemin en utilisant la table /aliases .
Sémantique de l’Arborescence Matérielle
Comment construire une Arborescence Matérielle – la meilleure façon est encore de récupérer la configuration de certains matériels – est un sujet vaste et complexe. Il y a beaucoup de ressources disponibles, dont certaines sont énumérées ci-dessous, et ce document ne veut pas en être un autre. Mais il y a certaines choses qui méritent d’être abordées.
Les propriétés compatible sont le lien entre la description du matériel et le logiciel du pilote. Quand un OS rencontre un nœud avec une propriété compatible il consulte sa base de données de pilotes de périphériques pour trouver celui qui convient le mieux. Sous Linux cela aboutit habituellement au chargement automatique du module, à condition qu’il ait été correctement désigné et qu’il ne figure pas sur la liste noire (blacklist).
La propriété status indique si un périphérique est activé ou désactivé. Si status vaut ok, okay ou est absent, alors le périphérique est activé. Sinon status doit être disabled, ce qui signifie que ce que vous pensez que cela signifie le périphérique est désactivé. Il peut être utile de placer des périphériques dans un fichier .dtsi avec un status défini sur disabled. Une configuration dérivée peut alors inclure ce .dtsi et définir l’état des périphériques nécessaires à okay.
Voici quelques liens vers des articles sur l’écriture de l’Arborescence Matérielle :
Partie 2 : Overlays d’Arborescence Matérielle
Un SoC moderne (System-on-Chip) est un circuit très compliqué – une arborescence de ce circuit complet pourrait nécessiter plusieurs centaines de lignes. Si on va un peu plus loin et qu’on place le SoC sur une carte avec d’autres composants, ça ne fait qu’aggraver les choses. Pour que tout cela reste gérable, surtout si certains périphériques partagent des composants, il est logique de mettre les éléments communs dans les fichiers .dtsi qui seront éventuellement inclus dans plusieurs fichiers .dts.
Mais quand un système comme LE Raspberry Pi peut aussi accueillir des cartes d’extension, comme les cartes HAT, le problème ne fait qu’empirer ! En fin de compte, chaque configuration possible nécessite une Arborescence Matérielle pour la décrire, mais une fois que vous avez pris en compte les différents modèles de base (modèles A, B, A+ et B+, Pi2) et les gadgets qui ne nécessitent que l’utilisation de quelques broches du GPIO et qui peuvent coexister, le nombre de combinaisons commence à croître très rapidement.
Ce qu’il faut c’est une façon de décrire ces composants optionnels en utilisant des Arborescences Matérielles (DT) partielles, puis d’être capable de construire une arborescence complète en prenant un DT de base et en lui ajoutant un certain nombre d’éléments optionnels. Eh bien c’est possible, et ces éléments optionnels sont appelés « overlays« .
Fragments
Un overlay d’Arborescence Matérielle (DT) comprend un certain nombre de fragments, dont chacun cible un nœud (et ses sous-nœuds). Bien que le concept semble assez simple, la syntaxe parait plutôt étrange au premier abord :
// Enable the i2s interface
/dts-v1/;
/plugin/;
/ {
compatible = "brcm,bcm2708";
fragment@0 {
target = <&i2s>;
__overlay__ {
status = "okay";
};
};
};
La chaîne compatible identifie ce point comme étant le bcm2708, qui est l’architecture de base de cette partie du BCM2835. Pour le BCM2836 vous pouvez utiliser une chaîne compatible de type « BRCM, bcm2709 », mais à moins que vous ne cibliez les caractéristiques propres des CPU ARM, les deux architectures devraient être équivalentes, donc utiliser « BRCM, bcm2708 » peut convenir. Puis vient le premier (et dans ce cas seulement) fragment. Les fragments sont numérotés de manière séquentielle à partir de zéro. Le non respect de ceci peut faire que certains de vos fragments voir la totalité peuvent manquer.
Chaque fragment est constitué de deux parties – une propriété target , identifiant le nœud auquel appliquer l’overlay et l’ __overlay__ lui-même, dont le corps est ajouté au nœud cible. L’exemple ci-dessus peut être interprété comme s’il avait été écrit comme ceci :
/dts-v1/;
/ {
compatible = "brcm,bcm2708";
};
&i2s {
status = "okay";
};
L’effet de la fusion de cet overlay avec l’Arborescence Matérielle de base du Raspberry Pi (bcm2708–rpi–b–plus.dtb, par exemple), à condition que l’overlay soit chargé en second, serait d’activer l’interface I2S en passant son statut en okay. Mais si vous essayez de compiler cet overlay en utilisant :
dtc -I dts -O dtb -o 2nd-overlay.dtb 2nd-overlay.dts
Vous allez obtenir une erreur :
Label or path i2s not found
Cela n’est pas étonnant, car il n’y a aucune référence au fichier .dtb de base ou .dts permettant au compilateur de trouver l’étiquette i2S.
Essayez à nouveau, cette fois avec l’exemple d’origine :
dtc -I dts -O dtb -o 1st-overlay.dtb 1st-overlay.dts
vous aurez une ou deux erreurs :
Si dtc retourne une erreur à propos de la troisième ligne, alors il n’a pas les extensions requises pour que l’overlay fonctionne. La directive /plugin/ signale au compilateur qu’il doit générer des informations de lien pour que les symboles non résolus puissent être corrigées plus tard.
Pour installer un dtc appropriés sur un Raspberry Pi, tapez :
sudo apt-get install device-tree-compiler
Sur les autres plateformes, vous avez deux options : si vous téléchargez les sources du noyau depuis le github de raspberrypi et faites make ARCH=arm dtbs vous obtiendrez un dtc utilisable dans le répertoire scripts/dtc. Sinon, suivez ces étapes dans un répertoire utilisable :
wget -c https://raw.githubusercontent.com/RobertCNelson/tools/master/pkgs/dtc.sh
chmod +x dtc.sh
./dtc.sh
Remarque : Ce script va télécharger la source principale, appliquer quelques patches, puis compiler et installer. Vous pouvez modifier dtc.sh avant de l’exécuter pour modifier le chemin de téléchargement (actuellement ~/ git/dtc) et le chemin d’installation (/usr/local/bin).
Si vous voyez Reference to non-existent node or label « i2s » tout ce que vous avez à faire est de modifier la ligne de commande pour indiquer au compilateur d’autoriser les symboles non résolus, en ajoutant –@ :
dtc -@ -I dts -O dtb -o 1st-overlay.dtb 1st-overlay.dts
Cette fois, la compilation devrait se terminer avec succès. Il est intéressant de lister le contenu du fichier DTB pour voir ce que le compilateur a généré :
$ fdtdump 1st-overlay.dtb
/dts-v1/;
// magic: 0xd00dfeed
// totalsize: 0x106 (262)
// off_dt_struct: 0x38
// off_dt_strings: 0xe8
// off_mem_rsvmap: 0x28
// version: 17
// last_comp_version: 16
// boot_cpuid_phys: 0x0
// size_dt_strings: 0x1e
// size_dt_struct: 0xb0
/ {
compatible = "brcm,bcm2708";
fragment@0 {
target = <0xdeadbeef>;
__overlay__ {
status = "okay";
};
};
__fixups__ {
i2s = "/fragment@0:target:0";
};
};
Après la description détaillée de la structure du fichier, voici notre fragment. Mais regardez attentivement – où nous avions écrit &I2S, il est maintenant écrit 0xdeadbeef, un indice que quelque chose d’étrange s’est passé… Après le fragment il y a un nouveau nœud, __fixups__. Il contient une liste de propriétés reliant les noms des symboles non résolus aux listes de chemins vers des cellules dans les fragments qui doivent être patchée avec le pHandle du nœud cible, une fois que la cible a été localisé. Dans ce cas, le chemin a la valeur 0xdeadbeef de target, mais des fragments peuvent contenir d’autres références non résolues qui nécessiteraient des modifications supplémentaires.
Si vous écrivez des fragments plus compliqués le compilateur peut générer deux noeuds supplémentaires __local_fixups__ et __symbols__. Le premier est nécessaire si un nœud dans les fragments a un pHandle, parce que le programme qui effectue la fusion devra veiller à ce que les numéros de pHandle soient séquentiels et uniques, mais le dernier est la clé qui indique comment les symboles non résolus seront traitées.
Retour à la section 2.3 où il est dit que « les étiquettes originales ne figurent pas dans la sortie compilée« , mais cela n’est pas vrai lorsque vous utilisez le commutateur -@. Au lieu de cela, toutes les étiquettes résultent d’une propriété du nœud __symbols__, reliant une étiquette à un chemin, exactement comme le nœud aliases. En fait, le mécanisme est si semblable que lors de la résolution de symboles, le chargeur du Raspberry Pi va rechercher le nœud « aliases » en l’absence d’un nœud __symbols__. Cela est utile car en fournissant alias suffisamment d’alias, nous pouvons utiliser un ancien DTC pour construire les fichiers DTB de base.
Paramètres de l’Arborescence Matérielle
Pour éviter d’avoir une superposition d’overlays dans l’Arborescence Matérielle, et (nous l’espérons) pour limiter l’écriture des fichiers DTS aux seuls fabricants de périphériques, le chargeur du Raspberry Pi prend en charge une nouvelle fonctionnalité – les paramètres d’Arborescence Matérielle. Cela permet de petits changements au DT en utilisant des paramètres nommés, un peu comme les modules du noyau peuvent recevoir des paramètres de modprobe et de la ligne de commande du noyau. Les paramètres peuvent être fournis par les DTB de base et par les overlays, y compris les overlays HAT.
Les paramètres sont définis dans les DTS par addition d’un noeud __overrides__ à la racine. Il contient des propriétés dont les noms sont les noms des paramètres choisis, et dont les valeurs sont une séquence comprenant un pHandle (référence à un label) pour le nœud cible, ainsi qu’ une chaîne indiquant la propriété cible. string, integer (cellule) et les propriétés booléennes sont pris en charge.
Paramètres string
Les paramètres string se déclarent ainsi :
name = <&label>,"property";
où label et property sont remplacés par les valeurs appropriées. Les paramètres string peuvent faire croître, décroître ou créer leurs propriétés cibles.
Notez que les propriétés appelées status sont traités spécialement – les valeurs non-zero/true/yes/on sont converties en la chaîne « okay », alors que zero/false/no/off deviennent « disabled ».
Paramètres entiers
Les paramètres entiers sont déclarées comme ceci :
name = <&label>,"property.offset"; // 8-bit
name = <&label>,"property;offset"; // 16-bit
name = <&label>,"property:offset"; // 32-bit
name = <&label>,"property#offset"; // 64-bit
où label, property et offset sont remplacés par les valeurs appropriées; l’offset (décalage) est spécifié en octets par rapport au début de la propriété (en décimal par défaut), et le séparateur précédent impose la taille du paramètre. Les paramètres entiers doivent se référer à une partie existante d’une propriété – ils ne peuvent pas faire croître leurs propriétés cibles.
Paramètres BOOLÉENS
L’Arborescence Matérielle code les valeurs booléennes comme des propriétés de longueur nulle – si elle existe alors la propriété est vraie, sinon elle est fausse. Ils sont définis comme suit :
boolean_property; // Set 'boolean_property' to true
Notez que la propriété se voit attribuer la valeur false simplement en ne la définissant pas. Les paramètres booléens sont déclarées comme ceci :
name = <&label>,"property?";
où label et property sont remplacés par les valeurs appropriées. Les paramètres booléens peuvent faire qu’une propriété soit crée ou supprimée.
Exemples
Voici quelques exemples des différents types de propriétés, avec les paramètres pour les modifier :
/ {
fragment@0 {
target-path = "/";
__overlay__ {
test: test_node {
string = "hello";
status = "disabled";
bytes = /bits/ 8 <0x67 0x89>;
u16s = /bits/ 16 <0xabcd 0xef01>;
u32s = /bits/ 32 <0xfedcba98 0x76543210>;
u64s = /bits/ 64 < 0xaaaaa5a55a5a5555 0x0000111122223333>;
bool1; // Defaults to true
// bool2 defaults to false
};
};
};
__overrides__ {
string = <&test>,"string";
enable = <&test>,"status";
byte_0 = <&test>,"bytes.0";
byte_1 = <&test>,"bytes.1";
u16_0 = <&test>,"u16s;0";
u16_1 = <&test>,"u16s;2";
u32_0 = <&test>,"u32s:0";
u32_1 = <&test>,"u32s:4";
u64_0 = <&test>,"u64s#0";
u64_1 = <&test>,"u64s#8";
bool1 = <&test>,"bool1?";
bool2 = <&test>,"bool2?";
};
};
Paramètres avec des cibles multiples
Il est parfois pratique de pouvoir régler la même valeur dans plusieurs endroits de l’Arborescence Matérielle. Plutôt que de créer maladroitement plusieurs paramètres, il est possible d’ajouter des cibles multiples à un seul paramètre en les enchaînant, comme ceci:
__overrides__ {
gpiopin = <&w1>,"gpios:4",
<&w1_pins>,"brcm,pins:0";
...
};
Notez qu’il est même possible de cibler des propriétés de types différents avec un seul paramètre. Vous pourriez raisonnablement connecter un paramètre « enable » pour une string status , les cellules contenant zéro ou un, et une propriété booléenne correcte.
D’autres exemples d’overlays
Il y a une quantité croissante de fichiers sources d’overlays hébergés dans le dépot raspberrypi/linux github.
Partie 3 : Utilisation de l’Arborescence Matérielle sur le Raspberry Pi
Overlays et config.txt
Sur le Raspberry Pi c’est au chargeur (une des images start*.elf) de combiner les overlays avec une arborescence matérielle de base appropriée, puis de transmettre une arborescence matérielle entièrement résolue au noyau. Les arborescences matérielles de base sont situés près de start.elf dans la partition FAT (/boot de Linux), elles sont appelés bcm2708-RPI-b.dtb, bcm2708-RPI-b-plus.dtb, bcm2708-RPI-cm.dtb et bcm2709- RPI-2-b.dtb. Notez que les Modèles A et A+ utiliseront les variantes « b » et « b-plus », respectivement. Cette sélection est automatique et permet à une même image de carte SD d’être utilisée sur les différents Raspberry Pi.
ATTENTION : DT et ATAGs sont mutuellement exclusifs. En conséquence, le passage d’un blob DT à un noyau qui ne le comprend pas bloque le démarrage (boot failure). Pour éviter cela, le chargeur vérifie la compatibilité des images du noyau avec les DT. Elle est indiquée par un marqueur ajouté par l’utilitaire mkknlimg (que vous trouverez ici, ou dans le répertoire des scripts d’une source récente du noyau). Tout noyau sans marqueur est supposé être incapable d’utiliser le DT.
Le chargeur prend désormais également en charge les builds utilisant bcm2835_defconfig, qui supportent le BCM2835. Cette configuration va provoquer la construction de bcm2835-RPI-b.dtb et bcm2835-RPI-b-plus.dtb. Si ces fichiers sont copiés avec le noyau, et si le noyau a été marqué par un mkknlimg récent, le chargeur va essayer de charger un de ces DTB par défaut.
Afin de gérer l’Arborescence Matérielle et les overlays, le chargeur prend en charge un certain nombre de nouvelles directives de config.txt :
dtoverlay=acme-board
dtparam=foo=bar,level=42
Ceci amène le chargeur à rechercher overlays/acme-board-overlay.dtb dans la partition du firmware, que Raspbian monte sur /boot. Il recherche alors les paramètres foo et level, et leur attribue les valeurs indiquées.
Le chargeur recherchera également une carte HAT avec une mémoire EEPROM programmée, et chargera l’overlay correspondant : cela se produit sans aucune intervention de l’utilisateur.
Il ya plusieurs façons de dire que le noyau utilise l’Arborescence Matérielle :
- Le « Machine Model : » un message du noyau pendant le démarrage affiche une valeur spécifique à la carte comme « Raspberry Pi 2 Model B », plutôt que « BCM2709 ».
- Quelque temps plus tard, un autre message du noyau indique « No ATAGs? » – C’est ce qui est attendu.
- /proc/device-tree existe, et contient des sous-répertoires et des fichiers qui reflètent exactement les nœuds et les propriétés du DT.
Avec une Arborescence Matérielle, le noyau va rechercher automatiquement et charger les modules qui prennent en charge les périphériques activés indiqués. Par conséquent, si vous créez l’overlay de DT approprié pour un périphérique, vous évitez aux utilisateurs du périphérique d’avoir à modifier /etc/modules – toute la configuration se passe dans config.txt (dans le cas d’une carte HAT, même cette étape devient inutile). Notez, cependant, que les modules comme i2c-dev doivent toujours être chargée explicitement.
L’autre conséquence c’est que puisque les périphériques de la plate-forme ne sont pas créés, sauf sur demande du DTB, il ne devrait plus être nécessaire de blacklister les modules habituellement chargés du fait de leur définition dans le code de la carte. En fait, les images actuelles de Raspbian sont fournies sans fichier blacklist (liste noire).
Paramètres du DT
Comme décrit ci-dessus, les paramètres du DT sont un moyen pratique d’appliquer de petits changements à la configuration d’un périphérique. Les DTB de base actuels acceptent des paramètres pour activer et contrôler les interfaces I2C, I2S et SPI sans utiliser d’overlays dédiés. Les paramètres ressemblent à ceci :
dtparam=i2c_arm=on,i2c_arm_baudrate=400000,spi=on
Notez que plusieurs affectations peuvent être placés sur la même ligne (mais il ne faut pas dépasser 80 caractères (à moins que ce soit 79 ?). Parce que ce serait mal).
Un futur config.txt par défaut pourrait contenir une section comme celle-ci :
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
#dtparam=spi=on
Si vous avez un overlay qui définit certains paramètres, ils peuvent être spécifié soit sur des lignes consécutives comme ceci :
dtoverlay=lirc-rpi
dtparam=gpio_out_pin=16
dtparam=gpio_in_pin=17
dtparam=gpio_in_pull=down
soit ajoutés à la ligne de l’overlay comme ceci :
dtoverlay=lirc-rpi:gpio_out_pin=16,gpio_in_pin=17,gpio_in_pull=down
Notez ici l’utilisation de deux points ( : ) pour séparer le nom de l’overlay de ses paramètres, ce qui est une variante valable de la syntaxe.
Les paramètres d’overlay ne sont accessible que jusqu’au chargement de l’overlay suivant. Dans le cas où un paramètre portant le même nom est exporté à la fois par l’overlay et par la base (ne pas le faire – c’est source de confusion), le paramètre dans l’overlay est prioritaire. Pour utiliser le paramètre exporté par la base DTB , il faut terminer l’overlay par :
dtoverlay=
Labels et paramètres spécifiques à la carte
Les cartes Raspberry Pi ont deux interfaces I2C. Ce sont séparées d’origine – une pour l’ARM (CPU) et une pour le VideoCore (le «GPU»). Sur presque tous les modèles, i2c1 appartient à l’ARM et i2c0 au GPU, où elle est utilisé pour contrôler la caméra et lire l’EEPROM de la carte HAT. Cependant, dans les deux premières révisions du modèle B (les plus anciennes) ces rôles étaient inversés.
Pour rendre possible l’utilisation d’un jeu d’overlays et de paramètres avec tous les Raspberry Pi, le firmware crée certains paramètres de DT spécifiques à la carte. Ce sont :
<pre>i2c/i2c_arm
i2c_vc
i2c_baudrate/i2c_arm_baudrate
i2c_vc_baudrate</pre>
Ce sont des alias pour i2c0, i2c1, i2c0_baudrate et i2c1_baudrate. Il est recommandé d’utiliser uniquement i2c_vc et i2c_vc_baudrate si vous en avez vraiment besoin – par exemple, si vous programmez une mémoire EEPROM de carte HAT. L’activation de I2c_v c peut par exemple empêcher la détection de la caméra Pi.
Pour les gens qui écrivent des overlays, le même aliasing a été appliqué aux labels sur les nœuds I2C du DT. Ainsi, vous devriez écrire :
fragment@0 {
target = <&i2c_arm>;
__overlay__ {
status = "okay";
};
};
Tous les overlays utilisant les variantes numériques seront modifiés pour utiliser les nouveaux alias.
Carte HAT et Arborescence Matérielle
Une carte HAT pour Raspberry Pi est une carte d’extension (add-on) pour une carte de format « Plus » (A+, B+ ou Pi 2 B) avec une mémoire EEPROM intégrée. L’EEPROM comprend l’overlay au DT nécessaire pour activer la carte, et cet overlay peut aussi fournir des paramètres.
L’overlay de la carte HAT est automatiquement chargé par le firmware après la DTB de base, de sorte que ses paramètres sont accessibles jusqu’au chargement d’un autre overlay (ou jusqu’à ce que la portée de l’overlay soit terminée en utilisant dtoverlay =. Si pour une raison quelconque vous souhaitez supprimer le chargement de l’overlay de la carte HAT, indiquez dtoverlay = avant tout autre directive dtoverlay ou dtparam.
Overlays et paramètres pris en charge
Plutôt que de documenter les overlays individuels ici, nous vous invitons à lire le fichier README qui se trouve dans le répertoire des fichiers overlay /boot/overlays. Il est régulièrement mis à jour et inclut des ajouts et modifications.
PARTIE 4 : Dépannage et astuces
Debugging
Le chargeur va ignorer les overlays manquants et les mauvais paramètres, mais s’ il y a des erreurs graves comme un dtb de base manquant ou endommagé ou une fusion d’overlay échouée, le chargeur va revenir à un boot sans DT. Si cela se produit, ou si vos paramètres ne se comportent pas comme prévu, il vaut mieux vérifier les avertissements et erreurs du chargeur :
sudo vcdbg log msg
Le débogage supplémentaire peut être activée en ajoutant dtdebug = 1 à config.txt.
Si le noyau ne parvient pas à démarrer en mode DT, c’est probablement parce que l’image du noyau n’a pas un marqueur valide. Utilisez knlinfo pour vérifier si ce marqueur existe, et l’utilitaire mkknlimg pour en ajouter un. Notez que les deux utilitaires sont également inclus dans le répertoire de scripts des souces actuelles du noyau du raspberrypi.
Vous pouvez créer une représentation (semi-)lisible par un humain de l’état actuel du DT comme ceci :
dtc -I fs /proc/device-tree
ce qui peut être utile pour voir l’effet de la fusion d’overlays sur l’arborescence d’origine.
Si les modules du noyau ne se chargent pas comme prévu, vérifier qu’ils ne sont pas dans la liste noire (dans /etc/modprobe.d/raspi-blacklist.conf); il ne devrait pas être nécessaire de les mettre en liste noire lorsque vous utilisez l’Arborescence Matérielle. Si cela ne donne rien, vous pouvez également vérifier que le module exporte les bons alias corrects en vérifiant que la valeur compatible existe dans /lib/modules/<version>/modules.alias. Si ce n’est pas le cas, votre driver n’avait pas soit :
.of_match_table = xxx_of_match,
ou :
MODULE_DEVICE_TABLE(of, xxx_of_match);
A défaut, depmod a échoué ou les modules mis à jour n’ont pas été installés sur le système de fichiers cible.
Force une Arborescence Matérielle spécifique
Si vous avez des besoins très spécifiques qui ne sont pas pris en charge par les DTB par défaut (en particulier, les gens qui expérimentent avec l’approche tout-DT utilisée par le projet de ARCH_BCM2835), ou si vous voulez juste vous lancer dans l’écriture de vos propres DT, vous pouvez forcer le chargeur à charger un fichier DTB alternatif de la façon suivante :
device_tree=my-pi.dtb
Désactiver l’utilisation de l’Arborescence Matérielle
Si vous décidez que le DT n’est pas pour vous (ou à des fins de diagnostic), vous pouvez désactiver le charfment du DT et forcer le noyau à revenir à l’ancien comportement en ajoutant :
device_tree=
à config.txt. Notez, cependant, que les futures versions du noyau pourraient à un moment donné ne plus soutenir cette option.
Raccourcis et variantes de syntaxe
Le chargeur accepte quelques raccourcis :
dtparam=i2c_arm=on
dtparam=i2s=on
peut être réduit en :
dtparam=i2c,i2s
(i2c est un alias de i2c_arm, et =on est par défaut). Il accepte également toujours les versions longues – device_tree_overlay et device_tree_param.
Vous pouvez également utiliser des séparateurs alternatifs si vous pensez que = est galvaudé. Tout ce qui suit est accepté :
dtoverlay thing:name=value,othername=othervalue
dtparam setme andsetme='long string with spaces and "a quote"'
dtparam quote="'"
Ces exemples utilisent des espaces pour séparer la directive du reste de la ligne au lieu de =. Ils utilisent également une virgule pour séparer l’overlay de ses paramètres, et setme prend la valeur par défaut 1/true /on/okay.
Conclusion
Qu’on le veuille ou non, l’utilisation de l’Arborescence Matérielle devient la norme sur le Raspberry Pi. Les utilisateurs « lamba » n’ont pas à s’en inquiéter, et cela devrait même leur faciliter la vie.
Ceux qui sont concernés sont ceux qui interviennent dans la création de cartes d’extension, en particulier les cartes HAT, ou dans le développement de périphériques destinés au Raspberry Pi.
Avec cette traduction, j’espère avoir fourni un point de départ pour tous ceux qui sont intéressés mais rechignent à se lancer dans la doc. en anglais.
Cette traduction n’est certainement pas parfaite et si vous pensez que certaines parties (mots, phrases, tournures…) méritent d’être revues, n’hésitez pas à me contacter via les commentaires ci-dessous !
Sources