linux内核总线驱动模型-总线篇

总线是处理器与设备之间的通道,在设备模型中,所有的设备都是通过总线相连的。在设备模型中,总线由bus_type表示

struct bus_type {
    
    
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	const struct attribute_group **bus_groups;
	const struct attribute_group **dev_groups;
	const struct attribute_group **drv_groups;

	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 (*online)(struct device *dev);
	int (*offline)(struct device *dev);

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

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};
  • name:总线的名称
  • device_attrs:设备的属性
  • match:匹配函数,用于在driver和device做适配,为设备找驱动,为驱动找设备
  • uevent:用于总线对uevent环境变量的添加
  • probe:探针函数,当device和driver配对成功以后,总线的probe或者driver的probe有一个会被调用
  • remove:总线上有驱动或者设备移除时调用
  • shutdown:在总线上所有设备都关闭时调用
  • resume:处理热插拔,电源管理

在最后有指向subsys_private的指针,定义了将于总线同其他类型联系起来的关系,将总线同设备,驱动程序,sysfs联系起来,这里不再展开描述。

在kernel_init->do_basic_setup->driver_init有很多关于文件系统的初始化:

void __init driver_init(void)
{
    
    
	/* These are the core pieces */
	devtmpfs_init();//创建文件系统
	devices_init(); //创建devices /dev /block /char目录
	buses_init();	 //总线初始化
	classes_init(); //创建class目录
	firmware_init();
	hypervisor_init();

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


我们主要关心buses_init()

int __init buses_init(void)
{
    
    
	/*创建sys 下bus目录*/
	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
	if (!bus_kset)
		return -ENOMEM;

	system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
	if (!system_kset)
		return -ENOMEM;

	return 0;
}

用于在/sys目录下创建bus目录以及在/sys/devices/下创建system目录。接着看下总线注册函数:

int bus_register(struct bus_type *bus)
{
    
    
	int retval;
	struct subsys_private *priv;
	struct lock_class_key *key = &bus->lock_key;
	/*分配资源*/
	priv = kzalloc(sizeof(struct subsys_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;
	/*总线注册到/sys/bus下*/
	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;
	}

	INIT_LIST_HEAD(&priv->interfaces);
	__mutex_init(&priv->mutex, "subsys mutex", key);
	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
	klist_init(&priv->klist_drivers, NULL, NULL);

	/*创建bus目录下对应总线下的drivers_autoprobe,drivers_probe文件*/
	retval = add_probe_files(bus);
	if (retval)
		goto bus_probe_files_fail;

	retval = bus_add_groups(bus, bus->bus_groups);
	if (retval)
		goto bus_groups_fail;

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

bus_groups_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;
}

第7-12行:分配subsys_private空间,并进行bus->p的初始化

第16-24行:初始化总线的名称,并在/sys/bus下创建总线的目录。

第28行:在对应总线下,创建uevent目录

第32-39行:在对应总线下,创建devices和driver目录

第52行:在对应总线目录下,创建drivers_probe,drivers_autoprobe 目录。

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;
}

下面看一个例子:

static char * Version = “ $ Revision:1.0 $” ;

staic int my_match (struct device* dev ,struct device_driver *driver)
{
    
    
    return !strncpm (dev-> bus_id ,driver->name, strlen(driver->name));
}

struct bus_type my_bus_type = {
    
    
    .name  = “ my_bus” ,
    .macth = my_match ,
} ;

static ssize_t show_bus_version ( struct bus_type * bus , char * buf )
{
    
    
    return snprintf (buf , PAGE_SIZE , "%s \ n" ,Version)}

static BUS_ATTR (version, S_IRUGO , show_bus_version , NULL );


static int __init my_bus_init(void)
{
    
    
    int ret ;
        
        / *注册总线* /
    ret = bus_register (&my_bus_type);
    if (ret)
        return ret ;
        
    / *创建属性文件* /    
    if (bus_create_file (&my_bus_type , &bus_attr_version))
        printk(KERN_NOTICE “无法创建版本属性!\ n” )return ret ;
}

static void my_bus_exit(void)
{
    
    
    bus_unregister(&my_bus_type);
}

module_init( my_bus_init);
module_exit(my_bus_exit);

猜你喜欢

转载自blog.csdn.net/wll1228/article/details/108392135
今日推荐