嵌入式软件开发之------浅谈linux驱动模型(二)bus

Linux代码版本:linux3.0

开发板环境: tiny4412

导读:上一节分析了kobject、kset和sys下目录的创建,这只是linux驱动模型最基本的一步,这一节开始分析bus的注册过程,后面会相继分析class、device和driver。下面以platform总线为例,分析platform总线的注册过程。

一、platform相关结构体分析

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 iommu_ops *iommu_ops;

	struct subsys_private *p;
};

const char    *name:

总线的名字

struct bus_attribute *bus_attrs:

总线默认的属性,如果不为BULL,会在相应的总线下创建属性文件

struct device_attribute *dev_attrs:

总线上device的默认属性,如果不为NULL,platform下各种device目录下会创建属性文件

struct driver_attribute *drv_attrs:

总线上driver的默认属性,如果不为NULL,platform下各种driver目录下会创建属性文件

int (*match)(struct device *dev, struct device_driver *drv):

总线上的device和driver就是通过此函数进行匹配的

int (*uevent)(struct device *dev, struct kobj_uevent_env *env):

总线上增加或移除device时调用

int (*probe)(struct device *dev):

当总线上增加device或driver并且成功匹配时调用此函数

int (*remove)(struct device *dev):

device从总线上移除的时候调用此函数

const struct dev_pm_ops *pm:

总线电源管理的操作集合,后面分析 电源管理的时候再详细分析各成员

struct subsys_private *p:

总线的私有数据成员

再看重要的数据结构

struct subsys_private {
	struct kset subsys;
	struct kset *devices_kset;

	struct kset *drivers_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;

	struct list_head class_interfaces;
	struct kset glue_dirs;
	struct mutex class_mutex;
	struct class *class;
};

struct kset subsys:

bus中封装的kset(sys下的目录总是要封装kobject或kset,不然怎么建目录和文件?)

struct kset *devices_kset:
相关设备列表,等注册device或driver的时候再分析用途

struct kset *drivers_kset:

相关驱动程序列表,等注册device或driver的时候再分析用途

struct klist klist_devices:

注册的device挂接在此链表中

struct klist klist_drivers:

注册的driver挂接在此链表中

struct blocking_notifier_head bus_notifier:

目前还未看出具体用途,代码中遇到再分析

unsigned int drivers_autoprobe:1:

在注册device 或driver的时候是否自动进行匹配

struct bus_type *bus:

指向的bus

struct list_head class_interfaces:

给class用,后面分析的class的时候再详细解释

struct kset glue_dirs:

给class用,后面分析的class的时候再详细解释

struct mutex class_mutex:

给class用,后面分析的class的时候再详细解释

struct class *class:

给class用,后面分析的class的时候再详细解释


无论加多少注释,都是干巴巴的不易理解,分析代码将会看到每个成员的用途,分析 代码前先介绍几个platform相关的结构体变量。

struct bus_type platform_bus_type = {
	.name		= "platform",
	.dev_attrs	= platform_dev_attrs,
	.match		= platform_match,
	.uevent		= platform_uevent,
	.pm		= &platform_dev_pm_ops,
};
static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);

展开:

static bus_attribute bus_attr_uevent = {
    .attr = {
        .name = uevent,
        .mode = S_IWUSR,
        }
    
    .show = NULL,
    .store = bus_uevent_store,
}

最终实际定义一个bus_attr_uevent的变量,代码中会在/sys/bus/platform下创建一个uevent 的属性文件,该文件为只写,写该文件的时候最终会调用到 bus_uevent_store函数


static BUS_ATTR(drivers_probe, S_IWUSR, NULL, store_drivers_probe);

展开:

static bus_attribute bus_attr_drivers_probe = {
    .attr = {
        .name = drivers_probe,
        .mode = S_IWUSR,
        }
    
    .show = NULL,
    .store = store_drivers_probe,
}

