linux内核学习之总线、驱动、设备、kset、kobject

时间:2021-11-18 17:57:39

  最近在研究总线的注册、设备与驱动在总线上的注册、驱动如何找到总线上的设备进行匹配、设备又如何找到总线上的设备进行匹配,在linux2.6以后,这些过程都离不开设备驱动模型,所以也与kset、kobjcet有关。

  kobject就是一个对象,kset就是所有相同对象的集合,linux的设备驱动模型是用C语言实现面向对象。用linux时使用ls命令查看的文件和目录就是对应每一个kobject。

 

  一.设备device、驱动device_driver、总线bus_type、kobject、kset结构如下:

  (1)struct device

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct device { struct device        *parent; struct device_private    *p; struct kobject kobj; const char        *init_name; /* initial name of the device */
    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 */
    void        *platform_data;    /* Platform specific data, device core doesn't touch it */
    struct dev_pm_info power; #ifdef CONFIG_NUMA int        numa_node;    /* NUMA node this device is close to */
#endif u64 *dma_mask;    /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */

    struct device_dma_parameters *dma_parms; struct list_head    dma_pools;    /* dma pools (if dma'ble) */

    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem override */
    /* arch specific additions */
    struct dev_archdata archdata; #ifdef CONFIG_OF struct device_node    *of_node; #endif dev_t devt; /* dev_t, creates the sysfs "dev" */ spinlock_t devres_lock; struct list_head devres_head; struct klist_node knode_class; struct class        *class; const struct attribute_group **groups;    /* optional groups */

    void    (*release)(struct device *dev); };
View Code

   struct device_private

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct device_private { struct klist klist_children; struct klist_node knode_parent; struct klist_node knode_driver; /* knode_bus: * 1.注册设备时会将knode_bus成员加入到设备所在总线bus->p->klist_devices链,表示已注册到总线 * 2.同理,注销设备时会在设备所在总线bus->p->klist_devices链寻找设备->p->knode_bus节点并移除 */
    struct klist_node knode_bus; void *driver_data; /* device: * 常指向设备自己,这样就可以通过device_private找到device */
    struct device *device; };
