函数add_mtd_blktrans_dev分析如下(在drivers/mtd/mtd_blkdevs.c中):
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) {
struct mtd_blktrans_ops *tr = new->tr; struct list_head *this; int last_devnum = -1; struct gendisk *gd;
if (!down_trylock(&mtd_table_mutex)) { up(&mtd_table_mutex); BUG(); }
//遍历MTD每个主块设备
list_for_each(this, &tr->devs) {
struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev,list);
if (new->devnum == -1) {//如果没有设备号 //使用第一个空闲的设备号
if (d->devnum != last_devnum+1) {
//找到空闲设备号,并把设备加到链表的尾部 new->devnum = last_devnum+1;
list_add_tail(&new->list, &d->list); goto added; }
} else if (d->devnum == new->devnum) {//设备号已被使用 /* Required number taken */ return -EBUSY;
} else if (d->devnum > new->devnum) { //申请的设备号是空闲的,加到链表的尾部 list_add_tail(&new->list, &d->list); goto added; }
last_devnum = d->devnum; }
if (new->devnum == -1)//如果新设备的设备号为-1,就赋上(最后一个设备号+1)
new->devnum = last_devnum+1; //所有的设备号*分区数 > 256
if ((new->devnum << tr->part_bits) > 256) { return -EBUSY;
}
init_MUTEX(&new->sem);
list_add_tail(&new->list, &tr->devs);//加到链表尾部
added:
if (!tr->writesect) new->readonly = 1;
//分配通知硬盘结构gendisk,每分区一个 gd = alloc_disk(1 << tr->part_bits); if (!gd) {
list_del(&new->list); return -ENOMEM; }
//初始化通用硬盘结构 gd->major = tr->major;
gd->first_minor = (new->devnum) << tr->part_bits; gd->fops = &mtd_blktrans_ops;
snprintf(gd->disk_name, sizeof(gd->disk_name), “%s%c”, tr->name, (tr->part_bits?’a’:’0’) + new->devnum); snprintf(gd->devfs_name, sizeof(gd->devfs_name),
“%s/%c”, tr->name, (tr->part_bits?’a’:’0’) + new->devnum);
/* 2.5 has capacity in units of 512 bytes while still
having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ set_capacity(gd, (new->size * new->blksize) >> 9);
gd->private_data = new; //通用硬盘结构的私有数据指向翻译层的MTD设备
new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq; //设置请求队列
if (new->readonly)
set_disk_ro(gd, 1); //设置硬盘读写模式 add_disk(gd);//加通用硬盘结构到全局链表中 return 0; }
MTD块设备的读写操作
函数mtdblock_writesect调用层次图
MTD翻译层设备操作函数集实例mtdblock_tr有对MTD设备的各种操作函数,这些操作函数调用了mtd_info结构中的操作函数。这里只分析了函数mtdblock_writesect,它的源代码都在drivers/mtd/mtdblock.c中。由于flash设备需要先擦除一个扇区,再才能写一个扇区,因而,使用了缓存来帮助不是正好一个扇区的数据的写操作。
函数mtdblock_writesect将数据写入到flash设备中。函数分析如下: static int mtdblock_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) {
//从MTD块设备数组中得到块设备结构
struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { //分配块设备用于擦除的缓存空间
mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); if (!mtdblk->cache_data) return -EINTR;
}
//从位置block开始写一个扇区(512字节)
return do_cached_write(mtdblk, block<<9, 512, buf); }
函数do_cached_write将数据写入到设备,由于flash设备需要先擦除再才能写
入,因而,在数据块大小不是正好扇区大小,需要通过缓存凑合成一个扇区时,才能写入到设备。 函数do_cached_write分析如下:
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;
if (!sect_size)//如果块设备的缓冲大小为0,直接写设备 return MTD_WRITE (mtd, pos, len, &retlen, buf);
while (len > 0) {
//将要写的在设备上的位置pos地址处,长度为len // |<-offset-->|<-size-->| // ----------sect_start---|pos-----len-| // |<- sect_size ->| //计算扇区开始位置
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) {//正好是擦除缓冲区大小 //直接写入,不需要通过缓冲区
ret = erase_write (mtd, pos, size, buf); if (ret)
return ret; } else {
//只有部分扇区大小的数据,需通过缓冲区补充成扇区大小 //方法是:先从设备中读出数据到缓冲区,再将buf中数据拷贝到缓冲区,
//这样,凑合成一个扇区大小的数据,再把缓冲区数据写入设备。 //如果缓冲区数据是脏的,把缓冲区数据写设备 if (mtdblk->cache_state == STATE_DIRTY &&
mtdblk->cache_offset != sect_start) { ret = write_cached_data(mtdblk); if (ret)
return ret; }
if (mtdblk->cache_state == STATE_EMPTY ||
mtdblk->cache_offset != sect_start) { //把当前的扇区数据填充缓冲区
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; }
//将数据从buf中拷贝到缓冲区中
memcpy (mtdblk->cache_data + offset, buf, size); mtdblk->cache_state = STATE_DIRTY; }
buf += size; pos += size; len -= size; }
return 0; }
函数write_cached_data将设备缓存中的数据写入到设备,在写完缓存中数据时,缓存的状态发生变化。函数write_cached_data列出如下: static int write_cached_data (struct mtdblk_dev *mtdblk) {
struct mtd_info *mtd = mtdblk->mtd; int ret;