[Bases de Linux] - Leçon 3: Traitement par le noyau Linux de l'arborescence des périphériques

Avant-propos:

Cet article est transféré de l'explication du professeur Wei Dongshan sur l'arborescence des périphériques, l'adresse d'origine: Leçon 3: Le traitement de l'arborescence des périphériques par le noyau linux .

1. Analyse à partir de la source - traitement simple de dtb par la tête du noyau.

Maintenant, nous commençons la première section, nous devons analyser à partir de la source, uboot transmet certains paramètres et fichiers d'arborescence de périphériques au noyau , puis comment le noyau gère-t-il ces fichiers d'arborescence de périphériques? Nous devons démarrer l'analyse à partir d'un fichier exécutable head.S dans le noyau.

1.1. Réglage des trois registres r0, r1, r2

Lorsque BootLoader démarre le noyau, trois registres, r0, r1 et r2 sont définis:

  • r0 est généralement mis à 0;
  • r1 est généralement défini sur l'identifiant de la machine (ce paramètre n'est pas utilisé lors de l'utilisation de l'arborescence des périphériques);
  • r2 est généralement mis à l'adresse de début de ATAGS ou DTB;

L'identifiant de la machine ici est d'indiquer au noyau de quel processeur il s'agit et d'appeler la fonction d'initialisation correspondante. Lorsque l'arborescence des périphériques n'était pas utilisée auparavant, BootLoader devait transmettre un identifiant d'ordinateur au noyau. Désormais, si l'arborescence des périphériques est utilisée, il n'est pas nécessaire de définir ce paramètre.

r2 est soit l'adresse de départ précédente d'ATAGS, soit l'adresse de départ du fichier DTB après l'utilisation de l'arborescence des appareils.

1.2, le contenu de head.S

