设备模型之 bus 、devices、drivers、class 分析

 在分析设备模型的bus之前,我们看一下初始化的过程,很有意思:

     start_kernel-->rest_init-->kernel_init-->do_basic_setup

     在do_basic_setup中有个usermodehelper_init有意思,凭感觉,这个函数与kobject_uevent有联系,kobject_uevent做什么的,就是内核空间来通知用户空间的,比如udev就是利用这个来在/dev创建设备文件的,再比如在嵌入式中,插拔个硬盘或者USB,gui总会提示插入或者拨出之类的提示,这个一般是内核中驱动模型根据kobject的uevent来告诉用户态的,如果通信的,是通过netlink这种特殊的socket来像用户空间广播的,这样gui中可以用socket来接受信息,并解析,然后显示出来。先提到这里,usermodehelper_init是不是这么回事,还需要更多的时间去研究。

      好,继续看do_basic_setup中的其它函数:driver_init

      

/**
 * driver_init - initialize driver model.
 *
 * Call the driver model init functions to initialize their
 * subsystems. Called early from init/main.c.
 */
void __init driver_init(void)
{
	/* These are the core pieces */
	devtmpfs_init();
	devices_init();
	buses_init();
	classes_init();
	firmware_init();
	hypervisor_init();

	/* These are also core pieces, but must come after the
	 * core core pieces.
	 */
	platform_bus_init();
	system_bus_init();
	cpu_dev_init();
	memory_dev_init();
}

      看下这个函数的注释,就清楚它就是接下来要分析的设备模型 的初始化。

      devtmpfs_init是什么?是在/dev下面挂载的一个内存文件系统,具体可以看《linux设备模型之字符设备》一文,与驱动是有一定联系,应该是2.6.32之后加入的。

      今天我们只分析core core pieces中的buses_init:

int __init buses_init(void)
{
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;
	return 0;
}

       很简单,新增一个kset,在/sys下有了bus目录。

       我们的重心在后面,如何在bus目录下添加总线类型:

       描述总线的结构体: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紧密相关的结构体:struct bus_type_private

struct bus_type_private {
	struct kset subsys;   //具体总线的kset
	struct kset *drivers_kset;  //总线上所有驱动的集合
	struct kset *devices_kset; //总线上所有设备的集合
	struct klist klist_devices;  //链表,上面是总线上的设备
	struct klist klist_drivers;   //链表,上面是总线上的驱动
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;    //指向总线类型结构体
};

        总线的注册:bus_register

        

/**
 * bus_register - register a bus with the system.
 * @bus: bus.
 *
 * Once we have that, we registered the bus with the kobject
 * infrastructure, then register the children subsystems it has:
 * the devices and drivers that belong to the bus.
 */
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;      //bus_type和bus_type_private紧密联系起来

	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);   //设置具体总线kset内嵌kobject的名称
	if (retval)
		goto out;

	priv->subsys.kobj.kset = bus_kset;        //总线kset内嵌kobject的kset指向bus_kset,也就是说上级目录是/sys/bus
	priv->subsys.kobj.ktype = &bus_ktype;
	priv->drivers_autoprobe = 1;

	retval = kset_register(&priv->subsys);    //注册总线的kset,生成/sys/bus/xxbus 目录
	if (retval)
		goto out;

	retval = bus_create_file(bus, &bus_attr_uevent);    //创建uevent目录,只写的文件,写文件会调用kobject_uevent函数
	if (retval)
		goto bus_uevent_fail;

	priv->devices_kset = kset_create_and_add("devices", NULL,      //在xxbus目录下建立devices目录
						 &priv->subsys.kobj);
	if (!priv->devices_kset) {
		retval = -ENOMEM;
		goto bus_devices_fail;
	}

	priv->drivers_kset = kset_create_and_add("drivers", NULL,      //在xxbus目录下建立drivers目录
						 &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_devices链表
	klist_init(&priv->klist_drivers, NULL, NULL);                             //初始化klist_drivers链表

	retval = add_probe_files(bus);                                //在xxbus目录下建立drivers_probe和drivers_autoprobe文件
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_attrs(bus);                                  //建立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;
}

        总线的注册方法就是上面这么多,比如platform总线的注册,就是在/sys/bus目录下多了个platform目录,而在platform目录下:

        devices  drivers  drivers_autoprobe  drivers_probe  uevent

        总线远没有目前看到的这么简单,因为它的方法我们还没用到。在分析完设备和驱动后,我们再分析一个完整的过程。

 

 

 

