ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [리눅스 커널] platform driver probe를 호출하는 4가지 방법.
    Linux/kernel 2023. 8. 3. 02:27

    글의 참고

    - https://docs.kernel.org/driver-api/driver-model/platform.html

    - https://linux-kernel-labs.github.io/refs/heads/master/labs/device_model.html#overview


    글의 전제

    - 밑줄로 작성된 글은 강조 표시를 의미한다.

    - 그림 출처는 항시 그림 아래에 표시했다.


    글의 내용

     

    - /driver/base/platform.c::platform_match() 함수에 probe()를 호출하는 4가지 조건이 있다. 

    /** 
    * platform_match - bind platform device to platform driver. 
    * @dev: device. 
    * @drv: driver. 
    * 
    * Platform device IDs are assumed to be encoded like this: 
    * "<name><instance>", where <name> is a short description of the type of 
    * device, like "pci" or "floppy", and <instance> is the enumerated 
    * instance of the device, like '0' or '42'.  Driver IDs are simply 
    * "<name>".  So, extract the <name> from the platform_device structure, 
    * and compare it against the name of the driver. Return whether they match 
    * or not.                                                                                            
    */ 
    
    static int platform_match(struct device *dev, struct device_driver *drv) 
    { 
        struct platform_device *pdev = to_platform_device(dev); 
        struct platform_driver *pdrv = to_platform_driver(drv); 
    
        /* When driver_override is set, only bind to the matching driver */ 
        if (pdev->driver_override) 
            return !strcmp(pdev->driver_override, drv->name); 
    
        /* Attempt an OF style match first */ 
        if (of_driver_match_device(dev, drv)) 
            return 1; 
    
        /* Then try ACPI style match */ 
        if (acpi_driver_match_device(dev, drv)) 
            return 1; 
    
        /* Then try to match against the id table */ 
        if (pdrv->id_table) 
            return platform_match_id(pdrv->id_table, pdev) != NULL; 
    
        /* fall-back to driver name match */ 
        return (strcmp(pdev->name, drv->name) == 0); 
    }

     

    1. OF(open firmware,즉, devicetree) style 매칭으로 devicetree의 compatible과 of_device_id의 compatible이 매칭될 때 probe()가 호출된다.
    2. ACPI를 이용하는 방법인데, 실무에서 잘 쓰이지 않는다. 여기서는 ACPI에 대한 부분은 설명하지 않는다.
    3. ID table을 이용한 매칭으로 XXX_device_id의 name과 platform_device의 name이 같으면 호출된다.
    4. platform_driver 이름과 platform_device 이름이 같을 때 probe()가 호출된다. 

     


     

    1. Static Platform Device를 통한 probe 호출.

    • platform_driver의 name과 동일하게 platform_device를 만들어 호출하는 방식. 일단 첫 번째로 probe는 platform_device의 name과 platform_driver의 name이 동일해야 호출한다. 

    일단 소스를 보자.

    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/platform_device.h>
    #include <linux/init.h> 
    
    
    static int yohda_kernel_debug_debugfs_drvier_probe(struct platform_device *pdev) 
    { 
        struct kobject *kobj_yohda;
        printk("===[%s][L:%d]", __func__, __LINE__); 
    
        return 0;
    } 
    
    static struct platform_device yohda_device = {
        .name = "yohda",
        .id = -1,
    };
    
    static struct platform_driver yohda_kernel_debug_debugfs_driver = { 
        .probe = yohda_kernel_debug_debugfs_drvier_probe, 
        .driver = { 
            .owner = THIS_MODULE,
            .name = "yohda",
        },
    }; 
    
    static int __init yohda_kernel_debug_debugfs_init(void) 
    {
        int err = 1;
    
        printk("===[%s][L:%d]===\n", __func__, __LINE__);
        err = platform_driver_register(&yohda_kernel_debug_debugfs_driver); 
        if(!err)
            err = platform_device_register(&yohda_device);
    
        return err;
    }
    
    late_initcall(yohda_kernel_debug_debugfs_init);

     

    위에서 중요하게 볼 부분은 2가지다. 

    1. device의 name과 driver의 name이 같아야 한다. 
    2. register() 함수 호출 시, driver가 device보다 먼저 호출되어야 한다. 

    그리고 driver 작성 시, probe와 remove 최소한 2개는 필요하다고 하지만, remove를 작성하지 않아도 오류는 나지 않았다. 

     


     

    2. Device-tree를 통한 probe 호출.

    • devicetree에 platform_device를 작성해서 호출하는 방식. devicetree의 compatible 속성과 driver 소스에 작성된 of_device_id의 .compatible의 값이 같아야 한다! 
    #include <linux/module.h>
    #include <linux/moduleparam.h>
    #include <linux/platform_device.h>
    #include <linux/init.h> 
    
    static struct of_device_id rpi3_b_dt_match[] = {
        { .compatible = "rpi,debug",},
        {}
    };
    MODULE_DEVICE_TABLE(of, );
    
    
    static int rpi3_b_drvier_probe(struct platform_device *pdev) 
    { 
        printk("===[%s][L:%d]", __func__, __LINE__); 
    
        return 0;
    } 
     
    static struct platform_driver rpi3_b_driver = { 
        .probe = rpi3_b_drvier_probe, 
        .driver = { 
            .owner = THIS_MODULE,
            .name = "rpi_debug",
            .of_match_table = rpi3_b_dt_match,
        },
    }; 
    
    static int __init rpi3_b_debugfs_init(void) 
    { 
        printk("===[%s][L:%d]===\n", __func__, __LINE__); 
        return platform_driver_register(&rpi3_b_driver); 
    } 
     
    late_initcall(rpi3_b_debugfs_init); 
    
    ...
    ...
    ...
    
    // SPDX-License-Identifier: GPL-2.0
    
    /* This include file covers the common peripherals and configuration between
     * bcm2835, bcm2836 and bcm2837 implementations.
     */
    
    / {
        soc {
            timer@7e003000 {
                compatible = "brcm,bcm2835-system-timer";
                reg = <0x7e003000 0x1000>;
                interrupts = <1 0>, <1 1>, <1 2>, <1 3>;
                /* This could be a reference to BCM2835_CLOCK_TIMER,
                 * but we don't have the driver using the common clock
                 * support yet.
                 */
                clock-frequency = <1000000>;
            };
    
            intc: interrupt-controller@7e00b200 {
                compatible = "brcm,bcm2835-armctrl-ic";
                reg = <0x7e00b200 0x200>;
                interrupt-controller;
                #interrupt-cells = <2>;
            };
    
            thermal: thermal@7e212000 {
                compatible = "brcm,bcm2835-thermal";
                reg = <0x7e212000 0x8>;
                clocks = <&clocks BCM2835_CLOCK_TSENS>;
                #thermal-sensor-cells = <0>;
                status = "disabled";
            };
    
            v3d: v3d@7ec00000 {
                compatible = "brcm,bcm2835-v3d";
                reg = <0x7ec00000 0x1000>;
                interrupts = <1 10>;
            };
            
            rpi_debug: rpi_debug {
                compatible = "rpi.debug";                                                                
            };
        };  
    };
    
    ...
    ...


    위와 같이 of_device_id.compatible과 node.compatible이 같으면 probe가 호출된다.

     

     


     

    3. ID Table 매칭.

    • ID table과 platform_device.name을 매칭될 때, probe가 호출된다.
    /drivers/base/platform.c
    static const struct platform_device_id *platform_match_id(
    			const struct platform_device_id *id,
    			struct platform_device *pdev)
    {
    	while (id->name[0]) {
    		if (strcmp(pdev->name, id->name) == 0) {
    			pdev->id_entry = id;
    			return id;
    		}
    		id++;
    	}
    	return NULL;
    }

    ID Table Matching이 발생하려면 platform_device.name과 id_table.name이 동일해야 한다.

     

    #include <linux/module.h>                                                                             
    #include <linux/moduleparam.h> 
    #include <linux/platform_device.h> 
    #include <linux/init.h>  
     
    enum { 
        AML7000 = 0, 
        EXYNOS980, 
    }; 
    
      
    struct yohda_platform_match_id_data{ 
        int tmp1; 
        int tmp2; 
    }; 
    
    struct yohda_platform_match_id_data data1 =  
    { 
        .tmp1 = 10, 
        .tmp2 = 20, 
    }; 
      
    struct yohda_platform_match_id_data data2 = 
    { 
        .tmp1 = 100, 
        .tmp2 = 200, 
    }; 
    
    static struct platform_device_id yohda_platform_match_ids[] = { 
        [AML7000]  
        = { 
            .name = "yohda_match_id1", 
            .driver_data = (kernel_ulong_t)&data1, 
        }, 
        [EXYNOS980] 
         = { 
            .name = "yohda_match_id2", 
            .driver_data = (kernel_ulong_t)&data2, 
        }, 
    }; 
    
    MODULE_DEVICE_TABLE(platform, yohda_platform_match_ids); 
    
    static struct platform_device yohda_platform_match_id_device = { 
        .name = "yohda_match_id1", 
        .id = -1, 
        .dev = { 
            .platform_data = &data2, 
        }, 
    }; 
      
    static int yohda_platform_match_id_probe(struct platform_device *pdev)  
    {  
        printk("===[%s][L:%d]", __func__, __LINE__);  
    
        return 0; 
    }  
    
    
    
    static struct platform_driver yohda_platform_match_id_driver = {  
        .id_table = yohda_platform_match_ids, 
        .probe = yohda_platform_match_id_probe,  
        .driver = {  
            .owner = THIS_MODULE, 
            .name = "yohda_platform_match_id", 
        }, 
    };  
    
    static int __init yohda_platform_match_id_init(void)  
    { 
        int err = 1; 
      
        printk("===[%s][L:%d]===\n", __func__, __LINE__); 
        err = platform_driver_register(&yohda_platform_match_id_driver);  
        if(!err) 
            err = platform_device_register(&yohda_platform_match_id_device); 
    
        return err; 
    } 
    
    late_initcall(yohda_platform_match_id_init);

     

     

    주의해서 볼 점은 2가지다.

    1. MODULE_DEVICE_TABLE()의 첫번째 인자로 platform이 들어가야 한다는 것을 유의하자. 그리고 드라이버에 .id_table에 어떤 값이 들어가는지 확인한다.
    2. platform_device.name과 platform_device_id가 동일한지 확인한다.

     


     

    확인

    • 위에서 보인 platform_device와 platform_driver는 각각 아래의 경로에서 확인이 가능하다. 
    • /sys/bus/platform/devices("yohda")

     

    • /sys/bus/platform/drivers ("yohda")

Designed by Tistory.