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;
}
这个函数主要就是调用了tr的open函数,
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设备操作的一个流程, 对于细节方面的东西需要在仔细琢磨,
我们先看tr的read函数
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读的(按一个page为512字节为例), 假如, 前面一次读操作, 读了一个page的内容, 而上层的请求只需要其前面的256个字节, 则后面的256个字节先暂存到缓冲区中, 这里就是cache_data, 如果这次的读操作刚好是从这256个字节开始的偏移处开始读数据, 则可以直接先把这256个字节copy到buf中, 然后在读需要的其他数据.
好的, 我们看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的读操作了, 这里就不分析,