[Conceptos básicos de Linux] - Lección 3: Procesamiento del árbol de dispositivos en el kernel de Linux

Prefacio:

Este artículo es una transferencia de la explicación del profesor Wei Dongshan sobre el árbol de dispositivos, la dirección original: Lección 3: El procesamiento del árbol de dispositivos por el kernel de Linux .

1. Analice desde la fuente: procesamiento simple de dtb por el cabezal del kernel. S

Ahora comenzamos la primera sección, tenemos que analizar desde la fuente, uboot pasa algunos parámetros y archivos de árbol de dispositivos al kernel , entonces, ¿cómo maneja el kernel estos archivos de árbol de dispositivos? Necesitamos comenzar el análisis desde un archivo ejecutable head.S en el kernel.

1.1. Configuración de r0, r1, r2 tres registros

Cuando BootLoader inicia el kernel, se establecen tres registros, r0, r1 y r2:

  • r0 generalmente se establece en 0;
  • r1 generalmente se establece en ID de máquina (este parámetro no se usa cuando se usa el árbol de dispositivos);
  • r2 generalmente se establece en la dirección de inicio de ATAGS o DTB;

La identificación de la máquina aquí es para que el kernel sepa qué CPU es y llamar a la función de inicialización correspondiente. Cuando el árbol de dispositivos no se usaba antes, se requería que BootLoader pasara una identificación de máquina al kernel. Ahora, si se usa el árbol de dispositivos, no es necesario configurar este parámetro.

r2 es la dirección inicial anterior de ATAGS o la dirección inicial del archivo DTB después de que ahora se usa el árbol de dispositivos.

1.2, el contenido de la cabeza. S

El trabajo realizado por el kernel head. S es el siguiente:

  1. __lookup_processor_type: utilice las instrucciones de ensamblaje para leer el ID de la CPU y busque la estructura proc_info_list correspondiente (que contiene la función de inicialización y la información de este tipo de CPU) de acuerdo con el ID;
  2. __vet_atags: Determine si hay ATAG o DTB disponibles;
  3. __create_pag_tables: crea tablas de páginas, es decir, crea una relación de mapeo entre direcciones virtuales y direcciones físicas;
  4. __enable_mmu: Habilite la MMU, la dirección virtual se utilizará en el futuro;
  5. __mmap_switched: __mmap_swtiched será llamado en la función anterior;
  6. Guarde el parámetro r2 pasado por BootLoader en la variable __atags_pointer;
  7. Llame a la función C start_kernel;

efecto final:

El efecto final de head.S y head-common.S:

  • Asigne el valor de r1 desde BootLoader a la variable C: __machine_arch_type;

  • El valor r2 pasado por BootLoader,

Uboot primero lee el archivo del árbol de dispositivos en la memoria y escribe la dirección del árbol de dispositivos en el registro r2 al iniciar el kernel.

2. Procesamiento de la información de la plataforma en el árbol de dispositivos (seleccione machine_desc)

Esta sección explica cómo el kernel maneja la información del dispositivo de la plataforma en el árbol de dispositivos.

2.1 ¿Cómo selecciona el kernel el machine_desc correspondiente?

Como se explicó anteriormente, un archivo de imagen del kernel compilado en uImage puede admitir varias placas. Se supone que smdk2410, smdk2440, jz2440 son compatibles (amdk2410, smdk2440 son las placas públicas de los fabricantes y los fabricantes nacionales han diseñado sus propias placas de referencia. Placa, como jz2440).

La configuración de estas placas es ligeramente diferente, y se requiere alguna inicialización individual En el kernel, para estas placas, se construye una estructura machine_desc, que contiene .init y .nr.

Para JZ2440, se deriva de smdk2440, el kernel no tiene su archivo separado, utiliza archivos y códigos relacionados con smdk2440.

En el video anterior, dijimos que cuando uboot usó ATAG para pasar parámetros al kernel, pasaría una ID de máquina, y el kernel usaría esta ID de máquina para encontrar la machine_desc más adecuada. Es decir, la ID de la máquina se compara con el .nr en machine_desc, y la igualdad significa que se encuentra la correspondiente machine_desc.

Cuando nuestro uboot no usa ATAG para pasar parámetros, pero usa archivos DTB, ¿cómo elige el kernel el correspondiente machine_desc?

En el nodo raíz del archivo de árbol de dispositivos, hay las dos líneas siguientes:

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

El atributo compatible aquí declara qué machine_desc desea, y el valor del atributo puede ser una serie de cadenas que coincidan con machine_desc a su vez.

El kernel es mejor para soportar sasung, smdk2440, si no, intente soportar sasung, smdk2410, si no, intente sasung, smdk24xx por fin.

En conclusión:

  • El atributo compatible del nodo raíz del árbol de dispositivos enumera una serie de cadenas, indicando los nombres de las placas con las que es compatible, desde "más compatible" hasta el segundo;

  • Hay varios machine_desc en el kernel, entre ellos hay un miembro dt_compat, que apunta a una matriz de cadenas, que indica qué tableros admite machine_desc;

  • Usando el valor del atributo compatible, compare con cada "machine_desc.dt_compat", el puntaje es "la posición del valor del atributo compatible que coincide", cuanto menor es el puntaje, más coincidencias y se selecciona el correspondiente machine_desc.

