MTD學習報告005

 

Drivers/mtd/mtd_blkdevs.c:

static int blktrans_open(struct inode *i, struct file *f)

{

       struct mtd_blktrans_dev *dev;

       struct mtd_blktrans_ops *tr;

       int ret = -ENODEV;

 

       dev = i->i_bdev->bd_disk->private_data;

       tr = dev->tr;

 

       if (!try_module_get(dev->mtd->owner))

              goto out;

 

       if (!try_module_get(tr->owner))

              goto out_tr;

 

       /* FIXME: Locking. A hot pluggable device can go away

          (del_mtd_device can be called for it) without its module

          being unloaded. */

       dev->mtd->usecount++;

 

       ret = 0;

       if (tr->open && (ret = tr->open(dev))) { 

              dev->mtd->usecount--;

              module_put(dev->mtd->owner);

       out_tr:

              module_put(tr->owner);

       }

 out:

       return ret;

}

這個函數主要就是調用了tropen函數,

Drivers/mtd/mtdblock.c:

static int mtdblock_open(struct mtd_blktrans_dev *mbd)

{

       struct mtdblk_dev *mtdblk;

       struct mtd_info *mtd = mbd->mtd;

       int dev = mbd->devnum;

 

       DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open/n");

      

       if (mtdblks[dev]) {

              mtdblks[dev]->count++;  /*如果該設備已open,則只要增加其引用計數就OK*/

              return 0;

       }

      

       /* OK, it's not open. Create cache info for it */

    /*分配mtdblk_dev對象來保存已打開的mtd block設備的信息*/

       mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);

       if (!mtdblk)

              return -ENOMEM;

   

    /*初始化這個對象*/

       memset(mtdblk, 0, sizeof(*mtdblk));

       mtdblk->count = 1;

       mtdblk->mtd = mtd;

 

       init_MUTEX (&mtdblk->cache_sem);

       mtdblk->cache_state = STATE_EMPTY;

       if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&

           mtdblk->mtd->erasesize) {

              mtdblk->cache_size = mtdblk->mtd->erasesize;

              mtdblk->cache_data = NULL;

       }

 

       mtdblks[dev] = mtdblk;

      

       DEBUG(MTD_DEBUG_LEVEL1, "ok/n");

 

       return 0;

}

這個函數爲每個mtd block設備分配一個私有對象並保存其相應信息供後續的操作使用, 這是linux下慣用的一種方法.

當有讀寫請求時會調用這個函數, 而關於如何會調用到這裏的, 涉及到block設備的讀寫流程, 可以參考其他文檔. 我們直接從這裏分析了.

Drivers/mtd/mtd_blkdevs.c:

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;

 

       if (block + nsect > get_capacity(req->rq_disk))

              return 0;

 

       switch(rq_data_dir(req)) {

       case READ:

              for (; nsect > 0; nsect--, block++, buf += 512)

                     if (tr->readsect(dev, block, buf))  /*固定讀512字節*/

                            return 0;

              return 1;

 

       case WRITE:

              if (!tr->writesect)

                     return 0;

 

              for (; nsect > 0; nsect--, block++, buf += 512)

                     if (tr->writesect(dev, block, buf))  /*固定寫512字節*/

                            return 0;

              return 1;

 

       default:

              printk(KERN_NOTICE "Unknown request %ld/n", rq_data_dir(req));

              return 0;

       }

}

這裏我們只是分析了mtd設備操作的一個流程, 對於細節方面的東西需要在仔細琢磨,

我們先看trread函數

Drivers/mtd/mtdblock.c:

static int mtdblock_readsect(struct mtd_blktrans_dev *dev,

                           unsigned long block, char *buf)

{

       struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];  /*得到open時後初始化的對象*/

       return do_cached_read(mtdblk, block<<9, 512, buf);  /*幹事實的函數*/

}

Drivers/mtd/mtdblock.c:

static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,

                        int len, char *buf)

