Chinaunix首页 | 论坛 | 博客
  • 博客访问: 932588
  • 博文数量: 63
  • 博客积分: 568
  • 博客等级: 中士
  • 技术积分: 3435
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-05 11:44
文章分类
文章存档

2016年(4)

2015年(6)

2014年(3)

2013年(27)

2012年(23)

分类: LINUX

2013-11-16 20:54:47

前面几篇文章介绍了MTD 硬件驱动层的一些东西。逐渐往上面看就会看到MTD Block层。什么是MTD Block层,就是为Flash设备在内核中建立块设备结构,建立一种和磁盘等块设备一样的身份告诉内核块层和文件系统层。在这层往上面,Flash就和磁盘是一样一样的地位了。或许有人会说,在MTD BlockMTD Char之上还有MTD核心层吗?是的确实有MTD核心层,但是逻辑上MTD Block层上面就是内核的块层和文件系统,MTD核心层只是提供相应的服务,逻辑结构上并没有在这种抽象中。

 

掉书袋的东西扯了一通,我们快速进入正题中来。所谓的MTD Block层,肯定需要一个初始化吧,这是linux的套路,搞一个init就出来一套东东。

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, 
    .remove_dev    = mtdblock_remove_dev, 
    .owner        = THIS_MODULE, 
}; 

Mtdblock_tr这个结构体是mtd block层中最关键的一个机构体,MTD Block之后的一系列重要操作都和这个结构体相关。

而下面register_mtd_blktrans函数就是mtd block层的初始化操作,这个函数没有太多复杂的操作简单总结就是:

1. block的工商局注册了一个31major号商标

2. 初始化了一个块设备IO请求的处理队列,这个队列之后用来处理所有的MTD Block设备的IO请求。

3. 初始化了一个处理MTD Block层设备IO请求的内核进程

4. 初始化mtd_table数组,之后mtd设备的mtd_info结构体会被依照需要加入到这个数组中来管理。

 int register_mtd_blktrans(struct mtd_blktrans_ops *tr) 

    int ret, i; 
 
    /* Register the notifier if/when the first device type is 
       registered, to prevent the link/init ordering from fucking 
       us over. */ 
    if (!blktrans_notifier.list.next) 
        register_mtd_user(&blktrans_notifier); 
 
    tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); 
    if (!tr->blkcore_priv) 
        return -ENOMEM; 
 
    mutex_lock(&mtd_table_mutex); 
 
    /*抢先道block的工商局注册major号商标*/ 
    ret = register_blkdev(tr->major, tr->name); 
    if (ret) 
    { 
        printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n"
               tr->name, tr->major, ret); 
        kfree(tr->blkcore_priv); 
        mutex_unlock(&mtd_table_mutex); 
        return ret; 
    } 
    spin_lock_init(&tr->blkcore_priv->queue_lock); 
 
    /*初始化一个块设备的请求的调度队列,这个很重要,所有的mtd块设备 
      都会公用这个队列来完成IO操作,但是这里暂时不详细展开描述*/ 
    tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); 
    if (!tr->blkcore_priv->rq) 
    { 
        unregister_blkdev(tr->major, tr->name); 
        kfree(tr->blkcore_priv); 
        mutex_unlock(&mtd_table_mutex); 
        return -ENOMEM; 
    } 
    /*你才blksize是多少,512呵呵*/ 
    tr->blkcore_priv->rq->queuedata = tr; 
    blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); 
    if (tr->discard) 
        blk_queue_set_discard(tr->blkcore_priv->rq, 
                              blktrans_discard_request); 
 
    tr->blkshift = ffs(tr->blksize) - 1
    /*初始化一个mtd block的内核进程,这个进程也是和处理mtd block 
      设备的IO请求有关系,这里不展开讨论,只有会在block层讨论 
      你在嵌入式linux下面使用ps命令,可以看到这个内核进程*/ 
    tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, 
                                           "%sd", tr->name); 
    if (IS_ERR(tr->blkcore_priv->thread)) 
    { 
        blk_cleanup_queue(tr->blkcore_priv->rq); 
        unregister_blkdev(tr->major, tr->name); 
        kfree(tr->blkcore_priv); 
        mutex_unlock(&mtd_table_mutex); 
        return PTR_ERR(tr->blkcore_priv->thread); 
    } 
 
    INIT_LIST_HEAD(&tr->devs); 
    list_add(&tr->list, &blktrans_majors); 
    /*初始化mtd_table数组,mtd 设备的mtd_info结构体都会放到这个 
      上面去*/ 
    for (i = 0; i < MAX_MTD_DEVICES; i++) 
    { 
        if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) 
            tr->add_mtd(tr, mtd_table[i]); 
    } 
 
    mutex_unlock(&mtd_table_mutex); 
 
    return 0

 

上面说了MTD Block层的初始化,它的主要作用是将MTD 顶层设备包装成block设备。这里要插入一点就是,一般而言mtd底层将一个Flash设备就分成了不同的分区。每个分区抽象成一个mtd设备。每个分区的mtd设备注册到MTD Block层之后,该层将这个mtd设备认为是一个不能再分区的block设备了。

嵌入式设备中一般都定义了类似下面的Flash设备分区表

