Linux设备驱动和设备匹配过程

  • 1. 设备驱动匹配简述
  • 2. 重点结构体介绍
    • 2.1 `struct device`
    • 2.2 `struct platform_device`
    • 2.3 `struct platform_driver`
    • 2.4 `struct device_driver`
  • 3. `device`端发起匹配:
    • 3.1 流程图
    • 3.2 `start_kernel`时候解析设备树
      • 3.2.1 `start_kernel`
      • 3.2.2 `setup_arch`
      • 3.2.3 `arch_mem_init`
      • 3.2.4 `plat_mem_setup`
      • 3.2.5 `__dt_setup_arch`
      • 3.2.6 `early_init_dt_scan`
      • 3.2.7 `early_init_dt_scan_nodes`
      • 3.2.8 `device_tree_init`
      • 3.2.9 `unflatten_and_copy_device_tree`
      • 3.2.10 `unflatten_device_tree`
      • 3.2.11 `__unflatten_device_tree`
      • 3.2.12 `unflatten_dt_node`
    • 3.3 设备树被解析后的匹配
      • 3.3.1 流程图
      • 3.3.2 `of_platform_default_populate_init`
      • 3.3.3 `of_platform_default_populate`
      • 3.3.4 `of_platform_populate`
      • 3.3.5 `of_platform_bus_create`
      • 3.3.6 `of_platform_device_create_pdata`
      • 3.3.7 `of_device_add`
      • 3.3.8 `device_add`
      • 3.3.9 `bus_probe_device`
      • 3.3.10 `device_initial_probe`
      • 3.3.11 `__device_attach`
      • 3.3.12 `__device_attach_driver`
      • 3.3.13 `driver_match_device`
    • 3.4 SPI子系统
      • 3.4.1 `devm_spi_register_master`
      • 3.4.2 `spi_register_master`
      • 3.4.3 `of_register_spi_devices`
      • 3.4.4 `of_register_spi_device`
  • 4. `driver`端发起匹配:
    • 4.1 流程图
    • 4.2 驱动总线注册`platform_driver_register`
    • 4.3 驱动的总线中的类型指向: `platform_bus_type`
      • 4.3.1 驱动的总线类型中用于匹配的`platform_match`
    • 4.4 注册设备:`driver_register`
    • 4.5 usb总线上添加hub驱动:`bus_add_driver() `
    • 4.6 驱动绑定`driver_attach()`
      • 4.6.1 `driver_attach`-> `__driver_attach`
      • 4.6.2 `__driver_attach`->`driver_match_device`
    • 4.7 回到 4.3.1 中的`platform_match`
      • 4.7.1 `of_driver_match_device`
      • 4.7.2 `platform_match_id`
  • 参考

在写驱动时我们都会用到一些注册函数比如:platform_driver_registerspi_register_driveri2c_add_driver,接下来分析内核是怎样将驱动和设备匹配起来并且调用prob函数,以platform_driver为例。

1. 设备驱动匹配简述

linux驱动模型是分成三个部分的,设备(结构体device),驱动(结构体device_driver),总线(结构体bus_type),这个模型管理着linux的驱动,让他们在开机之后能正常驱动硬件有序的干活。这一切需要从一棵树讲起,我说的这棵树就是我们常说的dts(device tree source),搞懂搞透设备树是我们可以正确开发硬件驱动的前提,因为设备树是配置板级信息的地方。

我们所说的设备树起源于openfirmware,所以linux关于设备树的接口都是以of开头的,存放这些核心API的源代码路径是drivers/of/

首先,我们需要搞清楚一个流程,那就是driver是如何找到硬件的,驱动模型告诉我们的是device主动去找driver或者driver主动去找device的,bus_type是两者的中间纽带,而devicedriverbus_type的通信是通过bus_type提供的注册接口来注册信息,同时只要两者进行注册后bus_type会马上触发匹配流程进行匹配。下来我们从device/driver两个角度结合代码分别说明这个匹配流程。

2. 重点结构体介绍

2.1 struct 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.* @driver_data: Private pointer for driver specific info.* @links:  Links to suppliers and consumers of this device.* @power:   For device power management.*       See Documentation/driver-api/pm/devices.rst 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/driver-api/pinctl.rst 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_ops:    DMA mapping operations for this device.* @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.* @bus_dma_mask: Mask of an upstream bridge or bus which imposes a smaller DMA*        limit than the device itself supports.* @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.* @iommu_fwspec: IOMMU-specific properties supplied by firmware.** @offline_disabled: If set, the device is permanently online.* @offline:    Set after successful invocation of bus type's .offline().* @of_node_reused: Set if the device-tree node is shared with an ancestor*              device.** 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 thisdevice */void       *platform_data;  /* Platform specific data, devicecore doesn't touch it */void     *driver_data;    /* Driver data, set and get withdev_set/get_drvdata */struct dev_links_info links;struct dev_pm_info    power;struct dev_pm_domain  *pm_domain;#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAINstruct irq_domain *msi_domain;
#endif
#ifdef CONFIG_PINCTRLstruct dev_pin_info    *pins;
#endif
#ifdef CONFIG_GENERIC_MSI_IRQstruct list_head   msi_list;
#endif#ifdef CONFIG_NUMAint      numa_node;   /* NUMA node this device is close to */
#endifconst struct dma_map_ops *dma_ops;u64     *dma_mask;   /* dma mask (if dma'able device) */u64       coherent_dma_mask;/* Like dma_mask, but foralloc_coherent mappings asnot all hardware supports64 bit addresses for consistentallocations such descriptors. */u64      bus_dma_mask;    /* upstream dma_mask constraint */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 memoverride */
#ifdef CONFIG_DMA_CMAstruct cma *cma_area;       /* contiguous memory area for dmaallocations */
#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;struct class        *class;const struct attribute_group **groups; /* optional groups */void (*release)(struct device *dev);struct iommu_group  *iommu_group;struct iommu_fwspec    *iommu_fwspec;bool           offline_disabled:1;bool            offline:1;bool         of_node_reused:1;
};