最终实际定义一个bus_attr_drivers_probe的变量,代码中在/sys/bus/platform下创建一个 drivers_probe 的属性文件,该文件为只写,写该文件的时候最终会调用到 store_drivers_probe 函数

static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,show_drivers_autoprobe, store_drivers_autoprobe);

展开:

static bus_attribute bus_attr_drivers_autoprobe = {
    .attr = {
        .name = drivers_autoprobe,
        .mode = S_IWUSR | S_IRUGO,
        }
    
    .show = show_drivers_autoprobe,
    .store = store_drivers_autoprobe,
}

最终实际定义一个bus_attr_drivers_autoprobe的变量,代码中在/sys/bus/platform下创建一个 名为drivers_autoprobe 的属性文件,该文件为可读写,读取该文件的时候最终会调用到 show_drivers_autoprobe函数,写该文件的时候最终调用到store_drivers_autoprobe

开始分析代码:

int __init platform_bus_init(void)
{
        ......
    /*注册了一个platform的device,但只有一个name成员赋值,
    后面分析注册device的时候再详细分析*/
    error = device_register(&platform_bus);
           
	if (error)
		return error;
    /*注册platform bus*/
	error =  bus_register(&platform_bus_type);
            {
                int retval;
                struct subsys_private *priv;
                
                /*为platform_bus_type申请一个私有结构体priv*/
                priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
                if (!priv)
                    return -ENOMEM;
                    
                /*赋值platform_bus_type->p->bus = platform_bus_type*/
                priv->bus = bus;
                bus->p = priv;
                
                /*初始化init_rwsem(platform_bus_type->p->bus_notifier->rwsem)
                  赋值platform_bus_type->p->bus_notifier->head = NULL*/
                BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
                {
                    do {	
                            init_rwsem(&(name)->rwsem);	
                            (name)->head = NULL;		
                    } while (0)
                
                }
                
                /*又看见了kobj,platform_bus_type的私有结构体封装了kset,
                platform_bus_type->p->subsys.kobj.name = "platform"*/
                retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
                if (retval)
                    goto out;
                
                /*赋值platform_bus_type->p->subsys.kobj.kset = bus_kset,还记得sys下的bus目录吗
                  bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);*/
                priv->subsys.kobj.kset = bus_kset;
                
                /*赋值platform_bus_type->p->subsys.kobj.ktype = &bus_ktype*/            
                priv->subsys.kobj.ktype = &bus_ktype;
                /*赋值platform_bus_type->p->drivers_autoprobe = 1*/
                priv->drivers_autoprobe = 1;
            
                retval = kset_register(&priv->subsys);
                         {
                            int err;

                            if (!k)
                                return -EINVAL;
                            
                            kset_init(k);
                            err = kobject_add_internal(&k->kobj);
                                  {
                                     int error = 0;
                                     struct kobject *parent;
                                     
                                     if (!kobj)
                                         return -ENOENT;
                                     /*前面已经初始化platform_bus_type->p->subsys.kobj.name = "platform"*/
                                     if (!kobj->name || !kobj->name[0]) {
                                         WARN(1, "kobject: (%p): attempted to be registered with empty "
                                             "name!\n", kobj);
                                         return -EINVAL;
                                     }
                                     
                                     /*前面platform_bus_type->p->subsys.kobj.parent并没有赋值,所以为NULL*/
                                     parent = kobject_get(kobj->parent);
                                     
                                     /* join kset if set, use it as parent if we do not already have one */
                                     /*前面已赋值platform_bus_type->p->subsys = bus_kset,因为
                                     platform_bus_type->p->subsys.kobj.parent = NULL,所以 
                                        platform_bus_type->p->subsys.kobj.parent = bus_kset-> kobj  
                                        也就意味着要在 /sys/bus目录下创建platorm目录*/
                                     if (kobj->kset) {
                                         if (!parent)
                                             parent = kobject_get(&kobj->kset->kobj);
                                         kobj_kset_join(kobj);
                                         kobj->parent = parent;
                                     }
                                     
                                     pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
                                         kobject_name(kobj), kobj, __func__,
                                         parent ? kobject_name(parent) : "<NULL>",
                                         kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
                                     /*在/sys/bus目录下创建platorm目录*/
                                     error = create_dir(kobj);
                                     if (error) {
                                         kobj_kset_leave(kobj);
                                         kobject_put(parent);
                                         kobj->parent = NULL;
                                     
                                         /* be noisy on error issues */
                                         if (error == -EEXIST)
                                             printk(KERN_ERR "%s failed for %s with "
                                                 "-EEXIST, don't try to register things with "
                                                 "the same name in the same directory.\n",
                                                 __func__, kobject_name(kobj));
                                         else
                                             printk(KERN_ERR "%s failed for %s (%d)\n",
                                                 __func__, kobject_name(kobj), error);
                                         dump_stack();
                                     } else
                                        /*置位已经在sys中建立目录的标志state_in_sysfs为1*/
                                         kobj->state_in_sysfs = 1;
                                     
                                     return error;
                                  
                                  }
                            if (err)
                                return err;
                            /*发送ADD事件*/
                            kobject_uevent(&k->kobj, KOBJ_ADD);
                            return 0;
                          
                         }
                if (retval)
                    goto out;
               /*在sys/bus/platform目录下建立uevent文件*/
                retval = bus_create_file(bus, &bus_attr_uevent);
                if (retval)
                    goto bus_uevent_fail;
                
                /*又是kset_create_and_add,是不是很熟悉,sys下bus目录就是调用此函数创建的;
                下面指定了parent为platform,也就是在/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;
                }
               
                /*在/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;
                }
            
                klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
                {
                    /*初始化platform_bus_type->p->klist_devices,
                    platform_bus_type->p->klist_devices->get = klist_devices_get
                    platform_bus_type->p->klist_devices->put = klist_devices_put*/
                    
                    INIT_LIST_HEAD(&k->k_list);
                    spin_lock_init(&k->k_lock);
                    k->get = get;
                    k->put = put;
                }
                
                /*初始化platform_bus_type->p->klist_drivers
                    platform_bus_type->p->klist_drivers->get = NULL
                    platform_bus_type->p->klist_drivers->put = NULL*/
                    
                klist_init(&priv->klist_drivers, NULL, NULL);
            
                retval = add_probe_files(bus);
                         {
                            int retval;
                           /*在/sys/bus/platform下创建drivers_probe目录*/
                            retval = bus_create_file(bus, &bus_attr_drivers_probe);
                                     {
                                        int error;
                                        if (bus_get(bus)) {
                                            /*在/sys/bus/platform下创建drivers_probe文件*/
                                            error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);
                                            bus_put(bus);
                                        } else
                                            error = -EINVAL;
                                        return error;
                                     }
                            if (retval)
                                goto out;
                        
                            /*在/sys/bus/platform下创建 drivers_autoprobe 文件*/
                            retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
                            if (retval)
                                bus_remove_file(bus, &bus_attr_drivers_probe);
                        out:
                            return retval;
                         
                         }
                if (retval)
                    goto bus_probe_files_fail;
            
                retval = bus_add_attrs(bus);
                         {
                            int error = 0;
                            int i;
                            /*platform_bus_type->bus_attrs未赋值,也就是为NULL,所以此步不会创建目录*/
                            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;
                         }
                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);
            out:
                kfree(bus->p);
                bus->p = NULL;
                return retval;
            }
	if (error)
		device_unregister(&platform_bus);
	return error;
}

