一、开篇
在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;
};