linux文件系统的系统分析--(十一)sysfs和设备模型--Bus

     在分析设备模型的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

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

       


       


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值