Le travail effectué par la tête du noyau S est le suivant:

  1. __lookup_processor_type: utilisez les instructions d'assemblage pour lire l'ID du processeur et recherchez la structure proc_info_list correspondante (qui contient la fonction d'initialisation et les informations de ce type de processeur) en fonction de l'ID;
  2. __vet_atags: détermine s'il existe des ATAG ou DTB disponibles;
  3. __create_pag_tables: créer des tables de pages, c'est-à-dire créer une relation de mappage entre les adresses virtuelles et les adresses physiques;
  4. __enable_mmu: Activez la MMU, l'adresse virtuelle sera utilisée à l'avenir;
  5. __mmap_switched: __mmap_swtiched sera appelé dans la fonction ci-dessus;
  6. Enregistrez le paramètre r2 passé par BootLoader dans la variable __atags_pointer;
  7. Appelez la fonction C start_kernel;

effet final:

L'effet final de head.S et head-common.S:

  • Attribuez la valeur de r1 de BootLoader à la variable C: __machine_arch_type;

  • La valeur r2 transmise par BootLoader,

Uboot lit d'abord le fichier de l'arborescence des périphériques dans la mémoire et écrit l'adresse de l'arborescence des périphériques dans le registre r2 lors du démarrage du noyau.

2. Traitement des informations de plate-forme dans l'arborescence des appareils (sélectionnez machine_desc)

Cette section explique comment le noyau gère les informations sur les périphériques de la plate-forme dans l'arborescence des périphériques.

2.1 Comment le noyau sélectionne-t-il le machine_desc correspondant?

Comme expliqué précédemment, un fichier image de noyau compilé dans uImage peut prendre en charge plusieurs cartes. On suppose que smdk2410, smdk2440, jz2440 sont pris en charge (amdk2410, smdk2440 sont les cartes publiques des fabricants et les fabricants nationaux ont conçu leurs propres cartes de référence. comme jz2440).

La configuration de ces cartes est légèrement différente, et une initialisation individuelle est nécessaire.Dans le noyau, pour ces cartes, une structure machine_desc est construite, qui contient .init et .nr.

Pour JZ2440, il est dérivé de smdk2440, le noyau n'a pas de fichier séparé, il utilise les fichiers et codes associés à smdk2440.

Dans la vidéo précédente, nous avons dit que lorsque uboot utilisait des ATAG pour transmettre des paramètres au noyau, il transmettait un ID de machine, et le noyau utilisait cet ID de machine pour trouver le machine_desc le plus approprié. Autrement dit, l'ID de la machine est comparé au .nr dans machine_desc, et l'égalité signifie que la machine_desc correspondante est trouvée.

Lorsque notre uboot n'utilise pas les ATAG pour passer des paramètres, mais utilise des fichiers DTB, comment le noyau choisit-il le machine_desc correspondant?

Dans le nœud racine du fichier de l'arborescence des périphériques, il y a les deux lignes suivantes:

model = "SMDK2440"
compatible = "samsung,smdk2440","samsung,smdk2410","samsung,smdk24xx";

L'attribut compatible déclare ici quel machine_desc vous voulez, et la valeur d'attribut peut être une série de chaînes qui correspondent tour à tour à machine_desc.

Le noyau est préférable pour prendre en charge sasung, smdk2440, sinon, essayez de prendre en charge sasung, smdk2410, sinon, essayez enfin sasung, smdk24xx.

en conclusion:

  • L'attribut compatible du nœud racine de l'arborescence des périphériques répertorie une série de chaînes, indiquant les noms des cartes avec lesquelles il est compatible, de «la plus compatible» à la seconde;

  • Il y a plusieurs machines_desc dans le noyau, parmi lesquelles il y a un membre dt_compat, qui pointe vers un tableau de chaînes, qui indique les cartes supportées par machine_desc;

  • En utilisant la valeur de l'attribut compatible, comparez avec chaque "machine_desc.dt_compat", le score est "la position de la valeur d'attribut compatible qui correspond", plus le score est bas, plus il y a de correspondance, et le machine_desc correspondant est sélectionné.

2.2, le processus d'appel de start_kernel

Dans la vidéo de la section précédente, head.S enregistrera l'emplacement du DTB dans la variable __atags_pointer, et appellera finalement start_kernel.

Le processus d'appel de start_kernel est le suivant:

start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        mdesc = setup_machine_fdt(__atags_pointer);  // arch/arm/kernel/devtree.c
                    early_init_dt_verify(phys_to_virt(dt_phys)  // 判断是否有效的dtb, drivers/of/ftd.c
                                    initial_boot_params = params;
                    mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);  // 找到最匹配的machine_desc, drivers/of/ftd.c
                                    while ((data = get_next_compat(&compat))) {
                                        score = of_flat_dt_match(dt_root, compat);
                                        if (score > 0 && score < best_score) {
                                            best_data = data;
                                            best_score = score;
                                        }
                                    }
                    
        machine_desc = mdesc;

Troisièmement, le traitement des informations de configuration au moment de l'exécution en nombre d'appareils

L'arborescence des équipements ne joue qu'un rôle de transmission d'informations, et le traitement de cette configuration d'informations est relativement simple, c'est-à-dire extraire ces informations du fichier DTB de l'arborescence des équipements et les affecter à une variable du noyau.

Le processus d'appel de fonction est le suivant:

start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        mdesc = setup_machine_fdt(__atags_pointer);  // arch/arm/kernel/devtree.c
                    early_init_dt_scan_nodes();      // drivers/of/ftd.c
                        /* Retrieve various information from the /chosen node */
                        of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);

                        /* Initialize {size,address}-cells info */
                        of_scan_flat_dt(early_init_dt_scan_root, NULL);

                        /* Setup memory, calling early_init_dt_add_memory_arch */
                        of_scan_flat_dt(early_init_dt_scan_memory, NULL);

Il existe principalement trois types de traitement de l'information, à savoir : l'attribut bootags dans le nœud / selected, les attributs # address-cells et # size-cells du nœud racine et l'attribut reg dans / memory.

  1. L'attribut bootargs dans le nœud / selected est le paramètre de ligne de commande pour le démarrage du noyau. Il peut spécifier où se trouve le système de fichiers racine, quelle application est la première à exécuter et à partir de quel périphérique les informations d'impression du noyau sont imprimées;

  2. L'attribut reg in / memory spécifie la taille et l'adresse de départ des différentes mémoires de la carte;

  3. Les attributs # address-cells et # size-cells du nœud racine spécifient le nombre de bits du paramètre d'attribut, par exemple en spécifiant si l'adresse de l'attribut reg dans le memroy précédent est 32 bits ou 64 bits, et si la taille est représentée par un 32 bits ou deux 32 bits Said.

Pour résumer:

  • La valeur de l'attribut bootargs dans le nœud / selected est stockée dans la variable globale: boot_command_line;
  • Déterminez les valeurs de ces deux attributs du nœud racine: # address-cells, # size-cells, stockez-les dans des variables globales: dt_root_addr_cells, dt_root_size_cells;
  • Analysez l'attribut reg dans / memory, extrayez "base, size" et enfin appelez memblock_add (base, size).

