platform driver注册过程

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

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

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

/* 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为例子展开如下

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,作用分别是在内核初始化的时候注册该驱动,退出的时候注销该驱动

#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方法注册驱动

/**
 * 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文件下

/**
 * 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文件

/**
 * 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中,

/**
 * 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中,

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中如下

/**
 * 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

/**
 * 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 真正的探测函数

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

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函数)

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

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处理函数

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_name}链接过来的

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

int driver_add_groups(struct device_driver *drv,
		      const struct attribute_group **groups)
{
	return sysfs_create_groups(&drv->p->kobj, groups);
}
/**
 * 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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • pytest的assert_assert断言语句

    pytest的assert_assert断言语句前言断言是写自动化测试基本最重要的一步,一个用例没有断言,就失去了自动化测试的意义了。什么是断言呢?简单来讲就是实际结果和期望结果去对比,符合预期那就测试pass,不符合预期那就测试failed

  • 王坚十年前的坚持,才有了今天世界顶级大数据计算平台MaxCompute

    王坚十年前的坚持,才有了今天世界顶级大数据计算平台MaxCompute

  • h3c路由器配置命令_h3c路由器命令大全

    h3c路由器配置命令_h3c路由器命令大全en进入特权模式conf进入全局配置模式ins0进入serial0端口配置ipaddxxx.xxx.xxx.xxxxxx.xxx.xxx.xxx添加ip地址和掩码,电信分配encahdlc/ppp捆绑链路协议hdlc或者pppipunne0exit回到全局配置模式ine0进入以太接口配置ipaddxxx.xxx.xxx.xxxxxx.xxx.xxx…

    2022年10月18日
  • Android基于百度OCR识别图片中的文字

    Android基于百度OCR识别图片中的文字    OCR(OpticalCharacterRecognition),即光学字符识别,指的是针对印刷体字符,采用光学的方式将纸质文档中的文字转换成为黑白点阵的图像文件,通过识别软件将图像中的文字转换成文本格式,供文字处理软件进一步编辑加工的技术。简单的来说,OCR技术就是可以把图片上的文字识别出来,并以文本格式的形式提取出来。    该技术已广泛应用于生活中。比如很多翻译软件都有的拍照翻译功能,就利用了该技术。这里尝试使用百度OCR接口实现Android拍照识别文字功能。请求模块定义   

  • idea2019最新可用激活码_通用破解码

    idea2019最新可用激活码_通用破解码,https://javaforall.cn/100143.html。详细ieda激活码不妨到全栈程序员必看教程网一起来了解一下吧!

  • numpy中randn_flattening

    numpy中randn_flatteningNumpy中的ravel()和flatten()两个函数可以对多维数据进行扁平化操作。flatten()返回的是一个数组的的副本,新的对象;ravel()返回的是一个数组的非副本视图。

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号