新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > Linux內核的Nand驅動流程分析

        Linux內核的Nand驅動流程分析

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

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

        本文引用地址:http://www.104case.com/article/201611/322799.htm

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

        這里以Linux內核的3.5.3中默認的mini2440開發板為例,首先定位到arm/arm/mach-s3c24xx/mach-mini2440.c,然后找到如下結構:

        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. };
        顯然,這里就是內核需要的設備列表,通過后面的mini2440_init函數中的
        1. platform_add_devices(mini2440_devices,ARRAY_SIZE(mini2440_devices));
        注冊到內核,然后由內核進行管理,顯然,跟我們分析的Nand相關的就是s3c_device_nand,這就代表我們開發版上的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. };
        第二個 結構就是s3c_device_nand的定義,之所以帶上第一個結構是因為定義s3c_device_nand時用到了s3c_nand_resource,我們先看一下s3c_device_nand的定義,s3c_device_nand只明確定義了Nand設備的名稱和設備ID,并沒有給出具體的寄存器信息,加上s3c_nand_resource的名字帶有資源的意思,因此我們斷定,寄存器信息應該在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中定義了起始,結束,名字等信息,我們再來看一下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)
        我這里整合了一下上面的信息,將相關的宏都做了一下追蹤,因此,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的寄存器并沒有那么多,這又是為什么呢?這些信息有什么用又在哪里用到了呢?答案很簡單,這肯定是給驅動程序使用的了,帶著這個疑問我們繼續分析代碼。定位到/drivers/mtd/nand/s3c2410.c,瀏覽代碼可以看到驅動結構定義

        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. };
        可以看到,這里指定了結構中的各種操作的函數指針,從名字上可以看出probe是加載驅動程序后執行的第一個函數,remove是移除驅動前最后執行的函數,suspend是掛起操作,等等。先不著急分析這些函數,先來看看內核是如何加載驅動的,s3c24xx_nand_driver又是如何注冊到內核的。往下瀏覽代碼可以看到
        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");
        顯然,加載該驅動時s3c2410_nand_init函數將s3c24xx_nand_driver注冊到了內核,卸載該驅動時s3c2410_nand_exit將s3c24xx_nand_driver注銷,但是這兩個函數也不過是兩個普通函數,內核如何知道加載驅動時運行s3c2410_nand_init,卸載驅動時運行s3c2410_nand_exit呢?下面的module_init和module_exit解決了這個問題,它們分別告訴內核驅動程序的入口和出口。至于下面的MODULE_LICENSE指定了內核的權限協議,這里指定內核為GPL協議的,只有符合這個協議才能調用這個協議內的函數,因此是驅動程序必須的部分,剩下的兩行是驅動的作者和描述,無關緊要,可以沒有。現在我們明白了內核如何加載驅動了,我們再去分析probe函數,往上瀏覽代碼可以找到

        上一頁 1 2 3 下一頁

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 千阳县| 曲麻莱县| 阳曲县| 屯门区| 吉隆县| 雅江县| 遵化市| 三门峡市| 马尔康县| 专栏| 西城区| 郴州市| 望城县| 定结县| 师宗县| 舟曲县| 武山县| 玉树县| 延安市| 洪江市| 梁山县| 临清市| 武夷山市| 临海市| 中阳县| 墨竹工卡县| 隆林| 灵宝市| 民丰县| 玉环县| 城口县| 武安市| 西峡县| 延庆县| 新沂市| 马山县| 三台县| 灵川县| 南丹县| 罗定市| 巴楚县|