2.2 struct platform_device

include/linux/platform_device.h

struct platform_device {const char    *name;int     id;bool      id_auto;struct device   dev;u32      num_resources;struct resource   *resource;const struct platform_device_id    *id_entry;char *driver_override; /* Driver name to force a match *//* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdata archdata;
};

2.3 struct platform_driver

struct platform_driver {**int (*probe)(struct platform_device *);**  //驱动和设备匹配成功之后调用int (*remove)(struct platform_device *);  //卸载驱动时调用void (*shutdown)(struct platform_device *);  int (*suspend)(struct platform_device *, pm_message_t state);int (*resume)(struct platform_device *);**struct device_driver driver;**  **const struct platform_device_id *id_table;**bool prevent_deferred_probe;
};

2.4 struct device_driver

struct device_driver {const char      *name;  //与设备匹配时会用到的驱动名,一样就匹配成struct bus_type     *bus;  //在注册函数platform_driver_register中指定struct module       *owner;const char      *mod_name;   /* used for built-in modules */bool suppress_bind_attrs;  /* disables bind/unbind via sysfs */enum probe_type probe_type;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;
};

3. device端发起匹配:

3.1 流程图

大致流程图为:

3.2 start_kernel时候解析设备树

在linux启动并初始化的地方,会有一个专门解析设备树的地方,就在著名的start_kernel这个函数里

3.2.1 start_kernel

asmlinkage __visible void __init start_kernel(void)
{char *command_line;char *after_dashes;......setup_arch(&command_line);//可以看到,这个地方开 始解析设备树....../* Do the rest non-__init'ed, we're now alive */rest_init();
}

3.2.2 setup_arch

void __init setup_arch(char **cmdline_p)
{......arch_mem_init(cmdline_p);resource_init();plat_smp_setup();prefill_possible_map();cpu_cache_init();paging_init();
}

3.2.3 arch_mem_init

static void __init arch_mem_init(char **cmdline_p)
{/* call board setup routine */plat_mem_setup();//第一步是把设备树三个重要的节点解析出来device_tree_init();//第二步是把所有子节点解析出来
}

3.2.4 plat_mem_setup

void __init plat_mem_setup(void)
{...if (loongson_fdt_blob)__dt_setup_arch(loongson_fdt_blob);//第一步是把设备树三个重要的节点解析出来
}

3.2.5 __dt_setup_arch

void __init __dt_setup_arch(void *bph)
{if (!early_init_dt_scan(bph))return;mips_set_machine_name(of_flat_dt_get_machine_name());
}

3.2.6 early_init_dt_scan

bool __init early_init_dt_scan(void *params)
{...... status = early_init_dt_verify(params);......early_init_dt_scan_nodes();
}

3.2.7 early_init_dt_scan_nodes

void __init early_init_dt_scan_nodes(void)
{/* Retrieve various information from the /chosen node */of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);/* Initialize {size,address}-cells info */of_scan_flat_dt(early_init_dt_scan_root, NULL);/* Setup memory, calling early_init_dt_add_memory_arch */of_scan_flat_dt(early_init_dt_scan_memory, NULL);
}

上面流程主要是第一次扫描节点并解析出三个对于系统来说非常重要的三个节点
接下来是重头戏,解析出root节点下所有的子设备节点,也是我们需要重点了解和掌握的。

3.2.8 device_tree_init

void __init device_tree_init(void)
{......if (early_init_dt_verify(initial_boot_params))unflatten_and_copy_device_tree();
}

3.2.9 unflatten_and_copy_device_tree

void __init unflatten_and_copy_device_tree(void)
{......unflatten_device_tree();
}

3.2.10 unflatten_device_tree

void __init unflatten_device_tree(void)
{__unflatten_device_tree(initial_boot_params, NULL, &of_root,early_init_dt_alloc_memory_arch, false);......
}

3.2.11 __unflatten_device_tree

static void __unflatten_device_tree(const void *blob,struct device_node **mynodes,void * (*dt_alloc)(u64 size, u64 align))
{unsigned long size;int start;void *mem;pr_debug(" -> unflatten_device_tree()\n");if (!blob) {pr_debug("No device tree pointer\n");return;}pr_debug("Unflattening device tree:\n");pr_debug("magic: %08x\n", fdt_magic(blob));pr_debug("size: %08x\n", fdt_totalsize(blob));pr_debug("version: %08x\n", fdt_version(blob));if (fdt_check_header(blob)) {pr_err("Invalid device tree blob header\n");return;}/* First pass, scan for size */start = 0;size = (unsigned long)unflatten_dt_node(blob, NULL, &start, NULL, NULL, 0, true);size = ALIGN(size, 4);pr_debug("  size is %lx, allocating...\n", size);/* Allocate memory for the expanded device tree */mem = dt_alloc(size + 4, __alignof__(struct device_node));memset(mem, 0, size);*(__be32 *)(mem + size) = cpu_to_be32(0xdeadbeef);pr_debug("  unflattening %p...\n", mem);/* Second pass, do actual unflattening */start = 0;unflatten_dt_node(blob, mem, &start, NULL, mynodes, 0, false);if (be32_to_cpup(mem + size) != 0xdeadbeef)pr_warning("End of tree marker overwritten: %08x\n",be32_to_cpup(mem + size));pr_debug(" <- unflatten_device_tree()\n");
}

3.2.12 unflatten_dt_node

static void * unflatten_dt_node(const void *blob,void *mem,int *poffset,struct device_node *dad,struct device_node **nodepp,unsigned long fpsize,bool dryrun)
{const __be32 *p;struct device_node *np;struct property *pp, **prev_pp = NULL;const char *pathp;unsigned int l, allocl;static int depth;int old_depth;int offset;int has_name = 0;int new_format = 0;/* 获取node节点的name指针到pathp中 */pathp = fdt_get_name(blob, *poffset, &l);if (!pathp)return mem;allocl = ++l;/* version 0x10 has a more compact unit name here instead of the full* path. we accumulate the full path size using "fpsize", we'll rebuild* it later. We detect this because the first character of the name is* not '/'.*/if ((*pathp) != '/') {new_format = 1;if (fpsize == 0) {/* root node: special case. fpsize accounts for path* plus terminating zero. root node only has '/', so* fpsize should be 2, but we want to avoid the first* level nodes to have two '/' so we use fpsize 1 here*/fpsize = 1;allocl = 2;l = 1;pathp = "";} else {/* account for '/' and path size minus terminal 0* already in 'l'*/fpsize += l;allocl = fpsize;}}/* 分配struct device_node内存,包括路径全称大小 */np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,__alignof__(struct device_node));if (!dryrun) {char *fn;of_node_init(np);/* 填充full_name,full_name指向该node节点的全路径名称字符串 */np->full_name = fn = ((char *)np) + sizeof(*np);if (new_format) {/* rebuild full path for new format */if (dad && dad->parent) {strcpy(fn, dad->full_name);fn += strlen(fn);}*(fn++) = '/';}memcpy(fn, pathp, l);/* 节点挂接到相应的父节点、子节点和姊妹节点 */prev_pp = &np->properties;if (dad != NULL) {np->parent = dad;np->sibling = dad->child;dad->child = np;}}/* 处理该node节点下面所有的property */for (offset = fdt_first_property_offset(blob, *poffset);(offset >= 0);(offset = fdt_next_property_offset(blob, offset))) {const char *pname;u32 sz;if (!(p = fdt_getprop_by_offset(blob, offset, &pname, &sz))) {offset = -FDT_ERR_INTERNAL;break;}if (pname == NULL) {pr_info("Can't find property name in list !\n");break;}if (strcmp(pname, "name") == 0)has_name = 1;pp = unflatten_dt_alloc(&mem, sizeof(struct property),__alignof__(struct property));if (!dryrun) {/* We accept flattened tree phandles either in* ePAPR-style "phandle" properties, or the* legacy "linux,phandle" properties.  If both* appear and have different values, things* will get weird.  Don't do that. *//* 处理phandle,得到phandle值 */if ((strcmp(pname, "phandle") == 0) ||(strcmp(pname, "linux,phandle") == 0)) {if (np->phandle == 0)np->phandle = be32_to_cpup(p);}/* And we process the "ibm,phandle" property* used in pSeries dynamic device tree* stuff */if (strcmp(pname, "ibm,phandle") == 0)np->phandle = be32_to_cpup(p);pp->name = (char *)pname;pp->length = sz;pp->value = (__be32 *)p;*prev_pp = pp;prev_pp = &pp->next;}}/* with version 0x10 we may not have the name property, recreate* it here from the unit name if absent*//* 为每个node节点添加一个name的属性 */if (!has_name) {const char *p1 = pathp, *ps = pathp, *pa = NULL;int sz;/* 属性name的value值为node节点的名称,取“/”和“@”之间的子串,设备和驱动的别名匹配用的就是这个地方的name */while (*p1) {if ((*p1) == '@')pa = p1;if ((*p1) == '/')ps = p1 + 1;p1++;}if (pa < ps)pa = p1;sz = (pa - ps) + 1;pp = unflatten_dt_alloc(&mem, sizeof(struct property) + sz,__alignof__(struct property));if (!dryrun) {pp->name = "name";pp->length = sz;pp->value = pp + 1;*prev_pp = pp;prev_pp = &pp->next;memcpy(pp->value, ps, sz - 1);((char *)pp->value)[sz - 1] = 0;}}/* 填充device_node结构体中的name和type成员 */if (!dryrun) {*prev_pp = NULL;np->name = of_get_property(np, "name", NULL);np->type = of_get_property(np, "device_type", NULL);if (!np->name)np->name = "<NULL>";if (!np->type)np->type = "<NULL>";}old_depth = depth;*poffset = fdt_next_node(blob, *poffset, &depth);if (depth < 0)depth = 0;/* 递归调用node节点下面的子节点 */while (*poffset > 0 && depth > old_depth)mem = unflatten_dt_node(blob, mem, poffset, np, NULL,fpsize, dryrun);if (*poffset < 0 && *poffset != -FDT_ERR_NOTFOUND)pr_err("unflatten: error %d processing FDT\n", *poffset);/** Reverse the child list. Some drivers assumes node order matches .dts* node order*/if (!dryrun && np->child) {struct device_node *child = np->child;np->child = NULL;while (child) {struct device_node *next = child->sibling;child->sibling = np->child;np->child = child;child = next;}}if (nodepp)*nodepp = np;return mem;
}

至此,内核已经全部把设备树解析出来并存放在device_node这个结构体指针当中,观察这个结构体的组成,我们可以看出来这个结构体是一个双向链表,因为一旦得到一个设备节点,我们就可以从他的parentchild中得到这个设备树中的每一个设备节点的信息。

3.3 设备树被解析后的匹配

3.3.1 流程图

3.3.2 of_platform_default_populate_init

drivers/of/platform.c文件中有一个函数被编译进入内核,成为内核img的一部分,这个函数就是为了在设备树被解析出来之后进行驱动匹配的。

static int __init of_platform_default_populate_init(void)
{....../* Populate everything else. */of_platform_default_populate(NULL, NULL, NULL);......
}
arch_initcall_sync(of_platform_default_populate_init);

3.3.3 of_platform_default_populate

int of_platform_default_populate(struct device_node *root,const struct of_dev_auxdata *lookup,struct device *parent)
{return of_platform_populate(root, of_default_bus_match_table, lookup,parent);
}
EXPORT_SYMBOL_GPL(of_platform_default_populate);

3.3.4 of_platform_populate