static struct mtd_partition qt2410_nand_part[] = 

    [0] = { 
        .name    = "U-Boot"
        .size    = 0x30000
        .offset    = 0
    }, 
    [1] = { 
        .name    = "U-Boot environment"
        .offset = 0x30000
        .size    = 0x4000
    }, 
    [2] = { 
        .name    = "kernel"
        .offset = 0x34000
        .size    = SZ_2M, 
    }, 
    [3] = { 
        .name    = "initrd"
        .offset    = 0x234000
        .size    = SZ_4M, 
    }, 
    [4] = { 
        .name    = "jffs2"
        .offset = 0x634000
        .size    = 0x39cc000
    }, 
}; 

 

上面的这个分区表就是将一个底层的Flash设备分为两若干个分区设备。

add_one_partition函数会将上面的每一个分区建立一个mtd_part的结构体,并初始化mtd_part->mtd中的数据成员,每个分区在mtd底层驱动中抽象成了独立的mtd设备

对于这样的分区mtd设备,最后调用add_mtd_device函数加入到mtd内核层

而后,add_mtd_device函数调用mtdblock_add_mtd函数将mtd硬件设备抽象成mtd block设备。

mtd block层内核使用blktrans_dev数据结构来描述一个mtd block设备

struct mtd_blktrans_dev 

    struct mtd_blktrans_ops *tr; 
    struct list_head list; 
    struct mtd_info *mtd; 
    struct mutex lock; 
    int devnum; 
    unsigned long size; 
    int readonly; 
    void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ 
}; 

mtdblock_add_mtd函数就是简单的分配了一个mtd_blktrans_dev结构体,并做了相应的初始化,之后就调用了add_mtd_blktrans_dev函数将。好戏在这个add_mtd_blktrans_dev函数里面。

 static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) 

    struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); 
 
    if (!dev) 
        return
 
    dev->mtd = mtd; 
    dev->devnum = mtd->index; 
 
    dev->size = mtd->size >> 9
    dev->tr = tr; 
 
    if (!(mtd->flags & MTD_WRITEABLE)) 
        dev->readonly = 1
 
    add_mtd_blktrans_dev(dev); 
}  

关于add_mtd_blktrans_dev的处理,下面代码中标注了详细的注释,这个函数基本上起到了承上启下的作用,将mtd block继续抽象成block设备,像block层进行了添加注册。

int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new

    struct mtd_blktrans_ops *tr = new->tr; 
    struct mtd_blktrans_dev *d; 
    int last_devnum = -1
    struct gendisk *gd; 
 
    if (mutex_trylock(&mtd_table_mutex)) 
    { 
        mutex_unlock(&mtd_table_mutex); 
        BUG(); 
    } 
 
    /*这段代码只是检查一下mtd block层中次设备号有没有被分区出去 
    从前面的分析知道mtd block设备的主设备号为31,而mtd设备将加入 
    mtd_talbe时的index作为mtd block设备的次设备号,这里需要首先 
    检查一下这个次设备号是否被占用*/ 
    list_for_each_entry(d, &tr->devs, list) 
    { 
        if (new->devnum == -1
        { 
            /* Use first free number */ 
            if (d->devnum != last_devnum + 1
            { 
                /* Found a free devnum. Plug it in here */ 
                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) 
        { 
            /* Required number was free */ 
            list_add_tail(&new->list, &d->list); 
            goto added; 
        } 
        last_devnum = d->devnum; 
    } 
    if (new->devnum == -1
        new->devnum = last_devnum + 1
 
    if ((new->devnum << tr->part_bits) > 256
    { 
        return -EBUSY; 
    } 
 
    /*分配次设备号成功,将mtd_blktrans_dev链接到 
    mtdblock_tr->devs上面*/ 
    list_add_tail(&new->list, &tr->devs); 
added: 
    mutex_init(&new->lock); 
    if (!tr->writesect) 
        new->readonly = 1
    /*下面就是承上启下的作用了,在分配了mtd block设备后,这里又 
    要将mtd block设备抽象成一个block设备,将它注册到block*/ 
    gd = alloc_disk(1 << tr->part_bits); 
    if (!gd) 
    { 
        list_del(&new->list); 
        return -ENOMEM; 
    } 
    /*下面的处理就是分配一个gendisk数据结构, 
    初始化这个数据结构, 
    将gendisk 添加注册到block层中*/ 
    /*下面的初始化需要注意,first_minor就是mtd block的次设备号 
    part_bits位 0,不允许在block设备上再有分区*/ 
    gd->major = tr->major; 
    gd->first_minor = (new->devnum) << tr->part_bits; 
    gd->fops = &mtd_blktrans_ops; 
 
    if (tr->part_bits) 
        if (new->devnum < 26
            snprintf(gd->disk_name, sizeof(gd->disk_name), 
                     "%s%c", tr->name, 'a' + new->devnum); 
        else 
            snprintf(gd->disk_name, sizeof(gd->disk_name), 
                     "%s%c%c", tr->name, 
                     'a' - 1 + new->devnum / 26
                     'a' + new->devnum % 26); 
    else 
        snprintf(gd->disk_name, sizeof(gd->disk_name), 
                 "%s%d", tr->name, 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 * tr->blksize) >> 9); 
 
    gd->private_data = new
    new->blkcore_priv = gd; 
    gd->queue = tr->blkcore_priv->rq; 
 
    if (new->readonly) 
        set_disk_ro(gd, 1); 
 
    add_disk(gd); 
 
    return 0

 

阅读(8142) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~