Linux MTD (Memory Technology Device) subsystem analysis -For Atheros char device


Linux MTD (Memory Technology Device) subsystem analysis

For Atheros char device

 

 

讀了Linux MTD 源代碼分析 對這部分有了整體的認識,結合現有代碼,分析一下Atheros的MTD是如何使用的。

Linux kernel: 2.6.31.

Atheros platform: QCA9890???

 

參考Linux MTD 源代碼分析,這里把MTD分為4層,從上到下依次是:字符設備節點、字符設備、MTD 核心、FLASH 硬件驅動。

字符設備節點:/dev/mtd0 /dev/mtd1 等節點,可以通過fopen()等直接使用。這些節點是MTD子系統創建的,用戶通過訪問這些節點操作MTD。

字符設備:MTD子系統把MTD封裝為字符設備。

MTD核心:管理所有的MTD設備,提供get/add/del MTD功能;同時,一個MTD設備可能邏輯上會被划分為多個區,每個區被當成一個MTD設備,所以還提供了通過分區信息批量加入、刪除MTD設備的功能。為了從kernel啟動參數獲取分區信息,MTD核心提供了分析啟動參數中分區信息的功能。

FLASH硬件驅動:提供FLASH的read/write/erase等功能。因為Atheros的分區信息是在kernel的啟動參數里面取得,所以這里還有分析啟動參數分區信息的功能;根據分區信息把MTD設備加到MTD核心中。

 

系統啟動時,MTD核心層可以直接編譯到kernel中,FLASH驅動層在系統啟動時解析分區信息,把MTD加入到MTD核心層,MTD核心層完成設備節點的創建,MTD字符設備注冊MTD字符設備信息,這樣訪問設備節點的時候就可以找到具體的執行函數。

 

 

圖1 Atheros MTD Subsystem Architecture

 

 

 

圖1顯示了各層的主要信息。

MTD設備的使用是通過訪問設備節點/dev/mtd0 /dev/mtd1,一般用法是

 

File *f = fopen(“/dev/mtd0”, “r”);

 

fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

 

/dev/mtd0 等設備是由MTD核心創建的,由add_mtd_device()->device_register()創建。fread/fwrite是由MTD字符設備層實現具體的讀寫,至於是如何找到MTD字符設備層的函數的,以及設備節點如何和這些函數關聯的,需要看下fread以及Linux的驅動模型(device_register/register_chrdev),以后再總結吧。

 

         MTD字符設備層是通過register_chrdev()來實現的,需要提供struct file_operations中的函數,這些函數完成對指定MTD的read/write/erase等。register_chrdev()之后,當用fread等訪問MTD的時候,就會找到這里注冊的函數。

 

static const struct file_operations mtd_fops = {

            .owner                     = THIS_MODULE,

            .llseek                       = mtd_lseek,

            .read             = mtd_read,

            .write                       = mtd_write,

            .ioctl              = mtd_ioctl,

#ifdef CONFIG_COMPAT

            .compat_ioctl       = mtd_compat_ioctl,

#endif

            .open                       = mtd_open,

            .release        = mtd_close,

            .mmap                    = mtd_mmap,

#ifndef CONFIG_MMU

            .get_unmapped_area = mtd_get_unmapped_area,

#endif

};

 

static int __init init_mtdchar(void)

{

            int status;

 

            status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);

            if (status < 0) {

                       printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",

                              MTD_CHAR_MAJOR);

            }

 

            return status;

}

 

         具體看下open/read/close是如何使用MTD的,get/put_mtd_device有MTD核心層提供,下面會具體看到。mtd_open 通過inode取得設備號,進一步通過get_mtd_device取得MTD的struct mtd_info信息,也就獲得了具體操作MTD的函數;以后的mtd_read/write等函數就可以直接使用mtd->read/mtd->write完成具體的操作。

static int mtd_open(struct inode *inode, struct file *file)

{

            int minor = iminor(inode);

            int devnum = minor >> 1;

            …

            mtd = get_mtd_device(NULL, devnum);

    ….

            mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);

            mfi->mtd = mtd;

            file->private_data = mfi;

out:

            unlock_kernel();

            return ret;

} /* mtd_open */

 

static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)

{

            struct mtd_file_info *mfi = file->private_data;

            struct mtd_info *mtd = mfi->mtd;

            …

            while (count) {

                       switch (mfi->mode) {

                       default:

                                   ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);

                       }

            }

    …

            return total_retlen;

} /* mtd_read */

 

static int mtd_close(struct inode *inode, struct file *file)

{

            struct mtd_file_info *mfi = file->private_data;

            struct mtd_info *mtd = mfi->mtd;

            put_mtd_device(mtd);

            file->private_data = NULL;

            kfree(mfi);

            return 0;

} /* mtd_close */

 

         MTD核心層提供的功能可以分為兩部分:面向上層用戶的API和面向底層用戶的API。上層用戶主要是指MTD字符設備層,字符設備層可以通過get_mtd_device獲得MTD的struct mtd_info 信息,然后通過mtd->read、mtd->write等操作MTD。使用方法可以參考字符設備層中函數的實現。下層用戶是FLASH驅動層,FLASH驅動層通過parse_mtd_partitions獲得kernel啟動參數中的分區信息,然后通過add_mtd_partitions把各個分區當做單獨的MTD設備加入到MTD核心層;為了分析kernel啟動參數,需要注冊parser函數,通過register_mtd_parser實現,這個函數的注冊可以由FLASH驅動層完成,也可以編譯到kernel中(make menuconfig->Device drivers->MTD support->Command line partition table parsing)。

 

至於注冊的parser是如何得到kernel啟動參數的,大概的過程是,bootloader通過bootargs的mtdparts傳入分區信息,這個分區信息會被當成kernel的啟動參數;kernel通過__setup("mtdparts=", mtdpart_setup) 注冊函數mtdpart_setup,mtdpart_setup保存分區信息,parser解析保存的信息,參考(cmdlinepart.c)。

 

         MTD核心層的實現原理是:

每個MTD用一個struct mtd_info描述,所有的mtd_info保存在struct mtd_info *mtd_table[]中,get/add/del_mtd_device都是操作mtd_table。

一個MTD FLASH可以邏輯上分為多個區,每個區當成一個MTD,分區信息用struct mtd_partition描述,通過分區信息加入MTD時,需要知道master MTD的信息,每個分區的信息多數來源於master MTD,master MTD不會加入MTD核心層,只有里面的各個分區會加入核心層,當成單獨的MTD。

MTD核心層還有notifier的功能,保存在struct mtd_notifier,所有的notifier保存在一個鏈表中。

具體可以參考Linux MTD 源代碼分析。

 

         FLASH驅動層提供mtd_info中read/write等的具體實現,如果有邏輯分區的話,這個mtd_info就是master MTD。然后調用parser_mtd_partitions 和add_mtd_partitions把邏輯分區加入到MTD核心層。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM