• Linux 内核:设备驱动模型(2)driver-bus-device与probe


    Linux 内核:设备驱动模型(2)driver-bus-device与probe

    系列:Linux 内核:设备驱动模型 学习总结

    参考:

    背景

    基于 Linux 3.14 来简单分析设备驱动模型。

    前言

    对于嵌入式Linux的底层程序员而言,对设备驱动模型的学习非常重要:以后看具体的总线设备模型时会更加清晰。

    建议先看了解:kobject、kset和ktype

    Linux设备模型的目的:为内核建立一个统一的设备模型,从而又一个对系统结构的一般性抽象描述。

    换句话说,Linux设备模型提取了设备操作的共同属性,进行抽象,并将这部分共同的属性在内核中实现,而为需要新添加设备或驱动提供一般性的统一接口,这使得驱动程序的开发变得更简单了,而程序员只需要去学习接口就行了。

    在内核里,有各种各样的总线,如 usb_bus_typespi_bus_typepci_bus_typeplatform_bus_typei2c_bus_type 等,内核通过总线将设备与驱动分离。

    设备模型是层次的结构,层次的每一个节点都是通过kobject实现的,在文件上则体现在sysfs文件系统。

    关于kobkect,如果不清楚请移步: http://blog.csdn.net/lizuobin2/article/details/51523693

    关于 uevet 、mdev 前面也说过了,请参考: http://blog.csdn.net/lizuobin2/article/details/51534385

    kobject 结构可能的层次结构如图:

    对于整个 设备总线驱动模型 的样子,大概如下图吧,也并不复杂。

    简单来说,bus 负责维护 注册进来的devcie driver ,每注册进来一个device 或者 driver 都会调用 Bus->match 函数 将devicedriver 进行配对,并将它们加入链表。

    如果配对成功,调用Bus->probe或者driver->probe函数, 调用kobject_uevent函数设置环境变量(通知用户空间),mdev进行创建设备节点等操作。

    我们从 Busdriverdevice三个部分进行详细的分析。

    总线:

    总线(bus)是linux发展过程中抽象出来的一种设备模型,为了统一管理所有的设备,内核中每个设备都会被挂载在总线上,这个bus可以是对应硬件的bus(i2c bus、spi bus)、可以是虚拟bus(platform bus)。

    bus将所有挂在上面的具体设备抽象成两部分,device_driverdevice

    driver与device:

    driver实现了同类型设备的驱动程序实现,而device则向系统注册具体的设备需要的资源,每当添加一个新的driver(device)到bus中时,都将调用bus的match函数,试图寻找匹配的device(driver)。

    如果匹配成功,就调用probe函数,在probe函数中实现设备的初始化、各种配置以及生成用户空间的文件接口。

    probe函数是总线在匹配成功时调用的函数,bus->probedrv->probe中只会有一个起效,同时存在时使用bus->probe

    初始化

    driver_init

    所有的bus都是在buses_init,kernel启动以后,进行初始化,最终执行到:

    // init/main.c
    kernel_init();
        kernel_init_freeable();
            do_basic_setup();
                driver_init(); // 注意这个
                do_initcalls();
    

    看看driver_init做了什么:

    // drivers/base/init.c
    void __init driver_init(void)
    {
        /* These are the core pieces */
        devtmpfs_init();
        devices_init();  // 初始化device
        buses_init();    // 初始化bus
        classes_init();
        firmware_init();
        hypervisor_init();
    
        /* These are also core pieces, but must come after the
         * core core pieces.
         */
        platform_bus_init();
        cpu_dev_init();
        memory_dev_init();
        container_dev_init();
    }
    

    注意devices_initbuses_init这两个函数会创建一些对应的对象,我们能够在sysfs中看到这些对应的对象,在后续中会用到。。

    devices_init

    // driversbase/base.h
    struct kset *devices_kset;
    extern struct kset *devices_kset;
    
    // drivers/base/core.c
    #include "base.h"
    struct kset *devices_kset;
    int __init devices_init(void)
    {
        // 创建 /sys/devices
        devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
        // 创建 /sys/dev
        dev_kobj = kobject_create_and_add("dev", NULL);
        // 创建 /sys/dev/block
        sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
        // 创建 /sys/dev/char
        sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
    
        return 0;
    }
    

    buses_init

    // drivers/base/bus.c
    static struct kset *system_kset;
    static struct kset *bus_kset;
    
    int __init buses_init(void)
    {
        // 创建 /sys/bus
        bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);
        if (!bus_kset)
            return -ENOMEM;
    
        // 创建 /sys/devices/system
        system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj);
        if (!system_kset)
            return -ENOMEM;
    
        return 0;
    }
    

    同样是调用了kset_create_and_add,这里涉及到了ksetkobject这些概念。

    kset_create_and_add

    kobject_create_and_add这个函数首先会调用kobject_create来分配并初始化一个kobject对象,然后调用kobject_add函数在sysfs文件系统中为新生成的kobject对象建立一个新的目录。

    // lib/kobject.c
    /**
     * kset_create_and_add - create a struct kset dynamically and add it to sysfs
     *
     * @name: the name for the kset
     * @uevent_ops: a struct kset_uevent_ops for the kset
     * @parent_kobj: the parent kobject of this kset, if any.
     *
     * This function creates a kset structure dynamically and registers it
     * with sysfs.  When you are finished with this structure, call
     * kset_unregister() and the structure will be dynamically freed when it
     * is no longer being used.
     *
     * If the kset was not able to be created, NULL will be returned.
     */
    struct kset *kset_create_and_add(const char *name,
                     const struct kset_uevent_ops *uevent_ops,
                     struct kobject *parent_kobj)
    {
        struct kset *kset;
        int error;
    
        kset = kset_create(name, uevent_ops, parent_kobj);
        if (!kset)
            return NULL;
        error = kset_register(kset);
        if (error) {
            kfree(kset);
            return NULL;
        }
        return kset;
    }
    EXPORT_SYMBOL_GPL(kset_create_and_add);
    

    此后,其他bus通过 bus_register 进行注册,实际上会注册到 bus_kest 中。

    bus

    bus_type原型

    // include/linux/device.h
    /**
     * struct bus_type - The bus type of the device
     *
     * @name:   The name of the bus.
     * @dev_name:   Used for subsystems to enumerate devices like ("foo%u", dev->id).
     * @dev_root:   Default device to use as the parent.
     * @dev_attrs:  Default attributes of the devices on the bus.
     * @bus_groups: Default attributes of the bus.
     * @dev_groups: Default attributes of the devices on the bus.
     * @drv_groups: Default attributes of the device drivers on the bus.
     * @match:  Called, perhaps multiple times, whenever a new device or driver
     *      is added for this bus. It should return a nonzero value if the
     *      given device can be handled by the given driver.
     * @uevent: Called when a device is added, removed, or a few other things
     *      that generate uevents to add the environment variables.
     * @probe:  Called when a new device or driver add to this bus, and callback
     *      the specific driver's probe to initial the matched device.
     * @remove: Called when a device removed from this bus.
     * @shutdown:   Called at shut-down time to quiesce the device.
     *
     * @online: Called to put the device back online (after offlining it).
     * @offline:    Called to put the device offline for hot-removal. May fail.
     *
     * @suspend:    Called when a device on this bus wants to go to sleep mode.
     * @resume: Called to bring a device on this bus out of sleep mode.
     * @pm:     Power management operations of this bus, callback the specific
     *      device driver's pm-ops.
     * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU
     *              driver implementations to a bus and allow the driver to do
     *              bus-specific setup
     * @p:      The private data of the driver core, only the driver core can
     *      touch this.
     * @lock_key:   Lock class key for use by the lock validator
     *
     * A bus is a channel between the processor and one or more devices. For the
     * purposes of the device model, all devices are connected via a bus, even if
     * it is an internal, virtual, "platform" bus. Buses can plug into each other.
     * A USB controller is usually a PCI device, for example. The device model
     * represents the actual connections between buses and the devices they control.
     * A bus is represented by the bus_type structure. It contains the name, the
     * default attributes, the bus' methods, PM operations, and the driver core's
     * private data.
     */
    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);
        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;
    
        struct iommu_ops *iommu_ops;
    
        struct subsys_private *p;
        struct lock_class_key lock_key;
    };
    

    注册bus:bus_register

    /**
     * bus_register - register a bus with the system.
     * @bus: bus.
     *
     * Once we have that, we registered the bus with the kobject
     * infrastructure, then register the children subsystems it has:
     * the devices and drivers that belong to the bus.
     */
    int bus_register(struct bus_type *bus)
    {
        int retval;
        struct subsys_private *priv;
    
        priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
        /* 1. bus 与 prv 相互建立联系 */
    	// 私有数据 .bus ->  bus 本身
    	priv->bus = bus;
    	// bus->p 指向 priv
    	bus->p = priv;
    	// 内核通知链
    	BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);
    
        /* 设置 bus->prv->subsys->kobj */
    	// 设置 priv->subsys.kobj.name = bus->name  对应于/sys/ 目录下的目录名
    	retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
    	// 所有的 priv->subsys.kobj.kset 指向 bus_kse 对应于图中④与六的关系
    	priv->subsys.kobj.kset = bus_kset;
    	// 所有的priv->subsys.kobj.ktype 等于 bus_ktype
    	priv->subsys.kobj.ktype = &bus_ktype;
        priv->drivers_autoprobe = 1;
    
        /* 注册 kset (bus->prv->subsys priv->devices_kset priv->drivers_kset) */	
    	// 注册 priv->subsys ,由于 priv->subsys.kobj.kset = bus_kset,所以会在 /sys/bus/目录下创建 目录 如/sys/bus/plateform
    	retval = kset_register(&priv->subsys);
    	// sysfs_create_file(&bus->p->subsys.kobj, &bus_attr_uevent->attr);
    	retval = bus_create_file(bus, &bus_attr_uevent);
    
    	// 由于 priv->subsys.kobj.kset = bus_kset ,因此会创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/devices
    	priv->devices_kset = kset_create_and_add("devices", NULL,
    						 &priv->subsys.kobj);
    
    	// 同理 创建 /sys/bus/XXX/devices 目录 如 /sys/bus/plateform/drivers
    	priv->drivers_kset = kset_create_and_add("drivers", NULL,
    						 &priv->subsys.kobj);
    
    	// 初始化 klist_devices 并设置get put 函数  初始化 klist_drivers 不知为何没有get put ?
    	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
    	klist_init(&priv->klist_drivers, NULL, NULL);
    
        retval = add_probe_files(bus);
    
    	// 添加 bus->attrs 属性文件
    	retval = bus_add_attrs(bus);
    
        pr_debug("bus: '%s': registered
    ", bus->name);
        return 0;
    }
    EXPORT_SYMBOL_GPL(bus_register);
    

    目前,能通过 bus_register 函数处理的工作有:

    1、将 Bus 与 priv 相互建立联系,用于处理私有数据

    2、注册通知链BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

    3、设置bus->priv->subsys(kset).kobj的名字为 bus->name

    4、设置 bus->priv->subsys(kset).kobj.kset 指向 bus_kset

    5、设置 bus->priv->subsys(kset).kobj.ktype 为 bus_ktype ,提供 show store 函数

    6、设置 bus->priv->drivers_autoprobe = 1;

    7、注册 bus->priv->subsys(kset) :对应于图中④与⑥的关系

    由于4,且没有指定bus->priv->subsys(kset).kobj.Parent,会将 bus_kest.kobj 设置为 bus->priv->subsys(kset).kobj.Parent

    因此,会将bus->priv->subsys(kset).kobj.entry 加入bus_kest链表,且会在/sys/bus目录下创建相应的总线目录/sys/bus/$(bus->name),例如 /sys/bus/platform

    8、创建 bus_attr_uevent->attr 属性文件

    9、创建并注册 devices_kset devices_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 device ,因此会创建 /sys/bus/$(bus->name)/devices

    10、创建并注册 drivers_ksetdrivers_kset.kobj.parent = bus->priv->subsys.kobj ,名字为 drivers ,因此会创建 /sys/bus/$(bus->name)/drivers

    11、初始化 bus->priv->klist_devices 链表

    12、初始化 bus->priv->klist_drivers 链表

    13、创建 bus->bus_attrs 属性文件

    例子(bus)

    下面来看个例子 ,修改自LDD3 。

    lddbus.h

    /*
     * Definitions for the virtual LDD bus.
     *
     * lddbus.h
     */
    
    extern struct device ldd_bus;
    extern struct bus_type ldd_bus_type;
    
    
    /*
     * The LDD driver type.
     */
    
    struct ldd_driver {
    	char *version;
    	struct module *module;
    	struct device_driver driver;
    	struct driver_attribute version_attr;
    };
    
    #define to_ldd_driver(drv) container_of(drv, struct ldd_driver, driver);
    
    /*
     * A device type for things "plugged" into the LDD bus.
     */
    
    struct ldd_device {
    	char *name;
    	struct ldd_driver *driver;
    	struct device dev;
    };
    
    #define to_ldd_device(dev) container_of(dev, struct ldd_device, dev);
    
    extern int register_ldd_device(struct ldd_device *);
    extern void unregister_ldd_device(struct ldd_device *);
    extern int register_ldd_driver(struct ldd_driver *);
    extern void unregister_ldd_driver(struct ldd_driver *);
    

    lddbus.c

    #include <linux/device.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/string.h>
    #include "lddbus.h"
    
    MODULE_AUTHOR("Jonathan Corbet");
    MODULE_LICENSE("Dual BSD/GPL");
    static char *Version = "$Revision: 1.9 $
    ";
    
    //--------------------------------- bus ----------------------------------------
    
    static int ldd_match(struct device *dev, struct device_driver *drv)
    {
        struct ldd_device *pdev = to_ldd_device(dev);
    
        return !strncmp(pdev->name, drv->name, strlen(drv->name));
    }
    
    struct bus_type ldd_bus_type = {
        .name = "ldd",
        .match = ldd_match,
    };
    
    //--------------------------------- device --------------------------------------
    
    static ssize_t show_bus_version(struct bus_type *bus, char *buf)
    {
        return snprintf(buf, strlen(Version), "%s
    ", Version);
    }
    
    static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
    
    // parent device
    static void ldd_bus_release(struct device *dev)
    {
        printk(KERN_DEBUG "lddbus release
    ");
    }
    static void ldd_dev_release(struct device *dev){ }
    
    struct device ldd_bus = {
        .init_name   = "ldd0", // ldd0 就是总线的名字,这里改成 ldd_bus 更恰当
        .release  = ldd_bus_release
    };
    
    int register_ldd_device(struct ldd_device *ldddev)
    {
    
        ldddev->dev.bus = &ldd_bus_type;
        ldddev->dev.parent = &ldd_bus;
        ldddev->dev.release = ldd_dev_release;
        return device_register(&ldddev->dev);
    }
    EXPORT_SYMBOL(register_ldd_device);
    
    void unregister_ldd_device(struct ldd_device *ldddev)
    {
        device_unregister(&ldddev->dev);
    }
    EXPORT_SYMBOL(unregister_ldd_device);
    
    //--------------------------------- driver --------------------------------------
    
    static ssize_t show_version(struct device_driver *driver, char *buf)
    {
        struct ldd_driver *ldriver = to_ldd_driver(driver);
    
        sprintf(buf, "%s
    ", ldriver->version);
        return strlen(buf);
    }
    
    int register_ldd_driver(struct ldd_driver *driver)
    {
        int ret;
    
        driver->driver.bus = &ldd_bus_type;
        ret = driver_register(&driver->driver);
        if (ret)
            return ret;
        driver->version_attr.attr.name = "version";
        //driver->version_attr.attr.owner = driver->module;
        driver->version_attr.attr.mode = S_IRUGO;
        driver->version_attr.show = show_version;
        driver->version_attr.store = NULL;
        return driver_create_file(&driver->driver, &driver->version_attr);
    }
    
    void unregister_ldd_driver(struct ldd_driver *driver)
    {
        driver_unregister(&driver->driver);
    }
    EXPORT_SYMBOL(register_ldd_driver);
    EXPORT_SYMBOL(unregister_ldd_driver);
    
    //--------------------------------- bus ----------------------------------------
    
    static int __init ldd_bus_init(void)
    {
        int ret;
        device_register(&ldd_bus);
        ret = bus_register(&ldd_bus_type);
        if (ret)
            return ret;
        if (bus_create_file(&ldd_bus_type, &bus_attr_version))
            printk(KERN_NOTICE "Unable to create version attribute
    ");
    
        return ret;
    }
    
    static void ldd_bus_exit(void)
    {
        bus_unregister(&ldd_bus_type);
    }
    
    module_init(ldd_bus_init);
    module_exit(ldd_bus_exit);
    

    Makefile

    EXTRA_CFLAGS += $(DEBFLAGS)
    #EXTRA_CFLAGS += -I$(INCDIR)
    
    ########## change your module name here
    MODULE   = lddbus
    
    ########## change your obj file(s) here
    $(MODULE)-objs:= lddbus.o
    #CROSS_COMPILE ?= arm-linux-gnueabihf-
    #ARCH 		  ?= arm
    
    ifneq ($(KERNELRELEASE), )
    	obj-m := $(MODULE).o
    
    else
    	KERNELDIR ?= /lib/modules/$(shell uname -r)/build
    	PWD := $(shell pwd)
    
    all:
    	$(MAKE_BEGIN)
    	@echo
    	@if 
    	$(MAKE) INCDIR=$(PWD)/configs -C $(KERNELDIR) M=$(PWD) modules; 
    	then $(MAKE_DONE);
    	else 
    	$(MAKE_ERR);
    	exit 1; 
    	fi
    
    endif
    
    show:
    	@echo "ARCH     :    ${ARCH}"
    	@echo "CC       :    ${CROSS_COMPILE}gcc"
    	@echo "KDIR     :    ${KERNELDIR}"
    	@echo "$(MODULE):    $(ALLOBJS)"
    clean:
    	$(CLEAN_BEGIN)
    	rm -rf *.cmd *.o *.ko *.mod.c *.symvers *.order *.markers .tmp_versions .*.cmd *~ .*.d
    	$(CLEAN_END)
    
    .PHONY:all clean show
    #xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    ### nothing
    #OFFSET=e[21G    # 21 col
    COLOR1=e[32m  # all --> bule
    COLOR2=e[33m  # clean --> brown
    COLOR3=e[31m  # error --> red
    RESET=e[0m
    
    CLEAN_BEGIN=@echo -e "$(OFFSET)$(COLOR2)Cleaning up...$(RESET)"
    CLEAN_END=@echo -e "$(OFFSET)$(COLOR2)Cleaned.$(RESET)"
    
    MAKE_BEGIN=@echo -ne "$(OFFSET)$(COLOR1)Compiling...$(RESET)"
    ### I do not forget "@", but it DOES NOT need "@"
    MAKE_DONE=echo -e "$(OFFSET)$(COLOR1)Compilied.$(RESET)"
    MAKE_ERR=echo -e "$(OFFSET)$(COLOR3)[Oops! Error occurred]$(RESET)"
    ### nothing end here
    	#$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=${CROSS_COMPILE} INCDIR=$(PWD)/configs -C $(KERNELDIR) M=$(PWD) modules; 
    
    ############# Makefile end here
    

    测试

    [root@FriendlyARM /]# cd sys
    [root@FriendlyARM /sys]# ls
    block class devices fs module
    bus dev     firmare kernel
    [root@FriendlyARM isys]# cd bus
    [root@FriendlyARM bus]# ls
    hid ldd platform sdio usb-serial i2c mmc scsi usb
    [root@FriendlyARM bus]# cd ldd
    [root@FriendlyARM ldd]# ls
    devices drivers_autoprobe uevent
    drivers drivers_probe     version
    

    insmod bus.ko 之后发现,/sys/bus 目录下多了一个 ldd目录,这个目录就是我们向内核注册的 总线 ldd 。

    该目录下有一个devices 和 drivers目录,因为现在并没有向该总线注册任何的驱动和设备,因此这两个文件夹是空的。

    [root@FriendlyARM ldd]#ls devices
    [root@FriendlyARM ldd]#ls drivers
    [root@FriendlyARM ldd]#
    

    cat version 会调用show函数,显示我们在 Bus 中设置的属性。

    [root@FriendlyARM ldd]#cat version
    $Revision: 1.9
    

    driver

    整体流程

    driver_register(drv) [core.c]
      bus_add_driver(drv) [bus.c]
        if (drv->bus->p->drivers_autoprobe)
          driver_attach(dev)[dd.c]
            bus_for_each_dev(dev->bus, NULL, drv,__driver_attach)
            __driver_attach(dev, drv) [dd.c]
              driver_match_device(drv, dev) [base.h]
                drv-bus->match ? drv->bus-amatch(dev, drv) : 1
                if false, return;
              driver_probe_device(drv, dev) [dd.c]
                really_probe(dev, drv) [dd.c]
                  dev-driver = drv;
                  if (dev-bus->probe)
                    dev->bus->probe(dev);
                  else if (drv->probe)
                    drv-aprobe(dev);
                  probe_failed:
                    dev->-driver = NULL;
    

    device_driver原型

    // include/linux/device.h
    /**
     * struct device_driver - The basic device driver structure
     * @name:   Name of the device driver.
     * @bus:    The bus which the device of this driver belongs to.
     * @owner:  The module owner.
     * @mod_name:   Used for built-in modules.
     * @suppress_bind_attrs: Disables bind/unbind via sysfs.
     * @of_match_table: The open firmware table.
     * @acpi_match_table: The ACPI match table.
     * @probe:  Called to query the existence of a specific device,
     *      whether this driver can work with it, and bind the driver
     *      to a specific device.
     * @remove: Called when the device is removed from the system to
     *      unbind a device from this driver.
     * @shutdown:   Called at shut-down time to quiesce the device.
     * @suspend:    Called to put the device to sleep mode. Usually to a
     *      low power state.
     * @resume: Called to bring a device from sleep mode.
     * @groups: Default attributes that get created by the driver core
     *      automatically.
     * @pm:     Power management operations of the device which matched
     *      this driver.
     * @p:      Driver core's private data, no one other than the driver
     *      core can touch this.
     *
     * The device driver-model tracks all of the drivers known to the system.
     * The main reason for this tracking is to enable the driver core to match
     * up drivers with new devices. Once drivers are known objects within the
     * system, however, a number of other things become possible. Device drivers
     * can export information and configuration variables that are independent
     * of any specific device.
     */
    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 */
    
        const struct of_device_id   *of_match_table;
        const struct acpi_device_id *acpi_match_table;
    
        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

    // drivers/base/driver.c
    
    /**
     * driver_register - register driver with bus
     * @drv: driver to register
     *
     * We pass off most of the work to the bus_add_driver() call,
     * since most of the things we have to do deal with the bus
     * structures.
     */
    int driver_register(struct device_driver *drv)
    {
        int ret;
        struct device_driver *other;
    
        // 判断是否被注册过了。
        other = driver_find(drv->name, drv->bus);
        if (other) {
            return -EBUSY;
        }
    
        // 1、添加驱动到bus中
        ret = bus_add_driver(drv);
        // 2、
        ret = driver_add_groups(drv, drv->groups);
        kobject_uevent(&drv->p->kobj, KOBJ_ADD);
    
        return ret;
    }
    EXPORT_SYMBOL_GPL(driver_register);
    

    driver_register做了这几件事情:

    1、判断driver是否被注册过:通过名字查找总线中是否已经存在同名的对象

    2、把驱动添加进bus中,

    3、进行通知到用户空间。

    在bus_add_driver中注册

    // drivers/base/base.h
    struct driver_private {
        struct kobject kobj;
        struct klist klist_devices;
        struct klist_node knode_bus;
        struct module_kobject *mkobj;
        struct device_driver *driver;
    };
    
    // drivers/base/bus.c
    /**
     * bus_add_driver - Add a driver to the bus.
     * @drv: driver.
     */
    int bus_add_driver(struct device_driver *drv)
    {
        struct bus_type *bus;
        // 驱动的私有数据
        struct driver_private *priv;
        int error = 0;
    
        // 找到对应的总线
        bus = bus_get(drv->bus);
    
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
    
        // 初始化私有数据,并登记到 驱动 中
        klist_init(&priv->klist_devices, NULL, NULL);
        priv->driver = drv;
        drv->p = priv;
    
        // 在/sys/bus/xxx/drivers 目录下创建目录
        priv->kobj.kset = bus->p->drivers_kset;
        error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                                     "%s", drv->name);
    
        // 1、匹配 dev
        if (drv->bus->p->drivers_autoprobe) {
            error = driver_attach(drv);
        }
    
        // 将 driver 加入 Bus 的 drivers 链表中
        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
        // 如果设置了drv->mod_name 根据名字寻找模块
        module_add_driver(drv->owner, drv);
    
        // 在/sys/bus/xxx/drivers/创建属性文件
        error = driver_create_file(drv, &driver_attr_uevent);
    
        error = driver_add_attrs(bus, drv);
    
    
        if (!drv->suppress_bind_attrs) {
            error = add_bind_files(drv);
        }
    
        kobject_uevent(&priv->kobj, KOBJ_ADD);
        return 0;
    }
    

    在向Bus注册一个driver时,会调用到 driver_attch来寻找与之配对的 deivice

    driver_attach

    从逻辑上来说,一个驱动可以支持多个设备;一个设备只能绑定一个驱动。

    因此,driver_attach最终一一遍历目前所有的驱动和设备,并绑定对应的设备。

    // drivers/base/dd.c
    /**
     * driver_attach - try to bind driver to devices.
     * @drv: driver.
     *
     * Walk the list of devices that the bus has on it and try to
     * match the driver with each one.  If driver_probe_device()
     * returns 0 and the @dev->driver is set, we've found a
     * compatible pair.
     */
    int driver_attach(struct device_driver *drv)
    {
        return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
    }
    EXPORT_SYMBOL_GPL(driver_attach);
    
    遍历bus_for_each_dev
    /**
     * bus_for_each_dev - device iterator.
     * @bus: bus type.
     * @start: device to start iterating from.
     * @data: data for the callback.
     * @fn: function to be called for each device.
     *
     * Iterate over @bus's list of devices, and call @fn for each,
     * passing it @data. If @start is not NULL, we use that device to
     * begin iterating from.
     *
     * We check the return of @fn each time. If it returns anything
     * other than 0, we break out and return that value.
     *
     * NOTE: The device that returns a non-zero value is not retained
     * in any way, nor is its refcount incremented. If the caller needs
     * to retain this data, it should do so, and increment the reference
     * count in the supplied callback.
     */
    int bus_for_each_dev(struct bus_type *bus, struct device *start,
                 void *data, int (*fn)(struct device *, void *))
    {
        // 迭代器,在这里用于遍历device
        struct klist_iter i;
        struct device *dev;
        int error = 0;
    
        if (!bus || !bus->p)
            return -EINVAL;
    
        // 设置迭代器的起点为 链表的头部
        klist_iter_init_node(&bus->p->klist_devices, &i,
                     (start ? &start->p->knode_bus : NULL));
        while ((dev = next_device(&i)) && !error)
            error = fn(dev, data);
        klist_iter_exit(&i);
        return error;
    }
    EXPORT_SYMBOL_GPL(bus_for_each_dev);
    

    根据名字我们应该能猜测出来,调用Bus的每一个 dev 与 driver 进行 __driver_attach

    具体的做法是通过初始化一个迭代器指向链表的头部(在这里是bus->p->klist_devices),然后通过next_device进行遍历,并逐一执行fn方法。

    关于迭代器的函数我这里贴出来,但是不再做深入的解释了。

    // lib/klist.c
    void klist_iter_init_node(struct klist *k, struct klist_iter *i,
                  struct klist_node *n)
    {
        i->i_klist = k;
        i->i_cur = n;
        if (n)
            kref_get(&n->n_ref);
    }
    EXPORT_SYMBOL_GPL(klist_iter_init_node);
    
    // drivers/base/core.c
    static struct device *next_device(struct klist_iter *i)
    {
        struct klist_node *n = klist_next(i);
        struct device *dev = NULL;
        struct device_private *p;
    
        if (n) {
            p = to_device_private_parent(n);
            dev = p->device;
        }
        return dev;
    }
    
    // drivers/base/base.h
    #define to_device_private_parent(obj)   
        container_of(obj, struct device_private, knode_parent)
    

    我们看看fn,在这里它执行的是__driver_attach

    __driver_attach
    static int __driver_attach(struct device *dev, void *data)
    {
        struct device_driver *drv = data;
    
        /*
         * Lock device and try to bind to it. We drop the error
         * here and always return 0, because we need to keep trying
         * to bind to devices and some drivers will return an error
         * simply if it didn't support the device.
         *
         * driver_probe_device() will spit a warning if there
         * is an error.
         */
    
        // 1、匹配 现有的 drv 与 现在的 dev
        if (!driver_match_device(drv, dev))
            return 0;
    
        if (dev->parent)    /* Needed for USB */
            device_lock(dev->parent);
        device_lock(dev);
        // 2、从这里开始probe
        if (!dev->driver)
            driver_probe_device(drv, dev);
        device_unlock(dev);
        if (dev->parent)
            device_unlock(dev->parent);
    
        return 0;
    }
    

    在 __driver_attach 中,首先会调用到 driver_match_device 函数(return drv->bus->match ? drv->bus->match(dev, drv) : 1;)进行匹配:

    // drivers/base/base.h
    static inline int driver_match_device(struct device_driver *drv,
                          struct device *dev)
    {
        return drv->bus->match ? drv->bus->match(dev, drv) : 1;
    }
    

    match方法存在时,进行匹配,返回0代表成功。否则代表失败。

    如果匹配成功,则继续调用 driver_probe_device(drv, dev)

    // drivers/base/dd.c
    /**
     * driver_probe_device - attempt to bind device & driver together
     * @drv: driver to bind a device to
     * @dev: device to try to bind to the driver
     *
     * This function returns -ENODEV if the device is not registered,
     * 1 if the device is bound successfully and 0 otherwise.
     *
     * This function must be called with @dev lock held.  When called for a
     * USB interface, @dev->parent lock must be held as well.
     */
    int driver_probe_device(struct device_driver *drv, struct device *dev)
    {
        int ret = 0;
    
        if (!device_is_registered(dev))
            return -ENODEV;
    
        pr_debug("bus: '%s': %s: matched device %s with driver %s
    ",
             drv->bus->name, __func__, dev_name(dev), drv->name);
    
        pm_runtime_barrier(dev);
        ret = really_probe(dev, drv);
        pm_request_idle(dev);
    
        return ret;
    }
    

    在really_probe中probe

    device原型
    // include/linux/device.h
    struct device {
        struct device       *parent;
    
        struct device_private   *p;
    
        struct kobject kobj;
        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 */
        void        *platform_data; /* Platform specific data, device
                           core doesn't touch it */
        struct dev_pm_info  power;
        struct dev_pm_domain    *pm_domain;
    
        struct device_dma_parameters *dma_parms;
    
        struct list_head    dma_pools;  /* dma pools (if dma'ble) */
    
    
        struct device_node  *of_node; /* associated device tree node */
        struct acpi_dev_node    acpi_node; /* associated ACPI device node */
    
        dev_t           devt;   /* dev_t, creates the sysfs "dev" */
        u32         id; /* device instance */
    
        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);
        struct iommu_group  *iommu_group;
    
        bool            offline_disabled:1;
        bool            offline:1;
    };
    
    really_probe
    // drivers/base/dd.c
    static int really_probe(struct device *dev, struct device_driver *drv)
    {
        int ret = 0;
    
        atomic_inc(&probe_count);
        pr_debug("bus: '%s': %s: probing driver %s with device %s
    ",
             drv->bus->name, __func__, drv->name, dev_name(dev));
        WARN_ON(!list_empty(&dev->devres_head));
    
        // 1、关联 dev 与 drv
        dev->driver = drv;
    
        /* If using pinctrl, bind pins now before probing */
        ret = pinctrl_bind_pins(dev);
        if (ret)
            goto probe_failed;
    
        // 2、更新 sysfs
        if (driver_sysfs_add(dev)) {
            printk(KERN_ERR "%s: driver_sysfs_add(%s) failed
    ",
                __func__, dev_name(dev));
            goto probe_failed;
        }
    
        // 3、执行真正的probe
        if (dev->bus->probe) {
            ret = dev->bus->probe(dev);
            if (ret)
                goto probe_failed;
        } else if (drv->probe) {
            ret = drv->probe(dev);
            if (ret)
                goto probe_failed;
        }
    
        // 4、绑定
        driver_bound(dev);
        ret = 1;
        pr_debug("bus: '%s': %s: bound device %s to driver %s
    ",
             drv->bus->name, __func__, dev_name(dev), drv->name);
        goto done;
    
    probe_failed:
        devres_release_all(dev);
        driver_sysfs_remove(dev);
        dev->driver = NULL;
        dev_set_drvdata(dev, NULL);
    
        if (ret == -EPROBE_DEFER) {
            /* Driver requested deferred probing */
            dev_info(dev, "Driver %s requests probe deferral
    ", drv->name);
            driver_deferred_probe_add(dev);
        } else if (ret != -ENODEV && ret != -ENXIO) {
            /* driver matched but the probe failed */
            printk(KERN_WARNING
                   "%s: probe of %s failed with error %d
    ",
                   drv->name, dev_name(dev), ret);
        } else {
            pr_debug("%s: probe of %s rejects match %d
    ",
                   drv->name, dev_name(dev), ret);
        }
        /*
         * Ignore errors returned by ->probe so that the next driver can try
         * its luck.
         */
        ret = 0;
    done:
        atomic_dec(&probe_count);
        wake_up(&probe_waitqueue);
        return ret;
    }
    

    really_probe 中干了四件大事。

    关联dev与drv

    在 dev 中记录 driver :

    dev->driver = drv;
    

    已经match上了配对成功了嘛,所以可以将该device和driver关联起来: dev <- drv

    然而device_driver中并没有device成员,因此并没有 drv <- dev

    通知bus、更新sysfs
    driver_sysfs_add(dev);
    

    1、通知总线绑定了设备和驱动

    2、创建两个symlink,更新sysfs

    • 在sysfs中该 dev.kobj 目录下创建与之匹配的driver的符号连接,名字为“driver”
    • 在sysfs中该 driver.kobj 目录下创建与之匹配的device的符号连接,名字为 kobject_name(&dev->kobj)
    // drivers/base/dd.c
    static int driver_sysfs_add(struct device *dev)
    {
        int ret;
    
        // 通知总线绑定了设备和驱动
        if (dev->bus)
            blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                                         BUS_NOTIFY_BIND_DRIVER, dev);
        /* 例如,
           在/sys/bus/XXX/drivers/XXX 目录下建立symlink,链接名为 kobj->name, 
           链接指向 /sys/devices/platform/XXX 
        */
        ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
                                kobject_name(&dev->kobj));
        if (ret == 0) {
            /*  例如,
                在/sys/devices/platform/XXX/下建立symlink,链接名为driver, 
                指向/sys/bus/xxx/drivers目录下的某个目录*/
            ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
                                    "driver");
            if (ret)
                sysfs_remove_link(&dev->driver->p->kobj,
                                  kobject_name(&dev->kobj));
        }
        return ret;
    }
    
    执行真正的 probe 方法

    多态

        if (dev->bus->probe) {
            ret = dev->bus->probe(dev);
            if (ret)
                goto probe_failed;
        } else if (drv->probe) {
            ret = drv->probe(dev);
            if (ret)
                goto probe_failed;
        }
    

    probe的规则是:如果BUS上实现了probe就用BUS的probe;否则才会用driver的probe。

    绑定
    driver_bound(dev);
    

    将 device 放入 driver 链表中。

    看来一个device只能有一个driver,但是driver可以支持多个device

    // drivers/base/dd.c
    static void driver_bound(struct device *dev)
    {
        // 判断是否绑定过
        if (klist_node_attached(&dev->p->knode_driver)) {
            printk(KERN_WARNING "%s: device %s already bound
    ",
                __func__, kobject_name(&dev->kobj));
            return;
        }
    
        // 将 device 放入 driver 链表中。
        klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
    
        /*
         * Make sure the device is no longer in one of the deferred lists and
         * kick off retrying all pending devices
         */
        driver_deferred_probe_del(dev);
        driver_deferred_probe_trigger();
    
        if (dev->bus)
            blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
                             BUS_NOTIFY_BOUND_DRIVER, dev);
    }
    
    // lib/klist.c
    /**
     * klist_node_attached - Say whether a node is bound to a list or not.
     * @n: Node that we're testing.
     */
    int klist_node_attached(struct klist_node *n)
    {
        return (n->n_klist != NULL);
    }
    EXPORT_SYMBOL_GPL(klist_node_attached);
    

    kobject_uevent通知用户空间

    主要是在/sys/devices/.../中添加dev的uevent属性文件,先不说这个。

    例子(drv)

    ldd_drv.c

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/sched.h> 
    #include <asm/uaccess.h>
    #include <linux/io.h>
    #include "lddbus.h"
     
    struct ldd_driver ldd_drv = {
    	.version 	= "version 1.0
    ",	
    	.driver = {
    		.name = "myldd",
    	},
    };
     
    static int ldd_drv_init(void){
    	register_ldd_driver(&ldd_drv);
    	return 0;
    }
     
    static void ldd_drv_exit(void){
    	
    	unregister_ldd_driver(&ldd_drv);
    }
     
    module_init(ldd_drv_init);
    module_exit(ldd_drv_exit);
    MODULE_LICENSE("GPL");
    

    测试

    [root@FriendlyARM ]# insmod drv.ko
    [root@FriendlyARM  ]# cd sys /bus/ldd/drivers/
    [root@FriendlyARM drivers]ls
    myldd
    [root@FriendlyARM drivers] cd myldd/
    [root@FriendlyARM myldd]#ls
    bind uevent unbind version
    [root@FriendlyARM myldd]# cat version
    version 1.0
    

    insmod drv.ko 之后,我们会发现 /sys/bus/ldd/drivers 目录下多了一个 myldd 目录,这就是我们向内核注册的ldd总线上的myldd驱动程序。同样 cat version 会显示设定好的属性。

    device

    整体流程

    device_register(dev)[core.c]
      device_initialize()
      device_add(dev) [core.c]
        bus_add_device(dev)
        bus_probe_device(dev) [bus.c]
          if (dev->bus && dev->bus-op->drivers_autoprobe)
            device_attach(dev) [dd.c]
              if (dev->driver)
                device_bind_driver(dev)
              else // 从这里开始,与 driver一样
                bus_for_each_dev(dev->bus, NULL, drv,__driver_attach)
                  __driver_attach(dev, drv) [dd.c]
                    driver_match_device(drv, dev) [base.h]
                      drv-bus->match ? drv->bus-amatch(dev, drv) : 1
                      if false, return;
                    driver_probe_device(drv, dev) [dd.c]
                      really_probe(dev, drv) [dd.c]
                        dev-driver = drv;
                        if (dev-bus->probe)
                          dev->bus->probe(dev);
                        else if (drv->probe)
                          drv-aprobe(dev);
                        probe_failed:
                          dev->-driver = NULL;
    

    device原型

    // include/linux/device.h
    /**
     * struct device - The basic device structure
     * @parent: The device's "parent" device, the device to which it is attached.
     *      In most cases, a parent device is some sort of bus or host
     *      controller. If parent is NULL, the device, is a top-level device,
     *      which is not usually what you want.
     * @p:      Holds the private data of the driver core portions of the device.
     *      See the comment of the struct device_private for detail.
     * @kobj:   A top-level, abstract class from which other classes are derived.
     * @init_name:  Initial name of the device.
     * @type:   The type of device.
     *      This identifies the device type and carries type-specific
     *      information.
     * @mutex:  Mutex to synchronize calls to its driver.
     * @bus:    Type of bus device is on.
     * @driver: Which driver has allocated this
     * @platform_data: Platform data specific to the device.
     *      Example: For devices on custom boards, as typical of embedded
     *      and SOC based hardware, Linux often uses platform_data to point
     *      to board-specific structures describing devices and how they
     *      are wired.  That can include what ports are available, chip
     *      variants, which GPIO pins act in what additional roles, and so
     *      on.  This shrinks the "Board Support Packages" (BSPs) and
     *      minimizes board-specific #ifdefs in drivers.
     * @power:  For device power management.
     *      See Documentation/power/devices.txt for details.
     * @pm_domain:  Provide callbacks that are executed during system suspend,
     *      hibernation, system resume and during runtime PM transitions
     *      along with subsystem-level and driver-level callbacks.
     * @pins:   For device pin management.
     *      See Documentation/pinctrl.txt for details.
     * @numa_node:  NUMA node this device is close to.
     * @dma_mask:   Dma mask (if dma'ble device).
     * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all
     *      hardware supports 64-bit addresses for consistent allocations
     *      such descriptors.
     * @dma_parms:  A low level driver may set these to teach IOMMU code about
     *      segment limitations.
     * @dma_pools:  Dma pools (if dma'ble device).
     * @dma_mem:    Internal for coherent mem override.
     * @cma_area:   Contiguous memory area for dma allocations
     * @archdata:   For arch-specific additions.
     * @of_node:    Associated device tree node.
     * @acpi_node:  Associated ACPI device node.
     * @devt:   For creating the sysfs "dev".
     * @id:     device instance
     * @devres_lock: Spinlock to protect the resource of the device.
     * @devres_head: The resources list of the device.
     * @knode_class: The node used to add the device to the class list.
     * @class:  The class of the device.
     * @groups: Optional attribute groups.
     * @release:    Callback to free the device after all references have
     *      gone away. This should be set by the allocator of the
     *      device (i.e. the bus driver that discovered the device).
     * @iommu_group: IOMMU group the device belongs to.
     *
     * @offline_disabled: If set, the device is permanently online.
     * @offline:    Set after successful invocation of bus type's .offline().
     *
     * At the lowest level, every device in a Linux system is represented by an
     * instance of struct device. The device structure contains the information
     * that the device model core needs to model the system. Most subsystems,
     * however, track additional information about the devices they host. As a
     * result, it is rare for devices to be represented by bare device structures;
     * instead, that structure, like kobject structures, is usually embedded within
     * a higher-level representation of the device.
     */
    struct device {
        struct device       *parent;
    
        struct device_private   *p;
    
        struct kobject kobj;
        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 */
        void        *platform_data; /* Platform specific data, device
                           core doesn't touch it */
        struct dev_pm_info  power;
        struct dev_pm_domain    *pm_domain;
    
    #ifdef CONFIG_PINCTRL
        struct dev_pin_info *pins;
    #endif
    
    #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 */
    #ifdef CONFIG_DMA_CMA
        struct cma *cma_area;       /* contiguous memory area for dma
                           allocations */
    #endif
        /* arch specific additions */
        struct dev_archdata archdata;
    
        struct device_node  *of_node; /* associated device tree node */
        struct acpi_dev_node    acpi_node; /* associated ACPI device node */
    
        dev_t           devt;   /* dev_t, creates the sysfs "dev" */
        u32         id; /* device instance */
    
        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);
        struct iommu_group  *iommu_group;
    
        bool            offline_disabled:1;
        bool            offline:1;
    };
    

    device_register

    /**
     * device_register - register a device with the system.
     * @dev: pointer to the device structure
     *
     * This happens in two clean steps - initialize the device
     * and add it to the system. The two steps can be called
     * separately, but this is the easiest and most common.
     * I.e. you should only call the two helpers separately if
     * have a clearly defined need to use and refcount the device
     * before it is added to the hierarchy.
     *
     * For more information, see the kerneldoc for device_initialize()
     * and device_add().
     *
     * NOTE: _Never_ directly free @dev after calling this function, even
     * if it returned an error! Always use put_device() to give up the
     * reference initialized in this function instead.
     */
    int device_register(struct device *dev)
    {
        device_initialize(dev);
        return device_add(dev);
    }
    EXPORT_SYMBOL_GPL(device_register);
    

    device_initialize

    /**
     * device_initialize - init device structure.
     * @dev: device.
     *
     * This prepares the device for use by other layers by initializing
     * its fields.
     * It is the first half of device_register(), if called by
     * that function, though it can also be called separately, so one
     * may use @dev's fields. In particular, get_device()/put_device()
     * may be used for reference counting of @dev after calling this
     * function.
     *
     * All fields in @dev must be initialized by the caller to 0, except
     * for those explicitly set to some other value.  The simplest
     * approach is to use kzalloc() to allocate the structure containing
     * @dev.
     *
     * NOTE: Use put_device() to give up your reference instead of freeing
     * @dev directly once you have called this function.
     */
    void device_initialize(struct device *dev)
    {
        // 设置 dev->kobj.kset 为 devices_kset
        dev->kobj.kset = devices_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);
    }
    EXPORT_SYMBOL_GPL(device_initialize);
    

    做了一些设备有关的基本初始化。

    device_add

    // drviers/base/core.c
    /**
     * device_add - add device to device hierarchy.
     * @dev: device.
     *
     * This is part 2 of device_register(), though may be called
     * separately _iff_ device_initialize() has been called separately.
     *
     * This adds @dev to the kobject hierarchy via kobject_add(), adds it
     * to the global and sibling lists for the device, then
     * adds it to the other relevant subsystems of the driver model.
     *
     * Do not call this routine or device_register() more than once for
     * any device structure.  The driver model core is not designed to work
     * with devices that get unregistered and then spring back to life.
     * (Among other things, it's very hard to guarantee that all references
     * to the previous incarnation of @dev have been dropped.)  Allocate
     * and register a fresh new struct device instead.
     *
     * NOTE: _Never_ directly free @dev after calling this function, even
     * if it returned an error! Always use put_device() to give up your
     * reference instead.
     */
    int device_add(struct device *dev)
    {
        struct device *parent = NULL;
        struct kobject *kobj;
        struct class_interface *class_intf;
        int error = -EINVAL;
    
        dev = get_device(dev);
        if (!dev)
            goto done;
    
        // 初始化 device的 私有数据
        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()
         */
        /* 初始化设备内部的kobject的名字 */
        // 如果初始名字(init_name)存在,则设为名字 为 init_name
        if (dev->init_name) {
            dev_set_name(dev, "%s", dev->init_name);
            dev->init_name = NULL;
        }
    
        /* subsystems can specify simple device enumeration */
        // 如果检查发现没有名字,但bus设置了设备名前缀,则以 类似foo%u的方式来设置设备的名字
        // 例如 tty0
        if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
            dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
    
        pr_debug("device: '%s': %s
    ", dev_name(dev), __func__);
    
        // 增加设备父设备并增加父设备引用计数,例如:csid的设备节点节v4l-subdev4的父设备是fd8c0000.qcom,msm-cam
        parent = get_device(dev->parent);
        // 获取v4l-subdev4设备目录的父目录是video4linux,video4linux的父目录是fd8c0000.qcom,msm-cam
        kobj = get_device_parent(dev, parent);
        // 在kobject层实现设备父子关系
        if (kobj)
            dev->kobj.parent = kobj;
    
    
        /* first, register with generic layer. */
        /* we require the name to be set before, and pass NULL */
        /* 
           把内嵌的kobject注册到设备模型中将设备加入到kobject模型中,
           创建sys相关目录 ,目录名字为kobj->name
        */
        error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
        if (error)
            goto Error;
    
        /* notify platform of device entry */
        if (platform_notify)
            platform_notify(dev);
    
        // 创建sys目录下设备的uevent属性文件,通过它可以查看设备的uevent事件
        error = device_create_file(dev, &dev_attr_uevent);
        if (error)
            goto attrError;
    
        // 如果有主次设备号 创建dev 属性文件
        if (MAJOR(dev->devt)) {
            // 在 /sys/devices中创建设备节点
            error = device_create_file(dev, &dev_attr_dev);
    
            /* 在/sys/dev/char/或者/sys/dev/block/创建devt的属性的连接文件,
            形如10:45,由主设备号和次设备号构成,指向/sys/devices/.../的具体设备目录,
            该链接文件只具备读属性,显示主设备号:次设备号,如10:45,用户空间udev相应uevent事件时,将根据设备号在/dev下创建节点文件
            */
            error = device_create_sys_dev_entry(dev);
    
    
            devtmpfs_create_node(dev);
        }
    
        // 创建类符号链接,相互创建dev和class之间的链接文件
        error = device_add_class_symlinks(dev);
        // 创建sys目录下设备其他属性文件
        error = device_add_attrs(dev);
        // 将设备加入到管理它的bus总线的设备链表上
        // 创建subsystem链接文件,链接class下的具体的子系统文件夹
        error = bus_add_device(dev);
    
        error = dpm_sysfs_add(dev);
    
        device_pm_add(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);
        if (parent)
            klist_add_tail(&dev->p->knode_parent,
                       &parent->p->klist_children);
    
        if (dev->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);
        }
    done:
        put_device(dev);
        return error;
    
    }
    EXPORT_SYMBOL_GPL(device_add);
    
    bus_add_device
    /**
     * bus_add_device - add device to bus
     * @dev: device being added
     *
     * - Add device's bus attributes.
     * - Create links to device's bus.
     * - Add the device to its bus's list of devices.
     */
    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
    ", bus->name, dev_name(dev));
            /* 创建相应的属性文件 */
            error = device_add_attrs(bus, dev);
    
    
            // 添加属性组。
            error = device_add_groups(dev, bus->dev_groups);
    
            // 创建 /sys/bus/$(bus->name)/devices/$(dev->name) 到 /sys/devices/$(dev->name) 的软连接
            error = sysfs_create_link(&bus->p->devices_kset->kobj,
                                      &dev->kobj, dev_name(dev));
    
            // 创建 /sys/devices/$(dev->name)/subsystem 到 /sys/bus/$(bus->name)/devices/$(dev->name) 的软连接
            error = sysfs_create_link(&dev->kobj,
                                      &dev->bus->p->subsys.kobj, "subsystem");
            if (error)
                goto out_subsys;
            // 将 dev 加入 bus 所管理的 devices 链表
            klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
        }
        return 0;
    
    }
    

    bus_probe_device

    // drivers/base/bus.c
    /**
     * bus_probe_device - probe drivers for a new device
     * @dev: device to probe
     *
     * - Automatically probe for a driver if the bus allows it.
     */
    void bus_probe_device(struct device *dev)
    {
        struct bus_type *bus = dev->bus;
        struct subsys_interface *sif;
        int ret;
    
        if (!bus)
            return;
    
        if (bus->p->drivers_autoprobe) {
            ret = device_attach(dev);
            WARN_ON(ret < 0);
        }
    
        mutex_lock(&bus->p->mutex);
        list_for_each_entry(sif, &bus->p->interfaces, node)
            if (sif->add_dev)
                sif->add_dev(dev, sif);
        mutex_unlock(&bus->p->mutex);
    }
    
    device_attach

    从逻辑上来说,一个驱动可以支持多个设备;一个设备只能绑定一个驱动。

    device_attach尝试为设备寻找到一个驱动;

    因此,device_attach稍微与driver_attach不一样:调用driver_match_device匹配设备和驱动,成功就结束循环退出(而不是执行到循环);

    // drivers/base/dd.c
    static int __device_attach(struct device_driver *drv, void *data)
    {
        struct device *dev = data;
    
        if (!driver_match_device(drv, dev))
            return 0;
    
        return driver_probe_device(drv, dev);
    }
    
    /**
     * device_attach - try to attach device to a driver.
     * @dev: device.
     *
     * Walk the list of drivers that the bus has and call
     * driver_probe_device() for each pair. If a compatible
     * pair is found, break out and return.
     *
     * Returns 1 if the device was bound to a driver(成功);
     * 0 if no matching driver was found(失败);
     * -ENODEV if the device is not registered(异常).
     *
     * When called for a USB interface, @dev->parent lock must be held.
     */
    int device_attach(struct device *dev)
    {
        int ret = 0;
    
        device_lock(dev);
        // 情况1:设备已经有驱动
        if (dev->driver) {
            if (klist_node_attached(&dev->p->knode_driver)) {
                ret = 1;
                goto out_unlock;
            }
            ret = device_bind_driver(dev);
            if (ret == 0)
                ret = 1; // 成功,并退出
            else {
                dev->driver = NULL;
                ret = 0;
            }
        } else { // 情况2:设备没有驱动(通常情况)
            // 遍历总线上的driver链表,进行匹配
            ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
            pm_request_idle(dev);
        }
    out_unlock:
        device_unlock(dev);
        return ret;
    }
    EXPORT_SYMBOL_GPL(device_attach);
    

    此后就是bus_for_each_drv,不再赘述。

    例子(dev)

    ldd_dev.c

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/device.h>
    #include <linux/sched.h> 
    #include <asm/uaccess.h>
    #include "lddbus.h"
    
    static dev_t devid;
    
    static struct ldd_device ldd_dev = {
        .name = "myldd",	
        .dev = {		
            .init_name = "myldd",
        },
    };
    
    static int ldd_dev_init(void) {
    
        alloc_chrdev_region(&devid, 0, 1, "mylddtest");
        //ldd_dev.dev.devt = devid;
        register_ldd_device(&ldd_dev);
        return 0;
    }
    
    static void ldd_dev_exit(void) {
        unregister_ldd_device(&ldd_dev);
    }
    
    module_init(ldd_dev_init);
    module_exit(ldd_dev_exit);
    MODULE_LICENSE("GPL");
    

    测试

    [root@FriendlyARM myldd]#cd /sys/devices
    [root@FriendlyARM devices] ls
    1dd0 platform system virtual
    [root@FriendlyARM devices] cd ldd0
    [root@FriendlyARM lddo]# ls
    myldd uevent
    [root@FriendlyARM lddo] cd myldd
    [root@FriendlyARM myldd] ls
    driver subsystm uevent
    [root@FriendlyARM myldd] ls -l
    lrwxrwXrwx1 rootroot      0 Dec 3 01:37 driver   -> ../../../bus /ldd/drivers/myldd
    lrwXrwXrwX1 rootroot      0 Dec 301:37 subsystem -> ../../..7bus/ldd
    -rw-r——r--1 root_root 4096 Dec 301:37 uevent
    [root@FriendlyARM myldd]cd /sys/bus/ldd/devices/
    [root@FriendlyARM devices]ls
    myldd
    [root@FriendlyARM devices]# ls -l
    lrwxrwXrwx1 rootroot
    0 Dec3 01:40 myldd -> ../../../ devices/ldd0/myldd
    [root@FriendlyARM devices] cd myldd
    [root@FriendlyARM myldd]# ls
    driver subsystem uevent
    [root@FriendlyARM myldd]# cd driver
    [root@FriendlyARM myldd]# ls
    bind myldd uevent unbind  version
    [root@FriendlyARM myldd] #cat version
    ' version 1.0
    

    device 相对driver 要复杂一些,insmod dev.ko 之后,我们可以在/sys/devices 目录下看到新增了一个目录 ldd0(ldd_bus) ,在 ldd0 (ldd_bus)目录下看到我们向ldd总线注册的myldd设备(ldd0是 myldd 的父设备),在/sys/bus/ldd/devices/ 目录下同样可以看到 myldd , 因为这里的Myldd 是指向 /sys/devices/ldd0/myldd 的软连接。

    /sys/devices/ldd0/myldd/driver 目录 与该设备匹配的驱动程序,我们在Bus->match中设置的匹配条件--名字相同。

    我们并未看到属性文件 dev ,是因为我们没有指定Myldd设备的设备号,将 dev.c 代码中的 ldd_dev.dev.devt = devid 注释去掉,卸载原来驱动,重新加载。

    [root@FriendlyARM myldd]#cd /sys/devices/ldd0/myldd
    [root@FriendlyARM myldd]#ls 
    dev driver subsystem uevent
    [root@FriendlyARM myldd]#cd /sys/bus/ldd/devices/myldd/
    [root@FriendlyARM myldd]#ls 
    dev driver subsystem uevent
    [root@FriendlyARM myldd]#cat dev
    253:0
    [root@FriendlyARM myldd]#ls -l /dev/my*
    crw-rw---- 1 root root 253,   0 Dec 3 02:05 /dev/myldd
    

    总结

    无论是bus,还是class,还是我们会在后面看到的一些虚拟的子系统,它都构成了一个“子系统(sub-system)”;该子系统会包含形形色色的device或device_driver,就像一个独立的王国一样,存在于内核中。

    而这些子系统的表现形式,就是/sys/bus(或/sys/class,或其它)目录下面的子目录,每一个子目录,都是一个子系统(如/sys/bus/spi/)。

    附录:subsys_private的演化

    参考:https://www.cnblogs.com/xinghuo123/p/12872026.html

    按理说,subsys_private就是集合了一些bus模块需要使用的私有数据,例如ksetklist等等,命名为bus_private会好点(就像device、driver模块一样)

    // drivers/base/base.h
    /**
     * struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure.
     *
     * @subsys - the struct kset that defines this subsystem
     * @devices_kset - the list of devices associated
     *
     * @drivers_kset - the list of drivers associated
     * @klist_devices - the klist to iterate over the @devices_kset
     * @klist_drivers - the klist to iterate over the @drivers_kset
     * @bus_notifier - the bus notifier list for anything that cares about things
     *                 on this bus.
     * @bus - pointer back to the struct bus_type that this structure is associated
     *        with.
     *
     * @class_interfaces - list of class_interfaces associated
     * @glue_dirs - "glue" directory to put in-between the parent device to
     *              avoid namespace conflicts
     * @class_mutex - mutex to protect the children, devices, and interfaces lists.
     * @class - pointer back to the struct class that this structure is associated
     *          with.
     *
     * This structure is the one that is the actual kobject allowing struct
     * bus_type/class to be statically allocated safely.  Nothing outside of the
     * driver core should ever touch these fields.
     */
    struct subsys_private {
        struct kset subsys; //代表bus在sysfs中的类型
        struct kset *devices_kset; //代表bus目录下的drivers子目录
        // ...
    
        struct kset *drivers_kset;  //代表bus目录下地devices子目录
        struct klist klist_devices; //bus的设备链表
        struct klist klist_drivers; //bus的驱动链表
        struct blocking_notifier_head bus_notifier; //用于在总线上内容发送变化时调用特定的函数
        
        // 标志定义是否允许device和driver自动匹配,
        // 如果允许会在device或者driver注册时就进行匹配工作,默认是1
        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; // 指向关联的bus_type类型。
    };
    

    早期版本

    事实上,早期版本确实是命名为bus_type_private。那个时候,class 的私有数据与 bus的私有数据是分开的,分别是class_privatebus_type_private

    linux 2.6.35.7

    struct class_private {
        struct kset class_subsys;
        struct klist class_devices;
        struct list_head class_interfaces;
        struct kset class_dirs;
        struct mutex class_mutex;
        struct class *class;
    };
    
    struct bus_type_private {
        struct kset subsys;
        struct kset *drivers_kset;
        struct kset *devices_kset;
        struct klist klist_devices;
        struct klist klist_drivers;
        struct blocking_notifier_head bus_notifier;
        unsigned int drivers_autoprobe:1;
        struct bus_type *bus;
    };
    

    linux 3.x早期

    bus因为需求升级为subsys_private ,同时为后面去掉class_private 做基础

    struct subsys_private {
        struct kset subsys;
        struct kset *devices_kset;
        struct list_head interfaces;
        struct mutex mutex;
        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 kset glue_dirs;
        struct class *class;
    };
    struct class_private {
        struct kset class_subsys;
        struct klist class_devices;
        struct list_head class_interfaces;
        struct kset class_dirs;
        struct mutex class_mutex;
        struct class *class;
    };
    

    linux 3.x后期

    两者完全统一用这个,class_private 在这个版本已经完全看不到了

    struct subsys_private {
        struct kset subsys;
        struct kset *devices_kset;
        struct list_head interfaces;
        struct mutex mutex;
        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 kset glue_dirs;
        struct class *class; //
    };
    

    附录:在bus中理解kobject的生命周期管理

    回到kobject_put(),它通常被具体对象做一个简单包装,如:bus_put(),它直接调用kset_put(),然后调用到kobject_put()

    // driver/base/bus.c
    static void bus_put(struct bus_type *bus)
    {
        if (bus)
            kset_put(&bus->p->subsys);
    }
    

    那对于这个bus_type对象而言,仅仅通过kobject_put(),如何来达到释放整个bus_type的目的呢?

    这里就需要kobject另一个成员struct kobj_type * ktype来完成。

    回到kobject_put()release()操作。当引用计数为0时,kobject核心会调用kobject_release(),最后会调用kobj_type->release(kobj)来完成对象的释放。可是具体对象的释放,最后却通过kobj->kobj_type->release()来释放,那这个release()函数,就必须得由具体的对象来指定。

    还是拿bus_type举例:

    在通过bus_register(struct bus_type *bus)进行总线注册时,该API内部会执行priv->subsys.kobj.ktype = &bus_ktype操作;

    // driver/base/bus.c
    int bus_register(struct bus_type *bus)
    {
        // ...
        
        priv->subsys.kobj.ktype = &bus_ktype;
        
        // ...
    }
    

    有了该操作,那么前面的bus_put()在执行bus_type->p-> subsys.kobj->ktype->release()时,就会执行注册的bus_ktype.release = bus_release函数。

    // driver/base/bus.c
    static struct kobj_type bus_ktype = {
        .sysfs_ops  = &bus_sysfs_ops,
        .release    = bus_release,
    };
    
    static void bus_release(struct kobject *kobj)
    {
        // 获取整个 具体的 bus子系统 对象
        struct subsys_private *priv =
            container_of(kobj, typeof(*priv), subsys.kobj);
        struct bus_type *bus = priv->bus;
    
        // 释放资源
        kfree(priv);
        bus->p = NULL;
    }
    

    由于bus_release()函数由具体的bus子系统提供,它必定知道如何释放包括kobj在内的bus_type对象。

    如果说我的文章对你有用,只不过是我站在巨人的肩膀上再继续努力罢了。
    若在页首无特别声明,本篇文章由 Schips 经过整理后发布。
    博客地址:https://www.cnblogs.com/schips/
  • 相关阅读:
    大话设计模式Python实现-代理模式
    大话设计模式Python实现-装饰模式
    大话设计模式Python实现-策略模式
    设计模式Python实现-简单工厂模式
    Python文件读写机制
    python 多线程剖析
    I/O多路复用-EPOLL探索
    Python学习笔记:魔术方法详解
    Django学习笔记:为Model添加Action
    【Django】Django Debug Toolbar调试工具配置
  • 原文地址:https://www.cnblogs.com/schips/p/linux_device_model_2.html
Copyright © 2020-2023  润新知