/*** of_platform_populate() - Populate platform_devices from device tree data* @root: parent of the first level to probe or NULL for the root of the tree* @matches: match table, NULL to use the default* @lookup: auxdata table for matching id and platform_data with device nodes* @parent: parent to hook devices from, NULL for toplevel** Similar to of_platform_bus_probe(), this function walks the device tree* and creates devices from nodes.  It differs in that it follows the modern* convention of requiring all device nodes to have a 'compatible' property,* and it is suitable for creating devices which are children of the root* node (of_platform_bus_probe will only create children of the root which* are selected by the @matches argument).** New board support should be using this function instead of* of_platform_bus_probe().** Returns 0 on success, < 0 on failure.*/
int of_platform_populate(struct device_node *root,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent)
{struct device_node *child;int rc = 0;root = root ? of_node_get(root) : of_find_node_by_path("/");if (!root)return -EINVAL;pr_debug("%s()\n", __func__);pr_debug(" starting at: %pOF\n", root);//遍历根节点下的每一个子设备节点并把device_node的信息填充到创建platform_device中for_each_child_of_node(root, child) {rc = of_platform_bus_create(child, matches, lookup, parent, true);if (rc) {of_node_put(child);break;}}of_node_set_flag(root, OF_POPULATED_BUS);of_node_put(root);return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);

3.3.5 of_platform_bus_create

/*** of_platform_bus_create() - Create a device for a node and its children.* @bus: device node of the bus to instantiate* @matches: match table for bus nodes* @lookup: auxdata table for matching id and platform_data with device nodes* @parent: parent for new device, or NULL for top level.* @strict: require compatible property** Creates a platform_device for the provided device_node, and optionally* recursively create devices for all the child nodes.*/
static int of_platform_bus_create(struct device_node *bus,const struct of_device_id *matches,const struct of_dev_auxdata *lookup,struct device *parent, bool strict)
{const struct of_dev_auxdata *auxdata;struct device_node *child;struct platform_device *dev;const char *bus_id = NULL;void *platform_data = NULL;int rc = 0;/* 只有包含"compatible"属性的node节点才会生成相应的platform_device结构体 *//* Make sure it has a compatible property */if (strict && (!of_get_property(bus, "compatible", NULL))) {pr_debug("%s() - skipping %pOF, no compatible prop\n",__func__, bus);return 0;}/* Skip nodes for which we don't want to create devices */if (unlikely(of_match_node(of_skipped_node_table, bus))) {pr_debug("%s() - skipping %pOF node\n", __func__, bus);return 0;}if (of_node_check_flag(bus, OF_POPULATED_BUS)) {pr_debug("%s() - skipping %pOF, already populated\n",__func__, bus);return 0;}auxdata = of_dev_lookup(lookup, bus);if (auxdata) {bus_id = auxdata->name;platform_data = auxdata->platform_data;}if (of_device_is_compatible(bus, "arm,primecell")) {/** Don't return an error here to keep compatibility with older* device tree files.*/of_amba_device_create(bus, bus_id, platform_data, parent);return 0;}dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);if (!dev || !of_match_node(matches, bus))return 0;for_each_child_of_node(bus, child) {pr_debug("   create child: %pOF\n", child);rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);if (rc) {of_node_put(child);break;}}of_node_set_flag(bus, OF_POPULATED_BUS);return rc;
}

3.3.6 of_platform_device_create_pdata

/*** of_platform_device_create_pdata - Alloc, initialize and register an of_device* @np: pointer to node to create device for* @bus_id: name to assign device* @platform_data: pointer to populate platform_data pointer with* @parent: Linux device model parent device.** Returns pointer to created platform device, or NULL if a device was not* registered.  Unavailable devices will not get registered.*/
static struct platform_device *of_platform_device_create_pdata(struct device_node *np,const char *bus_id,void *platform_data,struct device *parent)
{struct platform_device *dev;/* * 针对节点下面得到status = "ok" 或者status = "okay"或者不存在status属性的* 节点分配内存并填充platform_device结构体*/if (!of_device_is_available(np) ||of_node_test_and_set_flag(np, OF_POPULATED))return NULL;dev = of_device_alloc(np, bus_id, parent);if (!dev)goto err_clear_flag;dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);if (!dev->dev.dma_mask)dev->dev.dma_mask = &dev->dev.coherent_dma_mask;dev->dev.bus = &platform_bus_type;dev->dev.platform_data = platform_data;of_msi_configure(&dev->dev, dev->dev.of_node);//of_device_add函数就是把platform_device用平台总线去匹配驱动了if (of_device_add(dev) != 0) {platform_device_put(dev);goto err_clear_flag;}return dev;err_clear_flag:of_node_clear_flag(np, OF_POPULATED);return NULL;
}

3.3.7 of_device_add

int of_device_add(struct platform_device *ofdev)
{BUG_ON(ofdev->dev.of_node == NULL);/* name and id have to be set so that the platform bus doesn't get* confused on matching */ofdev->name = dev_name(&ofdev->dev);ofdev->id = PLATFORM_DEVID_NONE;/** If this device has not binding numa node in devicetree, that is* of_node_to_nid returns NUMA_NO_NODE. device_add will assume that this* device is on the same node as the parent.*/set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));return device_add(&ofdev->dev);
}

3.3.8 device_add

/*** 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;struct kobject *kobj;struct class_interface *class_intf;int error = -EINVAL;struct kobject *glue_dir = NULL;dev = get_device(dev);if (!dev)goto done;if (!dev->p) {error = device_private_init(dev);if (error)goto done;}/** for statically allocated devices, which should all be converted* some day, we need to initialize the name. We prevent reading back* the name, and force the use of dev_name()*/if (dev->init_name) {dev_set_name(dev, "%s", dev->init_name);dev->init_name = NULL;}/* subsystems can specify simple device enumeration */if (!dev_name(dev) && dev->bus && dev->bus->dev_name)dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);if (!dev_name(dev)) {error = -EINVAL;goto name_error;}pr_debug("device: '%s': %s\n", dev_name(dev), __func__);parent = get_device(dev->parent);kobj = get_device_parent(dev, parent);if (IS_ERR(kobj)) {error = PTR_ERR(kobj);goto parent_error;}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 */error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);if (error) {glue_dir = get_glue_dir(dev);goto Error;}/* notify platform of device entry */if (platform_notify)platform_notify(dev);error = device_create_file(dev, &dev_attr_uevent);if (error)goto attrError;error = device_add_class_symlinks(dev);if (error)goto SymlinkError;error = device_add_attrs(dev);if (error)goto AttrsError;error = bus_add_device(dev);if (error)goto BusError;error = dpm_sysfs_add(dev);if (error)goto DPMError;device_pm_add(dev);if (MAJOR(dev->devt)) {error = device_create_file(dev, &dev_attr_dev);if (error)goto DevAttrError;error = device_create_sys_dev_entry(dev);if (error)goto SysEntryError;devtmpfs_create_node(dev);}/* Notify clients of device addition.  This call must come* after dpm_sysfs_add() and before kobject_uevent().*/if (dev->bus)blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);kobject_uevent(&dev->kobj, KOBJ_ADD);bus_probe_device(dev);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);glue_dir = get_glue_dir(dev);kobject_del(&dev->kobj);Error:cleanup_glue_dir(dev, glue_dir);
parent_error:put_device(parent);
name_error:kfree(dev->p);dev->p = NULL;goto done;
}
EXPORT_SYMBOL_GPL(device_add);

3.3.9 bus_probe_device

/*** 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;if (!bus)return;if (bus->p->drivers_autoprobe)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);
}

3.3.10 device_initial_probe

void device_initial_probe(struct device *dev)
{__device_attach(dev, true);
}

3.3.11 __device_attach

static int __device_attach(struct device *dev, bool allow_async)
{int ret = 0;device_lock(dev);if (dev->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);//遍历总线上所有的platform_driver并调用总线的match函数触发匹配的动作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;
}

3.3.12 __device_attach_driver

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;ret = driver_match_device(drv, dev);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;匹配成功调用platform_driver的probe函数进行硬件的初始化动作return driver_probe_device(drv, dev);
}

3.3.13 driver_match_device

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

至此我们从设备端角度出发匹配驱动的流程梳理了一遍,但是对于I2C和SPI等外设总线从设备端出发匹配流程有一定疑问,所有设备都变成了platform_device了。其实我们要考虑清楚总线的本质,其实他们都是不存在的,是linux为了管理众多设备虚拟出来的概念,根本不存在。那为什么不用一个platform来搞定所有驱动,还单独需要I2C和SPI这些总线? 那是因为这两种总线有着特别强烈的个性色彩,又特别常用,所以linux单独拿出来并定义了属于他们的自己且有特色的结构体,好让我们用起来更方便。下面让我们从他们自己的角度来分析他们是如何和device_node结构体指针发生关联的。

首先我们说一下SPI。

3.4 SPI子系统

SPI和I2C子系统一样都是主从机分离的思想实现的,目的为了解耦,让每个模块的职责更独立,主机负责形成通讯波形,这一块一般是由平台厂商自行实现,子系统核心层作为连接枢纽为主从机注册提供通用接口,从机实现控制硬件,板级细节逻辑由设备树组织展开。SPI主机驱动是构建并实现一个spi_master结构体,并把它注册进SPI核心层,本质是一个platform_driver,所以当他的probe函数被调用的时候,必然会传进来一个platform_device指针,platform_device里面的device结构体指针会包含一个device_node指针,这个指针就是spi主机设备包含设备节点里面的信息。所以接下来分析注册spi_master的时候是如何寻找到自己的子设备(挂在主机节点下的从设备节点)并为他们构建spi_device,然后和spi总线上的spi_driver进行匹配的。

3.4.1 devm_spi_register_master

int devm_spi_register_master(struct device *dev, struct spi_master *master)
{struct spi_master **ptr;int ret;ptr = devres_alloc(devm_spi_unregister, sizeof(*ptr), GFP_KERNEL);if (!ptr)return -ENOMEM;ret = spi_register_master(master);if (!ret) {*ptr = master;devres_add(dev, ptr);} else {devres_free(ptr);}return ret;
}
EXPORT_SYMBOL_GPL(devm_spi_register_master);

3.4.2 spi_register_master

int spi_register_master(struct spi_master *master)
{static atomic_t        dyn_bus_id = ATOMIC_INIT((1<<15) - 1);struct device     *dev = master->dev.parent;struct boardinfo    *bi;int           status = -ENODEV;int          dynamic = 0;if (!dev)return -ENODEV;status = of_spi_register_master(master);if (status)return status;/* even if it's just one always-selected device, there must* be at least one chipselect*/if (master->num_chipselect == 0)return -EINVAL;if ((master->bus_num < 0) && master->dev.of_node)master->bus_num = of_alias_get_id(master->dev.of_node, "spi");/* convention:  dynamically assigned bus IDs count down from the max */if (master->bus_num < 0) {/* FIXME switch to an IDR based scheme, something like* I2C now uses, so we can't run out of "dynamic" IDs*/master->bus_num = atomic_dec_return(&dyn_bus_id);dynamic = 1;}INIT_LIST_HEAD(&master->queue);spin_lock_init(&master->queue_lock);spin_lock_init(&master->bus_lock_spinlock);mutex_init(&master->bus_lock_mutex);master->bus_lock_flag = 0;init_completion(&master->xfer_completion);if (!master->max_dma_len)master->max_dma_len = INT_MAX;/* register the device, then userspace will see it.* registration fails if the bus ID is in use.*/dev_set_name(&master->dev, "spi%u", master->bus_num);status = device_add(&master->dev);if (status < 0)goto done;dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),dynamic ? " (dynamic)" : "");/* If we're using a queued driver, start the queue */if (master->transfer)dev_info(dev, "master is unqueued, this is deprecated\n");else {status = spi_master_initialize_queue(master);if (status) {device_del(&master->dev);goto done;}}/* add statistics */spin_lock_init(&master->statistics.lock);mutex_lock(&board_lock);list_add_tail(&master->list, &spi_master_list);list_for_each_entry(bi, &board_list, list)spi_match_master_to_boardinfo(master, &bi->board_info);mutex_unlock(&board_lock);/* Register devices from the device tree and ACPI */of_register_spi_devices(master);acpi_register_spi_devices(master);
done:return status;
}

