bus总线相关的注册函数

本文是基于mini2440开发板Linux版本号是linux-2.6.32.2的学习笔记

一. bus_type结构体描述
struct bus_type {
	const char		*name;									             //总线类型的名称
	struct bus_attribute	*bus_attrs;						             //总线属性
	struct device_attribute	*dev_attrs;						             //设备属性
	struct driver_attribute	*drv_attrs;						             //驱动程序属性
	int (*match)(struct device *dev, struct device_driver *drv);
	int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
	int (*probe)(struct device *dev);
	int (*remove)(struct device *dev);
	void (*shutdown)(struct device *dev);                                //电源管理相关的实现
	int (*suspend)(struct device *dev, pm_message_t state);              //电源管理相关的实现
	int (*resume)(struct device *dev);                                   //电源管理相关的实现
	const struct dev_pm_ops *pm;                                         //电源管理相关的实现
	struct bus_type_private *p;                                          //bus_type的私有数据
};
二.bus模块初始化
int __init buses_init(void)
{
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;
	return 0;
}

bus_kset.uevent_ops = bus_uevent_ops;

static struct kset_uevent_ops bus_uevent_ops = {
	.filter = bus_uevent_filter,
};

static int bus_uevent_filter(struct kset *kset, struct kobject *kobj)
{
	struct kobj_type *ktype = get_ktype(kobj);

	if (ktype == &bus_ktype)
		return 1;
	return 0;
}

bus_uevent_filter过滤掉不等于bus_ktype的类型的消息。

创建了一个全局的kset为bus_kset,kset->kobj.kset = NULL,parent = NULL,名称为bus,因此在/sys目录下创建bus目录,为/sys/bus

三. 注册platform bus
error =  bus_register(&platform_bus_type);

platform_bus_type定义如下:

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};

dev_attrs展开后得到:

.dev_attrs =
{
    .attr = 
    {
        .name = "modalias",
        .owner =
        .mode = "0444",
    }
    .show = modalias_show
}

modalias_show函数的定义如下:

static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
			     char *buf)
{
	struct platform_device	*pdev = to_platform_device(dev);
	int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name);

	return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
}

就是把platform+pdev->name赋值到buffer中,返回字符串大小。

进入bus_register函数。bus_register函数定义如下:

int bus_register(struct bus_type *bus)
{
	int retval;
	struct bus_type_private *priv;

	priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->bus = bus;
	bus->p = priv;

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;

	retval = kset_register(&priv->subsys);
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,
						 &priv->subsys.kobj);
	if (!priv->drivers_kset) {
		retval = -ENOMEM;
		goto bus_drivers_fail;
	}

	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);

	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);
	if (retval)
		goto bus_attrs_fail;

	pr_debug("bus: '%s': registered\n", bus->name);
	return 0;

bus_attrs_fail:
	remove_probe_files(bus);
bus_probe_files_fail:
	kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
	kset_unregister(bus->p->devices_kset);
bus_devices_fail:
	bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
	kset_unregister(&bus->p->subsys);
	kfree(bus->p);
out:
	bus->p = NULL;
	return retval;
}

1.申请一个bus_type_private结构体priv,priv指针和bus指针互指

priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);
if (!priv)
    return -ENOMEM;

priv->bus = bus;
bus->p = priv;

2.priv里面有一个kset类型的subsys,给subsys赋值,然后注册这个kset

retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
    goto out;

priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
retval = kset_register(&priv->subsys);

priv->subsys.kobj.parent = NULL, priv->subsys.kobj.kset指向bus的kset,因此相当于在/sys/bus目录下新建了一个文件夹platform,得到/sys/bus/platform/

bus_ktype定义如下:

static struct kobj_type bus_ktype = {
	.sysfs_ops	=
	{
		.show	= bus_attr_show,
		.store	= bus_attr_store,
	}
};

static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
	struct bus_attribute *bus_attr = to_bus_attr(attr);
	struct bus_type_private *bus_priv = to_bus(kobj);
	ssize_t ret = 0;

	if (bus_attr->show)
		ret = bus_attr->show(bus_priv->bus, buf);
	return ret;
}

static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
	struct bus_attribute *bus_attr = to_bus_attr(attr);
	struct bus_type_private *bus_priv = to_bus(kobj);
	ssize_t ret = 0;

	if (bus_attr->store)
		ret = bus_attr->store(bus_priv->bus, buf, count);
	return ret;
}

3.设置driver和device自动匹配

priv->drivers_autoprobe = 1;

4.在/sys/bus/platform/目录下新建uenvet文件

retval = bus_create_file(bus, &bus_attr_uevent);
	sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);

5.创建一个devices_kset,新建/sys/bus/platform/devices目录

priv->devices_kset = kset_create_and_add("devices", NULL, &priv->subsys.kobj);
if (!priv->devices_kset) {
    retval = -ENOMEM;
    goto bus_devices_fail;
}

6.创建一个drivers_kset,新建/sys/bus/platform/drivers目录

priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);
if (!priv->drivers_kset) {
    retval = -ENOMEM;
    goto bus_drivers_fail;
}

7.在/sys/bus/platform/目录下新建了drivers_probe和drivers_autoprobe文件。

retval = add_probe_files(bus);
    static int add_probe_files(struct bus_type *bus)
    {
        int retval;

        retval = bus_create_file(bus, &bus_attr_drivers_probe);
        if (retval)
            goto out;

        retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
        if (retval)
            bus_remove_file(bus, &bus_attr_drivers_probe);
    out:
        return retval;
    }

