平台设备与平台驱动的注册

以下内容源于网络资源的学习与整理,如有侵权请告知删除。

参考资料

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成功则绑定该设备到该驱动。 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天糊土

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值