3.4.3 of_register_spi_devices

static void of_register_spi_devices(struct spi_master *master)
{struct spi_device *spi;struct device_node *nc;if (!master->dev.of_node)return;//遍历spi主机节点下的每一个子节点,并为他们注册spi_devicefor_each_available_child_of_node(master->dev.of_node, nc) {spi = of_register_spi_device(master, nc);if (IS_ERR(spi))dev_warn(&master->dev, "Failed to create SPI device for %s\n",nc->full_name);}
}

3.4.4 of_register_spi_device

of_register_spi_device(struct spi_master *master, struct device_node *nc)
{struct spi_device *spi;int rc;u32 value;/* Alloc an spi_device */spi = spi_alloc_device(master);if (!spi) {dev_err(&master->dev, "spi_device alloc error for %s\n",nc->full_name);rc = -ENOMEM;goto err_out;}/* Select device driver */rc = of_modalias_node(nc, spi->modalias,sizeof(spi->modalias));if (rc < 0) {dev_err(&master->dev, "cannot find modalias for %s\n",nc->full_name);goto err_out;}/* Device address */rc = of_property_read_u32(nc, "reg", &value);if (rc) {dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",nc->full_name, rc);goto err_out;}spi->chip_select = value;/* Mode (clock phase/polarity/etc.) */if (of_find_property(nc, "spi-cpha", NULL))spi->mode |= SPI_CPHA;if (of_find_property(nc, "spi-cpol", NULL))spi->mode |= SPI_CPOL;if (of_find_property(nc, "spi-cs-high", NULL))spi->mode |= SPI_CS_HIGH;if (of_find_property(nc, "spi-3wire", NULL))spi->mode |= SPI_3WIRE;if (of_find_property(nc, "spi-lsb-first", NULL))spi->mode |= SPI_LSB_FIRST;/* Device DUAL/QUAD mode */if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {switch (value) {case 1:break;case 2:spi->mode |= SPI_TX_DUAL;break;case 4:spi->mode |= SPI_TX_QUAD;break;default:dev_warn(&master->dev,"spi-tx-bus-width %d not supported\n",value);break;}}if (!of_property_read_u32(nc, "spi-rx-bus-width", &value)) {switch (value) {case 1:break;case 2:spi->mode |= SPI_RX_DUAL;break;case 4:spi->mode |= SPI_RX_QUAD;break;default:dev_warn(&master->dev,"spi-rx-bus-width %d not supported\n",value);break;}}/* Device speed */rc = of_property_read_u32(nc, "spi-max-frequency", &value);if (rc) {dev_err(&master->dev, "%s has no valid 'spi-max-frequency' property (%d)\n",nc->full_name, rc);goto err_out;}spi->max_speed_hz = value;/* Store a pointer to the node in the device structure */of_node_get(nc);spi->dev.of_node = nc;/* Register the new device *///看到这里,我想都会想到接下来这个函数会调用啥了就不用说了吧,对的,就是device_add函数了,这个后面就会有匹配对应种类总线上的驱动的动作了。rc = spi_add_device(spi); if (rc) {dev_err(&master->dev, "spi_device register error %s\n",nc->full_name);goto err_out;}return spi;err_out:spi_dev_put(spi);return ERR_PTR(rc);
}

说完了SPI设备匹配SPI总线上的SPI驱动之后,同样在主机驱动注册到i2c_adapter的时候去遍历主机节点下的每一个I2C子节点,然后去匹配总线上的I2C驱动,同样的流程。

4. driver端发起匹配:

4.1 流程图

其中,驱动总线注册时候已经指定了用于匹配时候需要调用的platform_match函数。

简单来说,我们在编写驱动时指定的 struct platform_device_id *id_tableconst char *nameconst struct of_device_id *of_match_table 将是判断我们的驱动和设备是否能匹配的依据。

4.2 驱动总线注册platform_driver_register

#define platform_driver_register(drv)\__platform_driver_register(drv, THIS_MODULE)

4.3 驱动的总线中的类型指向: platform_bus_type


int __platform_driver_register(struct platform_driver *drv,struct module *owner)
{drv->driver.owner = owner;drv->driver.bus = &platform_bus_type; //驱动的总线类型指向platform_bus_typedrv->driver.probe = platform_drv_probe;drv->driver.remove = platform_drv_remove;drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver);  //在此函数中进行注册
}

这个函数把平台驱动相关的属性或者回调函数赋值给drv这个指针,调用driver_register进行注册
在这个函数中指定了 drv->driver.bus = &platform_bus_type

4.3.1 驱动的总线类型中用于匹配的platform_match

struct bus_type platform_bus_type = {.name       = "platform",.dev_groups    = platform_dev_groups,.match      = platform_match,  //最后匹配的时候会调用.uevent     = platform_uevent,.pm     = &platform_dev_pm_ops,.force_dma = true,
};

即:驱动总线注册时候已经指定了用于匹配时候需要调用的platform_match函数。

4.4 注册设备:driver_register

/*** 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;if (!drv->bus->p) {pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",drv->name, drv->bus->name);return -EINVAL;}//检测总线的操作函数和驱动的操作函数是否同时存在//三种可能都会被考虑进行注册的动作,//drv的总线probe和drv的probe都已定义//drv的总线remove和drv的remove都已经定义//drv的总线shutdown和drv的shutdown都已经进行定义if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown))printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods\n", drv->name);//去已经注册的driver链表上查看,该driver是否已经被注册,防止重复注册同一个driverother = driver_find(drv->name, drv->bus);//这里查找drv->name="hub"驱动是否已经在drv->bus总线上注册,并增加引用计数,若已经注册,则返回提示信息if (other) {printk(KERN_ERR "Error: Driver '%s' is already registered, ""aborting...\n", drv->name);return -EBUSY;}/把这个driver添加到该总线维护的driver链表中ret = bus_add_driver(drv);//如果之前没注册,就在此注册if (ret)return ret;ret = driver_add_groups(drv, drv->groups);if (ret) {bus_remove_driver(drv);return ret;}kobject_uevent(&drv->p->kobj, KOBJ_ADD);return ret;
}
EXPORT_SYMBOL_GPL(driver_register);

可以看到 driver_register 中调用了 bus_add_driver 完成注册。

4.5 usb总线上添加hub驱动:bus_add_driver()

/*** 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的kobject的引用计数,返回的是其所属的顶层bus的指针。bus = bus_get(drv->bus);if (!bus)return -EINVAL;pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);priv = kzalloc(sizeof(*priv), GFP_KERNEL);//分配一个驱动私有数据driver_privateif (!priv) {error = -ENOMEM;goto out_put_bus;}klist_init(&priv->klist_devices, NULL, NULL);//初始化设备链表//以下相互绑定priv->driver = drv;//私有驱动数据绑定drvdrv->p = priv;//drv->p驱动绑定priv//指向顶层的bus的p->drivers_kse//设置私有数据的父容器,在这一步中,设置了kset为platform下的drivers_kset结构,也就是drivers那个目录priv->kobj.kset = bus->p->drivers_kset;//将priv->kobj.kset当前内核集合指向bus->p->drivers_kset内核集合(该集合在usb总线注册bus_register()描述)error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name);if (error)goto out_unregister;klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);//将当前注册的priv->knode_bus节点加入到全局bus->p->klist_drivers链表中if (drv->bus->p->drivers_autoprobe) {//在我们注册总线bus_register()中,将值置为1//系统则会调用下面的driver_attach函数进行驱动与设备的匹配if (driver_allows_async_probing(drv)) {//在usb总线注册时对该变量drivers_autoprobe初始化为1了pr_debug("bus: '%s': probing driver %s asynchronously\n",drv->bus->name, drv->name);async_schedule(driver_attach_async, drv);} else {//调用driver_attach匹配该总线上的device,看到这里肯定会有点熟悉,因为device去匹配driver 的时候也是调用了device_attach这个函数去匹配error = driver_attach(drv);//见下面,按照内核启动的流程分析,这个时候usb总线上只有hub这个驱动,没有设备,所以driver_attach函数将返回失败if (error)goto out_unregister;}}module_add_driver(drv->owner, drv);//见下面,从该函数这开始,本次hub驱动将不会执行下面的函数了,不过为了分析函数内部的功能,我们继续,因为在其他驱动模块注册时会用到error = driver_create_file(drv, &driver_attr_uevent);if (error) {printk(KERN_ERR "%s: uevent attr (%s) failed\n",__func__, drv->name);}error = driver_add_groups(drv, bus->drv_groups);if (error) {/* How the hell do we get out of this pickle? Give up */printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",__func__, drv->name);}if (!drv->suppress_bind_attrs) {error = add_bind_files(drv);if (error) {/* Ditto */printk(KERN_ERR "%s: add_bind_files(%s) failed\n",__func__, drv->name);}}return 0;out_unregister:kobject_put(&priv->kobj);/* drv->p is freed in driver_release()  */drv->p = NULL;
out_put_bus:bus_put(bus);return error;
}

