在写驱动时我们都会用到一些注册函数比如:platform_driver_register ,spi_register_driver 、i2c_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 是两者的中间纽带,而device 和 driver 和bus_type 的通信是通过bus_type 提供的注册接口来注册信息,同时只要两者进行注册后bus_type 会马上触发匹配流程进行匹配。下来我们从device/driver 两个角度结合代码分别说明这个匹配流程。
2. 重点结构体介绍
2.1 struct device
./include/linux/device.h
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
const char *init_name;
const struct device_type *type;
struct mutex mutex;
struct bus_type *bus;
struct device_driver *driver;
void *platform_data;
void *driver_data;
struct dev_links_info links;
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;
#endif
const struct dma_map_ops *dma_ops;
u64 *dma_mask;
u64 coherent_dma_mask;
u64 bus_dma_mask;
unsigned long dma_pfn_offset;
struct device_dma_parameters *dma_parms;
struct list_head dma_pools;
struct dma_coherent_mem *dma_mem;
#ifdef CONFIG_DMA_CMA
struct cma *cma_area;
#endif
struct dev_archdata archdata;
struct device_node *of_node;
struct fwnode_handle *fwnode;
dev_t devt;
u32 id;
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
const struct attribute_group **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;
struct mfd_cell *mfd_cell;
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;
struct module *owner;
const char *mod_name;
bool suppress_bind_attrs;
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);
...
...
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)
{
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)
{
of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
of_scan_flat_dt(early_init_dt_scan_root, NULL);
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;
}
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);
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);
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;
pathp = fdt_get_name(blob, *poffset, &l);
if (!pathp)
return mem;
allocl = ++l;
if ((*pathp) != '/') {
new_format = 1;
if (fpsize == 0) {
fpsize = 1;
allocl = 2;
l = 1;
pathp = "";
} else {
fpsize += l;
allocl = fpsize;
}
}
np = unflatten_dt_alloc(&mem, sizeof(struct device_node) + allocl,
__alignof__(struct device_node));
if (!dryrun) {
char *fn;
of_node_init(np);
np->full_name = fn = ((char *)np) + sizeof(*np);
if (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;
}
}
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) {
if ((strcmp(pname, "phandle") == 0) ||
(strcmp(pname, "linux,phandle") == 0)) {
if (np->phandle == 0)
np->phandle = be32_to_cpup(p);
}
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;
}
}
if (!has_name) {
const char *p1 = pathp, *ps = pathp, *pa = NULL;
int sz;
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;
}
}
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;
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);
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 这个结构体指针当中,观察这个结构体的组成,我们可以看出来这个结构体是一个双向链表,因为一旦得到一个设备节点,我们就可以从他的parent 和child 中得到这个设备树中的每一个设备节点的信息。
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)
{
...
...
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
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);
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
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;
if (strict && (!of_get_property(bus, "compatible", NULL))) {
pr_debug("%s() - skipping %pOF, no compatible prop\n",
__func__, bus);
return 0;
}
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")) {
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
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;
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);
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);
ofdev->name = dev_name(&ofdev->dev);
ofdev->id = PLATFORM_DEVID_NONE;
set_dev_node(&ofdev->dev, of_node_to_nid(ofdev->dev.of_node));
return device_add(&ofdev->dev);
}
3.3.8 device_add
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;
}
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
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;
if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
set_dev_node(dev, dev_to_node(parent));
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error) {
glue_dir = get_glue_dir(dev);
goto Error;
}
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);
}
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);
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
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
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);
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
if (!ret && allow_async && data.have_async) {
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) {
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;
}
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_nod e结构体指针发生关联的。
首先我们说一下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;
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");
if (master->bus_num < 0) {
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;
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 (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;
}
}
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);
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;
for_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;
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;
}
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;
}
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;
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;
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;
}
}
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;
of_node_get(nc);
spi->dev.of_node = nc;
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_table 、const char *name 、const 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;
drv->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
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;
}
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);
other = driver_find(drv->name, 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()
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
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);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
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);
if (drv->bus->p->drivers_autoprobe) {
if (driver_allows_async_probing(drv)) {
pr_debug("bus: '%s': probing driver %s asynchronously\n",
drv->bus->name, drv->name);
async_schedule(driver_attach_async, drv);
} else {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
}
module_add_driver(drv->owner, drv);
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) {
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) {
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
return 0;
out_unregister:
kobject_put(&priv->kobj);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
bus_add_driver 调用了 driver_attach 来继续完成注册的任务
4.6 驱动绑定driver_attach()
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
EXPORT_SYMBOL_GPL(driver_attach);
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));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_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;
ret = driver_match_device(drv, dev);
if (ret == 0) {
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;
}
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);
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;
}
4.7 回到 4.3.1 中的platform_match
又回到最初的起点,platform_match 是在最开始调用注册函数时指定的
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);
if (pdev->driver_override)
return !strcmp(pdev->driver_override, drv->name);
if (of_driver_match_device(dev, drv))
return 1;
if (acpi_driver_match_device(dev, drv))
return 1;
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
return (strcmp(pdev->name, drv->name) == 0);
}
4.7.1 of_driver_match_device
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 中的 name 和 platform_device 中的 name 来匹配 3、最后用platform_device 中的name 和 platform_driver 中的 driver 中的 name 来匹配
参考
Linux设备驱动和设备匹配过程 linux驱动设备匹配流程
|