【linux kenrel】一文总结linux内核设备驱动的注册和卸载

一、开篇

在linux驱动开发中,肯定会接触到driver_register()driver_unregister()函数,本文将从内核源码角度,来看看这两个函数的具体实现。在linux内核源码(/drivers/)目录中,放置的是某一类设备驱动框架,例如(spi、I2c,net)等。这些框架的核心大多都会调用到driver_register()函数。这两个函数的原型如下:

int driver_register(struct device_driver *drv);
void driver_unregister(struct device_driver *drv);

其中函数的参数是struct device_driver,定义如下(include/linux/device.h):

struct device_driver {
    
    
	const char		*name;					//设备驱动的名称
	struct bus_type		*bus;			 //驱动程序的设备所属的总线

	struct module		*owner;		 //模块所有者
	const char		*mod_name;	  //用于内置模块

	bool suppress_bind_attrs;	 /* 是否通过sysfs禁用绑定/取消绑定 */

	const struct of_device_id	*of_match_table;	//设备树匹配表
	const struct acpi_device_id	*acpi_match_table;	 //acpi匹配表

    //具体的回调函数指针
	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;//驱动核心的私有数据,除了驱动核心,其他都不可以操作它
};

二、driver_register函数

driver_register()函数用于向bus总线上注册驱动程序。该函数定义如下(/drivers/base/driver.c):

int driver_register(struct device_driver *drv)
{
    
    
	int ret;
	struct device_driver *other;

	BUG_ON(!drv->bus->p);

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);
	if (other) {
    
    
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}

	ret = bus_add_driver(drv);
	if (ret)
		return ret;
	ret = driver_add_groups(drv, drv->groups);
	if (ret) {
    
    
		bus_remove_driver(drv);
		return ret;
	}
    //通过发送KOBJ_ADD事件通知用户空间
	kobject_uevent(&drv->p->kobj, KOBJ_ADD);

	return ret;
}

上述代码第8-12行,判断是否指定了drv->bus->probe、drv->probe、drv->bus->remove、drv->remove、drv->bus->shutdown、drv->shutdown。

第14-19行代码,将调用driver_find()寻找指定bus上的驱动。其中传入driver_find的参数是:驱动名称和被扫描驱动的bus总线。如果找到了对应驱动名称下的驱动,则证明该驱动已经被注册,则返回函数;否则,将执行后续的驱动注册操作。

第21行代码,bus_add_driver()是驱动注册的核心操作函数,定义如下(/drivers/base/bus.c):

int bus_add_driver(struct device_driver *drv)
{
    
    
	struct bus_type *bus;
	struct driver_private *priv;
	int error = 0;
	//获取device_driver设备驱动中的bus总线
	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;
    //初始化驱动程序kobj,并将其添加到内核对象树中
	error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
				     "%s", drv->name);
	if (error)
		goto out_unregister;
	//将klist_drivers驱动添加到knode_bus链表的尾部
	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
	if (drv->bus->p->drivers_autoprobe) {
    
    
        //尝试将驱动程序绑定到设备
		error = driver_attach(drv);
		if (error)
			goto out_unregister;
	}
    //为驱动程序创建模块对象。
	module_add_driver(drv->owner, drv);
    
	//为驱动程序创建sysfs文件
	error = driver_create_file(drv, &driver_attr_uevent);
	if (error) {
    
    
		printk(KERN_ERR "%s: uevent attr (%s) failed\n",
			__func__, drv->name);
	}
    //创建bus->drv_groups属性组
	error = driver_add_groups(drv, bus->drv_groups);
	if (error) {
    
    
		/* How the hell do we get out of this pickle? Give up */
		printk(KERN_ERR "%s: driver_create_groups(%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);
		}
	}

	return 0;

out_unregister:
	kobject_put(&priv->kobj);
	kfree(drv->p);
	drv->p = NULL;
out_put_bus:
	bus_put(bus);
	return error;
}

三、driver_unregister函数

driver_unregister()函数用于从系统中移除驱动,其定义如下(/drivers/base/driver.c):

void driver_unregister(struct device_driver *drv)
{
    
    
	if (!drv || !drv->p) {
    
    
		WARN(1, "Unexpected driver unregister!\n");
		return;
	}
	driver_remove_groups(drv, drv->groups);
	bus_remove_driver(drv);
}

上述第7行代码,调用driver_remove_groups()从sysfs文件系统中移除drv->groups属性组。

第8行代码,调用bus_remove_driver()从已知的bus中删除驱动,该函数定义如下(/drivers/base/bus.c):

void bus_remove_driver(struct device_driver *drv)
{
    
    
	if (!drv->bus)
		return;

	if (!drv->suppress_bind_attrs)
		remove_bind_files(drv);
    //移除drv->groups组
	driver_remove_groups(drv, drv->bus->drv_groups);
    //将驱动从sysfs文件系统中移除
	driver_remove_file(drv, &driver_attr_uevent);
    //移除knode_bus链表
	klist_remove(&drv->p->knode_bus);
	pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name);
    //从控制的设备中分离驱动程序
	driver_detach(drv);
    //从驱动中内核模块中移除
	module_remove_driver(drv);
    //减少对drv->p->kobj对象的引用计数
	kobject_put(&drv->p->kobj);
    //减少对drv->bus总线对象的引用计数
	bus_put(drv->bus);
}

四、结尾

(4-1)对于linux内核中的大多数驱动框架都会使用到driver_register()driver_unregister()函数,本文从源码的角度分析了这两个函数。综合内核中大多驱动框架的结构和代码写法,有的是根据各自的驱动框架名称做了适当封装。例如下图所示:
请添加图片描述

(4-2)设备驱动程序是静态分配的结构。尽管在系统中可能存在多个设备,struct device_driver作为一个整体(而不是一个特定的设备实例)来表示驱动程序。所以,在初始化struct device_driver类型的设备驱动变量时必须初始化名称和总线字段。还应该初始化devclass字段,这样才可以在内部获得适当链接。除此之外还应该初始化尽可能多的回调函数(例如:probe、remove、shutdown、suspend、resume)。

(4-3)驱动程序应该在总线特定驱动程序的定义中包含一个通用的struct device_driver。例如,下面为一个pci_driver的驱动程序:

struct pci_driver {
    
    
       const struct pci_device_id *id_table;
       struct device_driver	  driver;
};

猜你喜欢

转载自blog.csdn.net/iriczhao/article/details/124283698#comments_27686200