4. Convertissez dtb en device_node (sans aplatir)

Avant d'expliquer, réfléchissons d'abord à une question. Notre uboot place le fichier DTB de l'arborescence des périphériques à un certain endroit de la mémoire et peut l'utiliser. Pourquoi ne remplace-t-il pas la mémoire occupée par DTB lorsque le noyau est en cours d'exécution?

Lorsque nous avons expliqué le format de l'arborescence des périphériques plus tôt, nous savions que dans le fichier de l'arborescence des périphériques, vous pouvez utiliser / memreserve / pour spécifier une partie de la mémoire. Même si vous ne spécifiez pas cette mémoire, lorsque notre noyau démarre, il conservera la zone occupée par le nombre de périphériques.

Voici le processus d'appel de fonction:

start_kernel // init/main.c
    setup_arch(&command_line);  // arch/arm/kernel/setup.c
        arm_memblock_init(mdesc);   // arch/arm/kernel/setup.c
            early_init_fdt_reserve_self();
                    /* Reserve the dtb region */
                    // 把DTB所占区域保留下来, 即调用: memblock_reserve
                    early_init_dt_reserve_memory_arch(__pa(initial_boot_params),
                                      fdt_totalsize(initial_boot_params),
                                      0);           
            early_init_fdt_scan_reserved_mem();  // 根据dtb中的memreserve信息, 调用memblock_reserve
            
        unflatten_device_tree();    // arch/arm/kernel/setup.c
            __unflatten_device_tree(initial_boot_params, NULL, &of_root,
                        early_init_dt_alloc_memory_arch, false);            // drivers/of/fdt.c
                
                /* First pass, scan for size */
                size = unflatten_dt_nodes(blob, NULL, dad, NULL);
                
                /* Allocate memory for the expanded device tree */
                mem = dt_alloc(size + 4, __alignof__(struct device_node));
                
                /* Second pass, do actual unflattening */
                unflatten_dt_nodes(blob, mem, dad, mynodes);
                    populate_node
                        np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
                                    __alignof__(struct device_node));
                        
                        np->full_name = fn = ((char *)np) + sizeof(*np);
                        
                        populate_properties
                                pp = unflatten_dt_alloc(mem, sizeof(struct property),
                                            __alignof__(struct property));
                            
                                pp->name   = (char *)pname;
                                pp->length = sz;
                                pp->value  = (__be32 *)val;

Comme vous pouvez le voir, dites d'abord au noyau les informations memreserve dans dtb, et réservez cette zone mémoire sans l'occuper.

Ensuite, extrayez la structure plate de l'arborescence des périphériques et construisez une arborescence, qui implique deux structures: la structure device_node et la structure des propriétés. En découvrant ces deux structures, vous comprendrez probablement le contenu principal de cette vidéo.

Dans le fichier dts, chaque accolade {} représente un nœud, par exemple, il y a une accolade dans le nœud racine, qui correspond à une structure device_node; la mémoire a également une accolade, qui correspond également à une structure device_node.

Il existe différents attributs dans les nœuds, et il peut également y avoir des nœuds enfants dans ceux-ci, ils ont donc également des relations parent-enfant.

La mémoire, choisie, menée et les autres nœuds sous le nœud racine sont dans une relation parallèle et une relation fraternelle.

Pour la relation parent-enfant et la relation fraternelle, la structure device_node est définie comme suit:

struct device_node {
    const char *name; // 来自节点中的name属性, 如果没有该属性, 则设为"NULL"
    const char *type; // 来自节点中的device_type属性, 如果没有该属性, 则设为"NULL"
    phandle phandle;
    const char *full_name; // 节点的名字, node-name[@unit-address]
    struct fwnode_handle fwnode;
    struct  property *properties;  // 节点的属性
        struct  property *deadprops;    /* removed properties */
        struct  device_node *parent;   // 节点的父亲
        struct  device_node *child;    // 节点的孩子(子节点)
        struct  device_node *sibling;  // 节点的兄弟(同级节点)
    #if defined(CONFIG_OF_KOBJ)
        struct  kobject kobj;
    #endif
        unsigned long _flags;
        void    *data;
    #if defined(CONFIG_SPARC)
        const char *path_component_name;
        unsigned int unique_id;
        struct of_irq_controller *irq_trans;
    #endif
};

La structure device_node représente un nœud et la structure de propriété représente les propriétés spécifiques du nœud.

