最近在调试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地址变动。
看来网站提供的原始驱动也不一定就是绝对没问题的(汗???),还是要自己移植动手验证才行。