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地址變動。
看來網站提供的原始驅動也不一定就是絕對沒問題的(汗???),還是要自己移植動手驗證才行。