前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >platform driver注册过程

platform driver注册过程

作者头像
全栈程序员站长
发布2022-09-14 15:10:29
1.1K0
发布2022-09-14 15:10:29
举报

大家好,又见面了,我是你们的朋友全栈君。

platform 总线上的驱动注册一般使用module_platform_driver宏,如goldfish设备的注册 module_platform_driver(goldfish_pipe); 这个宏定义在/goldfish/include/linux/platform_device.h文件

代码语言:javascript
复制
/* module_platform_driver() - Helper macro for drivers that don't do
 * anything special in module init/exit.  This eliminates a lot of
 * boilerplate.  Each module may only use this macro once, and
 * calling it replaces module_init() and module_exit()
 */
#define module_platform_driver(__platform_driver) \
	module_driver(__platform_driver, platform_driver_register, \
			platform_driver_unregister)
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

对于platform类型的驱动以goldfish_pipe为例子展开如下

代码语言:javascript
复制
static int __init goldfish_pipe_init(void) 
{ 
	return platform_driver_register(&(goldfish_pipe) ); 
} 
module_init(goldfish_pipe_init); 
static void __exit goldfish_pipe_exit(void) \
{ 
	platform_driver_unregister(&(goldfish_pipe) ); 
} 
module_exit(goldfish_pipe_exit);

也就是在.init.setup6段放了连个函数__goldfish_pipe_init和__goldfish_pipe_exit,作用分别是在内核初始化的时候注册该驱动,退出的时候注销该驱动

代码语言:javascript
复制
#define platform_driver_register(drv) \
	__platform_driver_register(drv, THIS_MODULE)
	
/**
 * __platform_driver_register - register a driver for platform-level devices
 * @drv: platform driver structure
 * @owner: owning module/driver
 */
int __platform_driver_register(struct platform_driver *drv,
				struct module *owner)
{
	drv->driver.owner = owner;
	drv->driver.bus = &platform_bus_type;
	if (drv->probe)
		drv->driver.probe = platform_drv_probe;
	if (drv->remove)
		drv->driver.remove = platform_drv_remove;
	if (drv->shutdown)
		drv->driver.shutdown = platform_drv_shutdown;

	return driver_register(&drv->driver);
}

__platform_driver_register的函数设置了一些driver的方法,如probe, remove和shutdown,如果用户没有自己设定该方法,就使用platform框架的,这有些面向对象的思想,子类可以使用父类方法,也可以复写父类方法 这里也设置了driver的bus成员,指向了platform_bus_type,设置了一些platform方法后,调用通用的driver_register方法注册驱动

代码语言:javascript
复制
/**
 * driver_register - register driver with bus
 * @drv: driver to register
 *
 * We pass off most of the work to the bus_add_driver() call,
 * since most of the things we have to do deal with the bus
 * structures.
 */
int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
		bus_remove_driver(drv);
		return ret;
	}
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}

1首先检查下设备自己实现的probe,remove,shutdown但是bus也支持probe,remove,shutdown的请款,提示用户升级驱动 2在bus上查找同名drive,如果找到了提示错误(driver_find) 3调用bus_add_driver添加driver到bus上 4 调用driver_add_groups添加到驱动组 5发送uevent add 事件

driver_find函数在drivers/base/driver.c文件下

代码语言:javascript
复制
/**
 * driver_find - locate driver on a bus by its name.
 * @name: name of the driver.
 * @bus: bus to scan for the driver.
 *
 * Call kset_find_obj() to iterate over list of drivers on
 * a bus to find driver by name. Return driver if found.
 *
 * This routine provides no locking to prevent the driver it returns
 * from being unregistered or unloaded while the caller is using it.
 * The caller is responsible for preventing this.
 */
struct device_driver *driver_find(const char *name, struct bus_type *bus)
{
	struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
	struct driver_private *priv;

	if (k) {
		/* Drop reference added by kset_find_obj() */
		kobject_put(k);
		priv = to_driver(k);
		return priv->driver;
	}
	return NULL;
}

在/sys/bus/${bus_name}/drivers对应的kset下查找以驱动为名字的文件

add_driver的实现在drivers/base/bus.c文件

代码语言:javascript
复制
/**
 * bus_add_driver - Add a driver to the bus.
 * @drv: driver.
 */
int bus_add_driver(struct device_driver *drv)
{
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;

	bus = bus_get(drv->bus);
	if (!bus)
		return -EINVAL;

	pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);

	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
	if (!priv) {
		error = -ENOMEM;
		goto out_put_bus;
	}
	klist_init(&priv->klist_devices, NULL, NULL);
	priv->driver = drv;
	drv->p = priv;
	priv->kobj.kset = bus->p->drivers_kset;
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;

	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
	module_add_driver(drv->owner, drv);

	error = driver_create_file(drv, &driver_attr_uevent);
	if (error) {
		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
			__func__, drv->name);
	}
	error = driver_add_groups(drv, bus->drv_groups);
	if (error) {
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
			__func__, drv->name);
	}

	if (!drv->suppress_bind_attrs) {
		error = add_bind_files(drv);
		if (error) {
			/* Ditto */
			printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
				__func__, drv->name);
		}
	}

	return 0;

