这个文件中并没有增加mtd块设备,mtd块设备的代码文件是Mtd_blkdevs.c。它的功能是为mtd块设备读写提供缓冲操作。
另外还有一个文件mtdblock_ro.c,它定义的是mtd块设备缓冲的只读操作。
驱动入口:
static struct mtd_blktrans_ops mtdblock_tr = {
.name = "mtdblock",
.major = 31,
.part_bits = 0,
.blksize = 512,
.open = mtdblock_open,
.flush = mtdblock_flush,
.release = mtdblock_release,
.readsect = mtdblock_readsect,
.writesect = mtdblock_writesect,
.add_mtd = mtdblock_add_mtd, //增加一个mtd_blktrans_dev
.remove_dev = mtdblock_remove_dev,
.owner = THIS_MODULE,
};
static int __init init_mtdblock(void)
{
return register_mtd_blktrans(&mtdblock_tr);
}
static void __exit cleanup_mtdblock(void)
{
deregister_mtd_blktrans(&mtdblock_tr);
}
module_init(init_mtdblock);
module_exit(cleanup_mtdblock);
缓冲数据结构定义:
static struct mtdblk_dev {
struct mtd_info *mtd;
int count;//使用计数
struct mutex cache_mutex;
unsigned char *cache_data; //缓冲区数据
unsigned long cache_offset; //缓冲区数据的偏移地址(全局)
unsigned int cache_size; //一般等于FLASH的擦除大小
enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
} *mtdblks[MAX_MTD_DEVICES];
mtd_blktrans_dev结构:
struct mtd_blktrans_dev {
struct mtd_blktrans_ops *tr;
struct list_head list;
struct mtd_info *mtd;
struct mutex lock;
int devnum; //分区序号,等于mtd->index
unsigned long size;//分区大小,单位是512字节
int readonly; //是否是只读的
void *blkcore_priv; //指向gen_disk结构};
mtd_blktrans_dev对应于mtd_info,每个分区有一个mtd_blktrans_dev实体。
(1)open例程
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]) {//如果如果mtdblk_dev存在,则返回
mtdblks[dev]->count++;
return 0;
}
//mtdblk_dev不存在,则新增一个
/* OK, it's not open. Create cache info for it */
mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
if (!mtdblk)
return -ENOMEM;
mtdblk->count = 1;
mtdblk->mtd = mtd;
mutex_init(&mtdblk->cache_mutex);
mtdblk->cache_state = STATE_EMPTY;
if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) {
mtdblk->cache_size = mtdblk->mtd->erasesize;
mtdblk->cache_data = NULL;
}
mtdblks[dev] = mtdblk;
DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
return 0;
}
(2)release例程
它检查检查mtdblk_dev的使用计数,若为0,则释放内存。
static int mtdblock_release(struct mtd_blktrans_dev *mbd)
{
int dev = mbd->devnum;
struct mtdblk_dev *mtdblk = mtdblks[dev];
DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
mutex_lock(&mtdblk->cache_mutex);
write_cached_data(mtdblk);
mutex_unlock(&mtdblk->cache_mutex);
if (!--mtdblk->count) {
/* It was the last usage. Free the device */
mtdblks[dev] = NULL;
if (mtdblk->mtd->sync)
mtdblk->mtd->sync(mtdblk->mtd);
vfree(mtdblk->cache_data);
kfree(mtdblk);
}
DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
return 0;
}
(3)writesect例程
static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
unsigned long block, char *buf)
{
struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) {//如果mtdblk->cache_data为空,则分配内存
mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
if (!mtdblk->cache_data)
return -EINTR;
/* -EINTR is not really correct, but it is the best match
* documented in man 2 write for all cases. We could also
* return -EAGAIN sometimes, but why bother?
*/
}
return do_cached_write(mtdblk, block<<9, 512, buf);//把512字节数据写入到block<<9的地址处
}
static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos,
int len, const 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: write on \"%s\" at 0x%lx, size 0x%x\n",
mtd->name, pos, len);
if (!sect_size) //直接写入
return mtd->write(mtd, pos, len, &retlen, buf);
///单次写入的数据大小不能超过sect_size,写入时如果小于sect_size,则先写入到cache
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;
if (size == sect_size) {//如果刚好是一个sect的大小,则不经cache直接写入
/*
* We are covering a whole sector. Thus there is no
* need to bother with the cache while it may still be
* useful for other partial writes.
*/
ret = erase_write (mtd, pos, size, buf);
if (ret)
return ret;
} else {//使用cache
/* Partial sector: need to use the cache */
if (mtdblk->cache_state == STATE_DIRTY && //如果cache有数据且cache的数据和写入的数据不在同一个sect中,则先把cache中的数据写入
mtdblk->cache_offset != sect_start) {
ret = write_cached_data(mtdblk);
if (ret)
return ret;
}
if (mtdblk->cache_state == STATE_EMPTY || //如果cache中没有数据,则先把FLASH中对应的数据读出来放到cache
mtdblk->cache_offset != sect_start) {
/* fill the cache with the current sector */
mtdblk->cache_state = STATE_EMPTY;
ret = mtd->read(mtd, sect_start, sect_size,
&retlen, mtdblk->cache_data);
if (ret)
return ret;
if (retlen != sect_size)
return -EIO;
mtdblk->cache_offset = sect_start;
mtdblk->cache_size = sect_size;
mtdblk->cache_state = STATE_CLEAN;
}
/* write data to our local cache *///修改cache中的数据
memcpy (mtdblk->cache_data + offset, buf, size);
mtdblk->cache_state = STATE_DIRTY;
}
buf += size;
pos += size;
len -= size;
}
return 0;
}
static int write_cached_data (struct mtdblk_dev *mtdblk)
{
struct mtd_info *mtd = mtdblk->mtd;
int ret;
if (mtdblk->cache_state != STATE_DIRTY)
return 0;
DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: writing cached data for \"%s\" "
"at 0x%lx, size 0x%x\n", mtd->name,
mtdblk->cache_offset, mtdblk->cache_size);
//erase_write调用mtd_info的相应操作
ret = erase_write (mtd, mtdblk->cache_offset,
mtdblk->cache_size, mtdblk->cache_data);
if (ret)
return ret;
/*
* Here we could argubly set the cache state to STATE_CLEAN.
* However this could lead to inconsistency since we will not
* be notified if this content is altered on the flash by other
* means. Let's declare it empty and leave buffering tasks to
* the buffer cache instead.
*/
mtdblk->cache_state = STATE_EMPTY;
return 0;
}
(3)readsect例程
static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
unsigned long block, char *buf)
{
struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
return do_cached_read(mtdblk, block<<9, 512, buf);
}
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 检查读取的数据是否已经在cache中
* 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);
if (ret)
return ret;
if (retlen != size)
return -EIO;
}
buf += size;
pos += size;
len -= size;
}
return 0;
}
(4)flush例程
它将cache中数据写入到存储
static int mtdblock_flush(struct mtd_blktrans_dev *dev)
{
struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
mutex_lock(&mtdblk->cache_mutex);
write_cached_data(mtdblk);
mutex_unlock(&mtdblk->cache_mutex);
if (mtdblk->mtd->sync)
mtdblk->mtd->sync(mtdblk->mtd);
return 0;
}