本文是基于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);