out_unregister:
	kobject_put(&priv->kobj);
	kfree(drv->p);
	drv->p = NULL;
out_put_bus:
	bus_put(bus);
	return error;
}

该函数实现的功能包含如下 1 创建kobj,在bus对应的driver kset下,如platform的/sys/bus/platform/drivers,名称为该驱动的名称 2 添加到bus->p->klist_drivers 链表中 3 driver_attach 进行device绑定 4 module_add_driver 5 driver_create_file 6 driver_add_groups 7 add_bind_files driver_attach 是实现在文件drivers/base/dd.c中,

代码语言:javascript
复制
/**
 * driver_attach - try to bind driver to devices.
 * @drv: driver.
 *
 * Walk the list of devices that the bus has on it and try to
 * match the driver with each one.  If driver_probe_device()
 * returns 0 and the @dev->driver is set, we've found a
 * compatible pair.
 */
int driver_attach(struct device_driver *drv)
{
	return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

实现很简单,遍历bus->p->klist_devices链表中的device, 执行__driver_attach __driver_attach也在文件drivers/base/dd.c中,

代码语言:javascript
复制
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;

	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;
}

static inline int driver_match_device(struct device_driver *drv,
				      struct device *dev)
{
	return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}

driver_match_device函数主要是调用bus的match函数去匹配设备和驱动。 完成匹配之后就调用driver_probe_device触发driver的探测函数

platform bus匹配驱动和设备的函数在文件drivers/base/platform.c中如下

代码语言:javascript
复制
/**
 * 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);
}

这部分逻辑可以参考总线驱动设备匹配过程分析这篇文章

匹配成功则代表设备和驱动都存在了,所以就可以进行下一步操作,绑定设备和驱动,是通过函数driver_probe_device完成的 drivers/base/dd.c

代码语言:javascript
复制
/**
 * driver_probe_device - attempt to bind device & driver together
 * @drv: driver to bind a device to
 * @dev: device to try to bind to the driver
 *
 * This function returns -ENODEV if the device is not registered,
 * 1 if the device is bound successfully and 0 otherwise.
 *
 * This function must be called with @dev lock held.  When called for a
 * USB interface, @dev->parent lock must be held as well.
 */
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
    int ret = 0;

    if (!device_is_registered(dev))
        return -ENODEV;

    pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
         drv->bus->name, __func__, dev_name(dev), drv->name);

    pm_runtime_barrier(dev);
    ret = really_probe(dev, drv);
    pm_request_idle(dev);

    return ret;
}

1 首先调用device_is_registered确保设备已经注册 2 pm_runtime_barrier防止设备掉电 3 really_probe 真正的探测函数

代码语言:javascript
复制
static int really_probe(struct device *dev, struct device_driver *drv)
{
    int ret = 0;
    int local_trigger_count = atomic_read(&deferred_trigger_count);

    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 using pinctrl, bind pins now before probing */
    ret = pinctrl_bind_pins(dev);
    if (ret)
        goto probe_failed;

    if (driver_sysfs_add(dev)) {
        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
            __func__, dev_name(dev));
        goto probe_failed;
    }

    if (dev->bus->probe) {
        ret = dev->bus->probe(dev);
        if (ret)
            goto probe_failed;
    } else if (drv->probe) {
        ret = drv->probe(dev);
        if (ret)
            goto probe_failed;
    }

    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;
    dev_set_drvdata(dev, NULL);

    if (ret == -EPROBE_DEFER) {
        /* Driver requested deferred probing */
        dev_info(dev, "Driver %s requests probe deferral\n", drv->name);
        driver_deferred_probe_add(dev);
        /* Did a trigger occur while probing? Need to re-trigger if yes */
        if (local_trigger_count != atomic_read(&deferred_trigger_count))
            driver_deferred_probe_trigger();
    } else 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;
}

1 绑定针脚控制器(pinctrl_bind_pins) 2 创建dev设备的sysfs对应目录 3 如果bus存在probe函数调用bus的,否则driver存在probe函数则调用 driver的,由此可见bus的优先级比较高 4 给driver绑定device 5 通知probe_waitqueue

代码语言:javascript
复制
struct bus_type platform_bus_type = {
    .name       = "platform",
    .dev_groups = platform_dev_groups,
    .match      = platform_match,
    .uevent     = platform_uevent,
    .pm     = &platform_dev_pm_ops,
};

platform_bus_type并没有实现自己的probe函数,另外如果driver实现了probe函数,则会被替换为platform_drv_probe函数,所以这里基本上调用的都是platform_drv_probe(除非driver不需要执行probe函数)

代码语言:javascript
复制
static int platform_drv_probe(struct device *_dev)
{
    struct platform_driver *drv = to_platform_driver(_dev->driver);
    struct platform_device *dev = to_platform_device(_dev);
    int ret;

    ret = of_clk_set_defaults(_dev->of_node, false);
    if (ret < 0)
        return ret;

    ret = dev_pm_domain_attach(_dev, true);
    if (ret != -EPROBE_DEFER) {
        ret = drv->probe(dev);
        if (ret)
            dev_pm_domain_detach(_dev, true);
    }

    if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
        dev_warn(_dev, "probe deferral not supported\n");
        ret = -ENXIO;
    }

    return ret;
}

