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的讀操作了, 這裏就不分析,