La définition de la structure de propriété est la suivante:

struct property {
            char    *name;    // 属性名字, 指向dtb文件中的字符串
            int length;       // 属性值的长度
            void    *value;   // 属性值, 指向dtb文件中value所在位置, 数据仍以big endian存储
            struct property *next;
        #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC)
            unsigned long _flags;
        #endif
        #if defined(CONFIG_OF_PROMTREE)
            unsigned int unique_id;
        #endif
        #if defined(CONFIG_OF_KOBJ)
            struct bin_attribute attr;
        #endif
};

La relation relative entre les deux structures et le contenu de dts est la suivante:

Pour une analyse de code spécifique, reportez-vous au contenu vidéo.

Cinq, device_node est converti en platform_device

Comment le noyau convertit device_node en platform_device.

Deux questions:

5.1. Quel device_node peut être converti en platform_device

/ {
	model = "SMDK24440";
	compatible = "samsung,smdk2440";

	#address-cells = <1>;
	#size-cells = <1>;
	//内存设备不会	
	memory@30000000 {
		device_type = "memory";
		reg =  <0x30000000 0x4000000>;
	};
/*
	cpus {
		cpu {
			compatible = "arm,arm926ej-s";
		};
	};
*/	//只是设置一些启动信息
	chosen {
		bootargs = "noinitrd root=/dev/mtdblock4 rw init=/linuxrc console=ttySAC0,115200";
	};

/*只有这个led设备才对转换成platfrom_device */	
	led {
		compatible = "jz2440_led";
		reg = <S3C2410_GPF(5) 1>;
	};
/************************************/
};

La fonction noyau of_platform_default_populate_init, parcourt l'arborescence device_node et génère le paltform_device;

Tous les device_nodes ne seront pas convertis en platform_device, seuls les device_nodes suivants seront convertis:

  • Le nœud doit contenir l'attribut compatible;
  • Les nœuds enfants du nœud racine (le nœud doit contenir l'attribut compatible);
  • Nœuds enfants d'un nœud avec des attributs compatibles spéciaux (les nœuds enfants doivent avoir des attributs compatibles): Ces attributs compatibles spéciaux sont: "simple-bus", "simple-mfd", "isa", "arm, amba-bus".

Le nœud racine est une exception. Lorsque platform_device est généré, il ne sera pas traité même s'il possède un attribut compatible .

Par exemple:

Le processeur peut accéder à de nombreux périphériques, contrôleur spi, contrôleur i2c, led.

Comment décrire ces matériels dans l'arborescence des périphériques?

Par exemple, le nœud suivant, / mytest sera converti en platform_device, car il est compatible avec "simple-bus", son nœud enfant / mytest / mytest @ 0 sera également converti en platform_device;

Le nœud / i2c représente généralement le contrôleur i2c, qui sera converti en platform_device, et il y a un platform_driver correspondant dans le noyau; le nœud / i2c / at24c02 ne sera pas converti en platform_device, et la façon dont il est traité est complètement déterminé par le platform_driver du nœud parent, généralement a été créé en tant que i2c_client.

De même, il y a le nœud / spi, qui est généralement utilisé pour représenter le contrôleur SPI. Il sera converti en platform_device, et il y a un platform_driver correspondant dans le noyau; le nœud / spi / flash @ 0 ne sera pas converti en platform_device , comment est-il géré Il est complètement déterminé par le driver_plateforme du nœud parent, et est généralement créé en tant que spi_device.

   / {
         mytest {
             compatile = "mytest", "simple-bus";
             mytest@0 {
                   compatile = "mytest_0";
             };
         };
         
         i2c {
             compatile = "samsung,i2c";
             at24c02 {
                   compatile = "at24c02";                      
             };
         };

         spi {
             compatile = "samsung,spi";              
             flash@0 {
                   compatible = "winbond,w25q32dw";
                   spi-max-frequency = <25000000>;
                   reg = <0>;
                 };
         };
     };

5.2 Comment convertir

Processus d'appel de fonction:

a. Le processus dans lequel la fonction d'entrée of_platform_default_populate_init (drivers / of / platform.c) est appelée:

Il existe des attributs de section et les variables des attributs de section du noyau compilés seront regroupées.

vim arch / arm / kernel / vmlinux.lds

start_kernel     // init/main.c
    rest_init();
        pid = kernel_thread(kernel_init, NULL, CLONE_FS);
                    kernel_init
                        kernel_init_freeable();
                            do_basic_setup();
                                do_initcalls();
                                    for (level = 0; level < ARRAY_SIZE(initcall_levels) - 1; level++)
                                        do_initcall_level(level);  // 比如 do_initcall_level(3)
                                                                               for (fn = initcall_levels[3]; fn < initcall_levels[3+1]; fn++)
                                                                                    do_one_initcall(initcall_from_entry(fn));  // 就是调用"arch_initcall_sync(fn)"中定义的fn函数

b. Le processus de of_platform_default_populate_init (drivers / of / platform.c) pour générer platform_device:

Parcourez l'arborescence des appareils:

of_platform_default_populate_init
    of_platform_default_populate(NULL, NULL, NULL);
        of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL)
            for_each_child_of_node(root, child) {
                rc = of_platform_bus_create(child, matches, lookup, parent, true);  // 调用过程看下面
                            dev = of_device_alloc(np, bus_id, parent);   // 根据device_node节点的属性设置platform_device的resource
                if (rc) {
                    of_node_put(child);
                    break;
                }
            }
 

