linux 通过device和driver分别管理系统中的设备和驱动,用bus将设备和驱动关联起来,bus可以看成是设备和驱动的媒介,可以匹配设备和驱动。这样设备和驱动可以独立加载,互不影响。sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。我们都知道设备和对应的驱动都是由内核管理的,这些对于用户空间是不可见的。现在通过sysfs,可以在用户空间直观的了解设备驱动的层次结构。
一、bus注册过程
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); //device和driver的匹配函数
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;
};
struct subsys_private {
struct kset subsys; //对应/sys/bus/xxx目录,xxx为自己取的bus名称
struct kset *devices_kset; //对应/sys/bus/xxx/devices目录
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset; //对应/sys/bus/xxx/drivers目录
struct klist klist_devices; //该bus下的所有device
struct klist klist_drivers; //该bus下的所有driver
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus;
struct kset glue_dirs;
struct class *class;
};
向系统添加一条bus_type总线时,改总线会自动添加到/sys/bus目录下,bus目录是系统自动创建的,这个bus目录为static struct kset *bus_kset,定义在kernel/drivers/base/bus.c中。创建过程如下所示:
/sys/bus目录的建立过程:kernel/drivers/base/bus.c
-->buses_init
-->kset_create_and_add //这行代码建立/sys下面的bus目录,其中bus_kset就代表bus目录
向系统注册一个yy_bus总线代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
static int yy_bus_match(struct device *dev, struct device_driver *drv)
{
printk("dev->kobj.name = %s, drv->name = %s", dev->kobj.name, drv->name);
if (!strncmp(dev->kobj.name, drv->name, strlen(drv->name)))
{
printk("device and driver is match!\n");
return 1;
}
else
{
printk("device and driver is match error ttt!\n");
return 0;
}
}
struct bus_type yy_bus = {
.name = "yy_bus", //总线名称,注册成功后,将在/sys/bus/ 目录下生成yy_bus目录
.match = yy_bus_match, //设备和驱动的匹配方法
};
EXPORT_SYMBOL(yy_bus);
static int __init yy_bus_init(void)
{
int ret = 0;
printk ("yy_bus_init is run\n");
ret = bus_register(&yy_bus);
if(ret < 0)
{
printk("bus register error!\n");
return ret;
}
return ret;
}
static void __exit yy_bus_exit(void)
{
printk ("yy_bus_exit is run\n");
bus_unregister(&yy_bus);
}
module_init(yy_bus_init);
module_exit(yy_bus_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yyfage");
编译完成后,执行 insmod yy_bus.ko命令,会在/sys/bus/目录下生成yy_bus目录,如下所示:
进入bus目录,里面还会有几个文件,如下所示:
devices: 挂在该总线上的设备,目前没有设备挂载,里面是空的
drivers: 挂在该总线上的驱动,目前没哟驱动挂载,里面是空的
yy_bus总线的注册过程及注释如下所示:
/*bus总线注册部分关键代码*/
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;
//bus_kset就是上面的/sys/bus,这样指定后,用户自己创建的bus就会在/sys/bus目录下面
priv->subsys.kobj.kset = bus_kset;
priv->subsys.kobj.ktype = &bus_ktype;
priv->drivers_autoprobe = 1;
//在/sys/bus/目录下创建xxx目录,xxx目录名字为bus->name
retval = kset_register(&priv->subsys);
if (retval)
goto out;
//在/sys/bus/xxx/目录下创建uevent文件
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;
//在/sys/bus/xxx/目录下创建devices目录
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}
//在/sys/bus/xxx/目录下创建drivers目录
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/XXX/建立文件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;
}
二、device注册过程
struct device 结构体部分成员变量
struct device {
struct device *parent; //该device的父类
struct device_private *p; //私有数据
struct kobject kobj; //device的目录,/sys/devices/xxx,xxx就是device的目录
const char *init_name; /* initial name of the device */
const struct device_type *type;
struct mutex mutex; /* mutex to synchronize calls to* its driver.*/
struct bus_type *bus; /* type of bus device is on */
struct device_driver *driver; /* which driver has allocated this device */
dev_t devt; /*设备号,如果有设备号,则会在/dev目录下创建设备*/
};
struct device_private结构体
struct device_private {
struct klist klist_children;
struct klist_node knode_parent; //
struct klist_node knode_driver;
struct klist_node knode_bus; //这个会加入到bus_type->subsys_private->klist_devices中
struct list_head deferred_probe; struct device *device; };
向系统添加一个device设备时,该设备会自动添加到/sys/devies目录中。devies目录是系统自动创建的,这个devices目录为struct kset *devices_kset;,定义在kernel/drivers/base/core.c中注册过程如下所示:
/sys/devices/目录注册过程:kernel/drivers/base/core.c
-->devices_init
-->kset_create_and_add //在/sys/目录下创建devices目录
向系统注册一个yy_dev设备的代码如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
extern struct bus_type yy_bus;
static struct device yy_dev = {
.kobj.name = "yy_dev",
.bus = &yy_bus,
};
static int __init init_yy_dev(void)
{
int ret = 0;
printk("yy init_yy_dev is run\n");
ret = device_register(&yy_dev);
if(ret < 0)
{
printk("device register error!\n");
return ret;
}
return ret;
}
static void __exit exit_yy_dev(void)
{
printk("yy exit_yy_dev is run\n");
device_unregister(&yy_dev);
}
module_init(init_yy_dev);
module_exit(exit_yy_dev);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yyfage");
编译完后执行 insmod yy_devices.ko命令,会在sys/devices/目录下生成yy_dev目录,如下所示:
device注册过程部分关键源码分析
kernel/drivers/base/core.c
-->device_register
-->device_initialize
-->device_add
device_register源码如下:
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset; //指定注册的设备添加到/sys/devices目录下,device_kset就是上面提到的那个
kobject_init(&dev->kobj, &device_ktype);
INIT_LIST_HEAD(&dev->dma_pools);
mutex_init(&dev->mutex);
lockdep_set_novalidate_class(&dev->mutex);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_pm_init(dev);
set_dev_node(dev, -1);
#ifdef CONFIG_GENERIC_MSI_IRQ
INIT_LIST_HEAD(&dev->msi_list);
#endif
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
struct kobject *glue_dir = NULL;
dev = get_device(dev); /*增加引用计数*/
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
/* 执行完以后,将在/sys/devices/下建立目录XXX,目录名XXX为dev->kobj->name*/
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error) {
glue_dir = get_glue_dir(dev);
goto Error;
}
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
/*在XXX下建立文件uevent*/
error = device_create_file(dev, &dev_attr_uevent);
if (error)
goto attrError;
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev); /*添加类设备属型文件和属性组*/
if (error)
goto AttrsError;
error = bus_add_device(dev); /*添加3个symlink*/
if (error)
goto BusError;
error = dpm_sysfs_add(dev); /*创建power子目录,并在其下添加电源管理的属性组文件*/
if (error)
goto DPMError;
device_pm_add(dev); /*将该device添加到电源管理链表中*/
if (MAJOR(dev->devt)) { /*如果有设备号,这在/dev 目录下创建设备*/
error = device_create_file(dev, &dev_attr_dev);
if (error)
goto DevAttrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto SysEntryError;
devtmpfs_create_node(dev);
}
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev); //查找总线下的所有驱动,为匹配的驱动执行prob函数
if (parent) /*有父设备,则将该设备添加到父设备的儿子链表中*/
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) { /*将device添加到class的类设备链表中*/
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
}
bus_add_device函数分析
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
error = device_add_groups(dev, bus->dev_groups);
if (error)
goto out_id;
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev)); //将/sys/devices/dev_name 链接到/sys/bus/bus_name/devices中
if (error)
goto out_groups;
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); //将device节点添加到bus链表中
}
return 0;
}
device_add函数中有个重要的函数bus_probe_device,这个函数用来匹配deviec和driver,匹配成功后执行drive的prob函数,下面对这个函数进行分析
kernel/drivers/base/bus.c
-->bus_probe_device
-->device_initial_probe
-->__device_attach
-->bus_for_each_drv(dev->bus, NULL, &data,__device_attach_driver); //查找该bus下的所有驱动,这个bus就是上面注册的yy_bus,并为改设备和所以驱动执行函数__device_attach_driver。
kernel/drivers/base/dd.c
-->__device_attach_driver //匹配device和driver,并执行drive的prob函数
-->driver_match_device //查看device和driver是否匹配,就是执行bus下面的math函数
-->driver_probe_device //如果device和drive匹配,则执行这个 函数
-->really_probe
-->drv->probe //最终执行drive的probe函数
三、driver注册过程
下面程序是向系统注册一个名称为“yy_dev”的driver
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
static int yy_drv_prob(struct device *dev)
{
int ret = 0;
printk("yy yy_drv_prob is run\n");
return ret;
}
static int yy_drv_remove(struct device *dev)
{
int ret = 0;
printk("yy yy_drv_remove is run\n");
return ret;
}
extern struct bus_type yy_bus;
static struct device_driver yy_drv = {
.name = "yy_dev",
.bus = &yy_bus,
.probe = yy_drv_prob,
.remove = yy_drv_remove,
};
static int __init init_yy_drv(void)
{
int ret = 0;
printk("yy init_yy_drv is run\n");
ret = driver_register(&yy_drv);
if(ret < 0)
{
printk("driver register error!\n");
return ret;
}
return ret;
}
static void __exit exit_yy_drv(void)
{
printk("yy exit_yy_drv is run\n");
driver_unregister(&yy_drv);
}
module_init(init_yy_drv);
module_exit(exit_yy_drv);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yyfage");
编码代码,执行insmod yy_driver.ko(先要加载yy_bus.ko),注册成功后,会在/sys/bus/yy_bus/drivers目录下生成yy_dev目录,如下所示
只要driver中的name和device中的.kobj.name一致,就会执行drive下的prob函数,因为yy_bus中的的math函数是根据device和driver的名称匹配的:
driver注册过程如下:
kernel/drivers/base/driver.c
-->driver_register
-->bus_add_driver //将driver添加到bus中,并匹配device,这个函数分析如下所示
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
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; //指定添加的driver目录在/sys/bus/drivers目录下
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL, //在/sys/bus/drivers目录下创建xxx目录,这xxx就是drv->name
"%s", drv->name);
if (error)
goto out_unregister;
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); //将driver添加到bus的链表中
if (drv->bus->p->drivers_autoprobe) {
if (driver_allows_async_probing(drv)) {
pr_debug("bus: '%s': probing driver %s asynchronously\n",
drv->bus->name, drv->name);
async_schedule(driver_attach_async, drv);
} else {
error = driver_attach(drv); //匹配driver和device,如果匹配成功,则执行driver的prob函数,匹配过程见上面的device
if (error)
goto out_unregister;
}
}
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
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;
}