linux mtdblock7,mtd块设备缓冲操作---mtdblock.c

这个文件中并没有增加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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值