c. Le processus d'appel de of_platform_bus_create (bus, matches, ...) (traite le nœud de bus pour générer platform_device, et décide s'il faut traiter ses nœuds enfants):

        dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);  // 生成bus节点的platform_device结构体
        if (!dev || !of_match_node(matches, bus))  // 如果bus节点的compatile属性不吻合matches成表, 就不处理它的子节点
            return 0;

        for_each_child_of_node(bus, child) {    // 取出每一个子节点
            pr_debug("   create child: %pOF\n", child);
            rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);   // 处理它的子节点, of_platform_bus_create是一个递归调用
            if (rc) {
                of_node_put(child);
                break;
            }
        }

d. Processus de traitement du nœud de bus I2C:

Le nœud / i2c représente généralement le contrôleur i2c, qui sera converti en platform_device, avec un platform_driver correspondant dans le noyau; i2c_add_numbered_adapter sera appelé dans la fonction de sonde de platform_drvier:

  i2c_add_numbered_adapter   // drivers/i2c/i2c-core-base.c
       __i2c_add_numbered_adapter
           i2c_register_adapter
               of_i2c_register_devices(adap);   // drivers/i2c/i2c-core-of.c
                   for_each_available_child_of_node(bus, node) {
                       client = of_i2c_register_device(adap, node);
                                       client = i2c_new_device(adap, &info);   // 设备树中的i2c子节点被转换为i2c_client

总结 : dtb -> device_node -> platform_device

Six, la correspondance entre platform_device et platform_driver

pilotes / base / platform.c :

a. Le processus d'enregistrement de platform_driver:

platform_driver_register
    __platform_driver_register
        drv->driver.probe = platform_drv_probe;
        driver_register
            bus_add_driver
                klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);    // 把 platform_driver 放入 platform_bus_type 的driver链表中
                driver_attach
                    bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);  // 对于plarform_bus_type下的每一个设备, 调用__driver_attach
                        __driver_attach
                            ret = driver_match_device(drv, dev);  // 判断dev和drv是否匹配成功
                                        return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用 platform_bus_type.match
                            driver_probe_device(drv, dev);
                                        really_probe
                                            drv->probe  // platform_drv_probe
                                                platform_drv_probe
                                                    struct platform_driver *drv = to_platform_driver(_dev->driver);
                                                    drv->probe

b. Le processus d'enregistrement de platform_device:

platform_device_register
    platform_device_add
        device_add
            bus_add_device
                klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 把 platform_device 放入 platform_bus_type的device链表中
            bus_probe_device(dev);
                device_initial_probe
                    __device_attach
                        ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); // // 对于plarform_bus_type下的每一个driver, 调用 __device_attach_driver
                                    __device_attach_driver
                                        ret = driver_match_device(drv, dev);
                                                    return drv->bus->match ? drv->bus->match(dev, drv) : 1;  // 调用platform_bus_type.match
                                        driver_probe_device

La fonction de correspondance est platform_bus_type.match, qui est platform_match. Le processus de correspondance est répertorié comme suit par ordre de priorité :

  • 比较 platform_dev.driver_override 和 platform_driver.drv (.driver) -> nom;
  • Comparez la propriété compatible de platform_dev.dev.of_node avec platform_driver.drv (.driver) -> of_match_table;
  • Comparez platform_dev.name et platform_driver.id_table;
  • Comparez platform_dev.name et platform_driver.drv (.driver) -> name;

