-
[리눅스 커널] 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); }
- OF(open firmware,즉, devicetree) style 매칭으로 devicetree의 compatible과 of_device_id의 compatible이 매칭될 때 probe()가 호출된다.
- ACPI를 이용하는 방법인데, 실무에서 잘 쓰이지 않는다. 여기서는 ACPI에 대한 부분은 설명하지 않는다.
- ID table을 이용한 매칭으로 XXX_device_id의 name과 platform_device의 name이 같으면 호출된다.
- 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가지다.
- device의 name과 driver의 name이 같아야 한다.
- 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가지다.
- MODULE_DEVICE_TABLE()의 첫번째 인자로 platform이 들어가야 한다는 것을 유의하자. 그리고 드라이버에 .id_table에 어떤 값이 들어가는지 확인한다.
- platform_device.name과 platform_device_id가 동일한지 확인한다.
확인
- 위에서 보인 platform_device와 platform_driver는 각각 아래의 경로에서 확인이 가능하다.
- /sys/bus/platform/devices("yohda")
- /sys/bus/platform/drivers ("yohda")
'Linux > kernel' 카테고리의 다른 글
[LINUX][KERNEL] sysfs store/show 함수의 반환값 (0) 2023.08.03 [리눅스 커널] Interrupt - high level reference (0) 2023.08.03 [커널][디바이스트리] 디바이스 트리에서 8, 16, 64 비트값 선언하고 읽기. (0) 2023.08.03 [커널][디바이스트리] Label을 사용하는 이유와 참조시 주의점 (0) 2023.08.03 [리눅스 커널] 커널 컨텍스트 (0) 2023.08.03