{

       struct mtd_info *mtd = mtdblk->mtd;

       unsigned int sect_size = mtdblk->cache_size;

       size_t retlen;

       int ret;

 

       DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on /"%s/" at 0x%lx, size 0x%x/n",

                     mtd->name, pos, len);

      

       if (!sect_size)

              return MTD_READ (mtd, pos, len, &retlen, buf);

   

    /*這裏完成了一個讀的算法問題*/

       while (len > 0) {

              unsigned long sect_start = (pos/sect_size)*sect_size;

              unsigned int offset = pos - sect_start;

              unsigned int size = sect_size - offset;

              if (size > len)

                     size = len;

 

              /*

               * Check if the requested data is already cached

               * Read the requested amount of data from our internal cache if it

               * contains what we want, otherwise we read the data directly

               * from flash.

               */

              if (mtdblk->cache_state != STATE_EMPTY &&

                  mtdblk->cache_offset == sect_start) {

                     memcpy (buf, mtdblk->cache_data + offset, size);

              } else {

                     ret = MTD_READ (mtd, pos, size, &retlen, buf);   /*讀真正的partition*/

                     if (ret)

                            return ret;

                     if (retlen != size)

                            return -EIO;

              }

 

              buf += size;

              pos += size;

              len -= size;

       }

 

       return 0;

}

一般對flash的讀都是一page讀的(按一個page512字節爲例), 假如, 前面一次讀操作, 讀了一個page的內容, 而上層的請求只需要其前面的256個字節, 則後面的256個字節先暫存到緩衝區中, 這裏就是cache_data, 如果這次的讀操作剛好是從這256個字節開始的偏移處開始讀數據, 則可以直接先把這256個字節copybuf, 然後在讀需要的其他數據.

好的, 我們看MTD_READ

Include/linux/mtd/mtd.h:

#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args)

 

根據前面的分析這裏調用的是前面註冊過的回調函數: part_read() (add_mtd_partitions裏註冊的)

Drivers/mtd/mtdpart.c:

static int part_read (struct mtd_info *mtd, loff_t from, size_t len,

                     size_t *retlen, u_char *buf)

{

       struct mtd_part *part = PART(mtd);

       if (from >= mtd->size)

              len = 0;

       else if (from + len > mtd->size)

              len = mtd->size - from;

       if (part->master->read_ecc == NULL)

              return part->master->read (part->master, from + part->offset,

                                   len, retlen, buf);

       else

              return part->master->read_ecc (part->master, from + part->offset,

                                   len, retlen, buf, NULL, &mtd->oobinfo);

}

這個函數僅僅是一個wrapper, 真真調的是master的操作函數, 我們上面分析過了, master指的就是整個原始的mtd設備.

而我們這裏的master是哪個呢?回顧一下,add_mtd_partitions中有這麼個語句

int add_mtd_partitions(struct mtd_info *master,

                     const struct mtd_partition *parts,

                     int nbparts)

{

        ….

              slave->master = master;

        ….

}

而這裏的slave就是mtd_part對象, master是傳進來的參數, 那麼在哪調用了這個函數呢, 沒錯就是我們s3c2410驅動了,

static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,

                                  struct s3c2410_nand_mtd *mtd,

                                  struct s3c2410_nand_set *set)

{

    ……

       if (set->nr_partitions > 0 && set->partitions != NULL) {

              return add_mtd_partitions(&mtd->mtd,

                                     set->partitions,

                                     set->nr_partitions);

       }

……

}

而上面這個函數又是在s3c2410_nand_probe中調的

    …..

       for (setno = 0; setno < nr_sets; setno++, nmtd++) {

              pr_debug("initialising set %d (%p, info %p)/n",

                      setno, nmtd, info);

             

              s3c2410_nand_init_chip(info, nmtd, sets);

 

              nmtd->scan_res = nand_scan(&nmtd->mtd,

                                      (sets) ? sets->nr_chips : 1);

 

              if (nmtd->scan_res == 0) {

                     s3c2410_nand_add_partition(info, nmtd, sets); 

              }

 

              if (sets != NULL)

                     sets++;

       }

    ……

 其中在nmtd-> mtd就是我們的master對象, 對它的初始化就是在nand_scan中進行的. 具體看上面的分析, 從而可知這裏的read_ecc就是nand_read_ecc.

nand_read_ecc主要就是涉及到對flash的讀操作了, 這裏就不分析,

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章