bus_add_driver 调用了 driver_attach 来继续完成注册的任务

4.6 驱动绑定driver_attach()

/*** 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 - 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 *))
{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));//设备通过bus_add_device(...)添加时,最终会将设备加入到bus->p->klist_devices设备链表中while (!error && (dev = next_device(&i)))error = fn(dev, data);//回调接口函数__driver_attachklist_iter_exit(&i);return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);

bus_for_each_dev()函数内部主要是遍历usb总线的设备链表(由于本次注册的是hub驱动),需找与本次注册的hub驱动相匹配;

4.6.1 driver_attach-> __driver_attach

static int __driver_attach(struct device *dev, void *data)
{struct device_driver *drv = data;int ret;/** 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.*///当设备和驱动的名字不匹配的时候返回的是0,然后就会调用下面的return 0;ret = driver_match_device(drv, dev);//驱动和设备是否匹配,不匹配将退出,bus_for_each_dev函数继续下一个设备来匹配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 */if (dev->parent && dev->bus->need_parent_lock)device_lock(dev->parent);device_lock(dev);if (!dev->p->dead && !dev->driver)driver_probe_device(drv, dev);//调用探测函数进行探测,并且调用platform_driver中的probe函数device_unlock(dev);if (dev->parent && dev->bus->need_parent_lock)device_unlock(dev->parent);return 0;
}

可以看到调用了函数 driver_match_device ,并且下面也做出了判断是否匹配成功,成功就调用了下面的 driver_probe_device 来调用prob函数。

4.6.2 __driver_attach->driver_match_device


static inline int driver_match_device(struct device_driver *drv, struct device *dev)
{return drv->bus->match ? drv->bus->match(dev, drv) : 1;//调用match函数,如果没用则默认返回1
}

4.7 回到 4.3.1 中的platform_match

又回到最初的起点,platform_match是在最开始调用注册函数时指定的

/*** platform_match - bind platform device to platform driver.* @dev: device.* @drv: driver.** Platform device IDs are assumed to be encoded like this:* "<name><instance>", where <name> is a short description of the type of* device, like "pci" or "floppy", and <instance> is the enumerated* instance of the device, like '0' or '42'.  Driver IDs are simply* "<name>".  So, extract the <name> from the platform_device structure,* and compare it against the name of the driver. Return whether they match* or not.*/
static int platform_match(struct device *dev, struct device_driver *drv)
{struct platform_device *pdev = to_platform_device(dev);struct platform_driver *pdrv = to_platform_driver(drv);/* When driver_override is set, only bind to the matching driver */if (pdev->driver_override)return !strcmp(pdev->driver_override, drv->name);/* Attempt an OF style match first */if (of_driver_match_device(dev, drv))return 1;/* Then try ACPI style match */if (acpi_driver_match_device(dev, drv))return 1;/* Then try to match against the id table */if (pdrv->id_table)return platform_match_id(pdrv->id_table, pdev) != NULL;/* fall-back to driver name match */return (strcmp(pdev->name, drv->name) == 0);
}