1 of_clk_set_defaults 用于设置时钟信息 2 dev_pm_domain_attach用于附加到电源管理域上 3 调用driver自己实现的prob函数

我们以goldfish_pipe为例子看下是如何实现的probe函数 drivers/platform/goldfish/goldfish_pipe.c

代码语言:javascript
复制
static int goldfish_pipe_probe(struct platform_device *pdev)
{
    DPRINT("%s: call. platform_device=0x%lx\n", __FUNCTION__, pdev);
    int err;
    struct resource *r;
    struct goldfish_pipe_dev *dev = pipe_dev;

    /* not thread safe, but this should not happen */
    WARN_ON(dev->base != NULL);

    spin_lock_init(&dev->lock);

    r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (r == NULL || resource_size(r) < PAGE_SIZE) {
        dev_err(&pdev->dev, "can't allocate i/o page\n");
        return -EINVAL;
    }
    dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE);
    if (dev->base == NULL) {
        dev_err(&pdev->dev, "ioremap failed\n");
        return -EINVAL;
    }

    r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (r == NULL) {
        err = -EINVAL;
        goto error;
    }
    dev->irq = r->start;

    err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt,
                IRQF_SHARED, "goldfish_pipe", dev);
    if (err) {
        dev_err(&pdev->dev, "unable to allocate IRQ\n");
        goto error;
    }

    err = misc_register(&goldfish_pipe_device);
    if (err) {
         dev_err(&pdev->dev, "unable to register device\n");
        goto error;
    }
    setup_access_params_addr(pdev, dev);

    /* Although the pipe device in the classic Android emulator does not
     * recognize the 'version' register, it won't treat this as an error
     * either and will simply return 0, which is fine. */
    dev->version = readl(dev->base + PIPE_REG_VERSION);
    return 0;

error:
    dev->base = NULL;
    return err;
}

1 获取设备=io地址空间(物理地址) 2 使用ioremap映射到一块虚拟地址 3 获取irq信息,注册irq处理函数 goldfish_pipe_interrupt 4 注册为设备goldfish_pipe_device

driver和device匹配到后就会执行module_add_driver, 这部分主要创建/sys/module下的kobject目录,注册irq处理函数

代码语言:javascript
复制
void module_add_driver(struct module *mod, struct device_driver *drv)
{
	char *driver_name;
	int no_warn;
	struct module_kobject *mk = NULL;

	if (!drv)
		return;

	if (mod)
		mk = &mod->mkobj;
	else if (drv->mod_name) {
		struct kobject *mkobj;

		/* Lookup built-in module entry in /sys/modules */
		mkobj = kset_find_obj(module_kset, drv->mod_name);
		if (mkobj) {
			mk = container_of(mkobj, struct module_kobject, kobj);
			/* remember our module structure */
			drv->p->mkobj = mk;
			/* kset_find_obj took a reference */
			kobject_put(mkobj);
		}
	}

	if (!mk)
		return;

	/* Don't check return codes; these calls are idempotent */
	no_warn = sysfs_create_link(&drv->p->kobj, &mk->kobj, "module");
	driver_name = make_driver_name(drv);
	if (driver_name) {
		module_create_drivers_dir(mk);
		no_warn = sysfs_create_link(mk->drivers_dir, &drv->p->kobj,
					    driver_name);
		kfree(driver_name);
	}
}

创建不创建对应的module模块还要看mk参数是否为空,不为空的情况下会把/sys/module/{driver_module} 目录符号链接到/sys/bus/{bus_name}/drivers/{driver_name}/module目录,给用户提供一个视角, 并在/sys/module下创建driver相关文件夹,下面的文件也是从/sys/bus/{bus_name}/drivers/

driver_create_file 创建uevent文件,用于发送uevent事件 driver_add_groups 创建这个组

代码语言:javascript
复制
int driver_add_groups(struct device_driver *drv,
		      const struct attribute_group **groups)
{
	return sysfs_create_groups(&drv->p->kobj, groups);
}
代码语言:javascript
复制
/**
 * driver_probe_device - attempt to bind device & driver together
 * @drv: driver to bind a device to
 * @dev: device to try to bind to the driver
 *
 * This function returns -ENODEV if the device is not registered,
 * 1 if the device is bound successfully and 0 otherwise.
 *
 * This function must be called with @dev lock held.  When called for a
 * USB interface, @dev->parent lock must be held as well.
 */
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
	int ret = 0;

	if (!device_is_registered(dev))
		return -ENODEV;

	pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
		 drv->bus->name, __func__, dev_name(dev), drv->name);

	pm_runtime_barrier(dev);
	ret = really_probe(dev, drv);
	pm_request_idle(dev);

	return ret;
}

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/159075.html原文链接:https://javaforall.cn

本文参与?腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022年7月1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客?前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与?腾讯云自媒体分享计划? ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
http://www.vxiaotou.com