以上程序中可以看到先在sys/bus/下创建了platform目录,然后又在sys/bus/platform下创建了devices和drivers目录,同时也创建了drivers_probe、drivers_autoprobe和uevent文件,下面在设备上验证以上的目录和文件

[root@FriendlyARM /]# cd sys/bus/
[root@FriendlyARM bus]# ls
hid         media       platform    sdio        spi         usb-serial
i2c         mmc         scsi        serio       usb

[root@FriendlyARM bus]# cd /sys/bus/platform/
[root@FriendlyARM platform]# ls -l
drwxr-xr-x    2 root     root             0 Jan  1 12:42 devices
drwxr-xr-x   50 root     root             0 Jan  1 12:42 drivers
-rw-r--r--    1 root     root          4096 Jan  1 12:42 drivers_autoprobe
--w-------    1 root     root          4096 Jan  1 12:42 drivers_probe
--w-------    1 root     root          4096 Jan  1 12:42 uevent

当对drivers_probe、drivers_autoprobe和uevent进行读写的时候,将会调用到相应的show和store函数,下面详细分析store和show函数:

static ssize_t store_drivers_probe(struct bus_type *bus,
				   const char *buf, size_t count)
{
	struct device *dev;
    
    /*从platform_bus上查找指定name的device*/
	dev = bus_find_device_by_name(bus, NULL, buf);
          {
             return bus_find_device(bus, start, (void *)name, match_name);
                    {
                        struct klist_iter i;
                        struct device *dev;
                        
                        /*platform_bus*/
                        if (!bus || !bus->p)
                            return NULL;
                        /*遍历platform的device链表,并调用match_name函数进行name对比*/
                        klist_iter_init_node(&bus->p->klist_devices, &i,
                                    (start ? &start->p->knode_bus : NULL));
                        {
                            i->i_klist = k;
                            i->i_cur = n;
                            if (n)
                                kref_get(&n->n_ref);
                        }
                        while ((dev = next_device(&i)))
                            if (match(dev, data) && get_device(dev))
                                break;
                        klist_iter_exit(&i);
                        return dev;
                    }
          }
	if (!dev)
		return -ENODEV;
    /*将上面查找到的device绑定相应driver,并调用probe函数*/
	if (bus_rescan_devices_helper(dev, NULL) != 0)
        {
            int ret = 0;
        
            if (!dev->driver) {
                if (dev->parent)	/* Needed for USB */
                    device_lock(dev->parent);
                 /*将device和driver绑定并调用probe函数,后面会在device注册的时候详细分析此函数*/
                ret = device_attach(dev);
                if (dev->parent)
                    device_unlock(dev->parent);
            }
            return ret < 0 ? ret : 0;
        }
        
		return -EINVAL;
	return count;
}