8.创建总线属性文件,但是不同的总线的bus->bus_attrs不一定存在。

retval = bus_add_attrs(bus);
    static int bus_add_attrs(struct bus_type *bus)
    {
        int error = 0;
        int i;

        if (bus->bus_attrs) {
            for (i = 0; attr_name(bus->bus_attrs[i]); i++) {
                error = bus_create_file(bus, &bus->bus_attrs[i]);
                if (error)
                    goto err;
            }
        }
    done:
        return error;
    err:
        while (--i >= 0)
            bus_remove_file(bus, &bus->bus_attrs[i]);
        goto done;
    }
四. bus_add_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;

	if (drv->bus->p->drivers_autoprobe) {
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	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_attrs(bus, drv);
	if (error) {
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_add_attrs(%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);
		}
	}

	kobject_uevent(&priv->kobj, KOBJ_ADD);
	return 0;

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

1.增加bus的引用计数

bus = bus_get(drv->bus);

2.申请一个driver_private类型的数据priv,这个priv的就是drv的私有数据,用drv->p表示。

priv = kzalloc(sizeof(*priv), GFP_KERNEL);
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;

3.假设总线是platform,那么这一步相当于在/sys/bus/platform/drivers目录下新建了一个文件夹,假设是led,那么得到/sys/bus/platform/drivers/led文件夹。

priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, "%s", drv->name);

4.如果设置了自动匹配标志位,那么尝试去绑定bus总线上的device。
bus_for_each_dev函数实现从bus总线上取出每一个device,然后调用__driver_attach去匹配这个driver。

if (drv->bus->p->drivers_autoprobe) 
{
    error = driver_attach(drv);
        bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}

int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *))
{
	struct klist_iter i;
	struct device *dev;
	int error = 0;

	if (!bus)
		return -EINVAL;

	klist_iter_init_node(&bus->p->klist_devices, &i,
			     (start ? &start->p->knode_bus : NULL));
	while ((dev = next_device(&i)) && !error)
		error = fn(dev, data);
	klist_iter_exit(&i);
	return error;
}

5.将这个driver添加到bus的driver链表。

klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);

6.在/sys/bus/platform/drivers/led目录下新建一个uevent文件。

error = driver_create_file(drv, &driver_attr_uevent);
	error = sysfs_create_file(&drv->p->kobj, &attr->attr);

7.在/sys/bus/platform/drivers/led目录下创建bus->drv_attrs属性文件。

error = driver_add_attrs(bus, drv);

8.在/sys/bus/platform/drivers/led目录下创建bind和unbind这两个文件。

if (!drv->suppress_bind_attrs) 
{
		error = add_bind_files(drv);
}

static int __must_check add_bind_files(struct device_driver *drv)
{
	int ret;

	ret = driver_create_file(drv, &driver_attr_unbind);
	if (ret == 0) {
		ret = driver_create_file(drv, &driver_attr_bind);
		if (ret)
			driver_remove_file(drv, &driver_attr_unbind);
	}
	return ret;
}

9.上抛KOBJ_ADD消息

kobject_uevent(&priv->kobj, KOBJ_ADD);
五. bus_add_device函数
int bus_add_device(struct device *dev)
{
	struct bus_type *bus = bus_get(dev->bus);
	int error = 0;

	if (bus) {
		pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
		error = device_add_attrs(bus, dev);
		if (error)
			goto out_put;
		error = sysfs_create_link(&bus->p->devices_kset->kobj,
						&dev->kobj, dev_name(dev));
		if (error)
			goto out_id;
		error = sysfs_create_link(&dev->kobj,
				&dev->bus->p->subsys.kobj, "subsystem");
		if (error)
			goto out_subsys;
		error = make_deprecated_bus_links(dev);
		if (error)
			goto out_deprecated;
		klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
	}
	return 0;

out_deprecated:
	sysfs_remove_link(&dev->kobj, "subsystem");
out_subsys:
	sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
	device_remove_attrs(bus, dev);
out_put:
	bus_put(dev->bus);
	return error;
}

1.添加bus自带的device的属性文件

error = device_add_attrs(bus, dev);

static int device_add_attrs(struct bus_type *bus, struct device *dev)
{
	int error = 0;
	int i;

	if (!bus->dev_attrs)
		return 0;

	for (i = 0; attr_name(bus->dev_attrs[i]); i++) {
		error = device_create_file(dev, &bus->dev_attrs[i]);
		if (error) {
			while (--i >= 0)
				device_remove_file(dev, &bus->dev_attrs[i]);
			break;
		}
	}
	return error;
}

platform总线的dev_attrs如下:

.dev_attrs =
{
    .attr = 
    {
        .name = "modalias",
        .owner =
        .mode = "0444",
    }
    .show = modalias_show
}

因此device_add_attrs会在/sys/devices/platform下的每次device里面创建一个modalias文件。
modalias文件的读函数是modalias_show,尝试在/sys/devices/platform/s3c2410-iis目录cat modalias,得到:

# cat modalias 
platform:s3c2410-iis

2.在/sys/bus/platform/devices目录下创建一个软链接,假设device是led,那么可以得到:
/sys/bus/platform/devices/led ------->/sys/devices/platform/led

error = sysfs_create_link(&bus->p->devices_kset->kobj, &dev->kobj, dev_name(dev));

3.在/sys/devices/platform/led目录新建一个软链接,名字是“subsystem”,软链接到/sys/bus/platform

error = sysfs_create_link(&dev->kobj, &dev->bus->p->subsys.kobj, "subsystem");

4.将dev放入bus的device链表中。

klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值