以下内容源于网络资源的学习与整理,如有侵权请告知删除。
参考资料
linux中 probe函数何时调用
platform总线的probe函数调用_Linux编程_Linux公社-Linux系统门户网站
Linux设备驱动模型之platform(平台)总线详解
Linux设备驱动模型2——总线式设备驱动组织方式_天糊土的博客-CSDN博客
简单总结
以x210/drivers/leds/leds-s3c24xx.c为例分析,得知:
(1)platform_device在系统初始化时就已经注册到系统之中。
(2)platform_driver是在驱动初始化的时候注册的,是通过platform_driver_register()来注册的,该注册函数最终会调用到platform_driver中的probe函数。
我们可将cdev有关的一系列操作(前提是字符设备的驱动开发)放到platform_driver的probe函数中去实现,这样就把cdev挂到platform bus上去了。
x210/drivers/leds/leds-s3c24xx.c内容如下。
#include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/gpio.h> #include <linux/slab.h> #include <mach/hardware.h> #include <mach/regs-gpio.h> #include <mach/leds-gpio.h>/* our context */struct s3c24xx_gpio_led {struct led_classdev cdev;struct s3c24xx_led_platdata *pdata; };static inline struct s3c24xx_gpio_led *pdev_to_gpio(struct platform_device *dev) {return platform_get_drvdata(dev); }static inline struct s3c24xx_gpio_led *to_gpio(struct led_classdev *led_cdev) {return container_of(led_cdev, struct s3c24xx_gpio_led, cdev); }static void s3c24xx_led_set(struct led_classdev *led_cdev,enum led_brightness value) {struct s3c24xx_gpio_led *led = to_gpio(led_cdev);struct s3c24xx_led_platdata *pd = led->pdata;/* there will be a short delay between setting the output and* going from output to input when using tristate. */s3c2410_gpio_setpin(pd->gpio, (value ? 1 : 0) ^(pd->flags & S3C24XX_LEDF_ACTLOW));if (pd->flags & S3C24XX_LEDF_TRISTATE)s3c2410_gpio_cfgpin(pd->gpio,value ? S3C2410_GPIO_OUTPUT : S3C2410_GPIO_INPUT);}static int s3c24xx_led_remove(struct platform_device *dev) {struct s3c24xx_gpio_led *led = pdev_to_gpio(dev);led_classdev_unregister(&led->cdev);kfree(led);return 0; }static int s3c24xx_led_probe(struct platform_device *dev) {struct s3c24xx_led_platdata *pdata = dev->dev.platform_data;struct s3c24xx_gpio_led *led; //设备的信息,在这里传给驱动int ret;led = kzalloc(sizeof(struct s3c24xx_gpio_led), GFP_KERNEL);if (led == NULL) {dev_err(&dev->dev, "No memory for device\n");return -ENOMEM;}platform_set_drvdata(dev, led);led->cdev.brightness_set = s3c24xx_led_set;led->cdev.default_trigger = pdata->def_trigger;led->cdev.name = pdata->name;led->cdev.flags |= LED_CORE_SUSPENDRESUME;led->pdata = pdata; //以上是填充结构体/* no point in having a pull-up if we are always driving *///根据传过来的数据,对设备进行一些初始化和设置if (pdata->flags & S3C24XX_LEDF_TRISTATE) {s3c2410_gpio_setpin(pdata->gpio, 0);s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_INPUT);} else {s3c2410_gpio_pullup(pdata->gpio, 0);s3c2410_gpio_setpin(pdata->gpio, 0);s3c2410_gpio_cfgpin(pdata->gpio, S3C2410_GPIO_OUTPUT);}/* register our new led device */ret = led_classdev_register(&dev->dev, &led->cdev);if (ret < 0) {dev_err(&dev->dev, "led_classdev_register failed\n");kfree(led);return ret;}return 0; }static struct platform_driver s3c24xx_led_driver = {.probe = s3c24xx_led_probe,.remove = s3c24xx_led_remove,.driver = {.name = "s3c24xx_led",.owner = THIS_MODULE,}, };static int __init s3c24xx_led_init(void) {return platform_driver_register(&s3c24xx_led_driver); }static void __exit s3c24xx_led_exit(void) {platform_driver_unregister(&s3c24xx_led_driver); }module_init(s3c24xx_led_init); module_exit(s3c24xx_led_exit);MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S3C24XX LED driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:s3c24xx_led");
1、platform_device的注册过程
(1)系统初始化时调用platform_add_devices函数,把所有放置在板级platform_device数组中的platform_device注册到系统中去。
- 此函数循环调用platform_device_register函数,来注册每个platform_device。
- 而platform_device_register中会调用platform_device_add函数。
(2)platform_device全部注册到系统之后,驱动模块insmod到系统时,驱动代码便可以通过platform的操作接口(见下),来获取platform_device中的resource资源(见下)。
- 比如plarform_driver_register这个函数会引用platform_driver中的probe函数。probe函数通过get_resource来获取寄存器物理基地址,然后ioremap到kernel的虚拟空间来,这样驱动就可以正式操纵和修改设备的寄存器,从而进行cdev的初始化及cdev_add的操作。
- platform的操作接口,包括platform_get_irq、platform_get_irq_byname、platform_get_resource、platform_get_resource_byname等。
- platform_device中的resource资源,包括寄存器地址、中断号等,以进行request_memregion、ioremap(将resource分配的物理地址映射到kernel的虚拟空间来)和request_irq操作。
2、platform_driver的注册过程
平台驱动注册的调用关系如下。
|----------platform_driver_register()
|---------------driver_register()
|-------------------bus_add_driver()
|----------------------driver_attach()
|--------------------------bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
其中bus_for_each_dev()函数对每个挂在虚拟的platform bus的设备作__driver_attach()。
|----------__driver_attach()
|-------------driver_match_device
|-------------driver_probe_device()
|---------------drv->bus
|-----------------match()==platform_match()-& gt
比较strncmp(pdev->name, drv->name, BUS_ID_SIZE),如果相符就调用platform_drv_probe()->driver->probe(),如果probe成功则绑定该设备到该驱动。