linux platform驱动实例之leds-gpio(三)

platform_driver就是驱动的核心,而probe函数又是核心中的核心,接上一节对platform_driver的probe函数做一个解析.

gpio_led_probe()函数是gpio_led_driver驱动的probe函数,probe函数在驱动匹配到platform_device时执行,调用过程如下:

static struct platform_driver gpio_led_driver = {
	.probe		= gpio_led_probe,
	.remove		= __devexit_p(gpio_led_remove),
	.driver		= {
		.name	= "leds-gpio",
		.owner	= THIS_MODULE,
		.of_match_table = of_gpio_leds_match,
	},
};
static int __init gpio_led_init(void)
{
	return platform_driver_register(&gpio_led_driver);
}
int platform_driver_register(struct platform_driver *drv)
{
......
	return driver_register(&drv->driver);
}
int driver_register(struct device_driver *drv)
{
.....
	ret = bus_add_driver(drv);
.....
}
int bus_add_driver(struct device_driver *drv)
{
......
	if (drv->bus->p->drivers_autoprobe) {
		//搜寻总线的设备列表,如果和驱动匹配,则把设备添加到驱动的klist_devices列表
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
......
}
int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
static int __driver_attach(struct device *dev, void *data)
{
	struct device_driver *drv = data;

	/*
	 * Lock device and try to bind to it. We drop the error
	 * here and always return 0, because we need to keep trying
	 * to bind to devices and some drivers will return an error
	 * simply if it didn't support the device.
	 *
	 * driver_probe_device() will spit a warning if there
	 * is an error.
	 */

	if (!driver_match_device(drv, dev))
		return 0; // 匹配失败返回0

	// 匹配成功
	if (dev->parent)	/* Needed for USB */
		device_lock(dev->parent);
	device_lock(dev);
	if (!dev->driver) // 如果设备还未有匹配的驱动
		driver_probe_device(drv, dev); // 执行绑定
	device_unlock(dev);
	if (dev->parent)
		device_unlock(dev->parent);

	return 0;
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
..........
	ret = really_probe(dev, drv);
.........
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
	int ret = 0;

	atomic_inc(&probe_count);
	pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
		 drv->bus->name, __func__, drv->name, dev_name(dev));
	WARN_ON(!list_empty(&dev->devres_head));

	// 设置设备绑定的驱动
	dev->driver = drv;
	if (driver_sysfs_add(dev)) {
		printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
			__func__, dev_name(dev));
		goto probe_failed;
	}

	// 执行probe函数
	if (dev->bus->probe) { // 优先执行bus的probe
		ret = dev->bus->probe(dev);
		if (ret)
			goto probe_failed;
	} else if (drv->probe) {// 执行驱动的probe
		ret = drv->probe(dev);
		if (ret)
			goto probe_failed;
	}

	// 添加设备到驱动的klist_devices列表
	driver_bound(dev);
	ret = 1;
	pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);
	goto done;

probe_failed:
	devres_release_all(dev);
	driver_sysfs_remove(dev);
	dev->driver = NULL;

	if (ret != -ENODEV && ret != -ENXIO) {
		/* driver matched but the probe failed */
		printk(KERN_WARNING
		       "%s: probe of %s failed with error %d\n",
		       drv->name, dev_name(dev), ret);
	} else {
		pr_debug("%s: probe of %s rejects match %d\n",
		       drv->name, dev_name(dev), ret);
	}
	/*
	 * Ignore errors returned by ->probe so that the next driver can try
	 * its luck.
	 */
	ret = 0;
done:
	atomic_dec(&probe_count);
	wake_up(&probe_waitqueue);
	return ret;
}

gpio_led_driver驱动的probe函数定义如下:

static int __devinit gpio_led_probe(struct platform_device *pdev)
{
	struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
	struct gpio_leds_priv *priv;
	int i, ret = 0;

	if (pdata && pdata->num_leds) {
		priv = kzalloc(sizeof_gpio_leds_priv(pdata->num_leds),
				GFP_KERNEL);
		if (!priv)
			return -ENOMEM;

		priv->num_leds = pdata->num_leds;
		for (i = 0; i < priv->num_leds; i++) {
			ret = create_gpio_led(&pdata->leds[i],
					      &priv->leds[i],
					      &pdev->dev, pdata->gpio_blink_set);
			if (ret < 0) {
				/* On failure: unwind the led creations */
				for (i = i - 1; i >= 0; i--)
					delete_gpio_led(&priv->leds[i]);
				kfree(priv);
				return ret;
			}
		}
	} else {
		priv = gpio_leds_create_of(pdev);
		if (!priv)
			return -ENODEV;
	}

	platform_set_drvdata(pdev, priv);

	return 0;
}

@struct gpio_led_platform_data *pdata = pdev->dev.platform_data;
gpio_led_platform_data 是platform_device设备的硬件资源,比如这里是这样定义的

static struct gpio_led_platform_data gpio_led_info = {
	.leds		= gpio_leds, // led灯的数组
	.num_leds	= ARRAY_SIZE(gpio_leds), // led灯的个数
};

该结构定义了platform_device对应的led灯个数和led灯数组,也就是驱动要处理的硬件对象;

@struct gpio_leds_priv *priv;
定义如下

struct led_classdev {
	const char		*name; // led灯名称,如"heartbeat"
	int			 brightness;// led灯亮度
	int			 max_brightness;
	int			 flags;

	/* Lower 16 bits reflect status */
#define LED_SUSPENDED		(1 << 0)
	/* Upper 16 bits reflect control information */
#define LED_CORE_SUSPENDRESUME	(1 << 16)

	/* Set LED brightness level */ // 设置led灯的亮度:关闭,一般亮度,全亮
	/* Must not sleep, use a workqueue if needed */
	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);
	/* Get LED brightness level */// 读取led灯的亮度
	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

	/*
	 * Activate hardware accelerated blink, delays are in milliseconds
	 * and if both are zero then a sensible default should be chosen.
	 * The call should adjust the timings in that case and if it can't
	 * match the values specified exactly.
	 * Deactivate blinking again when the brightness is set to a fixed
	 * value via the brightness_set() callback.
	 */
	int		(*blink_set)(struct led_classdev *led_cdev,
				     unsigned long *delay_on,
				     unsigned long *delay_off);

	struct device		*dev;
	struct list_head	 node;			/* LED Device list */
	const char		*default_trigger;	/* Trigger to use */// 字符串?"heartbeat"

	unsigned long		 blink_delay_on, blink_delay_off;
	struct timer_list	 blink_timer;
	int			 blink_brightness;

#ifdef CONFIG_LEDS_TRIGGERS
	/* Protects the trigger data below */
	struct rw_semaphore	 trigger_lock;

	struct led_trigger	*trigger;
	struct list_head	 trig_list;
	void			*trigger_data;
#endif
};
struct gpio_led_data {
	struct led_classdev cdev;
	unsigned gpio;
	struct work_struct work;
	u8 new_level;
	u8 can_sleep;
	u8 active_low;
	u8 blinking;
	int (*platform_gpio_blink_set)(unsigned gpio, int state,
			unsigned long *delay_on, unsigned long *delay_off);
};
struct gpio_leds_priv {
	int num_leds;// led灯个数
	struct gpio_led_data leds[];//led灯
};

@create_gpio_led函数

			ret = create_gpio_led(&pdata->leds[i],
					      &priv->leds[i],
					      &pdev->dev, pdata->gpio_blink_set);

参数:
pdata->leds[i]:platform_device.platform_data.leds
priv->leds[i]:gpio_leds_priv .leds
pdev->dev:platform_device.dev
pdata->gpio_blink_set:platform_device.platform_data.gpio_blink_set

功能:
该函数最后调用led_classdev_register,为每个led创建目录:如/sys/devices/platform/leds-gpio/leds/heartbeat,具体就不继续了,阅读起来也不难;

小结:
platform_device,platform_driver,platform_bus之间的关系可以这样来理解:
platform_device提供设备的硬件资源
platform_driver根据设备提供的资源编写驱动
platform_bus管理platform_device和platform_driver,一旦两者匹配成功,应用程序就可以访问了.

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值