View Code

  (2)struct device_driver

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct device_driver { const char        *name; struct bus_type        *bus; struct module        *owner; const char        *mod_name;    /* used for built-in modules */

    bool suppress_bind_attrs;    /* disables bind/unbind via sysfs */

#if defined(CONFIG_OF)
    const struct of_device_id    *of_match_table; #endif

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

    struct driver_private

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct driver_private { struct kobject kobj; struct klist klist_devices; /* knode_bus: * 1.注册设备时会将knode_bus成员加入到设备所在总线bus->p->klist_devices链,表示已注册到总线 * 2.同理,注销设备时会在设备所在总线bus->p->klist_devices链寻找设备->p->knode_bus节点并移除 */
    struct klist_node knode_bus; struct module_kobject *mkobj; /* driver: * 常指向驱动自己, 这样就可以通过driver_private找到device_driver */
    struct device_driver *driver; };
View Code

  (3)struct bus_type

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct bus_type { const char        *name; struct bus_attribute    *bus_attrs; struct device_attribute    *dev_attrs; struct driver_attribute    *drv_attrs; 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 (*suspend)(struct device *dev, pm_message_t state); int (*resume)(struct device *dev); const struct dev_pm_ops *pm; struct subsys_private *p; };
View Code

    struct subsys_private

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct subsys_private { struct kset subsys; struct kset *devices_kset; struct kset *drivers_kset; struct klist klist_devices; /*用于遍历总线下的设备成员*/
    struct klist klist_drivers; /* 用于遍历总线下的驱动成员 */
    struct blocking_notifier_head bus_notifier; /* 进程上下文通知链 */ unsigned int drivers_autoprobe:1; struct bus_type *bus; struct list_head class_interfaces; struct kset glue_dirs; struct mutex class_mutex; struct class *class; };
View Code 

  (4)struct kobject

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct kobject { const char        *name; struct list_head entry; struct kobject        *parent; struct kset        *kset; struct kobj_type    *ktype; struct sysfs_dirent    *sd; struct kref kref; unsigned int state_initialized:1; unsigned int state_in_sysfs:1; unsigned int state_add_uevent_sent:1; unsigned int state_remove_uevent_sent:1; unsigned int uevent_suppress:1; };
View Code

  (5)struct kset

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
struct kset { struct list_head list; spinlock_t list_lock; struct kobject kobj; const struct kset_uevent_ops *uevent_ops; };
View Code

  设备、驱动、总线结构都含有1个kobject结构。(device->kobj, device_driver->driver_private->kobj, bus_type->p->subsys->kobj).

  在说注册之前,先说一下二个结构bus_kset、devices_kset。这二个结构是在启动内核时调用driver_init中,调用devices_init和buses_init函数初始化的。bus_kset管理所有总线的,devices管理所有物理设备的。其中有一个例外,就是platform_bus结构,平台总线本来就是不存在的,是虚拟出来的一条总线,是一个设备。这里不详说这个。

  

  二.设备、总线、驱动注册

  这里我简单的说一下它们在注册时在干什么,以注册platform_bus、platform_dev、platform_driver为例子,其中platform_dev包含device结构,platform_driver包含device_driver结构。不贴代码,详细看内核源码。

  (1)总线注册device_register(&platform_bus)与bus_register(&platform_bus_type)。

  注册platform_bus时:

    初始化platform_bus->p私有数据。

    platform_bus的kobject->kset指向devices_kset。

    platform_bus的kobject->parent指向devices_kset->kobj。

    platform_bus的kobject->entry节点加入到devices_kset->list链表中。

  注册platform_bus_type时:

    初始化platform_bus_type->p私有数据。

    platform_bus_type的kobject->kset指向bus_kset。

    platform_bus_type的kobject->parent指向bus_kset->kobj,。

    platform_bus_type的kobject->entry节点加入到bus_kset->list链表中。

    初始化platform_bus_type->p->drivers_kset(用来管理总线下的驱动的集合)。

    初始化platform_bus_type->p->devices_kset(总线下的设备的kobject不会加入这个集合, 但是利用软连接链入这个集合)。

  (2)设备注册到总线platform_device_register

  我先贴个device_register函数源码出来:

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
int device_register(struct device *dev) { device_initialize(dev); return device_add(dev); }
View Code

  而注册platform_device是用platform_device_register函数的:

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); return platform_device_add(pdev); } int platform_device_add(struct platform_device *pdev) { int i, ret = 0; if (!pdev) return -EINVAL; if (!pdev->dev.parent) pdev->dev.parent = &platform_bus; pdev->dev.bus = &platform_bus_type; ... ... ret = device_add(&pdev->dev); ... ... }
View Code

  两者相差指定父设备与总线。device_register只是简单的注册一个设备,然后kobject交给device_kset管理。platform_device_register是注册一个platform_device到platform_bus_type总线下,kobject加入到device_kset但由platform_bus_type总线管理。

  所以注册一个platform_device时:

    初始化platform_device->dev->p私有数据。

    platform_device的kobject->kset指向devices_kset。

    platform_device->dev->parent指向platform_bus。

    platform_device->dev->bus指向platform_bus_type。

    因为platform_device->dev->parent指向platform_bus, 所以platform_device的kobject->parent指向platform_bus->kobj。

    platform_device的kobject->entry节点加入到device_kset->list链表中。

    platform_device->dev->kobj软连接到platform_bus->p->devices_kset中。

    platform_device->dev->p->knode_parent节点加入到platform_bus->p->klist_children链表(klist)中。

    platform_device->dev->p->knode_bus节点加入到platform_bus_type->p->klist_devices链表(klist)中。

    注册platform_device会遍历platform_bus_type下的所有驱动进行匹配操作。

  (3)注册驱动到总线platform_driver_register

   贴个platform_driver_register出来,指定总线并最后调用的driver_register

linux内核学习之总线、驱动、设备、kset、kobjectlinux内核学习之总线、驱动、设备、kset、kobject
int platform_driver_register(struct platform_driver *drv)
{
    drv->driver.bus = &platform_bus_type;
    if (drv->probe)
        drv->driver.probe = platform_drv_probe;
    if (drv->remove)
        drv->driver.remove = platform_drv_remove;
    if (drv->shutdown)
        drv->driver.shutdown = platform_drv_shutdown;

    return driver_register(&drv->driver);
}
View Code

  所以注册platform_driver时:

    初始化platform_driver->driver->私有数据。

    platform_driver->driver->bus指向platform_bus_type。

    platform_driver的kobject->kset指向platform_bus_type->p->drivers_kset

    platform_driver的kobject->entry节点加入到platform_bus_type->p->drivers_kset->list链表中。

    platform_driver->driver->p->knode_bus节点加入到platform_bus_type->p->klist_drivers链表(klist)中。

    注册platform_driver会遍历platform_bus_type下的所有设备进行匹配操作。

 

   三、平台总线、驱动、设备注册后的图如下。

 linux内核学习之总线、驱动、设备、kset、kobject