参考文章:http://blog.csdn.net/zqixiao_09/article/details/50889458
由上文 “linux下platform总线驱动” 知,platform驱动开发的流程大致为总线注册,设备(platform_device)注册,驱动注册(platform_driver)注册三大部分。
设备树(device tree)主要用来描述设备信息,每一个设备在设备树中是以节点的形式表现出来的。linux内核会将设备树中的设备信息自动构造成platform_device结构。
可以看到设备树中的设备节点完全可以替代掉platform_device。
之前的platform_device 中,是利用 .name 来实现device与driver的匹配的,但现在设备树替换掉了device,那我们将如何实现二者的匹配呢?
我们先看一下原来是如何匹配的 ,platform_bus_type 下有个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);
/* 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);
}
其中又调用了of_driver_match_device(dev, drv) ,其定义如下:
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;
}
其调用of_match_device(drv->of_match_table, dev) ,继续追踪下去,注意这里的参数drv->of_match_table
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
if ((!matches) || (!dev->of_node))
return NULL;
return of_match_node(matches, dev->of_node);
}
EXPORT_SYMBOL(of_match_device);
又调用 of_match_node(matches, dev->of_node) ,其中matches 是struct of_device_id 类型的
/**
* of_match_node - Tell if an device_node has a matching of_match structure
* @matches: array of of device match structures to search in
* @node: the of device structure to match against
*
* Low level utility function used by device matching.
*/
const struct of_device_id *of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
const struct of_device_id *match;
unsigned long flags;
raw_spin_lock_irqsave(&devtree_lock, flags);
match = __of_match_node(matches, node);
raw_spin_unlock_irqrestore(&devtree_lock, flags);
return match;
}
EXPORT_SYMBOL(of_match_node);
找到 match = __of_match_node(matches, node); 注意着里的node是struct device_node 类型的
const struct of_device_id *__of_match_node(const struct of_device_id *matches,
const struct device_node *node)
{
const struct of_device_id *best_match = NULL;
int score, best_score = 0;
if (!matches)
return NULL;
for (; matches->name[0] || matches->type[0] || matches->compatible[0]; matches++) {
score = __of_device_is_compatible(node, matches->compatible,
matches->type, matches->name);
if (score > best_score) {
best_match = matches;
best_score = score;
}
}
return best_match;
}
继续追踪下去
static int __of_device_is_compatible(const struct device_node *device,
const char *compat, const char *type, const char *name)
{
struct property *prop;
const char *cp;
int index = 0, score = 0;
/* Compatible match has highest priority */
if (compat && compat[0]) {
prop = __of_find_property(device, "compatible", NULL);
for (cp = of_prop_next_string(prop, NULL); cp;
cp = of_prop_next_string(prop, cp), index++) {
if (of_compat_cmp(cp, compat, strlen(compat)) == 0) {
score = INT_MAX/2 - (index << 2);
break;
}
}
if (!score)
return 0;
}
/* Matching type is better than matching name */
if (type && type[0]) {
if (!device->type || of_node_cmp(type, device->type))
return 0;
score += 2;
}
/* Matching name is a bit better than not */
if (name && name[0]) {
if (!device->name || of_node_cmp(name, device->name))
return 0;
score++;
}
return score;
}
看这句 prop = __of_find_property(device, “compatible”, NULL);
可以发现追溯到底,是利用”compatible”来匹配的,即设备树加载之后,内核会自动把设备树节点转换成 platform_device这种格式,同时把名字放到of_node这个地方。
可以看到原来是利用platform_driver 下的 struct driver 结构体中的 name 成员来匹配的,看一下 struct driver 结构体的定义:
struct device_driver {
const char *name;
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;
}
成员中有const struct of_device_id*of_match_table; 是struct of_device_id 类型,定义如下:
/*
* Struct used for matching a device
*/
struct of_device_id
{
char name[32];
char type[32];
char compatible[128];
const void *data;
};
可以看到其作用就是为了匹配一个设备。我们所要做的就是对 char compatible[128] 的填充;设备树加载之后,内核会自动把设备树节点转换成 platform_device这种格式,同时把名字放到of_node这个地方。
匹配的方式发生了改变,那我们的platform_driver 也要修改了
基于设备树的driver的结构体的填充:
static struct of_device_id beep_table[] = {
{.compatible = "fs4412,beep"},
{}
};
static struct platform_driver beep_driver=
{
.probe = beep_probe,
.remove = beep_remove,
.driver={
.name = "bigbang",
.of_match_table = beep_table,
},
};
原来的driver是这样的,可以对比一下
static struct platform_driver beep_driver=
{
.driver.name = "bigbang",
.probe = beep_probe,
.remove = beep_remove,
};
我们在 arch/arm/boot/dts/exynos4412-fs4412.dts 中添加
fs4412-beep{
compatible = "fs4412,beep";
reg = <0x114000a0 0x4 0x139D0000 0x14>;
};