zedboard adau1761之axi-i2c.c及axi-i2c.ko内核加载调试(三)

最近在调试zebboard开发板的adau1761音频接口,加载的i2s内核ko时,出现如下错误:Unable to handle kernel NULL pointer dereference at virtual address 000000b8,内核执行到regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL)是出现了空指针引用错误,即i2s->regmap指针异常。
查看源代码(axi-i2s.c):

static int axi_i2s_probe(struct platform_device *pdev)
{
    struct resource *res;
    struct axi_i2s *i2s;
    void __iomem *base;
    int ret;

    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);//为设备结构分配内存,用这个函数分配出来的内存会自动释放
    printk("i2s : %d %p\n", __LINE__, i2s);
    if (!i2s)
        return -ENOMEM;

    platform_set_drvdata(pdev, i2s);
    printk("i2s : %d %p\n", __LINE__, i2s);

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//得到寄存器地址资源
    base = devm_ioremap_resource(&pdev->dev, res);//申请资源,然后进行IO映射,ioremap的功能将一个IO地址空间映射到内核的虚拟地址空间上去,返回值就是虚拟地址
    printk("res : %d %p\n", __LINE__, res); 
    printk("base : %d %p\n", __LINE__, base); 

    if (IS_ERR(base))
        return PTR_ERR(base);

    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);//为设备结构分配内存
    i2s->regmap = devm_regmap_init_mmio(&pdev->dev, base,
        &axi_i2s_regmap_config);                   //i2s对应的是mmio类型
    if (IS_ERR(i2s->regmap))
        return PTR_ERR(i2s->regmap);
    printk("i2s : %d %p\n", __LINE__, i2s);
    printk("i2s->regmap : %d %p\n", __LINE__, i2s->regmap);

    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);//为设备结构分配内存
    i2s->clk = devm_clk_get(&pdev->dev, "axi");//根据设备树获得axi模块时钟的时钟
    if (IS_ERR(i2s->clk))
        return PTR_ERR(i2s->clk);
    printk("i2s : %d %p\n", __LINE__, i2s);
    printk("i2s->regmap : %d %p\n", __LINE__, i2s->regmap);
    printk("i2s->clk : %d %p\n", __LINE__, i2s->clk);

    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);//为设备结构分配内存
    i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");//根据设备树获得参考mclk时钟
    if (IS_ERR(i2s->clk_ref))
        return PTR_ERR(i2s->clk_ref);

    printk("i2s : %d %p\n", __LINE__, i2s);
    printk("i2s->regmap : %d %p\n", __LINE__, i2s->regmap);
    printk("i2s->clk_ref : %d %p\n", __LINE__, i2s->clk_ref);

    ret = clk_prepare_enable(i2s->clk);//使能i2s模块的时钟
    if (ret)
        return ret;

    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);//为设备结构分配内存
    printk("i2s : %d %p\n", __LINE__, i2s);
    printk("i2s->regmap : %d %p\n", __LINE__, i2s->regmap);

    //对iis的dma赋值,包括起始地址,地址位宽字节数以及突发传输数据长度
    i2s->playback_dma_data.addr = res->start + AXI_I2S_REG_TX_FIFO;
    i2s->playback_dma_data.addr_width = 4;
    i2s->playback_dma_data.maxburst = 1;

    i2s->capture_dma_data.addr = res->start + AXI_I2S_REG_RX_FIFO;
    i2s->capture_dma_data.addr_width = 4;
    i2s->capture_dma_data.maxburst = 1;

    printk("ucas : %d %s\n", __LINE__, __FUNCTION__);
    i2s->ratnum.num = clk_get_rate(i2s->clk_ref) / 2 / AXI_I2S_BITS_PER_FRAME;
    printk("ucas : %d %s\n", __LINE__, __FUNCTION__);
    i2s->ratnum.den_step = 1;
    i2s->ratnum.den_min = 1;
    i2s->ratnum.den_max = 64;

    i2s->rate_constraints.rats = &i2s->ratnum;
    i2s->rate_constraints.nrats = 1;

    printk("i2s->regmap : %d %p\n", __LINE__, i2s->regmap);
    regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL);//复位IIS控制模块

    ret = devm_snd_soc_register_component(&pdev->dev, &axi_i2s_component,
                     &axi_i2s_dai, 1);
    if (ret)
        goto err_clk_disable;

    printk("ucas : %d %s\n", __LINE__, __FUNCTION__);
    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
    ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
    if (ret)
        goto err_clk_disable;

    printk("ucas : %d %s\n", __LINE__, __FUNCTION__);
    i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
    return 0;

err_clk_disable:
    clk_disable_unprepare(i2s->clk);
    return ret;
}

源代码截图如下,里面显示了代码的行号,后面调试输出和代码的行号对比,查看输出。
这里写图片描述
这里写图片描述
加载i2s.ko文件后,Putty工具端口的输出如下图:
这里写图片描述
查看工具的输出:
i2s : 194 de476f10
i2s : 199 de476f10
res : 203 de52c3c0
base : 204 e099a000
i2s : 214 de7a5a10
可以看到打印194行和199行,i2s的虚拟地址均是de476f10,即执行193行的语句:i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);获得的i2s虚拟地址是de476f10,但是214行打印的i2s地址却变成了de7a5a10,这是为啥。对比源代码的行号,发现是执行了209行的代码:i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);i2s的地址变成了de7a5a10,即重新分配了一个i2s结构体大小的空间。
执行215行打印的i2s->regmap的虚拟地址是de7b8800, 此时还不是空指针。
i2s->regmap : 215 de7b8800
但是执行222行,打印出的i2s->regmap地址确实null了,也就是这时已经导致了空指针的出现。
i2s : 221 de7a5710
i2s->regmap : 222 (null)
查看源代码217行,重新执行了以下语句;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);即重新又分配了空间,空间的首地址赋给i2s,地址是221行打印的地址de7a5710,即因为i2s的地址已经改变,导致了新分配的i2s结构体内部的i2s->regmap还未赋地址值,即是空指针。所以在内核执行到262行regmap_write(i2s->regmap, AXI_I2S_REG_RESET, AXI_I2S_RESET_GLOBAL)调用i2s->regmap时出现了空指针应用错误,找到了问题的所在,即多次执行i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);语句导致第一次分配的i2s地址已经改变。

解决方法,把axi_i2s_probe函数内部除了第一处的i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL)不用注释掉,其余的均要注释掉,防止再次分配空间导致i2s地址变动。
这里写图片描述
看来网站提供的原始驱动也不一定就是绝对没问题的(汗???),还是要自己移植动手验证才行。

猜你喜欢

转载自blog.csdn.net/u014470361/article/details/80683592