flash閃存設備和SD插卡設備是嵌入式設備用到的主要存儲設備,它們相當于PC機的硬盤。在嵌入設備特別是手持設備中,flash閃存是焊接在嵌入設備主板上的flash閃存芯片。在嵌入設備上有MMC/SD卡控制器及插槽,可通過MMC/SD來擴充存儲空間。
嵌入設備的存儲設備的空間劃分及所有邏輯設備和文件系統(tǒng)示例列出如下圖:
在嵌入設備上的flash芯片上blob和zImage直接按內(nèi)存線性地址存儲管理,對于flash芯片上留出的供用戶使用的存儲空間,使用MTDBLOCK塊設備和JFFS2文件系統(tǒng)。對于flash芯片的分區(qū)表信息則以MTDCHAR字符設備來存儲管理。
在嵌入設備上的MMC/SD插卡則由MMCBLOCK驅(qū)動程序和VFAT文件系統(tǒng)進行存儲管理。本章分析了MTD設備和MMC/SD驅(qū)動程序。
Figure 3-1. UBI/MTD Integration
目錄 |
Linux中MTD子系統(tǒng)在系統(tǒng)的硬件驅(qū)動程序和文件系統(tǒng)之間提供通用接口。在MTD上常用的文件文件系統(tǒng)是JFFS2日志閃存文件系統(tǒng)版本2(Journaling Flash FileSystem)。JFFS2用于微型嵌入式設備的原始閃存芯片的文件系統(tǒng)。JFFS2文件系統(tǒng)是日志結(jié)構(gòu)化的,這意味著它基本上是一長列節(jié)點。每個節(jié)點包含有關文件的部分信息 ― 可能是文件的名稱、也許是一些數(shù)據(jù)。與Ext2文件系統(tǒng)相比,JFFS2因為有以下這些優(yōu)點:
JFFS2在扇區(qū)級別上執(zhí)行閃存擦除/寫/讀操作要比Ext2文件系統(tǒng)好。JFFS2提供了比Ext2fs更好的崩潰/掉電安全保護。當需要更改少量數(shù)據(jù)時,Ext2文件系統(tǒng)將整個扇區(qū)復制到內(nèi)存(DRAM)中,在內(nèi)存中合并新數(shù)據(jù),并寫回整個扇區(qū)。這意味著為了更改單個字,必須對整個扇區(qū)(64 KB)執(zhí)行讀/擦除/寫例程 ,這樣做的效率非常低。JFFS2是附加文件而不是重寫整個扇區(qū),并且具有崩潰/掉電安全保護這一功能。
JFFS2是是為FLASH定制的文件系統(tǒng),JFFS1實現(xiàn)了日志功能,JFFS2實現(xiàn)了壓縮功能。它的整個設計提供了更好的閃存管理。JFFS2的 缺點很少,主要是當文件系統(tǒng)已滿或接近滿時,JFFS2會大大放慢運行速度。這是因為垃圾收集的問題。
MTD驅(qū)動程序是專門為基于閃存的設備所設計的,它提供了基于扇區(qū)的擦除和讀寫操作的更好的接口。MTD子系統(tǒng)支持眾多的閃存設備,并且有越來越多的驅(qū)動程序正被添加進來以用于不同的閃存芯片。
MTD子系統(tǒng)提供了對字符設備MTD_CHAR和塊設備MTD_BLOCK的支持。MTD_CHAR提供對閃存的原始字符訪問,象通常的IDE硬盤一樣,在MTD_BLOCK塊設備上可創(chuàng)建文件系統(tǒng)。MTD_CHAR字符設備文件是/dev/mtd0、mtd1、mtd2等,MTD_BLOCK塊設備文件是 /dev/mtdblock0、mtdblock1等等。
NAND和NOR是制作Flash的工藝,CFI和JEDEC是flash硬件提供的接口,linux通過這些用通用接口抽象出MTD設備。JFFS2文件系統(tǒng)就建立在MTD設備上。
NOR flash帶有SRAM接口,可以直接存取內(nèi)部的每一個字節(jié)。NAND器件使用串行I/O口來存取數(shù)據(jù), 8個引腳用來傳送控制、地址和數(shù)據(jù)信息。NAND讀和寫操作用512字節(jié)的塊。
MTD(memory technology device內(nèi)存技術設備)在硬件和文件系統(tǒng)層之間的提供了一個抽象的接口,MTD是用來訪問內(nèi)存設備(如:ROM、flash)的中間層,它將內(nèi)存設備的共有特性抽取出來,從而使增加新的內(nèi)存設備驅(qū)動程序變得更簡單。MTD的源代碼都在/drivers/mtd目錄中。
MTD中間層細分為四層,按從上到下依次為:設備節(jié)點、MTD設備層、MTD原始設備層和硬件驅(qū)動層。MTD中間層層次結(jié)構(gòu)圖如下:
Flash硬件驅(qū)動層對應的是不同硬件的驅(qū)動程序,它負責驅(qū)動具體的硬件。例如:符合CFI接口標準的Flash芯片驅(qū)動驅(qū)動程序在drivers/mtd/chips目錄中,NAND型Flash的驅(qū)動程序在/drivers/mtd/nand中。
在原始設備層中,各種內(nèi)存設備抽象化為原始設備,原始設備實際上是一種塊設備,MTD字符設備的讀寫函數(shù)也調(diào)用原始設備的操作函數(shù)來實現(xiàn)。MTD使用MTD信息結(jié)構(gòu)mtd_info來描述了原始設備的操作函數(shù)、各種信息,所有原始設備的信息也用一個全局的結(jié)構(gòu)數(shù)組來描述,列出如下(在drivers/mtd/mtdcore.c中):
struct mtd_info *mtd_table[MAX_MTD_DEVICES];
/* Our partition linked list */static LIST_HEAD(mtd_partitions);
static struct mtdblk_dev {struct mtd_info *mtd;int count;struct semaphore cache_sem;unsigned char *cache_data;unsigned long cache_offset;unsigned int cache_size;enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;} *mtdblks[MAX_MTD_DEVICES];
MTD原始設備層中封裝了三大類設備,分別是Inverse Flash、NAND Flash和MTD。它們的上體讀寫方法不一樣。這里只分析了MTD,因為它是最常用的。
原始設備層主要是通過mtd_info結(jié)構(gòu)來管理設備,函數(shù)add_mtd_partitions()和del_mtd_partitions()將的設備分區(qū)的mtd_info結(jié)構(gòu)加入mtd_table數(shù)組中,mtdpart.c中還實現(xiàn)了part_read、part_write等函數(shù),這些函數(shù)注冊在每個分區(qū)中,指向主分區(qū)的read、write函數(shù),之所以這樣做而不直接將主分區(qū)的read、write函數(shù)連接到每個分區(qū)中的原因是因為函數(shù)中的參數(shù)mtd_info會被調(diào)用者置為函數(shù)所屬的mtd_info,即mtd->read(mtd…),而參數(shù)mtd_info其實應該指向主分區(qū)。
設備層和原始設備層的函數(shù)調(diào)用關系圖如圖2。MTD各種結(jié)構(gòu)之間的關系圖如圖3。
struct mtdblk_dev {struct mtd_info mtd; / Locked */ 下層原始設備層的MTD設備結(jié)構(gòu)int count;struct semaphore cache_sem;unsigned char *cache_data; //緩沖區(qū)數(shù)據(jù)地址unsigned long cache_offset;//在緩沖區(qū)中讀寫位置偏移//緩沖區(qū)中的讀寫數(shù)據(jù)大?。ㄍǔ1辉O置為MTD設備的erasesize)unsigned int cache_size;enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;//緩沖區(qū)狀態(tài)}
struct mtd_info {u_char type; //內(nèi)存技術的類型u_int32_t flags; //標志位u_int32_t size; // mtd設備的大小
//“主要的”erasesize(同一個mtd設備可能有數(shù)種不同的erasesize)u_int32_t erasesize;u_int32_t oobblock; // oob塊大小,例如:512u_int32_t oobsize; //每個塊oob數(shù)據(jù)量,例如16u_int32_t ecctype; //ecc類型u_int32_t eccsize; //自動ecc可以工作的范圍
// Kernel-only stuff starts here.char *name;int index;
//可變擦除區(qū)域的數(shù)據(jù),如果是0,意味著整個設備為erasesizeint numeraseregions; //不同erasesize的區(qū)域的數(shù)目(通常是1)struct mtd_erase_region_info *eraseregions;u_int32_t bank_size;struct module *module;//此routine用于將一個erase_info加入erase queueint (*erase) (struct mtd_info *mtd, struct erase_info *instr);/* This stuff for eXecute-In-Place */int (*point) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char **mtdbuf);/* We probably shouldn’t allow XIP if the unpoint isn’t a NULL */void (*unpoint) (struct mtd_info *mtd, u_char * addr);int (*read) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf);int (*write) (struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf);int (*read_ecc) (struct mtd_info *mtd, loff_t from,size_t len, size_t *retlen, u_char *buf, u_char *eccbuf);int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf, u_char *eccbuf);int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len,size_t *retlen, u_char *buf);int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len,size_t *retlen, const u_char *buf);/* iovec-based read/write methods. We need these especially for NAND flash,with its limited number of write cycles per erase.NB: The ‘count’ parameter is the number of vectors, each ofwhich contains an (ofs, len) tuple.*/int (*readv) (struct mtd_info *mtd, struct iovec *vecs,unsigned long count, loff_t from, size_t *retlen);int (*writev) (struct mtd_info *mtd, const struct iovec *vecs,unsigned long count, loff_t to, size_t *retlen);/* Sync */void (*sync) (struct mtd_info *mtd);
/* Chip-supported device locking */int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);/* Power Management functions */int (*suspend) (struct mtd_info *mtd);void (*resume) (struct mtd_info *mtd);
void *priv; //指向map_info結(jié)構(gòu)}
static struct mtd_notifier notifier = {mtd_notify_add,mtd_notify_remove,NULL};
/* Our partition linked list */static LIST_HEAD(mtd_partitions); MTD原始設備分區(qū)的鏈表struct mtd_part {struct mtd_info mtd; //分區(qū)的信息(大部分由其master決定)struct mtd_info *master; //該分區(qū)的主分區(qū)u_int32_t offset; //該分區(qū)的偏移地址int index; //分區(qū)號struct list_head list;};
struct mtd_partition {char *name; //分區(qū)名 u_int32_t size; //分區(qū)大小u_int32_t offset; //在主MTD空間的偏移u_int32_t mask_flags;};
在具體的設備驅(qū)動程序初始化時,它會添加一個MTD設備結(jié)構(gòu)到mtd_table數(shù)組中。MTD翻譯層通過查找這個數(shù)組,可訪問到各個具體設備驅(qū)動程序。
函數(shù)init_mtdblock注冊一個MTD翻譯層設備,初始化處理請求的線程,賦上MTD翻譯層設備操作函數(shù)集實例,注冊這個設備的通用硬盤結(jié)構(gòu)。函數(shù)init_mtdblock調(diào)用層次圖如上圖。
mtd塊設備驅(qū)動程序利用一個線程,當有讀寫請求時,從緩沖區(qū)將數(shù)據(jù)寫入塊設備或從塊設備讀入到緩沖區(qū)中。
函數(shù)init_mtdblock分析如下(在drivers/mtd/mtdblock.c中):static int __init init_mtdblock(void){return register_mtd_blktrans(&mtdblock_tr);}
static struct mtd_blktrans_ops mtdblock_tr = {.name = “mtdblock”,.major = 31,.part_bits = 0,.open = mtdblock_open,.flush = mtdblock_flush,.release = mtdblock_release,.readsect = mtdblock_readsect,.writesect = mtdblock_writesect,.add_mtd = mtdblock_add_mtd,.remove_dev = mtdblock_remove_dev,.owner = THIS_MODULE,}; static LIST_HEAD(blktrans_majors);int register_mtd_blktrans(struct mtd_blktrans_ops *tr){
int ret, i;//如果第一個設備類型被注冊了,注冊notifier來阻止/* Register the notifier if/when the first device type isregistered, to prevent the link/init ordering from fuckingus over. */if (!blktrans_notifier.list.next)//如果不存在//注冊MTD翻譯層塊設備,創(chuàng)建通用硬盤結(jié)構(gòu)并注冊register_mtd_user(&blktrans_notifier);tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
if (!tr->blkcore_priv)return -ENOMEM;memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));down(&mtd_table_mutex);
//創(chuàng)建blk_major_name結(jié)構(gòu)初始化后加到&major_names[]數(shù)組中ret = register_blkdev(tr->major, tr->name);…
spin_lock_init(&tr->blkcore_priv->queue_lock);init_completion(&tr->blkcore_priv->thread_dead);init_waitqueue_head(&tr->blkcore_priv->thread_wq);
//創(chuàng)建請求隊列并初始化,賦上塊設備特定的請求處理函數(shù)mtd_blktrans_requesttr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request,&tr->blkcore_priv->queue_lock);…
tr->blkcore_priv->rq->queuedata = tr;//賦上MTD翻譯層塊設備操作函數(shù)集//創(chuàng)建線程mtd_blktrans_threadret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL);…
//在devfs文件系統(tǒng)中創(chuàng)建設備的目錄名devfs_mk_dir(tr->name);
INIT_LIST_HEAD(&tr->devs);//初始化設備的鏈表list_add(&tr->list, &blktrans_majors);for (i=0; i<MAX_MTD_DEVICES; i++) {if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)//創(chuàng)建MTD翻譯層設備結(jié)構(gòu)并初始化,然后到MTD設備鏈表中tr->add_mtd(tr, mtd_table[i]);}
up(&mtd_table_mutex);return 0;}
static void mtd_blktrans_request(struct request_queue *rq){
struct mtd_blktrans_ops *tr = rq->queuedata;wake_up(&tr->blkcore_priv->thread_wq);}
static int mtd_blktrans_thread(void *arg){
struct mtd_blktrans_ops *tr = arg;struct request_queue *rq = tr->blkcore_priv->rq;/* we might get involved when memory gets low, so use PF_MEMALLOC */ current->flags |= PF_MEMALLOC | PF_NOFREEZE;//變成以init為父進程的后臺進程daemonize(“%sd”, tr->name);
//因為一些內(nèi)核線程實際上要與信號打交道,daemonize()沒有做后臺化工作。//我們不能僅調(diào)用exit_sighand函數(shù),//因為當最終退出時這樣將可能引起oop(對象指針溢出錯誤)。 spin_lock_irq(¤t->sighand->siglock);sigfillset(¤t->blocked);
// 重新分析是否有掛起信號并設置或清除TIF_SIGPENDING標識給當前進程recalc_sigpending();spin_unlock_irq(¤t->sighand->siglock);spin_lock_irq(rq->queue_lock);
while (!tr->blkcore_priv->exiting) {struct request *req;struct mtd_blktrans_dev *dev;int res = 0;DECLARE_WAITQUEUE(wait, current); //聲明當前進程的等待隊列
req = elv_next_request(rq);//從塊設備的請求隊列中得到下一個請求
if (!req) {//如果請求不存在//將設備的等待線程加到等待隊列中add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);set_current_state(TASK_INTERRUPTIBLE);spin_unlock_irq(rq->queue_lock);schedule(); //調(diào)度讓CPU有機會執(zhí)行等待的線程 remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);spin_lock_irq(rq->queue_lock);continue;}
//如果請求存在dev = req->rq_disk->private_data;//得到請求的設備tr = dev->tr; //得到MTD翻譯層設備操作函數(shù)集實例spin_unlock_irq(rq->queue_lock);down(&dev->sem); res = do_blktrans_request(tr, dev, req);//處理請求 up(&dev->sem);spin_lock_irq(rq->queue_lock);end_request(req, res); //從請求隊列中刪除請求并更新統(tǒng)計信息}spin_unlock_irq(rq->queue_lock);//調(diào)用所有請求處理完的回調(diào)函數(shù),并調(diào)用do_exit函數(shù)退出線程complete_and_exit(&tr->blkcore_priv->thread_dead, 0);}
static int do_blktrans_request(struct mtd_blktrans_ops *tr,struct mtd_blktrans_dev *dev,struct request *req){unsigned long block, nsect;char *buf; block = req->sector;nsect = req->current_nr_sectors;buf = req->buffer; if (!(req->flags & REQ_CMD))return 0;//如果讀寫的扇區(qū)數(shù)超出了塊設備的容量,返回if (block + nsect > get_capacity(req->rq_disk))return 0; //根據(jù)(rq)->flags & 1標識來判斷操作方式,調(diào)用具體的設備操作函數(shù)switch(rq_data_dir(req)) {case READ:for (; nsect > 0; nsect--, block++, buf += 512)if (tr->readsect(dev, block, buf))return 0;return 1; case WRITE:if (!tr->writesect)return 0;for (; nsect > 0; nsect--, block++, buf += 512)if (tr->writesect(dev, block, buf))return 0;return 1; default:printk(KERN_NOTICE “Unknown request %ld\n”, rq_data_dir(req));return 0;}}
static struct mtd_notifier blktrans_notifier = {.add = blktrans_notify_add,.remove = blktrans_notify_remove,};
static LIST_HEAD(mtd_notifiers);void register_mtd_user (struct mtd_notifier *new){int i;down(&mtd_table_mutex);//將MTD塊設備的通知結(jié)構(gòu)實例blktrans_notifier加入//到全局鏈表mtd_notifiers上list_add(&new->list, &mtd_notifiers);//模塊引用計數(shù)加1__module_get(THIS_MODULE); //對每個MTD塊設備調(diào)用MTD通知結(jié)構(gòu)實例的加設備函數(shù)for (i=0; i< MAX_MTD_DEVICES; i++)if (mtd_table[i])new->add(mtd_table[i]);up(&mtd_table_mutex);}
static LIST_HEAD(blktrans_majors);static void blktrans_notify_add(struct mtd_info *mtd){struct list_head *this;if (mtd->type == MTD_ABSENT)//設備不存在return; //遍歷每個MTD主塊設備list_for_each(this, &blktrans_majors) {struct mtd_blktrans_ops *tr = list_entry(this,struct mtd_blktrans_ops, list);tr->add_mtd(tr, mtd);} }
static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr,struct mtd_info *mtd){
struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return;memset(dev, 0, sizeof(*dev));dev->mtd = mtd;dev->devnum = mtd->index;dev->blksize = 512;dev->size = mtd->size >> 9;dev->tr = tr;
if (!(mtd->flags & MTD_WRITEABLE))dev->readonly = 1;add_mtd_blktrans_dev(dev);}
函數(shù)add_mtd_blktrans_dev分析如下(在drivers/mtd/mtd_blkdevs.c中):
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new){struct mtd_blktrans_ops *tr = new->tr;struct list_head *this;int last_devnum = -1;struct gendisk *gd; if (!down_trylock(&mtd_table_mutex)) {up(&mtd_table_mutex);BUG();}//遍歷MTD每個主塊設備list_for_each(this, &tr->devs) {struct mtd_blktrans_dev *d = list_entry(this,struct mtd_blktrans_dev,list); if (new->devnum == -1) {//如果沒有設備號//使用第一個空閑的設備號if (d->devnum != last_devnum+1) {//找到空閑設備號,并把設備加到鏈表的尾部new->devnum = last_devnum+1;list_add_tail(&new->list, &d->list);goto added;}} else if (d->devnum == new->devnum) {//設備號已被使用/* Required number taken */return -EBUSY;} else if (d->devnum > new->devnum) {//申請的設備號是空閑的,加到鏈表的尾部list_add_tail(&new->list, &d->list);goto added;}last_devnum = d->devnum;} if (new->devnum == -1)//如果新設備的設備號為-1,就賦上(最后一個設備號+1)new->devnum = last_devnum+1;//所有的設備號*分區(qū)數(shù) > 256 if ((new->devnum << tr->part_bits) > 256) {return -EBUSY;} init_MUTEX(&new->sem);list_add_tail(&new->list, &tr->devs);//加到鏈表尾部 added:if (!tr->writesect)new->readonly = 1;//分配通知硬盤結(jié)構(gòu)gendisk,每分區(qū)一個gd = alloc_disk(1 << tr->part_bits);if (!gd) {list_del(&new->list);return -ENOMEM;} //初始化通用硬盤結(jié)構(gòu)gd->major = tr->major;gd->first_minor = (new->devnum) << tr->part_bits;gd->fops = &mtd_blktrans_ops;snprintf(gd->disk_name, sizeof(gd->disk_name),“%s%c”, tr->name, (tr->part_bits?’a’:’0’) + new->devnum);snprintf(gd->devfs_name, sizeof(gd->devfs_name),“%s/%c”, tr->name, (tr->part_bits?’a’:’0’) + new->devnum); /* 2.5 has capacity in units of 512 bytes while stillhaving BLOCK_SIZE_BITS set to 10. Just to keep us amused. */set_capacity(gd, (new->size * new->blksize) >> 9);gd->private_data = new; //通用硬盤結(jié)構(gòu)的私有數(shù)據(jù)指向翻譯層的MTD設備new->blkcore_priv = gd;gd->queue = tr->blkcore_priv->rq; //設置請求隊列 if (new->readonly)set_disk_ro(gd, 1); //設置硬盤讀寫模式add_disk(gd);//加通用硬盤結(jié)構(gòu)到全局鏈表中return 0;}
MTD翻譯層設備操作函數(shù)集實例mtdblock_tr有對MTD設備的各種操作函數(shù),這些操作函數(shù)調(diào)用了mtd_info結(jié)構(gòu)中的操作函數(shù)。這里只分析了函數(shù)mtdblock_writesect,它的源代碼都在drivers/mtd/mtdblock.c中。由于flash設備需要先擦除一個扇區(qū),再才能寫一個扇區(qū),因而,使用了緩存來幫助不是正好一個扇區(qū)的數(shù)據(jù)的寫操作。
函數(shù)mtdblock_writesect將數(shù)據(jù)寫入到flash設備中。函數(shù)分析如下:static int mtdblock_writesect(struct mtd_blktrans_dev *dev,unsigned long block, char *buf){//從MTD塊設備數(shù)組中得到塊設備結(jié)構(gòu)struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {//分配塊設備用于擦除的緩存空間mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);if (!mtdblk->cache_data)return -EINTR; }//從位置block開始寫一個扇區(qū)(512字節(jié))return do_cached_write(mtdblk, block<<9, 512, buf);}
static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,int len, const char *buf){struct mtd_info *mtd = mtdblk->mtd;//得到擦除緩沖區(qū)大小unsigned int sect_size = mtdblk->cache_size;size_t retlen;int ret; if (!sect_size)//如果塊設備的緩沖大小為0,直接寫設備return MTD_WRITE (mtd, pos, len, &retlen, buf); while (len > 0) {//將要寫的在設備上的位置pos地址處,長度為len // |<-offset-->|<-size-->| // ----------sect_start---|pos-----len-|// |<- sect_size ->|//計算扇區(qū)開始位置unsigned long sect_start = (pos/sect_size)*sect_size;//計算出相對扇區(qū)開始位置的偏移unsigned int offset = pos - sect_start;//計算出所寫的大小unsigned int size = sect_size - offset;if( size > len )size = len; if (size == sect_size) {//正好是擦除緩沖區(qū)大小//直接寫入,不需要通過緩沖區(qū) ret = erase_write (mtd, pos, size, buf);if (ret)return ret;} else {//只有部分扇區(qū)大小的數(shù)據(jù),需通過緩沖區(qū)補充成扇區(qū)大小 //方法是:先從設備中讀出數(shù)據(jù)到緩沖區(qū),再將buf中數(shù)據(jù)拷貝到緩沖區(qū),//這樣,湊合成一個扇區(qū)大小的數(shù)據(jù),再把緩沖區(qū)數(shù)據(jù)寫入設備。//如果緩沖區(qū)數(shù)據(jù)是臟的,把緩沖區(qū)數(shù)據(jù)寫設備if (mtdblk->cache_state == STATE_DIRTY &&mtdblk->cache_offset != sect_start) {ret = write_cached_data(mtdblk);if (ret)return ret;} if (mtdblk->cache_state == STATE_EMPTY ||mtdblk->cache_offset != sect_start) {//把當前的扇區(qū)數(shù)據(jù)填充緩沖區(qū) mtdblk->cache_state = STATE_EMPTY;ret = MTD_READ(mtd, sect_start, sect_size, &retlen,mtdblk->cache_data);if (ret)return ret;if (retlen != sect_size)return -EIO;mtdblk->cache_offset = sect_start;mtdblk->cache_size = sect_size;mtdblk->cache_state = STATE_CLEAN;} //將數(shù)據(jù)從buf中拷貝到緩沖區(qū)中memcpy (mtdblk->cache_data + offset, buf, size);mtdblk->cache_state = STATE_DIRTY;} buf += size;pos += size;len -= size;} return 0;}
static int write_cached_data (struct mtdblk_dev *mtdblk){
struct mtd_info *mtd = mtdblk->mtd;int ret;
if (mtdblk->cache_state != STATE_DIRTY)return 0;ret = erase_write (mtd, mtdblk->cache_offset,mtdblk->cache_size, mtdblk->cache_data);
if (ret)return ret;mtdblk->cache_state = STATE_EMPTY;return 0;}
static int erase_write (struct mtd_info *mtd, unsigned long pos,int len, const char *buf){
struct erase_info erase;DECLARE_WAITQUEUE(wait, current);wait_queue_head_t wait_q;size_t retlen;int ret;
//首先,擦除flash閃存塊init_waitqueue_head(&wait_q);erase.mtd = mtd;erase.callback = erase_callback;erase.addr = pos;erase.len = len;erase.priv = (u_long)&wait_q;
set_current_state(TASK_INTERRUPTIBLE);add_wait_queue(&wait_q, &wait);
ret = MTD_ERASE(mtd, &erase);if (ret) {//如果擦除完成set_current_state(TASK_RUNNING);//運行當前進程remove_wait_queue(&wait_q, &wait);//清除等待隊列 return ret;}
schedule(); //調(diào)度來等待擦除工作的完成remove_wait_queue(&wait_q, &wait); //清除等待隊列//第二步,寫數(shù)據(jù)到flash設備 ret = MTD_WRITE (mtd, pos, len, &retlen, buf); if (ret)return ret;if (retlen != len)return -EIO;return 0;}
static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,int len, char *buf)
MTD核心主要工作是進行電源管理及在/proc文件系統(tǒng)中輸出MTD設備的信息。函數(shù)init_mtd初始化proc文件系統(tǒng)函數(shù)、注冊電源管理函數(shù)、初始化mtd設備函數(shù),清除模塊函數(shù)做相反的一些清除工作。
函數(shù)init_mtd分析如下(在linux/drivers/mtd/mtd_core.c中):int __init init_mtd(void){if ((proc_mtd = create_proc_entry( “mtd”, 0, 0 )))proc_mtd->read_proc = mtd_read_proc;mtd_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, mtd_pm_callback);return 0;}static void __exit cleanup_mtd(void){if (mtd_pm_dev) {pm_unregister(mtd_pm_dev);mtd_pm_dev = NULL;} if (proc_mtd)remove_proc_entry( “mtd”, 0);}
mtd_pm_callback函數(shù)通過各個設備的MTD設備結(jié)構(gòu)mtd_info將電源管理請求傳給具體的設備驅(qū)動程序。mtd_pm_callback函數(shù)列出如下:
static int mtd_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data){int ret = 0, i;if (down_trylock(&mtd_table_mutex))return -EAGAIN; if (rqst == PM_SUSPEND) {//電源掛起狀態(tài)for (i = 0; ret == 0 && i < MAX_MTD_DEVICES; i++) {if (mtd_table[i] && mtd_table[i]->suspend)ret = mtd_table[i]->suspend(mtd_table[i]);}} else i = MAX_MTD_DEVICES-1; if (rqst == PM_RESUME || ret) {//電源恢復for ( ; i >= 0; i--) {if (mtd_table[i] && mtd_table[i]->resume)mtd_table[i]->resume(mtd_table[i]);}}up(&mtd_table_mutex);return ret;}
當系統(tǒng)打開flash設備上的文件,它建立好了文件的操作函數(shù)集實例,當對文件操作時,就調(diào)用了這個文件操作函數(shù)集實例中的函數(shù)。當flash設備當作字符設備時,這些操作函數(shù)通過MTD設備的操作函數(shù)把數(shù)據(jù)直接讀入/寫出flash設備。
函數(shù)init_mtdchar注冊了一個字符設備,列出如下(在drivers/mtd/mtdchar.c中):static int __init init_mtdchar(void){if (register_chrdev(MTD_CHAR_MAJOR, “mtd”, &mtd_fops)) {printk(KERN_NOTICE “Can’t allocate major number %dfor Memory Technology Devices.\n”,MTD_CHAR_MAJOR);return -EAGAIN;} mtdchar_devfs_init();return 0;}
static struct file_operations mtd_fops = {.owner = THIS_MODULE,.llseek = mtd_lseek,.read = mtd_read,.write = mtd_write,.ioctl = mtd_ioctl,.open = mtd_open,.release = mtd_close,
};
函數(shù)mtd_write分析如下:
static ssize_t mtd_write(struct file *file, const char __user *buf,size_t count,loff_t *ppos){struct mtd_info *mtd = file->private_data; //得到MTD設備結(jié)構(gòu)char *kbuf;size_t retlen;size_t total_retlen=0;int ret=0;int len; DEBUG(MTD_DEBUG_LEVEL0,”MTD_write\n”);if (*ppos == mtd->size)return -ENOSPC;if (*ppos + count > mtd->size)count = mtd->size - *ppos; if (!count)return 0;while (count) {if (count > MAX_KMALLOC_SIZE)len = MAX_KMALLOC_SIZE;elselen = count; kbuf=kmalloc(len,GFP_KERNEL);//分配bufferif (!kbuf) {printk(“kmalloc is null\n”);return -ENOMEM;} //從用戶空間buf拷貝數(shù)據(jù)到內(nèi)核空間kbufif (copy_from_user(kbuf, buf, len)) {kfree(kbuf);return -EFAULT;} //調(diào)用設備的寫函數(shù)ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);if (!ret) {*ppos += retlen;total_retlen += retlen;count -= retlen;buf += retlen;}else {kfree(kbuf);return ret;} kfree(kbuf);} return total_retlen;} /* mtd_write */
(1)flash芯片映射信息結(jié)構(gòu)
flash芯片映射信息結(jié)構(gòu)map_info描述了每一排閃存的映射信息。如:每一排閃存的驅(qū)動程序、物理地址、讀寫操作函數(shù)和映射地址等。如果設備需要它,系統(tǒng)就必須把它傳遞到芯片探測例程do_map_probe中。JEDEC和CFI接口的芯片都用這個探測函數(shù)。如果芯片被識別,就會激活合適的芯片驅(qū)動程序并返回一個mtd_info結(jié)構(gòu)。同時,系統(tǒng)使用這個驅(qū)動程序的模塊地址填充mtd->module,并把它注冊到MTD核心代碼中?;蛘呷绻蟹謪^(qū),就注冊分區(qū)。map_info結(jié)構(gòu)保存在mtd->priv域,芯片驅(qū)動程序需要的更多的信息通過鏈接mtd->priv->fldrv_priv可得到。
flash芯片映射信息結(jié)構(gòu)map_info列出如下(在include/linux/mtd/mtd.h中):struct map_info {char *name;unsigned long size; //flash大小unsigned long phys; //起始物理地址 #define NO_XIP (-1UL)void __iomem *virt; //I/O映射的虛擬地址void *cached; //8位的字節(jié),它不是實際總線的必要寬度。//在再次與第一個芯片通信之前,它是字節(jié)上重復的間隔int bankwidth;#ifdef CONFIG_MTD_COMPLEX_MAPPINGS map_word (*read)(struct map_info *, unsigned long);void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);void (*write)(struct map_info *, const map_word, unsigned long);void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);/* We can perhaps put in ‘point’ and ‘unpoint’ methods, if we reallywant to enable XIP for non-linear mappings. Not yet though. */#endif /*在映射驅(qū)動程序的copy_from應用中,映射驅(qū)動程序使用緩存是可能的。然而,當芯片驅(qū)動程序知道一些flash區(qū)域已改變內(nèi)容時,系統(tǒng)在必要時將通過這個例程發(fā)信號給芯片驅(qū)動程序,讓映射驅(qū)動程序使緩存無效。如果沒有緩存時,把這個域設為NULL。*/void (*inval_cache)(struct map_info *, unsigned long, ssize_t);/* set_vpp() must handle being reentered—enable, enable, disablemust leave it enabled. */void (*set_vpp)(struct map_info *, int);unsigned long map_priv_1;unsigned long map_priv_2;void *fldrv_priv;struct mtd_chip_driver *fldrv; //flash芯片驅(qū)動程序};
struct mtd_chip_driver {struct mtd_info *(*probe)(struct map_info *map);//探測函數(shù)void (*destroy)(struct mtd_info *);struct module *module; //驅(qū)動程序的模塊結(jié)構(gòu)char *name; //驅(qū)動程序名struct list_head list;};
每種flash控制芯片可控制多種閃存,這個控制芯片的驅(qū)動程序有自己的讀寫和探測操作函數(shù)或者使用通用的操作函數(shù),它注冊MTD驅(qū)動程序結(jié)構(gòu)mtd_chip_driver到一個全局鏈表chip_drvs_list中。當用戶使用一種flash閃存時,用戶在稱為"映射驅(qū)動程序"的文件中分配好地址、進行閃存空間分區(qū)后,使用探測程序查找相應的控制芯片驅(qū)動程序。映射驅(qū)動程序用來填充一些閃存空間分配的一些信息,代碼放在drivers/mtd/map目錄下。
在/drivers/mtd/chips目錄下有各種flash控制芯片的驅(qū)動程序及芯片探測程序,這些文件有chipreg.c、gen_probe.c、cfi_probe.c、jedec_probe.c、cfi_cmdset_0001.c、cfi_cmdset_0002.c、map_rom.c、map_ram.c、map_absent.c、amd_flash.c、jedec.c和sharp.c。CFI設備和JEDEC設備都要用到gen_probe.c文件。
確定flash閃存芯片是否支持CFI接口的方法是:向flash閃存的地址0x55H寫入數(shù)據(jù)0x98H,再從flash閃存的地址0x10H處開始,讀取3個存儲單元,如果字符分別為’Q’,’R’和’Y’,那么flash閃存芯片是支持CFI接口的。這個方法在文件cfi_probe.c函數(shù)qry_present中實現(xiàn)。支持CFI接口flash閃存芯片的類型名稱為 "cfi_probe"。
也可以用JEDEC(電子電器設備聯(lián)合會)標準設備模仿CFI接口,探測JEDEC設備的程序在jedec_probe.c中,JEDEC設備的類型為"jedec_probe"。
對于flash芯片,不同的制造商使用不同的命令集,目前Linux的MTD實現(xiàn)的命令集有AMD/Fujitsu的標準命令集和Intel/Sharp的擴展命令集(兼容Intel/Sharp標準命令集)兩個,這兩個命令集分別在cfi_cmdset_0002.c和cfi_cmdset_0001.c中實現(xiàn)。
此外還有一些非CFI標準的Flash,其中"jedec"類型的Flash的探測程序在jedec.c中,"sharp"類型的Flash的探測程序在sharp.c中,"amd_flash"類型的Flash的探測程序在amd_flash.c中。
最后,還有一些非Flash的MTD,比如ROM或absent(無)設備。這些設備的探測程序在map_rom.c、map_ram.c和map_absent.c中。
chip_drvs_list是所有芯片類型的驅(qū)動器鏈表,flash控制芯片的驅(qū)動程序通過調(diào)用register_mtd_chip_driver()和unregister_mtd_chip_driver()向此鏈表中添加或去除MTD芯片驅(qū)動結(jié)構(gòu)。這兩個函數(shù)列出如下(在drivers/mtd/chips/chipreg.c中):void register_mtd_chip_driver(struct mtd_chip_driver *drv){spin_lock(&chip_drvs_lock);list_add(&drv->list, &chip_drvs_list);spin_unlock(&chip_drvs_lock);}void unregister_mtd_chip_driver(struct mtd_chip_driver *drv){spin_lock(&chip_drvs_lock);list_del(&drv->list);spin_unlock(&chip_drvs_lock);}
函數(shù)do_map_probe分析如下(在drivers/mtd/chips/chipreg.c中):
struct mtd_info *do_map_probe(const char *name, struct map_info *map){struct mtd_chip_driver *drv;struct mtd_info *ret;//查找得到name類型的控制芯片驅(qū)動程序結(jié)構(gòu)drv = get_mtd_chip_driver(name);if (!drv && !request_module(“%s”, name))drv = get_mtd_chip_driver(name);if (!drv)return NULL; ret = drv->probe(map); //具體控制芯片驅(qū)動程序的探測函數(shù)//使用計數(shù)減1,它可能已是一個探測過的模塊,在這不需要再探測, //而在實際的驅(qū)動程序中已做處理。module_put(drv->module);if (ret)return ret;return NULL;}
(1)CFI控制芯片驅(qū)動程序
CFI控制芯片驅(qū)動程序cfi_probe在drivers/mtd/chip/cfi_probe.c中,這里只做了簡單的說明。static struct mtd_chip_driver cfi_chipdrv = {.probe = cfi_probe,.name = “cfi_probe”,.module = THIS_MODULE};
int __init cfi_probe_init(void){
register_mtd_chip_driver(&cfi_chipdrv);return 0;};static void __exit cfi_probe_exit(void){
unregister_mtd_chip_driver(&cfi_chipdrv);};
struct mtd_info *cfi_probe(struct map_info *map){return mtd_do_chip_probe(map, &cfi_chip_probe);} static struct chip_probe cfi_chip_probe = {.name = “CFI”,.probe_chip = cfi_probe_chip};
用戶可設置flash空間映射信息填充在映射驅(qū)動程序中,包括該MTD原始設備的起始物理地址、大小、分區(qū)情況等。映射驅(qū)動程序都在drivers/mtd/maps子目錄下。這里簡單說明cfi_flagadm映射驅(qū)動程序(在cfi_flagadm.c中)。
flagadm_map是映射信息結(jié)構(gòu),它含有flash存儲空間的配置信息,列出如下:struct map_info flagadm_map = {.name = “FlagaDM flash device”,.size = FLASH_SIZE,.bankwidth = 2,};
struct mtd_partition flagadm_parts[] = {{.name = “Bootloader”,.offset = FLASH_PARTITION0_ADDR,.size = FLASH_PARTITION0_SIZE},{.name = “Kernel image”,.offset = FLASH_PARTITION1_ADDR,.size = FLASH_PARTITION1_SIZE},{.name = “Initial ramdisk image”,.offset = FLASH_PARTITION2_ADDR,.size = FLASH_PARTITION2_SIZE},{.name = “Persistant storage”,.offset = FLASH_PARTITION3_ADDR,.size = FLASH_PARTITION3_SIZE}}; #define PARTITION_COUNT (sizeof(flagadm_parts)/sizeof(struct mtd_partition?
static struct mtd_info *mymtd;
int __init init_flagadm(void){printk(KERN_NOTICE “FlagaDM flash device: %x at %x\n”,FLASH_SIZE, FLASH_PHYS_ADDR);flagadm_map.phys = FLASH_PHYS_ADDR; //端口映射flagadm_map.virt = ioremap(FLASH_PHYS_ADDR,FLASH_SIZE);if (!flagadm_map.virt) {printk(“Failed to ioremap\n”);return -EIO;} //賦上通用的讀寫操作函數(shù),如:__raw_writeb等simple_map_init(&flagadm_map);//探測CFI類型接口得到MTD設備結(jié)構(gòu)mymtd = do_map_probe(“cfi_probe”, &flagadm_map);if (mymtd) {mymtd->owner = THIS_MODULE;//將分區(qū)信息加到MTD設備結(jié)構(gòu)實例mymtd中add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT);printk(KERN_NOTICE “FlagaDM flash device initialized\n”);return 0;} iounmap((void *)flagadm_map.virt);//取消端口映射 return -ENXIO;} static void __exit cleanup_flagadm(void){if (mymtd) {del_mtd_partitions(mymtd);map_destroy(mymtd);} if (flagadm_map.virt) {iounmap((void *)flagadm_map.virt);flagadm_map.virt = 0;}}
SD/MMC卡組成的存儲系統(tǒng)是許多嵌入設備的主要存儲設備,相當于PC機的硬盤,在嵌入設備上的SD/MMC卡控制器通過MMC協(xié)議來解析命令控制SD/MMC卡的操作。SD/MMC卡上有一些寄存器來控制卡的狀態(tài)及讀寫操作。MMC協(xié)議規(guī)定的寄存器有:CID寄存器,128位,是卡的鑒別寄存器,存有卡的鑒別信息;RCA寄存器是16位,存有卡的本地系統(tǒng)的相對地址,在初始化時由控制器動態(tài)指定。DSR寄存器是16位,是配置卡的驅(qū)動程序的寄存器,是可選的。CSD寄存器是卡特定數(shù)據(jù)信息描述寄存器,是可選的。OCR寄存器是操作控制寄存器。MMC卡的系統(tǒng)定義及相關協(xié)議請查詢《MMC卡系統(tǒng)定義3.1版本》。
MMC驅(qū)動程序以分通用設備層、MMC抽象設備層、MMC協(xié)議層和具體設備層四層來構(gòu)建,上一層抽象出下一層的共有特性,每一層以相應的結(jié)構(gòu)來描述。通用設備層對于塊設備來說,主要負責設備內(nèi)核對象在sysfs文件系統(tǒng)中的管理、請求隊列管理、及與文件系統(tǒng)的接口,MMC抽象設備層抽出MMC卡的共有特性,如:MMC卡的請求管理、電源管理等。MMC協(xié)議層將MMC操作分解成標準的MMC協(xié)議,具體設備層則負責具體物理設備的寄存器控制等。這種分層結(jié)構(gòu)層次分明,管理有效。MMC驅(qū)動程序的層次結(jié)構(gòu)如下圖。
MMC驅(qū)動程序主要處理兩部分的內(nèi)容,一是創(chuàng)建通用硬盤結(jié)構(gòu)向系統(tǒng)注冊,以便系統(tǒng)對MMC設備的管理。另一方面,要完成系統(tǒng)分發(fā)過來的讀寫請求的處理。
MMC設備由控制器及插卡組成,對應的設備結(jié)構(gòu)為mmc_host結(jié)構(gòu)和mmc_card結(jié)構(gòu)。MMC卡設備相關結(jié)構(gòu)關系圖如上圖。下面分別說明設備相關結(jié)構(gòu):
每個卡的插槽對應一個塊的數(shù)據(jù)結(jié)構(gòu)mmc_blk_data,結(jié)構(gòu)列出如下(在drivers/mmc/mmc_block.c中):
struct mmc_blk_data {spinlock_t lock;struct gendisk *disk; //通用硬盤結(jié)構(gòu)struct mmc_queue queue; //MMC請求隊列結(jié)構(gòu) unsigned int usage;unsigned int block_bits; //卡每一塊大小所占的bit位};
結(jié)構(gòu)mmc_card是一個插卡的特性描述結(jié)構(gòu),它代有了一個插卡。列出如下(在include/linux/mmc/card.h中):
struct mmc_card {struct list_head node; //在主設備鏈表中的節(jié)點struct mmc_host *host; // 卡所屬的控制器struct device dev; //通用設備結(jié)構(gòu)unsigned int rca; //設備的相對本地系統(tǒng)的地址unsigned int state; //卡的狀態(tài)#define MMC_STATE_PRESENT (1<<0) //卡出現(xiàn)在sysfs文件系統(tǒng)中#define MMC_STATE_DEAD (1<<1) //卡不在工作狀態(tài)#define MMC_STATE_BAD (1<<2) //不認識的設備u32 raw_cid[4]; /* raw card CID */u32 raw_csd[4]; /* raw card CSD */struct mmc_cid cid; //卡的身份鑒別,值來自卡的CID寄存器struct mmc_csd csd; //卡特定信息,值來自卡的CSD寄存器};
結(jié)構(gòu)mmc_host描述了一個MMC卡控制器的特性及操作等,結(jié)構(gòu)mmc_host列出如下(在include/linux/mmc/host.h中):
struct mmc_host {struct device *dev; //通用設備結(jié)構(gòu)struct mmc_host_ops *ops; //控制器操作函數(shù)集結(jié)構(gòu)unsigned int f_min;unsigned int f_max;u32 ocr_avail; //卡可用的OCR寄存器值char host_name[8]; //控制器名字 //主控制器中與塊層請求隊列相關數(shù)據(jù)unsigned int max_seg_size; //最大片斷的尺寸 unsigned short max_hw_segs; //最大硬件片斷數(shù) unsigned short max_phys_segs; //最大物理片斷數(shù) unsigned short max_sectors; //最大扇區(qū)數(shù) unsigned short unused; //私有數(shù)據(jù)struct mmc_ios ios; //當前i/o總線設置u32 ocr; //當前的OCR設置 struct list_head cards; //接在這個主控制器上的設備 wait_queue_head_t wq; //等待隊列spinlock_t lock; //卡忙時的鎖struct mmc_card *card_busy; //正與主控制器通信的卡 struct mmc_card *card_selected; //選擇的MMC卡 struct work_struct detect; //工作結(jié)構(gòu)};
結(jié)構(gòu)mmc_host_ops是控制器的操作函數(shù)集,它包括請求處理函數(shù)指針和控制器對卡I/O的狀態(tài)的設置函數(shù)指針,結(jié)構(gòu)mmc_host_ops列出如下:
struct mmc_host_ops {void (*request)(struct mmc_host *host, struct mmc_request *req);void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);}
結(jié)構(gòu)mmc_ios描述了控制器對卡的I/O狀態(tài),列出如下:
struct mmc_ios {unsigned int clock; //時鐘頻率unsigned short vdd;unsigned char bus_mode; //命令輸出模式unsigned char power_mode; //電源供應模式};
結(jié)構(gòu)mmc_driver是MMC設備驅(qū)動程序結(jié)構(gòu),列出如下:
struct mmc_driver {struct device_driver drv;int (*probe)(struct mmc_card *);void (*remove)(struct mmc_card *);int (*suspend)(struct mmc_card *, u32);int (*resume)(struct mmc_card *);};
對于MMC卡的操作是通過MMC請求結(jié)構(gòu)mmc_request的傳遞來完成的,來自系統(tǒng)塊層的讀寫請求到達MMC設備抽象層時,用系統(tǒng)的讀寫請求填充初始化MMC卡的讀寫請求,經(jīng)MMC協(xié)議分解后,然后把請求發(fā)給設備,再調(diào)用具體設備的請求處理函數(shù)來完成請求的處理。MMC卡讀寫請求結(jié)構(gòu)示意圖中上圖。下面分析MMC卡讀寫請求相關結(jié)構(gòu):
結(jié)構(gòu)mmc_request描述了讀寫MMC卡的請求,它包括命令、數(shù)據(jù)及請求完成后的回調(diào)函數(shù)。結(jié)構(gòu)mmc_request列出如下(在include/linux/mmc/mmc.h中):
struct mmc_request {struct mmc_command *cmd;struct mmc_data *data;struct mmc_command *stop; void *done_data; //回調(diào)函數(shù)的參數(shù)void (*done)(struct mmc_request *);//請求完成的回調(diào)函數(shù)};
結(jié)構(gòu)mmc_queue是MMC的請求隊列結(jié)構(gòu),它封裝了通用請求隊列結(jié)構(gòu),加入了MMC卡相關結(jié)構(gòu),結(jié)構(gòu)mmc_queue列出如下(在drivers/mmc/mmc_queue.h中):
struct mmc_queue {struct mmc_card *card; //MMC卡結(jié)構(gòu)struct completion thread_complete; //線程完成結(jié)構(gòu)wait_queue_head_t thread_wq; //等待隊列struct semaphore thread_sem;unsigned int flags;struct request *req; //通用請求結(jié)構(gòu)int (*prep_fn)(struct mmc_queue *, struct request *);//發(fā)出讀寫請求函數(shù)int (*issue_fn)(struct mmc_queue *, struct request *);void *data;struct request_queue *queue; //塊層通用請求隊列struct scatterlist *sg; //碎片鏈表};
結(jié)構(gòu)mmc_data描述了MMC卡讀寫的數(shù)據(jù)相關信息,如:請求、操作命令、數(shù)據(jù)及狀態(tài)等。結(jié)構(gòu)mmc_data列出如下(在include/linuc/mmc/mmc.h中):
struct mmc_data {unsigned int timeout_ns; //數(shù)據(jù)超時( ns,最大80ms) unsigned int timeout_clks; //數(shù)據(jù)超時(以時鐘計數(shù))unsigned int blksz_bits; //數(shù)據(jù)塊大小的bit位unsigned int blocks; //塊數(shù)unsigned int error; //數(shù)據(jù)錯誤unsigned int flags; //數(shù)據(jù)操作標識 #define MMC_DATA_WRITE (1 << 8)#define MMC_DATA_READ (1 << 9)#define MMC_DATA_STREAM (1 << 10) unsigned int bytes_xfered; struct mmc_command *stop; //停止命令struct mmc_request *mrq; //相關的請求 unsigned int sg_len; //碎片鏈表的長度struct scatterlist *sg; // I/O碎片鏈表指針};
結(jié)構(gòu)mmc_command描述了MMC卡操作相關命令及數(shù)據(jù)、狀態(tài)信息等,結(jié)構(gòu)列出如下:
struct mmc_command {u32 opcode;u32 arg;u32 resp[4];unsigned int flags; //期望的反應類型#define MMC_RSP_NONE (0 << 0)#define MMC_RSP_SHORT (1 << 0)#define MMC_RSP_LONG (2 << 0)#define MMC_RSP_MASK (3 << 0)#define MMC_RSP_CRC (1 << 3) /* expect valid crc */#define MMC_RSP_BUSY (1 << 4) /* card may send busy */ /** These are the response types, and correspond to valid bit* patterns of the above flags. One additional valid pattern* is all zeros, which means we don't expect a response.*/#define MMC_RSP_R1 (MMC_RSP_SHORT|MMC_RSP_CRC)#define MMC_RSP_R1B (MMC_RSP_SHORT|MMC_RSP_CRC|MMC_RSP_BUSY)#define MMC_RSP_R2 (MMC_RSP_LONG|MMC_RSP_CRC)#define MMC_RSP_R3 (MMC_RSP_SHORT) unsigned int retries; /* max number of retries */unsigned int error; /* command error */ #define MMC_ERR_NONE 0#define MMC_ERR_TIMEOUT 1#define MMC_ERR_BADCRC 2#define MMC_ERR_FIFO 3#define MMC_ERR_FAILED 4#define MMC_ERR_INVALID 5 struct mmc_data *data; //與命令相關的數(shù)據(jù)片斷 struct mmc_request *mrq; //與命令相關的請求};
函數(shù)mmc_blk_init注冊一個MMC塊設備驅(qū)動程序,它先將MMC塊設備名注冊到名稱數(shù)組major_names中,然后,還把驅(qū)動程序注冊到sysfs文件系統(tǒng)中的總線和設備目錄中。一方面,sysfs文件系統(tǒng)中可顯示MMC塊設備相關信息,另一方面,sysfs文件系統(tǒng)以樹形結(jié)構(gòu)管理著MMC塊設備驅(qū)動程序。
函數(shù)mmc_blk_init分析如下(在drivers/mmc/mmc_block.c中):
static int __init mmc_blk_init(void){int res = -ENOMEM; //將卡名字mmc和major注冊到塊設備的名稱數(shù)組major_names中res = register_blkdev(major, "mmc");if (res < 0) {printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n",major, res);goto out;}if (major == 0)major = res;//在devfs文件系統(tǒng)中創(chuàng)建mmc目錄devfs_mk_dir("mmc");return mmc_register_driver(&mmc_driver); out:return res;}
mmc_driver驅(qū)動程序?qū)嵗暶魅缦拢?/p>
static struct mmc_driver mmc_driver = {.drv = {.name = "mmcblk",},.probe = mmc_blk_probe, // MMC塊設備驅(qū)動程序探測函數(shù).remove = mmc_blk_remove,.suspend = mmc_blk_suspend,.resume = mmc_blk_resume,};
函數(shù)mmc_register_driver 注冊一個媒介層驅(qū)動程序。其中參數(shù)drv是MMC媒介層驅(qū)動程序結(jié)構(gòu)。
函數(shù)mmc_register_driver分析如下(在drivers/mmc/mmc_sysfs.c中):
int mmc_register_driver(struct mmc_driver *drv){drv->drv.bus = &mmc_bus_type;drv->drv.probe = mmc_drv_probe;drv->drv.remove = mmc_drv_remove;//把塊設備注冊到sysfs文件系統(tǒng)中對應的總線及設備目錄下return driver_register(&drv->drv);}
函數(shù)mmc_blk_probe是MMC控制器探測函數(shù),它探測MMC控制器是否存在,并初始化控制器的結(jié)構(gòu),同時,還探測MMC卡的狀態(tài)并初始化。函數(shù)mmc_blk_probe調(diào)用層次圖如上圖。
函數(shù)mmc_blk_probe列出如下:
static int mmc_blk_probe(struct mmc_card *card){struct mmc_blk_data *md; //每個插槽一個結(jié)構(gòu)mmc_blk_dataint err; if (card->csd.cmdclass & ~0x1ff)return -ENODEV; if (card->csd.read_blkbits < 9) {//所讀的塊小于1扇區(qū)printk(KERN_WARNING "%s: read blocksize too small (%u)\n",mmc_card_id(card), 1 << card->csd.read_blkbits);return -ENODEV;}//分配每個插槽的卡的塊數(shù)據(jù)結(jié)構(gòu),初始化了通用硬盤及請求隊列md = mmc_blk_alloc(card);if (IS_ERR(md))return PTR_ERR(md);//設置塊大小,發(fā)命令設置卡為選中狀態(tài)err = mmc_blk_set_blksize(md, card);if (err)goto out; printk(KERN_INFO "%s: %s %s %dKiB\n",md->disk->disk_name, mmc_card_id(card), mmc_card_name(card),(card->csd.capacity << card->csd.read_blkbits) / 1024); mmc_set_drvdata(card, md);//即card ->driver_data = mdadd_disk(md->disk); //向系統(tǒng)注冊通用硬盤結(jié)構(gòu),它包括每分區(qū)信息return 0; out:mmc_blk_put(md); return err;}
函數(shù)*mmc_blk_alloc給每一插槽分配一個結(jié)構(gòu)mmc_blk_data,并分配設置通用硬盤結(jié)構(gòu)和初始了請求隊列結(jié)構(gòu)。
函數(shù)*mmc_blk_alloc分析如下(在drivers/mmc/mmc_block.c中):
//最大支持8個控制器,每個控制器可控制4個卡,每個卡最大8個分區(qū)。#define MMC_SHIFT 3 //表示每個卡最大支持8個分區(qū)#define MMC_NUM_MINORS (256 >> MMC_SHIFT) //為256/8=32//即定義dev_use[32/(8*4)] = devuse[1],一個控制器用32位表示使用情況static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))];static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card){struct mmc_blk_data *md;int devidx, ret;//查找dev_use中第一個bit為0的位序號(在MMC_NUM_MINORS位以內(nèi))//找到第個空閑的分區(qū)devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS);if (devidx >= MMC_NUM_MINORS)return ERR_PTR(-ENOSPC);__set_bit(devidx, dev_use);//將分區(qū)對應的位設置為1,表示使用。 md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);//分配對象空間if (md) {memset(md, 0, sizeof(struct mmc_blk_data));//分配gendisk結(jié)構(gòu)及通用硬盤的分區(qū)hd_struct結(jié)構(gòu),并初始化內(nèi)核對象md->disk = alloc_disk(1 << MMC_SHIFT);if (md->disk == NULL) {kfree(md);md = ERR_PTR(-ENOMEM);goto out;} spin_lock_init(&md->lock);md->usage = 1;//初始化請求隊列ret = mmc_init_queue(&md->queue, card, &md->lock);if (ret) {put_disk(md->disk);kfree(md);md = ERR_PTR(ret);goto out;}//賦上各種請求隊列處理函數(shù)md->queue.prep_fn = mmc_blk_prep_rq;//準備請求md->queue.issue_fn = mmc_blk_issue_rq;//發(fā)出請求讓設備開始處理md->queue.data = md;//初始化通用硬盤md->disk->major = major;md->disk->first_minor = devidx << MMC_SHIFT;md->disk->fops = &mmc_bdops; //塊設備操作函數(shù)集md->disk->private_data = md;md->disk->queue = md->queue.queue;md->disk->driverfs_dev = &card->dev; /*帶有永久的塊設備可移去的介質(zhì)應被設置GENHD_FL_REMOVABLE標識,對于永久的介質(zhì)可移去的塊設備不應設置GENHD_FL_REMOVABLE。MMC塊設備屬于永久介質(zhì)可移去的塊設備,不能設置GENHD_FL_REMOVABLE。用戶空間應使用塊設備創(chuàng)建/銷毀熱插拔消息來告訴何時卡存在。*/ sprintf(md->disk->disk_name, "mmcblk%d", devidx);sprintf(md->disk->devfs_name, "mmc/blk%d", devidx); md->block_bits = card->csd.read_blkbits;//為請求隊列設置硬件扇區(qū)大小blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits);set_capacity(md->disk, card->csd.capacity);//設置卡的容量}out:return md;}
函數(shù)mmc_init_queue初始化一個MMC卡請求隊列結(jié)構(gòu),其中參數(shù)mq是mmc請求隊列,參數(shù)card是加在這個隊列里的mmc卡,參數(shù)lock是隊列鎖。函數(shù)mmc_init_queue調(diào)用層次圖如上圖。
函數(shù)mmc_init_queue分析如下(在drivers/mmc/mmc_queue.c中):
int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card,spinlock_t *lock){struct mmc_host *host = card->host;u64 limit = BLK_BOUNCE_HIGH;int ret; if (host->dev->dma_mask && *host->dev->dma_mask)limit = *host->dev->dma_mask; mq->card = card;//初始化塊層的請求隊列,請求合并策略,賦上請求處理函數(shù)mmc_request。mq->queue = blk_init_queue(mmc_request, lock);if (!mq->queue)return -ENOMEM;//初始化請求隊列的扇區(qū)及片斷限制blk_queue_prep_rq(mq->queue, mmc_prep_request);//賦上準備請求函數(shù)blk_queue_bounce_limit(mq->queue, limit);blk_queue_max_sectors(mq->queue, host->max_sectors);blk_queue_max_phys_segments(mq->queue, host->max_phys_segs);blk_queue_max_hw_segments(mq->queue, host->max_hw_segs);blk_queue_max_segment_size(mq->queue, host->max_seg_size); mq->queue->queuedata = mq;mq->req = NULL; mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs,GFP_KERNEL);if (!mq->sg) {ret = -ENOMEM;goto cleanup;} init_completion(&mq->thread_complete);init_waitqueue_head(&mq->thread_wq);init_MUTEX(&mq->thread_sem); //創(chuàng)建請求隊列處理線程mmc_queue_threadret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL);if (ret >= 0) {wait_for_completion(&mq->thread_complete);init_completion(&mq->thread_complete);ret = 0;goto out;} cleanup:kfree(mq->sg);mq->sg = NULL; blk_cleanup_queue(mq->queue);out:return ret;}
函數(shù)mmc_prep_request 在準備一個MMC請求時做一些狀態(tài)轉(zhuǎn)移及保護操作,函數(shù)列出如下(在drivers/mmc/ mmc_queue.c中):
static int mmc_prep_request(struct request_queue *q, struct request *req){struct mmc_queue *mq = q->queuedata;int ret = BLKPREP_KILL; if (req->flags & REQ_SPECIAL) {//在req->special 中已建立命令塊,表示請求已準備好BUG_ON(!req->special);ret = BLKPREP_OK;} else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {//塊I/O請求需要按照協(xié)議進行翻譯ret = mq->prep_fn(mq, req);} else {//無效的請求blk_dump_rq_flags(req, "MMC bad request");} if (ret == BLKPREP_OK)//請求已準備好,不需再準備了req->flags |= REQ_DONTPREP; return ret;}
函數(shù)mmc_blk_prep_rq是準備請求時調(diào)用的函數(shù),這里僅做了簡單的保護處理,列出如下(在drivers/mmc/mmc_block.c中):
static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req){struct mmc_blk_data *md = mq->data;int stat = BLKPREP_OK;//如果沒有設備,沒法完成初始化if (!md || !mq->card) {printk(KERN_ERR "%s: killing request - no device/host\n",req->rq_disk->disk_name);stat = BLKPREP_KILL;} return stat;}
函數(shù)mmc_request是通用MMC請求處理函數(shù),它喚醒請求隊列處理線程。它在特定的主控制器上被任何請求隊列調(diào)用。當主控制器不忙時,我們查找在這個主控制器上的任何一個隊列中的請求,并且嘗試發(fā)出這個請求進行處理。
函數(shù)mmc_request分析如下(在driver/mmd/mmc_queue.c中):
static void mmc_request(request_queue_t *q){struct mmc_queue *mq = q->queuedata; if (!mq->req)//如果有請求,喚醒請求隊列處理線程wake_up(&mq->thread_wq);}
線程函數(shù)mmc_queue_thread調(diào)用了具體設備的請求處理函數(shù),利用線程機制來處理請求。函數(shù)mmc_queue_thread分析如下:
static int mmc_queue_thread(void *d){struct mmc_queue *mq = d;struct request_queue *q = mq->queue;DECLARE_WAITQUEUE(wait, current); //聲明一個當前進程的等待隊列 //設置當前進程狀態(tài),來讓線程自己來處理掛起current->flags |= PF_MEMALLOC|PF_NOFREEZE;//讓線程繼承init進程,從而不會使用用戶進程資源daemonize("mmcqd"); complete(&mq->thread_complete); //設置線程完成時的回調(diào)函數(shù) down(&mq->thread_sem);add_wait_queue(&mq->thread_wq, &wait); //加線程到等待隊列do {struct request *req = NULL; spin_lock_irq(q->queue_lock);set_current_state(TASK_INTERRUPTIBLE);if (!blk_queue_plugged(q)) //如果隊列是非堵塞狀態(tài),得到下一個請求mq->req = req = elv_next_request(q);spin_unlock_irq(q->queue_lock); if (!req) {//如果請求為空if (mq->flags & MMC_QUEUE_EXIT)break;up(&mq->thread_sem);schedule();down(&mq->thread_sem);continue;}set_current_state(TASK_RUNNING);//這里調(diào)用了mmc_blk_issue_rq開始處理請求mq->issue_fn(mq, req);} while (1);remove_wait_queue(&mq->thread_wq, &wait);up(&mq->thread_sem);//調(diào)用請求處理完后的回調(diào)函數(shù)complete_and_exit(&mq->thread_complete, 0);return 0;}
函數(shù)mmc_blk_issue_rq初始化MMC塊請求結(jié)構(gòu)后,向卡發(fā)出請求命令,并等待請求的完成,函數(shù)分析如下:
static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req){struct mmc_blk_data *md = mq->data;struct mmc_card *card = md->queue.card;int ret;//認領控制器,發(fā)命令到卡設置card為選中狀態(tài)if (mmc_card_claim_host(card))goto cmd_err; do {struct mmc_blk_request brq;struct mmc_command cmd;//初始化MMC塊請求結(jié)構(gòu)memset(&brq, 0, sizeof(struct mmc_blk_request));brq.mrq.cmd = &brq.cmd;brq.mrq.data = &brq.data; brq.cmd.arg = req->sector << 9;brq.cmd.flags = MMC_RSP_R1;brq.data.timeout_ns = card->csd.tacc_ns * 10;brq.data.timeout_clks = card->csd.tacc_clks * 10;brq.data.blksz_bits = md->block_bits;brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);brq.stop.opcode = MMC_STOP_TRANSMISSION;brq.stop.arg = 0;brq.stop.flags = MMC_RSP_R1B; if (rq_data_dir(req) == READ) {//讀請求brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;brq.data.flags |= MMC_DATA_READ;} else {//寫brq.cmd.opcode = MMC_WRITE_BLOCK;brq.cmd.flags = MMC_RSP_R1B;brq.data.flags |= MMC_DATA_WRITE;brq.data.blocks = 1;}brq.mrq.stop = brq.data.blocks > 1 ? &brq.stop : NULL; brq.data.sg = mq->sg;brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);//等待請求完成mmc_wait_for_req(card->host, &brq.mrq);……do {int err; cmd.opcode = MMC_SEND_STATUS;cmd.arg = card->rca << 16;cmd.flags = MMC_RSP_R1;err = mmc_wait_for_cmd(card->host, &cmd, 5);if (err) {printk(KERN_ERR "%s: error %d requesting status\n",req->rq_disk->disk_name, err);goto cmd_err;}} while (!(cmd.resp[0] & R1_READY_FOR_DATA)); //一個塊被成功傳輸spin_lock_irq(&md->lock);ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered);if (!ret) {//整個請求完全成功完成 add_disk_randomness(req->rq_disk);blkdev_dequeue_request(req);//從隊列中刪除請求end_that_request_last(req);//寫一些更新信息}spin_unlock_irq(&md->lock);} while (ret); mmc_card_release_host(card); return 1; cmd_err:mmc_card_release_host(card); spin_lock_irq(&md->lock);do {//結(jié)束請求req上的I/O,操作成功時返回0ret = end_that_request_chunk(req, 0,req->current_nr_sectors << 9);} while (ret); add_disk_randomness(req->rq_disk);blkdev_dequeue_request(req);end_that_request_last(req);spin_unlock_irq(&md->lock); return 0;}
函數(shù)mmc_card_claim_host發(fā)出命令選擇這個卡card,函數(shù)列出如下(在 include/linuc/mmc/card.h中):
static inline int mmc_card_claim_host(struct mmc_card *card){return __mmc_claim_host(card->host, card);}
函數(shù)__mmc_claim_host專有地認領一個控制器,參數(shù)host是認領的mmc控制器,參數(shù)card是去認領控制器的卡。函數(shù)__mmc_claim_host為一套操作認領一個控制器,如果card是被傳遞的一個有效的卡,并且它不是上次被選擇的卡,那么在函數(shù)返回之前發(fā)出命令選擇這個卡card。
函數(shù)__mmc_claim_host分析如下(在drivers/mmc/mmc.c中):
int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card){DECLARE_WAITQUEUE(wait, current);//給當前進程聲明一個等待隊列unsigned long flags;int err = 0; add_wait_queue(&host->wq, &wait);//加host->wq到等待隊列中spin_lock_irqsave(&host->lock, flags);while (1) {set_current_state(TASK_UNINTERRUPTIBLE);//設置當前進程不可中斷狀態(tài)if (host->card_busy == NULL) //如果沒有忙的卡,跳出循環(huán)break;spin_unlock_irqrestore(&host->lock, flags);schedule(); //如果有忙的卡,去調(diào)度執(zhí)行spin_lock_irqsave(&host->lock, flags);}set_current_state(TASK_RUNNING);//設置當前進程為運行狀態(tài)host->card_busy = card; //指定當前忙的卡spin_unlock_irqrestore(&host->lock, flags);remove_wait_queue(&host->wq, &wait); //從等待隊列中移去host->wq//如果卡不是選擇狀態(tài),發(fā)出命令到卡設置為選擇狀態(tài)if (card != (void *)-1 && host->card_selected != card) {struct mmc_command cmd; host->card_selected = card; cmd.opcode = MMC_SELECT_CARD;cmd.arg = card->rca << 16;cmd.flags = MMC_RSP_R1;//等待命令完成err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES);} return err;}
函數(shù)mmc_wait_for_req開始執(zhí)行一個請求并等待請求完成,函數(shù)分析如下(在drivers/mmc/mmc.c中):
int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq){DECLARE_COMPLETION(complete); mrq->done_data = &complete;mrq->done = mmc_wait_done; mmc_start_request(host, mrq); wait_for_completion(&complete); return 0;}
函數(shù)mmc_start_request開始排隊執(zhí)行一個在控制器上的命令,參數(shù)host是執(zhí)行命令的控制器,參數(shù)mrq是將要開始執(zhí)行的請求。調(diào)用者應持有鎖并且關中斷。
函數(shù)mmc_start_request分析如下(在drivers/mmc/mmc.c中):
void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq){DBG("MMC: starting cmd %02x arg %08x flags %08x\n",mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); WARN_ON(host->card_busy == NULL); mrq->cmd->error = 0;mrq->cmd->mrq = mrq;if (mrq->data) {mrq->cmd->data = mrq->data;mrq->data->error = 0;mrq->data->mrq = mrq;if (mrq->stop) {mrq->data->stop = mrq->stop;mrq->stop->error = 0;mrq->stop->mrq = mrq;}}//調(diào)用請求處理函數(shù),對于amba主控制器來說就是mmci_request函數(shù)host->ops->request(host, mrq);}
下面以amba控制器為例分析MMC卡驅(qū)動程序。amba控制器是集成在ARM處理器中的MMC卡控制器。
amba控制器驅(qū)動程序相關結(jié)構(gòu)在include/asm-arm/hardware/amba.h中,分別說明如下:
結(jié)構(gòu)amba_device 是amba控制器設備結(jié)構(gòu),它在通用設備結(jié)構(gòu)的基礎上封裝了amba控制器選定的信息數(shù)據(jù),列出如下:
struct amba_device {struct device dev; //通用設備結(jié)構(gòu)struct resource res; //設備資源結(jié)構(gòu)u64 dma_mask;unsigned int periphid;unsigned int irq[AMBA_NR_IRQS];}; struct amba_id {unsigned int id;unsigned int mask;void *data;};
結(jié)構(gòu)amba_driver是amba控制器驅(qū)動程序描述結(jié)構(gòu),列出如下:
struct amba_driver {struct device_driver drv; //通用驅(qū)動程序結(jié)構(gòu)int (*probe)(struct amba_device *, void *);int (*remove)(struct amba_device *);void (*shutdown)(struct amba_device *);int (*suspend)(struct amba_device *, u32);int (*resume)(struct amba_device *);struct amba_id *id_table;};
J
amba控制器的描述結(jié)構(gòu)封裝了MMC通用的主控制器結(jié)構(gòu)、MMC請求結(jié)構(gòu)、MMC命令結(jié)構(gòu)、MMC數(shù)據(jù)結(jié)構(gòu)等,
struct mmci_host {//下面mmc_*結(jié)構(gòu)是MMC設備抽象層的各種設備結(jié)構(gòu)void __iomem *base;struct mmc_request *mrq; //MMC讀寫請求struct mmc_command *cmd;struct mmc_data *data;struct mmc_host *mmc;struct clk *clk; unsigned int data_xfered; spinlock_t lock; unsigned int mclk;unsigned int cclk;u32 pwr;struct mmc_platform_data *plat; //平臺中與MMC相關的數(shù)據(jù) struct timer_list timer; //定時器unsigned int oldstat; unsigned int sg_len; /* pio stuff */struct scatterlist *sg_ptr; //碎片鏈表指針unsigned int sg_off;unsigned int size;}
函數(shù)mmci_init 是amba控制器的初始化,它注冊了amba控制器驅(qū)動程序結(jié)構(gòu)mmci_driver。
amba控制器驅(qū)動程序的初始化函數(shù)和模塊清除函數(shù)分別列出如下:
static int __init mmci_init(void){return amba_driver_register(&mmci_driver);} static void __exit mmci_exit(void){amba_driver_unregister(&mmci_driver);}
amba控制器驅(qū)動程序結(jié)構(gòu)實例mmci_driver列出如下:
#define DRIVER_NAME "mmci-pl18x"static struct amba_driver mmci_driver = {.drv = {.name = DRIVER_NAME,},.probe = mmci_probe, //設備探測及初始化函數(shù).remove = mmci_remove, //移去設備處理函數(shù).suspend = mmci_suspend, //電源掛起函數(shù).resume = mmci_resume, //電源恢復函數(shù).id_table = mmci_ids,};
函數(shù)mmci_probe探測設備并初始化設備,它的工作包括:申請設備I/O內(nèi)存并進行I/O映射,初始化控制器結(jié)構(gòu),激活設備時鐘,申請中斷,檢測MMC卡,設置設備定時狀態(tài)檢測函數(shù)等。
函數(shù)mmci_probe分析如下(在drivers/mmc/mmci.c中):
static int mmci_probe(struct amba_device *dev, void *id){struct mmc_platform_data *plat = dev->dev.platform_data;struct mmci_host *host;struct mmc_host *mmc;int ret; /* must have platform data */if (!plat) {ret = -EINVAL;goto out;}//申請所有與設備相關的I/O內(nèi)存區(qū)域。ret = amba_request_regions(dev, DRIVER_NAME);if (ret)goto out; //分配并初始化主控制器結(jié)構(gòu)mmc_host。mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev);if (!mmc) {ret = -ENOMEM;goto rel_regions;} // 得到mmc對應有mmci結(jié)構(gòu),即host = ?void *)host = mmc_priv(mmc);//得到名為“MCLK”的時鐘結(jié)構(gòu),它描述了一個硬件時鐘的信息host->clk = clk_get(&dev->dev, "MCLK");if (IS_ERR(host->clk)) {ret = PTR_ERR(host->clk);host->clk = NULL;goto host_free;}//時鐘是否使用ret = clk_use(host->clk);if (ret)goto clk_free;//激活時鐘ret = clk_enable(host->clk);if (ret)goto clk_unuse; host->plat = plat;host->mclk = clk_get_rate(host->clk);//得到時鐘頻率host->mmc = mmc;host->base = ioremap(dev->res.start, SZ_4K); //I/O映射的虛擬地址if (!host->base) {ret = -ENOMEM;goto clk_disable;} //具體設備的控制器操作函數(shù)mmc->ops = &mmci_ops;mmc->f_min = (host->mclk + 511) / 512;mmc->f_max = min(host->mclk, fmax);mmc->ocr_avail = plat->ocr_mask; /** We can do SGIO*/mmc->max_hw_segs = 16;mmc->max_phys_segs = NR_SG;//定義為16 //因為僅有一個16位數(shù)據(jù)長度寄存器,我們必須保證一個請求中不能超過 -1。//選擇64(512字節(jié))扇區(qū)作為限制。mmc->max_sectors = 64; //設置最大片斷尺寸,因為不做DMA,僅受限制于數(shù)據(jù)長度寄存器*mmc->max_seg_size = mmc->max_sectors << 9; spin_lock_init(&host->lock);//寫寄存器writel(0, host->base + MMCIMASK0);writel(0, host->base + MMCIMASK1);writel(0xfff, host->base + MMCICLEAR);//分配共享中斷,中斷函數(shù)是mmci_irq處理命令及傳輸數(shù)據(jù)傳輸完成時的中斷處理函數(shù),中斷號是dev->irq[0]。ret = request_irq(dev->irq[0], mmci_irq, SA_SHIRQ,DRIVER_NAME " (cmd)", host);if (ret)goto unmap;//中斷mmci_pio_irq是PIO數(shù)據(jù)傳輸?shù)闹袛嗵幚砗瘮?shù)ret = request_irq(dev->irq[1], mmci_pio_irq, SA_SHIRQ,DRIVER_NAME " (pio)", host);if (ret)goto irq0_free;//將中斷使能寫入寄存器writel(MCI_IRQENABLE, host->base + MMCIMASK0); //設置dev->driver_data = mmc。amba_set_drvdata(dev, mmc);//初始化主控制器硬件,關控制器電源,檢測控制器插槽有否卡。mmc_add_host(mmc); printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%08lx irq %d,%d\n",mmc->host_name, amba_rev(dev), amba_config(dev),dev->res.start, dev->irq[0], dev->irq[1]);//初始化定義器init_timer(&host->timer);host->timer.data = (unsigned long)host;host->timer.function = mmci_check_status;//設置定時函數(shù)為檢測狀態(tài)函數(shù)host->timer.expires = jiffies + HZ;add_timer(&host->timer); return 0;……}
函數(shù)amba_request_regions申請所有與設備相關的I/O內(nèi)存區(qū)域。其參數(shù)dev是設備結(jié)構(gòu)amba_device,參數(shù)name若為NULL,表示是驅(qū)動程序名字。
函數(shù)amba_request_regions分析如下(在arch/arm/commom/amba.c中):
int amba_request_regions(struct amba_device *dev, const char *name){int ret = 0; if (!name)//如果為NULL,表示是設備驅(qū)動程序的名字。name = dev->dev.driver->name;//填寫資源結(jié)構(gòu),I/O空間大小為SZ_4K,并分析是否在//全局資源結(jié)構(gòu)iomem_resource所控制的地址范圍內(nèi),將資源加到資源樹中。if (!request_mem_region(dev->res.start, SZ_4K, name))ret = -EBUSY; return ret;}
函數(shù)mmc_alloc_host 分配并初始化主控制器結(jié)構(gòu)mmc_host。其參數(shù)extra是私有數(shù)據(jù)結(jié)構(gòu)的大小,參數(shù)dev是主控制器設備模型結(jié)構(gòu)的指針。
struct mmc_host *mmc_alloc_host(int extra, struct device *dev){struct mmc_host *host;//分配結(jié)構(gòu)對象空間host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);if (host) {memset(host, 0, sizeof(struct mmc_host) + extra);//清0 spin_lock_init(&host->lock);init_waitqueue_head(&host->wq);//初始化等待隊列INIT_LIST_HEAD(&host->cards); //初始化鏈表//設置工作的函數(shù)及定時器,函數(shù)是(&host->detect)-> mmc_rescan(host)INIT_WORK(&host->detect, mmc_rescan, host); host->dev = dev; //缺省時不支持SGIO(多個片斷)或大請求,//如需支持必須按控制器的能力設備下面的參數(shù)。host->max_hw_segs = 1;host->max_phys_segs = 1;//計數(shù)每頁的扇區(qū)數(shù)=頁大小/512host->max_sectors = 1 << (PAGE_CACHE_SHIFT - 9);host->max_seg_size = PAGE_CACHE_SIZE;} return host;}
amba控制器操作函數(shù)集實例列出如下:
static struct mmc_host_ops mmci_ops = {.request = mmci_request, //amba控制器請求處理函數(shù).set_ios = mmci_set_ios, //控制設備狀態(tài),將控制值寫入設備寄存器。};
函數(shù)mmci_request把在MMC抽象設備層封裝好的MMC協(xié)議命令寫入具體的amba控制器完成MMC卡的讀寫操作。
函數(shù)mmci_request列出如下:
static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq){struct mmci_host *host = mmc_priv(mmc); WARN_ON(host->mrq != NULL); spin_lock_irq(&host->lock); host->mrq = mrq; if (mrq->data && mrq->data->flags & MMC_DATA_READ)mmci_start_data(host, mrq->data);//開始執(zhí)行命令,即向寄存器寫入操作指令來執(zhí)行讀寫操作。mmci_start_command(host, mrq->cmd, 0); spin_unlock_irq(&host->lock);}