zoukankan      html  css  js  c++  java
  • 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/
  • 相关阅读:
    使用 ASP.NET Core MVC 创建 Web API(五)
    使用 ASP.NET Core MVC 创建 Web API(四)
    使用 ASP.NET Core MVC 创建 Web API(三)
    使用 ASP.NET Core MVC 创建 Web API(二)
    使用 ASP.NET Core MVC 创建 Web API(一)
    学习ASP.NET Core Razor 编程系列十九——分页
    学习ASP.NET Core Razor 编程系列十八——并发解决方案
    一个屌丝程序猿的人生(九十八)
    一个屌丝程序猿的人生(九十七)
    一个屌丝程序猿的人生(九十五)
  • 原文地址:https://www.cnblogs.com/schips/p/linux_device_model_2.html
Copyright © 2011-2022 走看看