以下内容源于网络资源的学习与整理,如有侵权请告知删除。
参考资料
简单总结
以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成功则绑定该设备到该驱动。