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;
}
这个函数主要就是调用了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字节*/
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设备操作的一个流程, 对于细节方面的东西需要在仔细琢磨,
我们先看tr的read函数
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读的(按一个page为512字节为例), 假如, 前面一次读操作, 读了一个page的内容, 而上层的请求只需要其前面的256个字节, 则后面的256个字节先暂存到缓冲区中, 这里就是cache_data, 如果这次的读操作刚好是从这256个字节开始的偏移处开始读数据, 则可以直接先把这256个字节copy到buf中, 然后在读需要的其他数据.
好的, 我们看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的读操作了, 这里就不分析,
- MTD学习报告005
- mtd学习报告002
- mtd学习报告003
- MTD学习报告004
- MTD源码学习报告001
- 初始mtd学习笔记
- MTD应用学习札记
- MTD 设备学习的笔记
- MTD系统架构学习笔记
- mtd
- MTD
- mtd
- MTD
- mtd
- MTD应用学习:mtd和mtdblock的区别
- MTD应用学习:mtd和mtdblock的区别
- MTD应用学习:mtd和mtdblock的区别
- MTD应用学习:mtd和mtdblock的区别
- SAP内时间戳和日期时间的转换
- JSP与Servlet的跳转及得到路径方法整理
- .Net序列化与数据压缩类 (DataSet)
- jQuery常用操作
- Visual Studio 2008 每日提示(三十五)
- MTD学习报告005
- 不要把ERP抬举的过高---论述:ERP的最大糟粕是什么?(上) (入选推荐日志,加10币)
- Ext+SqlServer+.Net(Ajax) 开发的系统-有演示
- 加密解密存储过程代码
- jQuery对select操作小结
- WordNet-Princeton 大学的心理学家,语言学家和计算机工程师联合设计的一种基于认知语言学的英语词典。
- jQuery实现checkbox全选和反选
- HTTP参数中Etag的重要性
- S3C2410:DMA介紹