设备驱动中的device(kernel-4.7)
2016-12-07 15:43
417 查看
device结构体定义,在kernel-4.7/include/linux/device.h中:
device结构体用于描述设备相关的信息设备之间的层次关系,以及设备与总线、驱动的关系。
Linux设备模型中,device相关的sysfs结构的初始化是由
注册一个device是通过调用
调用
调用device_add函数,将device添加到系统中,该函数定义在drivers/base/core.c文件中,其内容如下:
其中调用
从该函数的注释可以看出,该函数的作用是返回device对应的/sys/dev/下的一个目录。该函数的返回值是一个kobject,一个kobject在sysfs系统中对应一个目录。如果
该宏的作用是把“主设备号:次设备号”字符串写入buffer变量中
由注释可以看出,第一个参数是我们将要创建的链接所在的目录,第二个参数是链接指向的源,第三个参数是链接的名字。
其中调用
可以看到如果
可以看到while循环依次遍历
参数传递过来的fn函数是
其中调用
其中调用
调用
可以看到如果定义了
可以看到调用
将以上device的注册过程倒序捋顺:
这样,
/** * struct device - The basic device structure * @driver_data: Private pointer for driver specific info. * @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. * @msi_list: Hosts MSI descriptors * @msi_domain: The generic MSI domain this device is using. * @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_pfn_offset: offset of DMA memory range relatively of RAM * @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. * @fwnode: Associated device node supplied by platform firmware. * @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; // kobject类, 用于联系到sysfs中 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 */ void *driver_data; /* Driver data, set and get with dev_set/get_drvdata */ struct dev_pm_info power; struct dev_pm_domain *pm_domain; #ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN struct irq_domain *msi_domain; #endif #ifdef CONFIG_PINCTRL struct dev_pin_info *pins; #endif #ifdef CONFIG_GENERIC_MSI_IRQ struct list_head msi_list; #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. */ unsigned long dma_pfn_offset; 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 fwnode_handle *fwnode; /* firmware 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; //连入class链表时所用的klist节点 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结构体用于描述设备相关的信息设备之间的层次关系,以及设备与总线、驱动的关系。
/** * struct device_private - structure to hold the private to the driver core portions of the device structure. * * @klist_children - klist containing all children of this device * @knode_parent - node in sibling list * @knode_driver - node in driver list * @knode_bus - node in bus list * @deferred_probe - entry in deferred_probe_list which is used to retry the * binding of drivers which were unable to get all the resources needed by * the device; typically because it depends on another driver getting * probed first. * @device - pointer back to the struct device that this structure is * associated with. * * Nothing outside of the driver core should ever touch these fields. */ struct device_private { struct klist klist_children; //子设备的链表 struct klist_node knode_parent; //连入父设备的klist_children时所用的节点 struct klist_node knode_driver; //连入驱动的设备链表所用的节点 struct klist_node knode_bus; //连入总线的设备链表时所用的节点 struct list_head deferred_probe; struct device *device; }; //从父设备的klist_children上节点,获得相应的device_private #define to_device_private_parent(obj) \ container_of(obj, struct device_private, knode_parent) //从驱动的设备链表上节点,获得对应的device_private #define to_device_private_driver(obj) \ container_of(obj, struct device_private, knode_driver) 从总线的设备链表上节点,获得对应的device_private #define to_device_private_bus(obj) \ container_of(obj, struct device_private, knode_bus)
struct device中有一部分不愿意让外界看到,所以做出
struct device_private结构,包括了设备驱动模型内部的链接。这个结构体定义在drivers/base/base.h文件中。
Linux设备模型中,device相关的sysfs结构的初始化是由
devices_init函数完成的,该函数定义在drivers/base/core.c文件中,其内容如下:
int __init devices_init(void) { //创建devices_kset,它是所有device的集合,它的名字是“devices”,对应/sys/devices目录 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); if (!devices_kset) return -ENOMEM; //创建dev_kobj,它的名字是“dev”,对应/sys/dev目录 dev_kobj = kobject_create_and_add("dev", NULL); if (!dev_kobj) goto dev_kobj_err; //创建sysfs_dev_block_kobj,它的名字是“block”,对应/sys/dev/block目录 sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); if (!sysfs_dev_block_kobj) goto block_kobj_err; 创建sysfs_dev_char_kobj,它的名字是“char”,对应/sys/dev/char目录 sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); if (!sysfs_dev_char_kobj) goto char_kobj_err; return 0; char_kobj_err: kobject_put(sysfs_dev_block_kobj); block_kobj_err: kobject_put(dev_kobj); dev_kobj_err: kset_unregister(devices_kset); return -ENOMEM; }
注册一个device是通过调用
device_register函数完成的,该函数定义在drivers/base/core.c文件中,其内容如下:
/** * 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); //调用device_initialize函数对device结构体进行初始化 return device_add(dev); //调用device_add函数,将device添加到系统中 }
调用
device_initialize函数对device结构体进行初始化。该函数定义在drivers/base/core.c文件中,其内容如下:
/** * 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; kobject_init(&dev->kobj, &device_ktype); INIT_LIST_HEAD(&dev->dma_pools); mutex_init(&dev->mutex); lockdep_set_novalidate_class(&dev->mutex); spin_lock_init(&dev->devres_lock); INIT_LIST_HEAD(&dev->devres_head); device_pm_init(dev); set_dev_node(dev, -1); #ifdef CONFIG_GENERIC_MSI_IRQ INIT_LIST_HEAD(&dev->msi_list); #endif }
调用device_add函数,将device添加到系统中,该函数定义在drivers/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); //调用get_device增加device(其实是device.kobj)的引用计数 if (!dev) goto done; if (!dev->p) { //如果dev->p是NULL,则调用device_private_init函数为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() */ //如果dev->init_name不为空,则调用dev_set_name设置device的名字(其实是设置device.kobj的名字) if (dev->init_name) { dev_set_name(dev, "%s", dev->init_name); dev->init_name = NULL; } /* subsystems can specify simple device enumeration */ //如果device的名字为空,并且dev->bus->dev_name不为空,则用dev->bus->dev_name作为device的名字。 if (!dev_name(dev) && dev->bus && dev->bus->dev_name) dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); //如果device的名字仍然为空,则出错退出 if (!dev_name(dev)) { error = -EINVAL; goto name_error; } pr_debug("device: '%s': %s\n", dev_name(dev), __func__); //取得dev->parent,保存在parent变量中 parent = get_device(dev->parent); //调用get_device_parent函数,该函数的作用是取得dev->kobj.parent,即device对应的kobject的parent kobject kobj = get_device_parent(dev, parent); //如果通过get_device_parent函数取得了父kobject,则用它来设置dev->kobj.parent if (kobj) dev->kobj.parent = kobj; /* use parent numa_node */ if (parent && (dev_to_node(dev) == NUMA_NO_NODE)) set_dev_node(dev, dev_to_node(parent)); /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ //调用kobject_add将dev->kobj注册到sysfs系统中 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); if (error) goto Error; /* notify platform of device entry */ if (platform_notify) platform_notify(dev); //调用device_create_file创建uevent属性文件 error = device_create_file(dev, &dev_attr_uevent); if (error) goto attrError; //调用device_add_class_symlinks(dev)函数,创建dev->class相关的链接 error = device_add_class_symlinks(dev); if (error) goto SymlinkError; //调用device_add_attrs(dev)函数,该函数创建与dev->class相关的一些属性文件 error = device_add_attrs(dev); if (error) goto AttrsError; //调用bus_add_device(dev)函数,该函数在/sys/bus/devices目录下创建一个链接文件,指向/sys/devices/dev->init_name error = bus_add_device(dev); if (error) goto BusError; error = dpm_sysfs_add(dev); if (error) goto DPMError; device_pm_add(dev); //通过MAJOR(dev->devt)取得设备对应的主设备号 //如果设备的主设备号不为0,调用device_create_file创建dev属性文件 if (MAJOR(dev->devt)) { error = device_create_file(dev, &dev_attr_dev); if (error) goto DevAttrError; //调用device_create_sys_dev_entry(dev)函数 error = device_create_sys_dev_entry(dev); if (error) goto SysEntryError; /* 如果device主设备号不为0,还会调用devtmpfs_create_node(dev)函数在/dev目录下自动生成一个设备节点。在Linux早期,/dev下的设备节点是通过mknod命令手动添加的,现在通过devtmpfs文件系统,就可以在调用device_register函数注册设备时自动向/dev目录下添加设备节点,该节点的名字就是dev->init_name*/ devtmpfs_create_node(dev); } /* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */ if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); //调用bus_probe_device(dev)函数为新添加的device探测匹配的device_driver 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; SysEntryError: if (MAJOR(dev->devt)) device_remove_file(dev, &dev_attr_dev); DevAttrError: device_pm_remove(dev); dpm_sysfs_remove(dev); DPMError: bus_remove_device(dev); BusError: device_remove_attrs(dev); AttrsError: device_remove_class_symlinks(dev); SymlinkError: device_remove_file(dev, &dev_attr_uevent); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); Error: cleanup_device_parent(dev); put_device(parent); name_error: kfree(dev->p); dev->p = NULL; goto done; }
device_add函数中,如果
dev->p是NULL,则调用
device_private_init函数为
dev->p分配内存空间并进行初始化,该函数定义在drivers/base/core.c文件中,其内容如下
int device_private_init(struct device *dev) { dev->p = kzalloc(sizeof(*dev->p), GFP_KERNEL); if (!dev->p) return -ENOMEM; dev->p->device = dev; klist_init(&dev->p->klist_children, klist_children_get, klist_children_put); INIT_LIST_HEAD(&dev->p->deferred_probe); return 0; }
device_add中调用了
get_device_parent函数,该函数的作用是取得
dev->kobj.parent,即device对应的kobject的parent kobject。该函数定义在drivers/base/core.c文件中,其内容如下:
static struct kobject *get_device_parent(struct device *dev, struct device *parent) { if (dev->class) { //如果dev->class不为NULL,并且dev->parent为NULL, 这种情况下系统将为dev->kobj.parent建立一个虚拟上层对象“virtual”,如此,将dev对象加入系统将会在/sys/devices/virtual中产生一个新的目录/sys/devices/virtual/dev->init_name struct kobject *kobj = NULL; struct kobject *parent_kobj; struct kobject *k; #ifdef CONFIG_BLOCK /* block disks show up in /sys/block */ if (sysfs_deprecated && dev->class == &block_class) { if (parent && parent->class == &block_class) return &parent->kobj; return &block_class.p->subsys.kobj; } #endif /* * If we have no parent, we live in "virtual". * Class-devices with a non class-device as parent, live * in a "glue" directory to prevent namespace collisions. */ if (parent == NULL) parent_kobj = virtual_device_parent(dev); else if (parent->class && !dev->class->ns_type) return &parent->kobj; else parent_kobj = &parent->kobj; mutex_lock(&gdp_mutex); /* find our class-directory at the parent and reference it */ spin_lock(&dev->class->p->glue_dirs.list_lock); list_for_each_entry(k, &dev->class->p->glue_dirs.list, entry) if (k->parent == parent_kobj) { kobj = kobject_get(k); break; } spin_unlock(&dev->class->p->glue_dirs.list_lock); if (kobj) { mutex_unlock(&gdp_mutex); return kobj; } /* or create a new class-directory at the parent device */ k = class_dir_create_and_add(dev->class, parent_kobj); /* do not emit an uevent for this simple "glue" directory */ mutex_unlock(&gdp_mutex); return k; } /* subsystems can specify a default root directory for their devices */ if (!parent && dev->bus && dev->bus->dev_root) return &dev->bus->dev_root->kobj; if (parent) return &parent->kobj; return NULL; }
device_add函数中调用
device_create_file创建uevent属性文件。
dev_attr_uevent定义在drivers/base/core.c文件中,其内容如下:
static DEVICE_ATTR_RW(uevent);
device_attribute结构体定义在include/linux/device.h文件中,其内容如下:
/* interface for exporting device attributes */ struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); };
device_create_file定义在drivers/base/core.c文件中,其内容如下:
/** * device_create_file - create sysfs attribute file for device. * @dev: device. * @attr: device attribute descriptor. */ int device_create_file(struct device *dev, const struct device_attribute *attr) { int error = 0; if (dev) { WARN(((attr->attr.mode & S_IWUGO) && !attr->store), "Attribute %s: write permission without 'store'\n", attr->attr.name); WARN(((attr->attr.mode & S_IRUGO) && !attr->show), "Attribute %s: read permission without 'show'\n", attr->attr.name); error = sysfs_create_file(&dev->kobj, &attr->attr); } return error; }
device_add函数中如果设备的主设备号不为0,调用
device_create_file创建dev属性文件。
dev_attr定义在drivers/base/core.c文件中,其内容如下
static DEVICE_ATTR_RO(dev);
device_add函数中调用
device_create_sys_dev_entry(dev)函数,该函数定义在drivers/base/core.c文件中,其内容如下:
static int device_create_sys_dev_entry(struct device *dev) { struct kobject *kobj = device_to_dev_kobj(dev); int error = 0; char devt_str[15]; if (kobj) { format_dev_t(devt_str, dev->devt); error = sysfs_create_link(kobj, &dev->kobj, devt_str); } return error; }
其中调用
device_to_dev_kobj(dev),该函数定义在drivers/base/core.c文件中,其内容如下:
/** * device_to_dev_kobj - select a /sys/dev/ directory for the device * @dev: device * * By default we select char/ for new entries. Setting class->dev_obj * to NULL prevents an entry from being created. class->dev_kobj must * be set (or cleared) before any devices are registered to the class * otherwise device_create_sys_dev_entry() and * device_remove_sys_dev_entry() will disagree about the presence of * the link. */ static struct kobject *device_to_dev_kobj(struct device *dev) { struct kobject *kobj; if (dev->class) kobj = dev->class->dev_kobj; else kobj = sysfs_dev_char_kobj; return kobj; }
从该函数的注释可以看出,该函数的作用是返回device对应的/sys/dev/下的一个目录。该函数的返回值是一个kobject,一个kobject在sysfs系统中对应一个目录。如果
dev->class不为NULL,该函数返回
dev->class->dev_kobj;如果
dev->class为NULL,该函数返回
sysfs_dev_char_kobj,它对应/sys/dev/char目录。
device_create_sys_dev_entry调用
format_dev_t宏,该宏定义在include/linux/kdev_t.h文件中,其内容如下
#define format_dev_t(buffer, dev) \ ({ \ sprintf(buffer, "%u:%u", MAJOR(dev), MINOR(dev)); \ buffer; \ })
该宏的作用是把“主设备号:次设备号”字符串写入buffer变量中
device_create_sys_dev_entry调用
sysfs_create_link(kobj, &dev->kobj, devt_str)创建一个链接。该函数定义在fs/sysfs/symlink.c文件中,其内容如下:
/** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. * @target: object we're pointing to. * @name: name of the symlink. */ int sysfs_create_link(struct kobject *kobj, struct kobject *target, const char *name) { return sysfs_do_create_link(kobj, target, name, 1); }
由注释可以看出,第一个参数是我们将要创建的链接所在的目录,第二个参数是链接指向的源,第三个参数是链接的名字。
device_add函数中调用
bus_probe_device(dev)函数为新添加的device探测匹配的
device_driver。该函数定义在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;//取得device所依附的bus struct subsys_interface *sif; if (!bus) return; if (bus->p->drivers_autoprobe) // 如果bus->p->drivers_autoprobe值为1,调用device_initial_probe函数 device_initial_probe(dev); 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_initial_probe函数,该函数定义在drivers/base/dd.c文件中,其内容如下:
void device_initial_probe(struct device *dev) { __device_attach(dev, true); }
device_initial_probe函数继续调用
__device_attach,该函数定义在drivers/base/dd.c文件中,其内容如下:
static int __device_attach(struct device *dev, bool allow_async) { int ret = 0; device_lock(dev); if (dev->driver) { // dev->driver不为空, 使用device_bind_driver if (device_is_bound(dev)) { ret = 1; goto out_unlock; } ret = device_bind_driver(dev); if (ret == 0) ret = 1; else { dev->driver = NULL; ret = 0; } } else { struct device_attach_data data = { .dev = dev, .check_async = allow_async, .want_async = false, }; if (dev->parent) pm_runtime_get_sync(dev->parent); ret = bus_for_each_drv(dev->bus, NULL, &data, __device_attach_driver); if (!ret && allow_async && data.have_async) { /* * If we could not find appropriate driver * synchronously and we are allowed to do * async probes and there are drivers that * want to probe asynchronously, we'll * try them. */ dev_dbg(dev, "scheduling asynchronous probe\n"); get_device(dev); async_schedule(__device_attach_async_helper, dev); } else { pm_request_idle(dev); } if (dev->parent) pm_runtime_put(dev->parent); } out_unlock: device_unlock(dev); return ret; }
可以看到如果
dev->driver不为空,表明device已经与相应的
device_driver进行了绑定,这种情况下只需要调用
device_bind_driver函数在sysfs文件系统中建立device与其
device_driver的相互关联 。
__device_attach中调用了
bus_for_each_drv, 该函数定义在drivers/base/bus.c文件中,其内容如下:
/** * bus_for_each_drv - driver iterator * @bus: bus we're dealing with. * @start: driver to start iterating on. * @data: data to pass to the callback. * @fn: function to call for each driver. * * This is nearly identical to the device iterator above. * We iterate over each driver that belongs to @bus, and call * @fn for each. If @fn returns anything but 0, we break out * and return it. If @start is not NULL, we use it as the head * of the list. * * NOTE: we don't return the driver that returns a non-zero * value, nor do we leave the reference count incremented for that * driver. If the caller needs to know that info, it must set it * in the callback. It must also be sure to increment the refcount * so it doesn't disappear before returning to the caller. */ int bus_for_each_drv(struct bus_type *bus, struct device_driver *start, void *data, int (*fn)(struct device_driver *, void *)) { struct klist_iter i; struct device_driver *drv; int error = 0; if (!bus) return -EINVAL; klist_iter_init_node(&bus->p->klist_drivers, &i, start ? &start->p->knode_bus : NULL); while ((drv = next_driver(&i)) && !error) //循环依次遍历bus->p->klist_drivers中的所有device_driver error = fn(drv, data); klist_iter_exit(&i); return error; }
可以看到while循环依次遍历
bus->p->klist_drivers中的所有
device_driver,对于每个
device_driver,调用
fn(drv, data)函数,看该
device_driver是否支持该device。如果匹配成功,fn函数返回非0值。
参数传递过来的fn函数是
__device_attach_driver,该函数定义在drivers/base/dd.c文件中,其内容如下:
static int __device_attach_driver(struct device_driver *drv, void *_data) { struct device_attach_data *data = _data; struct device *dev = data->dev; bool async_allowed; int ret; /* * Check if device has already been claimed. This may * happen with driver loading, device discovery/registration, * and deferred probe processing happens all at once with * multiple threads. */ if (dev->driver) return -EBUSY; ret = driver_match_device(drv, dev); //检查device_driver是否支持该device if (ret == 0) { /* no match */ return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); driver_deferred_probe_add(dev); } else if (ret < 0) { dev_dbg(dev, "Bus failed to match device: %d", ret); return ret; } /* ret > 0 means positive match */ async_allowed = driver_allows_async_probing(drv); if (async_allowed) data->have_async = true; if (data->check_async && async_allowed != data->want_async) return 0; return driver_probe_device(drv, dev); }
其中调用
driver_match_device(drv, dev)函数,检查
device_driver是否支持该device,该函数定义在drivers/base/base.h文件中,其内容如下:
static inline int driver_match_device(struct device_driver *drv, struct device *dev) { //如果定义了drv->bus->match函数,则调用该函数,否则返回1。 return drv->bus->match ? drv->bus->match(dev, drv) : 1; }
__device_attach_driver函数最后还调用了
driver_probe_device, 该函数定义在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. * * If the device has a parent, runtime-resume the parent before driver probing. */ 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\n", drv->bus->name, __func__, dev_name(dev), drv->name); if (dev->parent) pm_runtime_get_sync(dev->parent); pm_runtime_barrier(dev); ret = really_probe(dev, drv); pm_request_idle(dev); if (dev->parent) pm_runtime_put(dev->parent); return ret; }
其中调用
device_is_registered函数判断device是否已经在sysfs系统中注册过,如果还没有注册过,则返回ENODEV,退出。该函数定义在include/linux/device.h文件中,其内容如下:
static inline int device_is_registered(struct device *dev) { return dev->kobj.state_in_sysfs; }
调用
really_probe(dev, drv)函数,该函数定义在drivers/base/dd.c文件中,其内容如下:
static int really_probe(struct device *dev, struct device_driver *drv) { int ret = -EPROBE_DEFER; int local_trigger_count = atomic_read(&deferred_trigger_count); if (defer_all_probes) { /* * Value of defer_all_probes can be set only by * device_defer_all_probes_enable() which, in turn, will call * wait_for_device_probe() right after that to avoid any races. */ dev_dbg(dev, "Driver %s force probe deferral\n", drv->name); driver_deferred_probe_add(dev); return ret; } atomic_inc(&probe_count); pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); dev->driver = drv; /* If using pinctrl, bind pins now before probing */ ret = pinctrl_bind_pins(dev); if (ret) goto pinctrl_bind_failed; if (driver_sysfs_add(dev)) { printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n", __func__, dev_name(dev)); goto probe_failed; } if (dev->pm_domain && dev->pm_domain->activate) { ret = dev->pm_domain->activate(dev); if (ret) goto probe_failed; } /* * Ensure devices are listed in devices_kset in correct order * It's important to move Dev to the end of devices_kset before * calling .probe, because it could be recursive and parent Dev * should always go first */ devices_kset_move_last(dev); 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; } pinctrl_init_done(dev); if (dev->pm_domain && dev->pm_domain->sync) dev->pm_domain->sync(dev); driver_bound(dev); ret = 1; pr_debug("bus: '%s': %s: bound device %s to driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); goto done; probe_failed: if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); pinctrl_bind_failed: devres_release_all(dev); driver_sysfs_remove(dev); dev->driver = NULL; dev_set_drvdata(dev, NULL); if (dev->pm_domain && dev->pm_domain->dismiss) dev->pm_domain->dismiss(dev); pm_runtime_reinit(dev); switch (ret) { case -EPROBE_DEFER: /* Driver requested deferred probing */ dev_dbg(dev, "Driver %s requests probe deferral\n", drv->name); driver_deferred_probe_add(dev); /* Did a trigger occur while probing? Need to re-trigger if yes */ if (local_trigger_count != atomic_read(&deferred_trigger_count)) driver_deferred_probe_trigger(); break; case -ENODEV: case -ENXIO: pr_debug("%s: probe of %s rejects match %d\n", drv->name, dev_name(dev), ret); break; default: /* driver matched but the probe failed */ printk(KERN_WARNING "%s: probe of %s failed with error %d\n", 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; }
可以看到如果定义了
dev->bus->probe函数,则调用该函数;如果没有定义
dev->bus->probe函数,但是定义了
drv->probe函数,则调用
drv->probe函数。这里,我们一般写Linux驱动程序时都要实现的probe函数就会被调用了。
really_probe中还调用
driver_bound(dev)函数,该函数定义在drivers/base/dd.c文件中,其内容如下:
static void driver_bound(struct device *dev) { if (device_is_bound(dev)) { printk(KERN_WARNING "%s: device %s already bound\n", __func__, kobject_name(&dev->kobj)); return; } pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name, __func__, dev_name(dev)); klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); device_pm_check_callbacks(dev); /* * 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); }
可以看到调用
klist_add_tail函数将device加入到
device_driver的
driver->p->klist_devices链表中。
将以上device的注册过程倒序捋顺:
driver_bound->
really_probe->
driver_probe_device->
__device_attach_driver->
bus_for_each_drv->
__device_attach->
device_initial_probe->
bus_probe_device->
device_add->
device_register
这样,
device_register函数就分析完了,也就清楚了device的注册过程。
相关文章推荐
- 设备驱动中的device_driver(kernel-4.7)
- 设备驱动中的platform(kernel-4.7)
- 设备驱动中的mutex(kernel-4.7)
- 设备驱动中的pinctrl(kernel-4.7)
- 嵌入式 linux下kernel代码中设备驱动模型之device-driver
- 设备驱动中的inode(kernel-4.7)
- 设备驱动中的class(kernel-4.7)
- 设备驱动中的mmc(kernel-4.7)
- 设备驱动中的spin_lock(kernel-4.7)
- 设备驱动中的misc(kernel-4.7)
- 设备驱动中的iomem(kernel-4.7)
- 设备驱动中的bus(kernel-4.7)
- 设备驱动中的pci(kernel-4.7)
- 设备驱动中的tty(kernel-4.7)
- 设备驱动中的i2c(kernel-4.7)
- 设备驱动中的kobject(kernel-4.7)
- 设备驱动中的gadget(kernel-4.7)
- 嵌入式 linux下kernel代码中设备驱动模型之device
- 设备驱动中的kset(kernel-4.7)
- 设备驱动中cdev(kernel-4.7)