Linux内核MTD驱动程序与SD卡驱动程序(5)

2019-03-29 16:54

函数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;


Linux内核MTD驱动程序与SD卡驱动程序(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:出色的主持

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: