新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > Linux內(nèi)核的Nand驅(qū)動流程分析

Linux內(nèi)核的Nand驅(qū)動流程分析

作者: 時間:2016-11-28 來源:網(wǎng)絡(luò) 收藏
最近在做Linux內(nèi)核移植,總體的感覺是這樣的,想要徹底的閱讀Linux內(nèi)核代碼幾乎是不可能的,至少這還不是嵌入式學(xué)期初期的重要任務(wù)。內(nèi)核代碼解壓后有250M左右,據(jù)統(tǒng)計,有400多萬行,而且涉及到了軟件和硬件兩方面的諸多知識,憑一人之力在短時間內(nèi)閱讀Linux內(nèi)核代碼是根本不可能的,強行閱讀可能會打消我們嵌入式學(xué)習(xí)的積極性,最后甚至可能放棄嵌入式學(xué)習(xí),如果真的想閱讀內(nèi)核代碼來提高自己水平的話可以等熟練掌握嵌入式以后再回過頭來閱讀,這樣理解也會更深刻,更透徹。

我認(rèn)為Linux內(nèi)核移植的初期階段應(yīng)該將重點放在分析內(nèi)核設(shè)備驅(qū)動上。實際上,Linux內(nèi)核的移植就是設(shè)備驅(qū)動的移植,內(nèi)核本身不會直接訪問硬件,是通過驅(qū)動程序來間接控制硬件的,而其他的高級功能如內(nèi)存管理,進程管理等是通用的,無需做其他配置,所以我們只需要配置相關(guān)的驅(qū)動即可實現(xiàn)Linux內(nèi)核移植。驅(qū)動移植的關(guān)鍵在于了解在驅(qū)動的結(jié)構(gòu),本文將以Nand驅(qū)動為例,分析Linux內(nèi)核的驅(qū)動結(jié)構(gòu)。

本文引用地址:http://butianyuan.cn/article/201611/322799.htm

在分析驅(qū)動結(jié)構(gòu)之前,還需要了解下內(nèi)核識別設(shè)備的方式,內(nèi)核通過驅(qū)動程序識別設(shè)備的方法有兩種,一種是驅(qū)動程序本身帶有設(shè)備信息,比如開始地址、中斷號等,加載驅(qū)動時就可以根據(jù)驅(qū)動中的信息來識別設(shè)備;另一種是驅(qū)動程序本身沒有設(shè)備信息,但是內(nèi)核中已經(jīng)根據(jù)其他方式確定了很多設(shè)備信息,加載驅(qū)動時將驅(qū)動程序與這些設(shè)備逐個比較,確定兩者是否匹配,如果匹配就可以使用該驅(qū)動來識別設(shè)備了。內(nèi)核常采用的是第二種方式,這樣方式可將各種設(shè)備集中在一個文件中管理,當(dāng)開發(fā)板的配置改變時便于修改代碼。對應(yīng)的,內(nèi)核文件include/linux/platform_device.h中定義了兩個結(jié)構(gòu),一個是platform_device,用來描述設(shè)備信息,一個是platform_driver,用來描述驅(qū)動信息,內(nèi)核啟動后首先構(gòu)造鏈表將plartfrom_device結(jié)構(gòu)組織起來得到一個設(shè)備鏈表,當(dāng)加載某個驅(qū)動時根據(jù)platform_driver提供的信息與設(shè)備鏈表一一進行匹配,這就是內(nèi)核設(shè)備識別的大體過程,具體的過程比這復(fù)雜很多,這里不做過多研究。下面我們開始分析Linux內(nèi)核的Nand驅(qū)動。

這里以Linux內(nèi)核的3.5.3中默認(rèn)的mini2440開發(fā)板為例,首先定位到arm/arm/mach-s3c24xx/mach-mini2440.c,然后找到如下結(jié)構(gòu):

  1. staticstructplatform_device*mini2440_devices[]__initdata={
  2. &s3c_device_ohci,
  3. &s3c_device_wdt,
  4. &s3c_device_i2c0,
  5. &s3c_device_rtc,
  6. &s3c_device_usbgadget,
  7. &mini2440_device_eth,
  8. &mini2440_led1,
  9. &mini2440_led2,
  10. &mini2440_led3,
  11. &mini2440_led4,
  12. &mini2440_button_device,
  13. &s3c_device_nand,
  14. &s3c_device_sdi,
  15. &s3c_device_iis,
  16. &uda1340_codec,
  17. &mini2440_audio,
  18. &samsung_asoc_dma,
  19. };
顯然,這里就是內(nèi)核需要的設(shè)備列表,通過后面的mini2440_init函數(shù)中的
  1. platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));
注冊到內(nèi)核,然后由內(nèi)核進行管理,顯然,跟我們分析的Nand相關(guān)的就是s3c_device_nand,這就代表我們開發(fā)版上的Nand flash,我們先定位到它的定義,在arch/arm/plat-samsung/devs.c中有如下代碼
  1. staticstructresources3c_nand_resource[]={
  2. [0]=DEFINE_RES_MEM(S3C_PA_NAND,SZ_1M),
  3. };
  4. structplatform_devices3c_device_nand={
  5. .name="s3c2410-nand",
  6. .id=-1,
  7. .num_resources=ARRAY_SIZE(s3c_nand_resource),
  8. .resource=s3c_nand_resource,
  9. };