分析完了bus,接下来分析device:

      同样在driver_init-->devices_init

  1. <SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int __init devices_init(void)  
  2. {  
  3.     devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);  
  4.     if (!devices_kset)  
  5.         return -ENOMEM;  
  6.     dev_kobj = kobject_create_and_add("dev", NULL);  
  7.     if (!dev_kobj)  
  8.         goto dev_kobj_err;  
  9.     sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);  
  10.     if (!sysfs_dev_block_kobj)  
  11.         goto block_kobj_err;  
  12.     sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);  
  13.     if (!sysfs_dev_char_kobj)  
  14.         goto char_kobj_err;  
  15.   
  16.     return 0;  
  17.   
  18.  char_kobj_err:  
  19.     kobject_put(sysfs_dev_block_kobj);  
  20.  block_kobj_err:  
  21.     kobject_put(dev_kobj);  
  22.  dev_kobj_err:  
  23.     kset_unregister(devices_kset);  
  24.     return -ENOMEM;  
  25. }</SPAN>  
int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}

       /sys目录下有了 devices目录和 dev目录, dev下面有 blockchar两个目录


       设备的注册:

  1. <SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int device_register(struct device *dev)  
  2. {  
  3.     device_initialize(dev);  
  4.     return device_add(dev);  
  5. }  
  6. </SPAN>  
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}


  1. 设备的初始化device_initialize

void device_initialize(structdevice *dev)

{

dev->kobj.kset =devices_kset; //这个device结构体内嵌的kobject代表的目录在/sys/devices

kobject_init(&dev->kobj,&device_ktype); //初始化内嵌的kobjectkobj_typedevice_ktype

INIT_LIST_HEAD(&dev->dma_pools);

init_MUTEX(&dev->sem);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_init_wakeup(dev, 0);

device_pm_init(dev);

set_dev_node(dev, -1);

}

  1. 添加设备device_add

2.1、初始化devicestructdevice_private *p;指针

int device_private_init(structdevice *dev)

{

dev->p =kzalloc(sizeof(*dev->p), GFP_KERNEL);

if (!dev->p)

return -ENOMEM;

dev->p->device = dev; //指向dev

klist_init(&dev->p->klist_children,klist_children_get,

klist_children_put); //初始化klist_children链表

return 0;

}

2.2、设置device结构体内嵌的kobject的名称

2.3setup_parent(dev,parent);

根据device结构体的父子层次关系设置内嵌kobject对象的父子层次关系

2.4、在kobject层注册device内嵌的kobject对象

2.5、创建设备属性文件ueventuevent文件的读写与kobject_uevent相关

2.6、如果device结构体的devt设备号存在,那么创建devt属性文件文件,从这个属性文件中可以读出设备号;sysfs中会创建链接文件;devtmpfs会创建结点node

2.7、设备添加class的链接文件

2.8、添加设备属性文件

2.9bus_add_device(dev); 添加设备到总线

A、添加设备的总线属性:bus->dev_attrs

B、创建链接文件

C、添加设备到总线的设备链表中:

klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);

2.10bus_probe_device(dev);device探测driver

bus_probe_device-->device_attach

如果dev的driver指针已经指向了driver结构体,那么调用device_bind_driver将device和driver绑定,否则调用

bus_for_each_drv(dev->bus, NULL, dev, __device_attach);对于bus总线klist_drivers链表上所有挂着的driver,都

来和我们的device进行匹配,负责的函数就是__device_attach


如果devdriver指针已经

static int__device_attach(struct device_driver *drv, void *data)

{

struct device *dev = data;


if (!driver_match_device(drv,dev))

return 0;


return driver_probe_device(drv,dev);

}

首先调用总线级别的match函数,然后调用driver_probe_device(drv,dev);

driver_probe_device-->really_probe-->dev->bus->probe(dev);以及drv->probe(dev);

最后调用driver_bound

klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices);


                       devices的注册过程就如上所述,至于最后的bus_probe_device具体是怎么实现的?等到分析完busdevice driverclass后,再举一个实例来具体分析。分析完了bus,接下来分析device:

      同样在driver_init-->devices_init

  1. <SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int __init devices_init(void)  
  2. {  
  3.     devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);  
  4.     if (!devices_kset)  
  5.         return -ENOMEM;  
  6.     dev_kobj = kobject_create_and_add("dev", NULL);  
  7.     if (!dev_kobj)  
  8.         goto dev_kobj_err;  
  9.     sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);  
  10.     if (!sysfs_dev_block_kobj)  
  11.         goto block_kobj_err;  
  12.     sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);  
  13.     if (!sysfs_dev_char_kobj)  
  14.         goto char_kobj_err;  
  15.   
  16.     return 0;  
  17.   
  18.  char_kobj_err:  
  19.     kobject_put(sysfs_dev_block_kobj);  
  20.  block_kobj_err:  
  21.     kobject_put(dev_kobj);  
  22.  dev_kobj_err:  
  23.     kset_unregister(devices_kset);  
  24.     return -ENOMEM;  
  25. }</SPAN>  
int __init devices_init(void)
{
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}


       /sys目录下有了devices目录和dev目录,dev下面有blockchar两个目录


       设备的注册:

  1. <SPAN style="FONT-FAMILY: 'Liberation Serif', serif">int device_register(struct device *dev)  
  2. {  
  3.     device_initialize(dev);  
  4.     return device_add(dev);  
  5. }  
  6. </SPAN>  
int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}



  1. 设备的初始化device_initialize

void device_initialize(structdevice *dev)

{

dev->kobj.kset =devices_kset; //这个device结构体内嵌的kobject代表的目录在/sys/devices

kobject_init(&dev->kobj,&device_ktype); //初始化内嵌的kobjectkobj_typedevice_ktype

INIT_LIST_HEAD(&dev->dma_pools);

init_MUTEX(&dev->sem);

spin_lock_init(&dev->devres_lock);

INIT_LIST_HEAD(&dev->devres_head);

device_init_wakeup(dev, 0);

device_pm_init(dev);

set_dev_node(dev, -1);

}

  1. 添加设备device_add

2.1、初始化devicestructdevice_private *p;指针

int device_private_init(structdevice *dev)

{

dev->p =kzalloc(sizeof(*dev->p), GFP_KERNEL);

if (!dev->p)

return -ENOMEM;

dev->p->device = dev; //指向dev

klist_init(&dev->p->klist_children,klist_children_get,

klist_children_put); //初始化klist_children链表

return 0;

}

2.2、设置device结构体内嵌的kobject的名称

2.3setup_parent(dev,parent);

根据device结构体的父子层次关系设置内嵌kobject对象的父子层次关系

2.4、在kobject层注册device内嵌的kobject对象

2.5、创建设备属性文件ueventuevent文件的读写与kobject_uevent相关

2.6、如果device结构体的devt设备号存在,那么创建devt属性文件文件,从这个属性文件中可以读出设备号;sysfs中会创建链接文件;devtmpfs会创建结点node

2.7、设备添加class的链接文件

2.8、添加设备属性文件

2.9bus_add_device(dev); 添加设备到总线

A、添加设备的总线属性:bus->dev_attrs

B、创建链接文件

C、添加设备到总线的设备链表中:

klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);

2.10bus_probe_device(dev);device探测driver

bus_probe_device-->device_attach

如果dev的driver指针已经指向了driver结构体,那么调用device_bind_driver将device和driver绑定,否则调用

bus_for_each_drv(dev->bus, NULL, dev, __device_attach);对于bus总线klist_drivers链表上所有挂着的driver,都

来和我们的device进行匹配,负责的函数就是__device_attach


如果devdriver指针已经

static int__device_attach(struct device_driver *drv, void *data)

{

struct device *dev = data;


if (!driver_match_device(drv,dev))

return 0;


return driver_probe_device(drv,dev);

}

首先调用总线级别的match函数,然后调用driver_probe_device(drv,dev);

driver_probe_device-->really_probe-->dev->bus->probe(dev);以及drv->probe(dev);

最后调用driver_bound

