platform_get_resource=NULL内核源码分析
文章目录
- platform_get_resource=NULL内核源码分析
- 1.第一步,我们看一下什么情况下platform_get_resource函才会返回NULL,也就是没有获取到资源。
- 2.第二步,因为myled这个设节点会转成了platform_device,所以我们只需要分忻构建resources赋值的代码即可。
- 总结
platform_get_resource获取设备树资源出现失败,确保参数没有错误,且设备树中确实有对应device,举例如下:
代码如下
设备树内容如下
通过代码我们知当没有获取到资源的时候,就会报错。
1.第一步,我们看一下什么情况下platform_get_resource函才会返回NULL,也就是没有获取到资源。
platform_get_resource函数定又在drivers/base/platform.c中
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;
}
return NULL;
}
说明根本没有进入for循环,为什么?也就是说dev->num_resources为0,为什么为0?正常的话dev->num_resources应该为1。
通过代码我们可知,当for循环不成立的时候,也就是为时,才会返回NULL。
所以我们要分析为什么dev->num_resources为0?
通过之前的内容我们知道设备树最终转换成platform_device(并不是所有节点都会转),在转成platform_device的时候,会对num_resources进行值,所以跟踪下设备的转换流程,看一下什么情况下会被值成0。
2.第二步,因为myled这个设节点会转成了platform_device,所以我们只需要分忻构建resources赋值的代码即可。
通过的面的学习我们知道:
在drivers/of/platform.c下的of_platform_device_create_pdata下的of_device_alloc函数会创建
resources,
struct platform_device *of_device_alloc(struct device_node *np,
const char *bus_id,
struct device *parent)
{
struct platform_device *dev;
int rc, i, num_reg = 0, num_irq;
struct resource *res, temp_res;
dev = platform_device_alloc("", -1);
if (!dev)
return NULL;
/* count the io and irq resources */
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
num_reg++;
num_irq = of_irq_count(np);
/* Populate the resource table */
if (num_irq || num_reg) {
res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
if (!res) {
platform_device_put(dev);
return NULL;
}
dev->num_resources = num_reg + num_irq;
dev->resource = res;
for (i = 0; i < num_reg; i++, res++) {
rc = of_address_to_resource(np, i, res);
WARN_ON(rc);
}
if (of_irq_to_resource_table(np, res, num_irq) != num_irq)
pr_debug("not all legacy IRQ resources mapped for %s\n",
np->name);
}
dev->dev.of_node = of_node_get(np);
dev->dev.parent = parent ? : &platform_bus;
if (bus_id)
dev_set_name(&dev->dev, "%s", bus_id);
else
of_device_make_bus_id(&dev->dev);
return dev;
}
dev->num_resources = num_reg + num_irq;
节点中没有中断资源,所以num_irq为0,num_reg初始化是0,下面代码统计num_reg数量
/* count the io and irq resources */
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
num_reg++;
of_device_alloc
of_address_to_resource
int of_address_to_resource(struct device_node *dev, int index,
struct resource *r)
{
const __be32 *addrp;
u64 size;
unsigned int flags;
const char *name = NULL;
addrp = of_get_address(dev, index, &size, &flags);
if (addrp == NULL)
return -EINVAL;
/* Get optional "reg-names" property to add a name to a resource */
of_property_read_string_index(dev, "reg-names", index, &name);
return __of_address_to_resource(dev, addrp, size, flags, name, r);
}
EXPORT_SYMBOL_GPL(of_address_to_resource);
of_device_alloc
of_address_to_resource
__of_address_to_resource
static int __of_address_to_resource(struct device_node *dev,
const __be32 *addrp, u64 size, unsigned int flags,
const char *name, struct resource *r)
{
u64 taddr;
if ((flags & (IORESOURCE_IO | IORESOURCE_MEM)) == 0)
return -EINVAL;
taddr = of_translate_address(dev, addrp);
if (taddr == OF_BAD_ADDR)
return -EINVAL;
memset(r, 0, sizeof(struct resource));
if (flags & IORESOURCE_IO) {
unsigned long port;
port = pci_address_to_pio(taddr);
if (port == (unsigned long)-1)
return -EINVAL;
r->start = port;
r->end = port + size - 1;
} else {
r->start = taddr;
r->end = taddr + size - 1;
}
r->flags = flags;
r->name = name ? name : dev->full_name;
return 0;
}
taddr = of_translate_address(dev, addrp);
of_device_alloc
of_address_to_resource
__of_address_to_resource
of_translate_address(传入了ranges属性)
u64 of_translate_address(struct device_node *dev, const __be32 *in_addr)
{
return __of_translate_address(dev, in_addr, "ranges");
}
EXPORT_SYMBOL(of_translate_address);
__of_translate_address
static u64 __of_translate_address(struct device_node *dev,
const __be32 *in_addr, const char *rprop)
{
struct device_node *parent = NULL;
struct of_bus *bus, *pbus;
__be32 addr[OF_MAX_ADDR_CELLS];
int na, ns, pna, pns;
u64 result = OF_BAD_ADDR;
pr_debug("OF: ** translation for device %s **\n", of_node_full_name(dev));
/* Increase refcount at current level */
of_node_get(dev);
/* Get parent & match bus type */
parent = of_get_parent(dev);
if (parent == NULL)
goto bail;
bus = of_match_bus(parent);
/* Count address cells & copy address locally */
bus->count_cells(dev, &na, &ns);
if (!OF_CHECK_COUNTS(na, ns)) {
pr_debug("OF: Bad cell count for %s\n", of_node_full_name(dev));
goto bail;
}
memcpy(addr, in_addr, na * 4);
pr_debug("OF: bus is %s (na=%d, ns=%d) on %s\n",
bus->name, na, ns, of_node_full_name(parent));
of_dump_addr("OF: translating address:", addr, na);
/* Translate */
for (;;) {
/* Switch to parent bus */
of_node_put(dev);
dev = parent;
parent = of_get_parent(dev);
/* If root, we have finished */
if (parent == NULL) {
pr_debug("OF: reached root node\n");
result = of_read_number(addr, na);
break;
}
/* Get new parent bus and counts */
pbus = of_match_bus(parent);
pbus->count_cells(dev, &pna, &pns);
if (!OF_CHECK_COUNTS(pna, pns)) {
printk(KERN_ERR "prom_parse: Bad cell count for %s\n",
of_node_full_name(dev));
break;
}
pr_debug("OF: parent bus is %s (na=%d, ns=%d) on %s\n",
pbus->name, pna, pns, of_node_full_name(parent));
/* Apply bus translation */
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
break;
/* Complete the move up one level */
na = pna;
ns = pns;
bus = pbus;
of_dump_addr("OF: one level translation:", addr, na);
}
bail:
of_node_put(parent);
of_node_put(dev);
return result;
}
/* Apply bus translation */
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
break;
注意:of_translate_one参数rprop为“ranges"
static int of_translate_one(struct device_node *parent, struct of_bus *bus,
struct of_bus *pbus, __be32 *addr,
int na, int ns, int pna, const char *rprop)
{
const __be32 *ranges;
unsigned int rlen;
int rone;
u64 offset = OF_BAD_ADDR;
/* Normally, an absence of a "ranges" property means we are
* crossing a non-translatable boundary, and thus the addresses
* below the current not cannot be converted to CPU physical ones.
* Unfortunately, while this is very clear in the spec, it's not
* what Apple understood, and they do have things like /uni-n or
* /ht nodes with no "ranges" property and a lot of perfectly
* useable mapped devices below them. Thus we treat the absence of
* "ranges" as equivalent to an empty "ranges" property which means
* a 1:1 translation at that level. It's up to the caller not to try
* to translate addresses that aren't supposed to be translated in
* the first place. --BenH.
*
* As far as we know, this damage only exists on Apple machines, so
* This code is only enabled on powerpc. --gcl
*/
ranges = of_get_property(parent, rprop, &rlen);
if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
pr_debug("OF: no ranges; cannot translate\n");
return 1;
}
if (ranges == NULL || rlen == 0) {
offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
pr_debug("OF: empty ranges; 1:1 translation\n");
goto finish;
}
pr_debug("OF: walking ranges...\n");
/* Now walk through the ranges */
rlen /= 4;
rone = na + pna + ns;
for (; rlen >= rone; rlen -= rone, ranges += rone) {
offset = bus->map(addr, ranges, na, ns, pna);
if (offset != OF_BAD_ADDR)
break;
}
if (offset == OF_BAD_ADDR) {
pr_debug("OF: not found !\n");
return 1;
}
memcpy(addr, ranges + na, 4 * pna);
finish:
of_dump_addr("OF: parent translation for:", addr, pna);
pr_debug("OF: with offset: %llx\n", (unsigned long long)offset);
/* Translate it into parent bus space */
return pbus->translate(addr, offset, pna);
}
由于属性中没有ranges属性,所以ranges=NULL
ranges = of_get_property(parent, rprop, &rlen);
if (ranges == NULL && !of_empty_ranges_quirk(parent)) {
pr_debug("OF: no ranges; cannot translate\n");
return 1;
导致of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop)=1
if (of_translate_one(dev, bus, pbus, addr, na, ns, pna, rprop))
break;
导致函数直接返回result
return result;
而result的值如下
u64 result = OF_BAD_ADDR;
所以
u64 of_translate_dma_address(struct device_node *dev, const __be32 *in_addr)
{
return __of_translate_address(dev, in_addr, "dma-ranges");
}
返回OF_BAD_ADDR
taddr = of_translate_address(dev, addrp);
if (taddr == OF_BAD_ADDR)
return -EINVAL;
int of_address_to_resource(struct device_node *dev, int index,
struct resource *r)
{
const __be32 *addrp;
u64 size;
unsigned int flags;
const char *name = NULL;
addrp = of_get_address(dev, index, &size, &flags);
if (addrp == NULL)
return -EINVAL;
/* Get optional "reg-names" property to add a name to a resource */
of_property_read_string_index(dev, "reg-names", index, &name);
return __of_address_to_resource(dev, addrp, size, flags, name, r);
}
返回-EINVAL
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
num_reg++;
所以不会进入while循环,也就是说num_reg=0;所以num_resources=0;
总结
of_translateone返回1
of_translate_address回OFBAD_ADDR
ofaddresstoresource返回EINVAL
ofaddresstoresource返回EINVAL
所以numreg为0,
通过代码的分折,当有ranges属性的时,但是属性值为0,则不对地址进行转,所以在设备节点中添加ranges属性即可。