4.7.1 of_driver_match_device

/*** of_driver_match_device - Tell if a driver's of_match_table matches a device.* @drv: the device_driver structure to test* @dev: the device structure to match against*/
static inline int of_driver_match_device(struct device *dev,const struct device_driver *drv)
{return of_match_device(drv->of_match_table, dev) != NULL;
}

4.7.2 platform_match_id

static const struct platform_device_id *platform_match_id(const struct platform_device_id *id,struct platform_device *pdev)
{while (id->name[0]) {if (strcmp(pdev->name, id->name) == 0) {pdev->id_entry = id;return id;}id++;}return NULL;
}

我们可以看到匹配的顺序:
1、先用设备树中的 compatible 属性和 platform_driver中的driver中的 of_match_table 来匹配
2、再用 platform_driver 中的 id_table 中的 nameplatform_device 中的 name来匹配
3、最后用platform_device中的nameplatform_driver 中的 driver中的 name来匹配

参考

Linux设备驱动和设备匹配过程
linux驱动设备匹配流程

Linux设备驱动和设备匹配过程相关推荐

  1. <Linux开发>--驱动开发-- 字符设备驱动(3) 过程详细记录

    <Linux开发>–驱动开发-- 字符设备驱动(3) 过程详细记录 驱动开发是建立再系统之上的,前面作者也记录了系统移植的过程记录,如果有兴趣,可进入博主的主页查看相关文章,这里就不添加链 ...

  2. Linux设备模型、平台设备驱动、设备树(device tree)、GPIO子系统以及pinctrl子系统介绍

    文章目录 一.Linux设备模型介绍 (1)设备驱动模型总体介绍 (2)设备驱动模型文件表现 (3)设备驱动模型工作原理 [1]总线 [2]设备 [3]驱动 [4]注册流程 二.平台设备驱动介绍 (1 ...

  3. linux设备驱动--字符设备模型

    linux设备驱动--字符设备模型 最近正在学习设备驱动开发,因此打算写一个系列博客,即是对自己学习的一个总结,也是对自己的一个督促,有不对,不足,需要改正的地方还望大家指出,而且希望结识志同道合的朋 ...

  4. LINUX设备驱动之设备模型一--kobject

    http://blog.csdn.net/yangzhu1982/article/details/6186016 Linux设备驱动之设备模型一kobject Eric Fang  2010-01-1 ...

  5. 【驱动】linux设备驱动·字符设备驱动开发

    Preface 前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段! <linux设备驱动入门篇>:http://infohacker.blog.51cto.com/6751 ...

  6. LINUX设备驱动之设备模型一kobject

    LINUX设备驱动之设备模型一kobject -------------------------------------------------------------- 转载请注明出处:http:/ ...

  7. 非即插即用型设备驱动的加载过程

    非即插即用型设备驱动的加载过程 1. 非PnP总线驱动在系统启动时通过扫描注册表发现非PnP设备的存在,并向OS报告ID信息.(例如根总线驱动通过扫描 HKLM\ SYSTEM\ CurrentCon ...

  8. 字符设备驱动、平台设备驱动、设备驱动模型、sysfs的比较和关联

    参考原文:https://www.kancloud.cn/yueqian_scut/emlinux/106829 对原文笔误地方做了修改.重新排版 目录 字符设备驱动.平台设备驱动.设备驱动模型.sy ...

  9. 基于块的linux驱动程序,基于块的Linux驱动程序 块设备驱动 centos内核编译过程 操作系统课程设计...

    操作系统的课程设计,本人也是一头雾水地做完了课程设计,在这里贴下操作过程,放下当时参考的一篇CSDN文章链接:https://blog.csdn.net/cxy_chen/article/detail ...

最新文章

  1. 年收入百万美元AI科学家的烦恼与思考
  2. Semantic UI实现一个landing page
  3. 拦截导弹 (加了神奇的位运算)
  4. Json,Hashlib与Base64(占坑待补)
  5. Android studio无法连接识别检测各种模拟器和手机的问题 (万能方案)。
  6. 腾讯企业IT部蔡晨:从有界到无界,新一代企业安全防御之道
  7. 在有序数列里插入新元素(C语言)
  8. SSL、OPENSSL、SSH、OPENSSH
  9. 深入浅出Mybatis系列(七)---mapper映射文件配置之insert、update、delete
  10. 清华毕业生开发新特效编程语言,99行代码实现《冰雪奇缘》,网友:大神碉堡!创世的快乐
  11. 黑苹果无线网卡的三种解决方案
  12. 计算机 在电厂的应用,计算机自动控制系统在火电厂中的应用
  13. 【MFC】CTabSheet类之再改造
  14. ecshop 添加php标签,ecshop模板调用标签大全
  15. 帝国cms内容页使用真实下载地址或播放地址
  16. C++ 鼠标模拟程序
  17. Android的MvVM模式探讨: Databinding 与 ViewModel+LiveData+Repository对比
  18. Linux重启命令shutdown与reboot
  19. vue 父子组件mounted执行顺序
  20. 奥维地图中加载天地图方法

热门文章

  1. 快速实现Excel到ODX/PDX数据文件的自动转换工具:VisualODX—ODX
  2. mysql复数记录,MySQL 数据库铁律(小结)
  3. 在项目开发中统计代码行数的6种方式
  4. Mac OS 使用Metal渲染NV12、YUV420、CMSampleBufferRef视频
  5. 怎么控制填充时的比例
  6. java map foreach_java foreach遍历map集合的方法案例
  7. activiti 6 和 7有什么区别
  8. js中json转excel
  9. 随记 - Uber和Lyft
  10. 1.2数字图像获取时的基本参数