Il y a un succès, c'est-à-dire que le match est réussi!

Sept, la fonction d'opération de l'arborescence des périphériques dans le noyau

Il existe de nombreux fichiers d'en-tête commençant par de dans le répertoire include / linux /:

7.1. Traitement DTB

of_fdt.h    //dtb 文件的相关操作函数,我们一般用不到,因为 dtb 文件在内核中已经被转换为 device_node 树(它更易于使用)

7.2, traitement device_node

 of.h               // 提供设备树的一般处理函数, 比如 of_property_read_u32(读取某个属性的u32值), *of_get_child_count(获取某个device_node的子节点数)
 of_address.h       // 地址相关的函数, 比如 of_get_address(获得reg属性中的addr, size值)
 of_match_device(从matches数组中取出与当前设备最匹配的一项)
 of_dma.h           // 设备树中DMA相关属性的函数
 of_gpio.h          // GPIO相关的函数
 of_graph.h         // GPU相关驱动中用到的函数, 从设备树中获得GPU信息
 of_iommu.h         // 很少用到
 of_irq.h           // 中断相关的函数
 of_mdio.h          // MDIO (Ethernet PHY) API
 of_net.h           // OF helpers for network devices. 
 of_pci.h           // PCI相关函数
 of_pdt.h           // 很少用到
 of_reserved_mem.h  // reserved_mem的相关函数

En prenant comme exemple l'interruption, un appareil peut émettre une interruption, qui doit inclure le numéro d'interruption et le mode de déclenchement d'interruption.

Exemples d'appareils dans l'arbre de spécifications officiel de l'arborescence d'appareils:

soc {
    #address-cells = <1>;
    #size-cells = <1>;
    serial {
        compatible = "ns16550";
        reg = <0x4600 0x100>;
        clock-frequency = <0>;
        interrupts = <0xA 0x8>;
        interrupt-parent = <&ipic>;
    };
};

Il y a des valeurs limites dans les propriétés à l'intérieur.

par:

int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq);

Analysez une certaine paire de valeurs, ou nous pouvons analyser les données d'origine.

int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq);

addr pointe vers une certaine paire de valeurs, analyse la méthode de déclenchement du numéro d'interruption à l'intérieur et l'enregistre dans la structure of_phandle_args.

7.3, traitement platform_device

of_platform.h    //把 device_node 转换为 platform_device 时用到的函数
/* Platform drivers register/unregister */
extern struct platform_device *of_device_alloc(struct device_node *np, const char *bus_id, struct device *parent);

Les fonctions impliquées dans le fichier sont largement utilisées dans device_node-> platform_device.

 // 比如of_device_alloc(根据device_node分配设置platform_device), 
 //     of_find_device_by_node (根据device_node查找到platform_device),
 //     of_platform_bus_probe (处理device_node及它的子节点)
 of_device.h        // 设备相关的函数, 比如 of_match_device
可以通过of_match_device找出哪一项最匹配

des fichiers sont divisés en trois catégories:

  • Process DTB;
  • Process device_node;
  • Traitement des informations relatives au périphérique platform_device;

8. Affichez l'arborescence des périphériques dans le système de fichiers racine (utile pour le débogage)

8.1 、 / sys / firmware / fdt

Affichez le fichier dtb d'origine:

hexdump -C /sys/firmware/fdt

8.2 、 / sys / firmware / devicetree

Le fichier dtb présenté dans une structure de répertoires, le nœud racine correspond au répertoire de base, chaque nœud correspond à un répertoire et chaque attribut correspond à un fichier. Par exemple, vérifiez le nombre hexadécimal de # cellules-adresse:

hexdump -C "#address-cells"

Voir compatible:

cat compatible

Si vous définissez un attribut d'interruption incorrect dans le nœud de périphérique de l'arborescence des périphériques, le nœud de périphérique de plate-forme correspondant au voyant ne peut pas être créé.

8.3 、 / sys / appareil / plateforme

Tous les platform_devices du système proviennent de l'arborescence des périphériques et certains sont enregistrés dans le fichier .c.

Pour platform_device de l'arborescence des appareils, vous pouvez entrer / sys / devices / platform / <nom de l'appareil> / of_node pour afficher les propriétés de l'arborescence des appareils.

8.4 、 / proc / device-tree

Fichier de lien, pointant vers / sys / firmware / devicetree / base.

 

Je suppose que tu aimes

Origine blog.csdn.net/u014674293/article/details/114833254
conseillé
Classement