花了點時間學習Linux MTD層,下面簡單總結一下!
以下是MTD層的結構圖
MTD層實現了檔案系統與Flash之間的橋樑,下面就粗略說明字元型MTD驅動與下層驅動的關係。
分析對象:/drivers/mtdchar.c 字元型mtd裝置
字元裝置中定義了mtd_fops字元類的檔案指標操作函數,完成字元裝置讀寫與開啟等功能。
1. static int mtd_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
int devnum = minor >> 1;
int ret = 0;
struct mtd_info *mtd;
struct mtd_file_info *mfi;
........
mtd = get_mtd_device(NULL, devnum); //在原始裝置層實現, mtd/mtdcore.c檔案中被EXPORTS_SYMBOL
........
} /* mtd_open */
/*====================
get_mtd_device在原始裝置層中被實現,原始裝置層中維護了一個struct mtd_info *mtd_table[MAX_MTD_DEVICES],用來儲存註冊mtd的裝置列表。該檔案中(該層)實現了如下的函數對外Export_Symbol:
EXPORT_SYMBOL_GPL(add_mtd_device);
//用於硬體驅動層調用,註冊到原始裝置層,調用路徑為:s3c24xx_nand_probe()(/drivers/mtd/nand/s3c2410.c) --> add_one_partition(mtdpart.c) --> add_mtd_device()(mtdcore.c)
EXPORT_SYMBOL_GPL(del_mtd_device); //用法與上面差不多
EXPORT_SYMBOL_GPL(get_mtd_device); //得到table裡面的具體mtd裝置(mtd_info),該結構裡有read等函數;該函數主要為mtd字元裝置層調用
EXPORT_SYMBOL_GPL(get_mtd_device_nm); //通過名字得到mtd裝置(mtd_info)
EXPORT_SYMBOL_GPL(put_mtd_device); //釋放
EXPORT_SYMBOL_GPL(register_mtd_user); //註冊mtd_user;mtd原始層維護了一個mtd_notifiers鏈表,用於提供上層向下層註冊 struct mtd_notifier {void (*add)(struct mtd_info *mtd); void (*remove)(struct mtd_info *mtd);struct list_head
list; }
EXPORT_SYMBOL_GPL(unregister_mtd_user);
EXPORT_SYMBOL_GPL(default_mtd_writev);
2. 字元裝置mtdchar.c中的
static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{
。。。。。
ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
。。。。。
return total_retlen;
} /* mtd_read */
通過調用mtd原始層struct mtd_info *mtd_table[MAX_MTD_DEVICES]中對應的mtd_info中的封裝的read方法實現讀寫。
那麼該read方法是怎麼實現的呢?在/drivers/mtd/nand下有個nand_base.c中,公開了一些函數,在s3c2440.c中被調用
EXPORT_SYMBOL_GPL(nand_scan);
EXPORT_SYMBOL_GPL(nand_scan_ident);
EXPORT_SYMBOL_GPL(nand_scan_tail);
EXPORT_SYMBOL_GPL(nand_release);
其中nand_scan函數中完成了Mtd_info的read等函數的賦值:
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = nand_suspend;
mtd->resume = nand_resume;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
這些函數的功能,使得硬體驅動層struct nand_chip中的相關read函數與之關聯起來。
3. /driver/mtd/nand/s3c2410.c實現了與硬體相關的驅動程式:
該驅動是platform類型的驅動,其裝置的註冊在/arch/arm/plat-s3c24xx/common-smdk.c中:
static struct mtd_partition smdk_default_nand_part[]
將源結構體刪除,或者注釋掉,修改為以下形式
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "Boot",
.size = 0x00100000,
.offset = 0
},
[1] = {
.name = "MyApp",
.size = 0x003c0000,
.offset = 0x00140000,
},
[2] = {
.name = "Kernel",
.size = 0x00300000,
.offset = 0x00500000,
},
[3] = {
.name = "fs_yaffs",
.size = 0x03c00000, //30M
.offset = 0x00800000,
},
[4] = {
.name = "WINCE",
.size = 0x03c00000,
.offset = 0x04400000,
}
}; //該配置針對的是 FL2440中的nandflash分區劃分。common-smdk中smdk_machine_init調用了platform_add_devices(smdk_devs, ARRAY_SIZE(smdk_devs));函數,實現了平台裝置的註冊,這些裝置還包括看門狗,LCD等等。。。根據Platform的特點,在/drivers/mtd/nand/s3c2410.c中實現驅動的註冊就行了,下面這個函數會在platform類型的驅動被核心自動調用。該函數取出上面結構的資訊,分配給驅動儲存空間,nand_chip
初始化,以及調用mtd原始驅動提供的函數完成Mtd裝置的增加(s3c24xx_nand_probe()(/drivers/mtd/nand/s3c2410.c) --> add_one_partition(mtdpart.c) --> add_mtd_device()(mtdcore.c))。
static int s3c24xx_nand_probe(struct platform_device *pdev,
enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
。。。。。。
}
4. 對於一個Soc晶片來說,之需要實現與硬體相關的驅動層的結構體(nand_chip),mtd原始裝置層與mtd字元裝置,快裝置系統核心已經實現了,
nand_chip結構體為:
struct nand_chip {
void __iomem* IO_ADDR_R;
void __iomem* IO_ADDR_W;
uint8_t (* read_byte) ( struct mtd_info * mtd) ;
u16 ( *read_word) ( struct mtd_info * mtd) ;
void ( *write_buf) ( struct mtd_info * mtd, const uint8_t * buf, int len);
void ( *read_buf) ( struct mtd_info * mtd, uint8_t * buf, int len) ;
int ( *verify_buf) ( struct mtd_info * mtd, const uint8_t * buf, int len);
void ( *select_chip) ( struct mtd_info * mtd, int chip) ;
int ( *block_bad) ( struct mtd_info * mtd, loff_t ofs, int getchip);
int ( *block_markbad) ( struct mtd_info * mtd, loff_t ofs) ;
void ( *cmd_ctrl) ( struct mtd_info * mtd, int dat, unsigned int ctrl);
int ( *dev_ready) ( struct mtd_info * mtd) ;
void ( *cmdfunc) ( struct mtd_info * mtd, unsigned command, int column, intpage_addr) ;
int ( *waitfunc) ( struct mtd_info * mtd, struct nand_chip * this );
void ( *erase_cmd) ( struct mtd_info * mtd, int page) ;
int ( *scan_bbt) ( struct mtd_info * mtd) ;
int ( *errstat) ( struct mtd_info * mtd, struct nand_chip * this , intstate, int status, int page) ;
int ( *write_page) ( struct mtd_info * mtd, struct nand_chip * chip, constuint8_t * buf, int page, int cached, int raw) ;
……
structnand_ecc_ctrl ecc;
……
}
要想些SOC類型的Nandflash驅動可以重點研究/drivers/mtd/nand/s3c2410.c,仿造其來實現。注意:這裡可以調用的函數常常有/drivers/mtd/mtdcore.c,/dirvers/mtd/mtdpart.c , /driver/mtd/nand/nand_base.c中匯出的供下層使用的函數(kernel 2.6.28),另外,/drivers/mtd/chips中主要為Norflash驅動的實現,還沒來得及研究,後續在寫。