当向 drivers_probe 文件中写入一个device名字的时候,将会在platform总线device链表platform_bus->p->klist_devices上查找该设备,如果查找到,将会查找相应的driver(还记得device和drive匹配的match函数吗),匹配成功则想用驱动中的probe函数。有没有觉得很像driver或device的注册过程?

static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)
{
	return sprintf(buf, "%d\n", bus->p->drivers_autoprobe);
}
static ssize_t store_drivers_autoprobe(struct bus_type *bus,
				       const char *buf, size_t count)
{
	if (buf[0] == '0')
		bus->p->drivers_autoprobe = 0;
	else
		bus->p->drivers_autoprobe = 1;
	return count;
}

上面的两个函数就是读写 bus->p->drivers_autoprobe ,这个成员的作用将会在device或者driver注册过程中分析,其实就是在将device或driver注册到platform总线后是否自动进行相应的match,当然初始化的时候就已经设置为1了(priv->drivers_autoprobe = 1;)。

static ssize_t bus_uevent_store(struct bus_type *bus,
				const char *buf, size_t count)
{
	enum kobject_action action;

	if (kobject_action_type(buf, count, &action) == 0)
		kobject_uevent(&bus->p->subsys.kobj, action);
	return count;
}
还记得代码中的 kobject_uevent(&k->kobj, KOBJ_ADD) ,uevent文件为用户空间提供的接口,可通过指令触发add和remove等命令,如 echo add > uevent 。 kobject_uevent会发送event给用户空间,udev检测到时间会进行相应的/dev/目录下节点的创建和删除。


下一节将进行class的分析。




猜你喜欢

转载自blog.csdn.net/lujian186/article/details/80878678