MTD学习报告005

来源:互联网 发布:淘宝劲霸男装是真的吗 编辑:程序博客网 时间:2024/04/30 11:55

 

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 hotpluggable 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字节*/

                            return0;

              return 1;

 

       case WRITE:

              if(!tr->writesect)

                     return 0;

 

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

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

                            return0;

              return 1;

 

       default:

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

              return 0;

       }

}

这里我们只是分析了mtd设备操作的一个流程, 对于细节方面的东西需要在仔细琢磨,

我们先看trread函数

Drivers/mtd/mtdblock.c:

static intmtdblock_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 intdo_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 alreadycached

               * Read the requested amount of data from ourinternal cache if it

               * contains what we want, otherwise we read thedata 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:

#defineMTD_READ(mtd, args...) (*(mtd->read))(mtd, args)

 

根据前面的分析这里调用的是前面注册过的回调函数: part_read() (add_mtd_partitions里注册的)

Drivers/mtd/mtdpart.c:

static intpart_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

              returnpart->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(structmtd_info *master,

                     const struct mtd_partition *parts,

                     int nbparts)

{

        ….

              slave->master = master;

        ….

}

而这里的slave就是mtd_part对象, master是传进来的参数, 那么在哪调用了这个函数呢, 没错就是我们s3c2410驱动了,

static ints3c2410_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) {

              returnadd_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的读操作了, 这里就不分析,