klist_add_tail(&dev->p->knode_driver,&dev->driver->p->klist_devices);


                       devices的注册过程就如上所述,至于最后的bus_probe_device具体是怎么实现的?等到分析完busdevice driverclass后,再举一个实例来具体分析。

 

 看完了device看driver,给出device_driver的结构体:

struct device_driver {
	const char		*name;
	struct bus_type		*bus;

	struct module		*owner;
	const char		*mod_name;	/* used for built-in modules */

	bool suppress_bind_attrs;	/* disables bind/unbind via sysfs */

	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 attribute_group **groups;

	const struct dev_pm_ops *pm;

	struct driver_private *p;
};


 

struct driver_private {
	struct kobject kobj;
	struct klist klist_devices;
	struct klist_node knode_bus;
	struct module_kobject *mkobj;
	struct device_driver *driver;
};

      驱动的注册:

int driver_register(struct device_driver *drv)

主体部分分为三块看:

1、other = driver_find(drv->name, drv->bus);

      driver_find函数是利用驱动模型的基石kobject和kset结构体的双向循环链表,根据name查找。这里的kset是bus->p->drivers_kset,也

      就是bus上已经挂着的驱动的集合。如果找到了,就有问题了:已经注册了该驱动,不能再注册了,kernel会打印error并返回EBUSY。

      如果没找到,就到了第二步

2、bus_add_driver(drv);

       这个过程和bus_add_device比较类似

       2.1、建立driver_private和device_driver你中有我 我中有你的亲密关系

       2.2、priv->kobj.kset = bus->p->drivers_kset;   意味了/bus/xxbus/driver下面就是具体driver的目录

       2.3、

if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}

        调用driver_attach-->bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)

        对于bus的klist_devices链表上挂着的所有device,都来和我们的driver进行匹配,进行匹配的函数是__driver_attach

        __driver_attach首先调用driver_match_device,也就是总线级别的match函数进行匹配

        如果没匹配上,就调用driver_probe_device

        这里的函数都是与device中用到的一样

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

                将driver加入bus的klist_drivers链表中

        2.5、driver_create_file(drv, &driver_attr_uevent);

                创建uevent属性文件

        2.6、添加driver指向的bus的驱动属性文件bus->drv_attrs

        2.7、如果需要的话,添加bind和unbind属性文件

        2.8、kobject_uevent(&priv->kobj, KOBJ_ADD);  发送uevent


driver的注册就是这样了,没什么好说的了。。。



看完了bus device和driver,终于到了设备模型上层架构的最后一个单元:class.class的初始化同样是在driver_init下:

      driver_init-->classes_init

int __init classes_init(void)
{
	class_kset = kset_create_and_add("class", NULL, NULL);
	if (!class_kset)
		return -ENOMEM;
	return 0;
}


        新增一个名为class的kset,这样/sys下面就有了class

        class到底是什么,它是对同类型设备的抽象,是device classes,class结构体的定义如下:

struct class {
	const char		*name;
	struct module		*owner;

	struct class_attribute		*class_attrs;
	struct device_attribute		*dev_attrs;
	struct kobject			*dev_kobj;

	int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
	char *(*devnode)(struct device *dev, mode_t *mode);

	void (*class_release)(struct class *class);
	void (*dev_release)(struct device *dev);

	int (*suspend)(struct device *dev, pm_message_t state);
	int (*resume)(struct device *dev);

	const struct dev_pm_ops *pm;

	struct class_private *p;
};



 

        比如我们的rtc子系统,就会在子系统初始化时创建rtc的class:

        rtc_init-->rtc_class = class_create(THIS_MODULE, "rtc");

        class_create是个宏定义:

#define class_create(owner, name)		\
({						\
	static struct lock_class_key __key;	\
	__class_create(owner, name, &__key);	\
})

         __class_create首先做初始话操作,然后调用__class_register函数做class的注册操作:


        注册过程主要是建立class名称的目录,然后建立class的属性文件.

        class实在没什么好说的了,其实讲了这么多,这个设备模型到底是什么还没讲清楚,只有自己动手做一次才明白,后面以s3c2440的rtc为例来全面的体验一下设备模型.

        s3c2440的rtc是一个很好的例子,既有platform这样的虚拟总线,也有rtc的class,完全符合体验的需求.

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值