2.2, el proceso de llamada de start_kernel

En el video de la sección anterior, head.S guardará la ubicación del DTB en la variable __atags_pointer, y finalmente llamará a start_kernel.

El proceso de llamada de start_kernel es el siguiente:

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;

Tres, el procesamiento de la información de configuración en el tiempo de ejecución en el número de dispositivos

El árbol de dispositivos solo juega un papel de transmisión de información, y el procesamiento de esta configuración de información es relativamente simple, es decir, extraer esta información del archivo DTB del árbol de dispositivos y asignarla a una variable en el kernel.

El proceso de llamada a la función es el siguiente:

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);

Hay principalmente tres tipos de procesamiento de información, a saber : el atributo bootags en el nodo / elegido, los atributos # address-cells y # size-cells del nodo raíz, y el atributo reg en / memory.

  1. El atributo bootargs en el nodo / elegido es el parámetro de la línea de comandos para el inicio del kernel.Puede especificar dónde está el sistema de archivos raíz, qué aplicación es la primera en ejecutarse y desde qué dispositivo se imprime la información de impresión del kernel;

  2. El atributo reg en / memory especifica el tamaño y la dirección de inicio de diferentes memorias de placa;

  3. Los atributos # address-cells y # size-cells del nodo raíz especifican el número de bits del parámetro de atributo, como especificar si la dirección del atributo reg en la memoria anterior es de 32 bits o de 64 bits, y si el tamaño está representado por un Said de 32 bits o dos de 32 bits.

para resumir:

  • El valor del atributo bootargs en el nodo / elegido se almacena en la variable global: boot_command_line;
  • Determine los valores de estos dos atributos del nodo raíz: # address-cells, # size-cells, guárdelos en variables globales: dt_root_addr_cells, dt_root_size_cells;
  • Analice el atributo reg en / memory, extraiga "base, size" y finalmente llame a memblock_add (base, size).

4. Convierta dtb en device_node (unflatten)

Antes de explicar, pensemos primero en una pregunta. Nuestro uboot coloca el archivo DTB del árbol de dispositivos en un lugar determinado de la memoria y puede usarlo. ¿Por qué no sobrescribe la memoria ocupada por DTB cuando el kernel se está ejecutando?

Cuando explicamos el formato del árbol de dispositivos anteriormente, sabíamos que en el archivo del árbol de dispositivos, puede usar / memreserve / para especificar una parte de la memoria. Esta memoria es memoria reservada y el kernel no la ocupará. Incluso si no especifica esta memoria, cuando nuestro kernel se inicia, retendrá el área ocupada por el número de dispositivos.

El siguiente es el proceso de llamada a la función:

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;

Como puede ver, primero dígale al kernel la información de memreserve en dtb y reserve esta área de memoria sin ocuparla.

Luego, extraiga la estructura plana del árbol de dispositivos y construya un árbol, que involucra dos estructuras: estructura device_node y estructura de propiedad. Al descubrir estas dos estructuras, probablemente comprenderá el contenido principal de este video.

En el archivo dts, cada llave {} representa un nodo, por ejemplo, hay una llave en el nodo raíz, que corresponde a una estructura device_node; la memoria también tiene una llave, que también corresponde a una estructura device_node.

Hay varios atributos en los nodos y también puede haber nodos secundarios en ellos, por lo que también tienen algunas relaciones entre padres e hijos.

Los nodos de memoria, elegido, led y otros bajo el nodo raíz están en una relación paralela y una relación de hermanos.

Para la relación padre-hijo y la relación entre hermanos, la estructura device_node se define de la siguiente manera:

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 estructura device_node representa un nodo y la estructura de propiedad representa las propiedades específicas del nodo.

La definición de la estructura de la propiedad es la siguiente:

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 relación relativa entre las dos estructuras y el contenido de dts es la siguiente:

Para un análisis de código específico, consulte el contenido del video.

Cinco, device_node se convierte en platform_device

Cómo el kernel convierte device_node en platform_device.

Dos preguntas:

5.1. ¿Qué device_node se puede convertir 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 función del kernel de_platform_default_populate_init, atraviesa el árbol de device_node y genera el paltform_device;

No todos los device_nodes se convertirán en platform_device, solo se convertirán los siguientes device_nodes:

  • El nodo debe contener el atributo compatible;
  • Los nodos secundarios del nodo raíz (el nodo debe contener el atributo compatible);
  • Nodos secundarios de un nodo con atributos especiales compatibles (los nodos secundarios deben tener atributos compatibles): Estos atributos especiales compatibles son: "simple-bus", "simple-mfd", "isa", "arm, amba-bus".

El nodo raíz es una excepción. Cuando se genera platform_device, no se procesará incluso si tiene un atributo compatible .

