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核心層。