linux的 bus、device、driver介绍
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; }