Por ejemplo:

La CPU puede acceder a muchos periféricos, controlador spi, controlador i2c, led.

¿Cómo describir este hardware en el árbol de dispositivos?

Por ejemplo, el siguiente nodo, / mytest se convertirá en platform_device, porque es compatible con "bus simple", su nodo hijo / mytest / mytest @ 0 también se convertirá en platform_device;

El nodo / i2c generalmente representa el controlador i2c, que se convertirá en platform_device, y hay un platform_driver correspondiente en el kernel; el nodo / i2c / at24c02 no se convertirá en platform_device, y la forma en que se procesa está completamente determinada por el platform_driver del nodo principal, generalmente se creó como i2c_client.

De manera similar, existe el nodo / spi, que generalmente se usa para representar el controlador SPI. Se convertirá a platform_device, y hay un platform_driver correspondiente en el kernel; el nodo / spi / flash @ 0 no se convertirá a platform_device , ¿cómo se maneja? Está completamente determinado por el platform_driver del nodo principal, y generalmente se crea como un 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 Cómo convertir

Proceso de llamada de función:

a. El proceso en el que se llama a la función de entrada de_platform_default_populate_init (drivers / of / platform.c):

Hay atributos de sección y las variables de los atributos de sección del kernel compilados se agruparán.

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. El proceso de of_platform_default_populate_init (drivers / of / platform.c) para generar platform_device:

Recorre el árbol de dispositivos:

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. El proceso de llamada de of_platform_bus_create (bus, matches, ...) (procesa el nodo de bus para generar platform_device y decide si procesar sus nodos secundarios):

        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. Proceso de procesamiento del nodo de bus I2C:

El nodo / i2c generalmente representa el controlador i2c, que se convertirá en platform_device, con un platform_driver correspondiente en el kernel; i2c_add_numbered_adapter se llamará en la función de prueba 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

Seis, la coincidencia entre platform_device y platform_driver

drivers / base / platform.c :

a. El proceso de registro 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. El proceso de registro 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 función de coincidencia es platform_bus_type.match, que es platform_match. El proceso de coincidencia se enumera de la siguiente manera en orden de prioridad :

  • 比较 platform_dev.driver_override 和 platform_driver.drv (.driver) -> nombre;
  • Compare la propiedad compatible de platform_dev.dev.of_node con platform_driver.drv (.driver) -> of_match_table;
  • Compare platform_dev.name y platform_driver.id_table;
  • Compare platform_dev.name y platform_driver.drv (.driver) -> nombre;

¡Hay un éxito, es decir, el partido es un éxito!

Siete, la función de operación del árbol de dispositivos en el kernel

Hay muchos archivos de encabezado que comienzan con of en el directorio include / linux /:

7.1. Procesamiento de DTB

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

7.2, procesamiento 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的相关函数

Tomando como ejemplo lo relacionado con la interrupción, un dispositivo puede emitir una interrupción, que debe incluir el número de interrupción y el modo de activación de la interrupción.

Ejemplos de dispositivos en el árbol de especificaciones del árbol de dispositivos oficial:

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

Hay valores de corte en las propiedades en el interior.

por:

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

Analizar un determinado par de valores o podemos analizar los datos originales.

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

addr apunta a un cierto par de valores, analiza el método de activación del número de interrupción dentro y lo guarda en la estructura of_phandle_args.

7.3, plataforma de procesamiento_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);

Las funciones involucradas en el archivo se utilizan ampliamente en 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找出哪一项最匹配

de archivos se dividen en tres categorías:

  • Proceso DTB;
  • Process device_node;
  • Procesamiento de información relacionada con el dispositivo platform_device;

8. Ver el árbol de dispositivos en el sistema de archivos raíz (útil para la depuración)

8.1 、 / sys / firmware / fdt

Vea el archivo dtb original:

hexdump -C /sys/firmware/fdt

8.2 、 / sys / firmware / árbol de dispositivos

El archivo dtb presentado en una estructura de directorio, el nodo raíz corresponde al directorio base, cada nodo corresponde a un directorio y cada atributo corresponde a un archivo. Por ejemplo, verifique el número hexadecimal de # direcciones-celdas:

hexdump -C "#address-cells"

Ver compatible:

cat compatible

Si establece un atributo de interrupción incorrecto en el nodo de dispositivo del árbol de dispositivos, no se puede crear el nodo de dispositivo de plataforma correspondiente al LED.

8.3 、 / sys / dispositivo / plataforma

Todos los platform_devices del sistema provienen del árbol de dispositivos y algunos están registrados en el archivo .c.

Para platform_device del árbol de dispositivos, puede ingresar / sys / devices / platform / <nombre del dispositivo> / of_node para ver las propiedades de su árbol de dispositivos

8.4 、 / proc / árbol de dispositivos

Archivo de enlace, apuntando a / sys / firmware / devicetree / base.

 

Supongo que te gusta

Origin blog.csdn.net/u014674293/article/details/114833254
Recomendado
Clasificación