1. 程式人生 > >zedboard——adau1761之axi-i2c.c及axi-i2c.ko核心載入除錯(三)

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