第二個 結(jié)構(gòu)就是s3c_device_nand的定義,之所以帶上第一個結(jié)構(gòu)是因為定義s3c_device_nand時用到了s3c_nand_resource,我們先看一下s3c_device_nand的定義,s3c_device_nand只明確定義了Nand設(shè)備的名稱和設(shè)備ID,并沒有給出具體的寄存器信息,加上s3c_nand_resource的名字帶有資源的意思,因此我們斷定,寄存器信息應(yīng)該在s3c_nand_resource中,從s3c_nand_resource的定義中我們只能看到很少的信息,要想了解具體信息需要看一下struct resource和宏DEFINE_RES_MEM的定義及
  1. structresource{
  2. resource_size_tstart;
  3. resource_size_tend;
  4. constchar*name;
  5. unsignedlongflags;
  6. structresource*parent,*sibling,*child;
  7. };
這里 可以看到,struct resource中定義了起始,結(jié)束,名字等信息,我們再來看一下DEFINE_RES_MEM的定義
  1. #defineDEFINE_RES_NAMED(_start,_size,_name,_flags)
  2. {
  3. .start=(_start),
  4. .end=(_start)+(_size)-1,
  5. .name=(_name),
  6. .flags=(_flags),
  7. }
  8. #defineDEFINE_RES_MEM_NAMED(_start,_size,_name)
  9. DEFINE_RES_NAMED((_start),(_size),(_name),IORESOURCE_MEM)
  10. #defineDEFINE_RES_MEM(_start,_size)
  11. DEFINE_RES_MEM_NAMED((_start),(_size),NULL)
我這里整合了一下上面的信息,將相關(guān)的宏都做了一下追蹤,因此,s3c_nand_resource的實際定義為
  1. {
  2. .start=(S3C_PA_NAND),
  3. .end=(S3C_PA_NAND)+(SZ_1M)-1,
  4. .name=(NULL),
  5. .flags=(IORESOURCE_MEM),
  6. }
追蹤可知,S3C_PA_NAND定義如下
  1. #defineS3C2410_PA_NAND(0x4E000000)
  2. #defineS3C24XX_PA_NANDS3C2410_PA_NAND
  3. #defineS3C_PA_NANDS3C24XX_PA_NAND

也就是說,S3C_PA_NAND是Nand flash寄存器首地址,而SZ_1M明顯是個長度,因此,這里的resource實際上是Nand flash寄存器首地址跟接下來的1M空間,可是,Nand的寄存器并沒有那么多,這又是為什么呢?這些信息有什么用又在哪里用到了呢?答案很簡單,這肯定是給驅(qū)動程序使用的了,帶著這個疑問我們繼續(xù)分析代碼。定位到/drivers/mtd/nand/s3c2410.c,瀏覽代碼可以看到驅(qū)動結(jié)構(gòu)定義

  1. staticstructplatform_drivers3c24xx_nand_driver={
  2. .probe=s3c24xx_nand_probe,
  3. .remove=s3c24xx_nand_remove,
  4. .suspend=s3c24xx_nand_suspend,
  5. .resume=s3c24xx_nand_resume,
  6. .id_table=s3c24xx_driver_ids,
  7. .driver={
  8. .name="s3c24xx-nand",
  9. .owner=THIS_MODULE,
  10. },
  11. };
可以看到,這里指定了結(jié)構(gòu)中的各種操作的函數(shù)指針,從名字上可以看出probe是加載驅(qū)動程序后執(zhí)行的第一個函數(shù),remove是移除驅(qū)動前最后執(zhí)行的函數(shù),suspend是掛起操作,等等。先不著急分析這些函數(shù),先來看看內(nèi)核是如何加載驅(qū)動的,s3c24xx_nand_driver又是如何注冊到內(nèi)核的。往下瀏覽代碼可以看到
  1. staticint__inits3c2410_nand_init(void)
  2. {
  3. printk("S3C24XXNANDDriver,(c)2004SimtecElectronics");
  4. returnplatform_driver_register(&s3c24xx_nand_driver);
  5. }
  6. staticvoid__exits3c2410_nand_exit(void)
  7. {
  8. platform_driver_unregister(&s3c24xx_nand_driver);
  9. }
  10. module_init(s3c2410_nand_init);
  11. module_exit(s3c2410_nand_exit);
  12. MODULE_LICENSE("GPL");
  13. MODULE_AUTHOR("BenDooks");
  14. MODULE_DESCRIPTION("S3C24XXMTDNANDdriver");
顯然,加載該驅(qū)動時s3c2410_nand_init函數(shù)將s3c24xx_nand_driver注冊到了內(nèi)核,卸載該驅(qū)動時s3c2410_nand_exit將s3c24xx_nand_driver注銷,但是這兩個函數(shù)也不過是兩個普通函數(shù),內(nèi)核如何知道加載驅(qū)動時運行s3c2410_nand_init,卸載驅(qū)動時運行s3c2410_nand_exit呢?下面的module_init和module_exit解決了這個問題,它們分別告訴內(nèi)核驅(qū)動程序的入口和出口。至于下面的MODULE_LICENSE指定了內(nèi)核的權(quán)限協(xié)議,這里指定內(nèi)核為GPL協(xié)議的,只有符合這個協(xié)議才能調(diào)用這個協(xié)議內(nèi)的函數(shù),因此是驅(qū)動程序必須的部分,剩下的兩行是驅(qū)動的作者和描述,無關(guān)緊要,可以沒有?,F(xiàn)在我們明白了內(nèi)核如何加載驅(qū)動了,我們再去分析probe函數(shù),往上瀏覽代碼可以找到

上一頁 1 2 3 下一頁

評論


技術(shù)專區(qū)

關(guān)閉