Chinaunix首页 | 论坛 | 博客
  • 博客访问: 172848
  • 博文数量: 44
  • 博客积分: 627
  • 博客等级: 中士
  • 技术积分: 345
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-20 21:55
文章分类

全部博文(44)

文章存档

2012年(44)

分类: LINUX

2012-08-08 17:23:17

MTD设备模型建立流程  

2010-07-21 21:23:05|  分类: arm linux设备驱 |  标签: |字号 

MTD设备模型建立流程 - 尘 - 我的电子小屋

/******************************************************************/

/******************************************************************/

/******************************************************************/

/******************************************************************/

一, MTD Flash硬件驱动层:

在文件s3c2410.c中有这样一个结构体:

struct s3c2410_nand_mtd {

struct mtd_info mtd;

struct nand_chip chip;

struct s3c2410_nand_set *set;

struct s3c2410_nand_info *info;

int scan_res;

};

这个结构体即描述了一个MTD设备。

struct nand_chip:包含了直接对物理设备操作的函数和描述物理状态的字段。

               一个MTD设备可能不只以块flash但是一个MTD设备只有一个nand_chip                     

               结构体。结构体nand_chip中的操作函数select_chip( );实现芯片选择。

struct mtd_info:包含MTD设备的相关信息和MTD设备操作函数。这些操作函数是由

               struct nand_chip中操作函数包装而来提供给字符设备或块设备层函数

               调用。

struct s3c2410_nand_set:包含MTD设备芯片数,分区数,分区信息等。

struct s3c2410_nand_infoMTD设备模型建立时存储一些临时状态信息。

/*****************************************************************************/

static int s3c24xx_nand_probe(struct platform_device *pdev,

      enum s3c_cpu_type cpu_type)

{

struct s3c2410_platform_nand *plat = to_nand_plat(pdev);

          。

          。

          。

    s3c2410_nand_init_chip(info, nmtd, sets);

nmtd->scan_res = nand_scan_ident(&nmtd->mtd,

 (sets) ? sets->nr_chips : 1);

if (nmtd->scan_res == 0) {

s3c2410_nand_update_chip(info, nmtd);

nand_scan_tail(&nmtd->mtd);

s3c2410_nand_add_partition(info, nmtd, sets);

}

     。

         。

         。

  }

/*****************************************************************************/

static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,

   struct s3c2410_nand_mtd *nmtd,

   struct s3c2410_nand_set *set)

{

     。

         。

         。

//初始化结构体nand_chip的各字段,以下几个重要函数即是在文件s3c2410.c中实现

chip->IO_ADDR_W = regs + S3C2440_NFDATA;

info->sel_reg   = regs + S3C2440_NFCONT;

info->sel_bit = S3C2440_NFCONT_nFCE;

chip->cmd_ctrl  = s3c2440_nand_hwcontrol;

chip->dev_ready = s3c2440_nand_devready;

chip->read_buf  = s3c2440_nand_read_buf;

chip->write_buf = s3c2440_nand_write_buf;

//建立几个重要结构体的指向关系。

    nmtd->info    = info;

      nmtd->mtd.priv    = chip;

    nmtd->mtd.owner    = THIS_MODULE;

    nmtd->set    = set;

     。

         。

         。

}

/*****************************************************************************/

int nand_scan_ident(struct mtd_info *mtd, int maxchips)

{

     。

         。

         。

//读取该flashid,并在数组nand_flash_ids[i]中查找,看该类flash是否被支持。

type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);

     。

         。

         。

}

/*****************************************************************************/

static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info,

     struct s3c2410_nand_mtd *nmtd)

{

struct nand_chip *chip = &nmtd->chip;

if (hardware_ecc) {

if (chip->page_shift > 10) {

chip->ecc.size     = 256;

chip->ecc.bytes     = 3;

} else {

chip->ecc.size     = 512;//521字节计算一次ECC

chip->ecc.bytes     = 3;//一次ECC的结果为3字节

chip->ecc.layout    = &nand_hw_eccoob;

/*

//nand flash的每页后有16字节的OOB

static struct nand_ecclayout nand_hw_eccoob = {

.eccbytes = 3,//用三个字节存放ECC

.eccpos = {0, 1, 2},//这三个字节为第0,1,2字节

.oobfree = {{8, 8}}//从第8字节开始的8字节空闲

};

*/

}

}

}

/*****************************************************************************/

nand_scan_tail(&nmtd->mtd);:在该函数中填充了结构体nand_chip的一些字段,对结构体               

                          mtd_info进行了全面的初始化。

/*****************************************************************************/

static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info,

      struct s3c2410_nand_mtd *mtd,

      struct s3c2410_nand_set *set)

{

if (set == NULL)

return add_mtd_device(&mtd->mtd);

//本例中的flash存在分区所以会执行函数add_mtd_partitions

if (set->nr_partitions > 0 && set->partitions != NULL) {

return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions);

}

return add_mtd_device(&mtd->mtd);

}

/*****************************************************************************/

int add_mtd_partitions(struct mtd_info *master,

       const struct mtd_partition *parts,

       int nbparts)

{

struct mtd_part *slave;

uint64_t cur_offset = 0;

int i;

printk(KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);

//有多少个分区就进行多少次add_one_partition

for (i = 0; i < nbparts; i++) {

slave = add_one_partition(master, parts + i, i, cur_offset);

if (!slave)

return -ENOMEM;

cur_offset = slave->offset + slave->mtd.size;

}

return 0;

}

/*****************************************************************************/

static struct mtd_part *add_one_partition(struct mtd_info *master,

const struct mtd_partition *part, int partno,

uint64_t cur_offset)

{

struct mtd_part *slave;

/*

一个结构体mtd_part 即代表一个分区,该结构体原型如下。

struct mtd_part {

struct mtd_info mtd;

struct mtd_info *master;

uint64_t offset;

int index;

struct list_head list;

int registered;

};

*/

/* allocate the partition structure */

slave = kzalloc(sizeof(*slave), GFP_KERNEL);

if (!slave) {

printk(KERN_ERR"memory allocation error while creating partitions for \"%s\"\n",

master->name);

del_mtd_partitions(master);

return NULL;

}

list_add(&slave->list, &mtd_partitions);

//master的各字段初始化slave的对应字段。Master在文件s3c2410.c中通过函数//masternand_scan_tail(&nmtd->mtd)进行了初始化。

/* set up the MTD object for this partition */

slave->mtd.type = master->type;

slave->mtd.flags = master->flags & ~part->mask_flags;

slave->mtd.size = part->size;

slave->mtd.writesize = master->writesize;

slave->mtd.oobsize = master->oobsize;

slave->mtd.oobavail = master->oobavail;

slave->mtd.subpage_sft = master->subpage_sft;

slave->mtd.name = part->name;

slave->mtd.owner = master->owner;

slave->mtd.backing_dev_info = master->backing_dev_info;

     。

         。

         。

out_register:

if (part->mtdp) {

/* store the object pointer (caller may or may not register it*/

*part->mtdp = &slave->mtd;

slave->registered = 0;

} else {

/* register our partition */

//slavemtd添加到内核,而Maste不会添加到内核,Maste只用于初始化slave->mtd add_mtd_device(&slave->mtd);

slave->registered = 1;

}

return slave;

}

/*****************************************************************************/

int add_mtd_device(struct mtd_info *mtd)

{

int i;

     。

         。

         。

//每一个添加到内核中的mtd_info挨个放到mtd_table[i]

for (i=0; i < MAX_MTD_DEVICES; i++)

if (!mtd_table[i]) {

struct mtd_notifier *not;

//在数组mtd_table[i]中找出第一个未被占用的位置把即将添加到内核的mtd_info

//放到该处。

            mtd_table[i] = mtd;

mtd->index = i;

mtd->usecount = 0;

     。

         。

         。

mtd->dev.type = &mtd_devtype;

mtd->dev.class = mtd_class;

/*

MTD_DEVT的原型如下。

MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)

MTD设备的主设备号MTD_CHAR_MAJOR90

(index)*2是因为每向内核添加一个mtd_info需要创建两个设备节点。

*/

mtd->dev.devt = MTD_DEVT(i);

dev_set_name(&mtd->dev, "mtd%d", i);

if (device_register(&mtd->dev) != 0) {

mtd_table[i] = NULL;

break;

}

  

if (MTD_DEVT(i))

device_create(mtd_class, mtd->dev.parent,

MTD_DEVT(i) + 1,

NULL, "mtd%dro", i);

     。

         。

         。

}

到此一个MTD设备的分区就添加到了内核。

/******************************************************************/

/******************************************************************/

/******************************************************************/

/******************************************************************/

二,MTD字符设备驱动层:

     在文件mtdchar. C中主要实现了文件操作函数

static const struct file_operations mtd_fops = {

.owner = THIS_MODULE,

.llseek = mtd_lseek,

.read = mtd_read,

.write = mtd_write,

.ioctl = mtd_ioctl,

.open = mtd_open,

.release = mtd_close,

.mmap = mtd_mmap,

#ifndef CONFIG_MMU

.get_unmapped_area = mtd_get_unmapped_area,

#endif

};

在设备文件打开时,通过设备的次设备号在数组mtd_table[]中找到与该设备对应的结构mtd_infomtd_fops中操作函数即是通过调用结构体mtd_info中的操作函数实现对设备的操作的。

static int __init init_mtdchar(void)

{

int status;

//注册MTD字符设备主设备号MTD_CHAR_MAJOR(90)并关联操作函数集mtd_fops

status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);

if (status < 0) {

printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",

       MTD_CHAR_MAJOR);

}

return status;

}

/******************************************************************/

/******************************************************************/

/******************************************************************/

/******************************************************************/

三,MTD块设备层:

在文件mtdblock.c中主要实现了一下操作函数集:

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,

};

这些函数也是通过调用结构体mtd_info中的操作函数实现对flash的操作的。

static int __init init_mtdblock(void)

{

//mtd_table[i]将该结构体中的每一个mtd_info都对应一个块设备struct gendisk注册到内核,

//这些所有块设备公用一个操作函数集mtdblock_tr

return register_mtd_blktrans(&mtdblock_tr);

}

/*****************************************************************************/

int register_mtd_blktrans(struct mtd_blktrans_ops *tr)

{

int ret, i;

/*

一下函数的执行最终会执行这么一句

list_for_each_entry(tr, &blktrans_majors, list)

tr->add_mtd(tr, mtd);

由于此时链表blktrans_majors为空,并无mtd_blktrans_ops添加到上面,所以函数

tr->add_mtd(tr, mtd);不会执行。

*/

if (!blktrans_notifier.list.next)

register_mtd_user(&blktrans_notifier);

/*

tr->blkcore_priv的结构原型如下:

struct mtd_blkcore_priv {

struct task_struct *thread;

struct request_queue *rq;

spinlock_t queue_lock;

};

这个结构中存放了两个重要对象,内核线程结构体,MTD块设备请求队列头。

*/

tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);

if (!tr->blkcore_priv)

return -ENOMEM;

 。

         。

         。

ret = register_blkdev(tr->major, tr->name);//注册MTD块设备,主设备号为31

//初始化请求队列头并关联请求处理函数mtd_blktrans_request

tr->blkcore_priv->rq=blk_init_queue(mtd_blktrans_request,&tr->blkcore_priv->queue_lock)

tr->blkcore_priv->rq->queuedata = tr;//建立指向关系以便后面查找调用。

//创建内核线程,线程处理函数mtd_blktrans_thread,该函数后面及讲解

tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,

"%sd", tr->name);

//mtd_blktrans_ops挂到链表blktrans_majors上。

INIT_LIST_HEAD(&tr->devs);

list_add(&tr->list, &blktrans_majors);

     。

         。

         。

//将数组mtd_table[]中的每一个mtd_info都对应一个块设备struct gendisk注册到内核。

//这些mtd_info都同属一个MTD块设备,拥有共同的主设备号31。上

for (i=0; 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;

}

/*****************************************************************************/

//请求处理函数的主要工作是唤醒内核线程

static void mtd_blktrans_request(struct request_queue *rq)

{

struct mtd_blktrans_ops *tr = rq->queuedata;

wake_up_process(tr->blkcore_priv->thread);

}

/*****************************************************************************/

static int mtd_blktrans_thread(void *arg)

{

struct mtd_blktrans_ops *tr = arg;

struct request_queue *rq = tr->blkcore_priv->rq;

/* we might get involved when memory gets low, so use PF_MEMALLOC */

current->flags |= PF_MEMALLOC;

spin_lock_irq(rq->queue_lock);

while (!kthread_should_stop()) {

struct request *req;

struct mtd_blktrans_dev *dev;

int res = 0;

//获取对列中第一个未完成的请求。

req = elv_next_request(rq);

if (!req) {

            //如果请求为空将线程置于睡眠状态

set_current_state(TASK_INTERRUPTIBLE);

spin_unlock_irq(rq->queue_lock);

schedule();

spin_lock_irq(rq->queue_lock);

continue;

}

 /*

一个请求队列管理着很多请求,但是每一个请求都只能针对一个块设备gendisk

所以每一个请求被创建出来后都会指向它的请求对象gendisk

这个指向关系在函数__make_request->init_request_from_bio->blk_rq_bio_prep中建立。

*/  

dev = req->rq_disk->private_data;

tr = dev->tr;

spin_unlock_irq(rq->queue_lock);

mutex_lock(&dev->lock);

 /*

在函数do_blktrans_request(tr, dev, req)

根据请求的数据传输方向来决定调用读或写函数进行数据传输

tr->readsect(dev, block, buf)

tr->writesect(dev, block, buf)

*/

res = do_blktrans_request(tr, dev, req);

mutex_unlock(&dev->lock);

spin_lock_irq(rq->queue_lock);

//如果res0表示不能成功完成请求,为非0表示成功完成请求。

end_request(req, res);

}

spin_unlock_irq(rq->queue_lock);

return 0;

}

/*****************************************************************************/

函数tr->add_mtd(tr, mtd_table[i])在文件s3c2410.c中实现,其原型如下:

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

{

//该函数将数组mtd_table[i]中的每一个mtd_info都包装成结构体mtd_blktrans_dev

/*

结构体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;

unsigned long size;

int readonly;

void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */

};

*/

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;

//将一个mtd_info结构对应一个块设备gendisk注册到内核

add_mtd_blktrans_dev(dev);

}

/*****************************************************************************/

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;

     。

         。

         。

/*

数组mtd_table[i]中的每一个mtd_info都包装成结构体mtd_blktrans_dev,

而所有mtd_info都对应同一个数据传输函数集mtd_blktrans_ops,所以要

将结构体mtd_blktrans_dev挂接到tr->devs上。

*/

list_add_tail(&new->list, &tr->devs);

 added:

mutex_init(&new->lock);

if (!tr->writesect)

new->readonly = 1;

//gendisk 分配内存。

gd = alloc_disk(1 << tr->part_bits);

if (!gd) {

list_del(&new->list);

return -ENOMEM;

}

//初始化gendisk

gd->major = tr->major;

gd->first_minor = (new->devnum) << tr->part_bits;

gd->fops = &mtd_blktrans_ops;

     。

         。

         。

set_capacity(gd, (new->size * tr->blksize) >> 9);

//mtd_blktrans_dev被设为对应gendisk的私有成员。

gd->private_data = new;

new->blkcore_priv = gd;

//请求对列tr->blkcore_priv->rq在函数register_mtd_blktrans(&mtdblock_tr)中初始化

//所有mtd_info对应的块设备gendisk都用同一个请求队列头。

/*

一个请求队列管理着很多请求,但是每一个请求都只能针对一个块设备gendisk

所以每一个请求被创建出来后都会指向它的请求对象gendisk

if (bio->bi_bdev)

rq->rq_disk = bio->bi_bdev->bd_disk;

以上这里两句在函数__make_request->init_request_from_bio->blk_rq_bio_prep 中。

bio->bi_bdevbio中的定义为struct block_device *bi_bdev;

bio->bi_bdev->bd_diskblock_device中的定义为struct gendisk * bd_disk;

rq->rq_diskrequest中的定义为struct gendisk *rq_disk;

*/

gd->queue = tr->blkcore_priv->rq;//

gd->driverfs_dev = new->mtd->dev.parent;

if (new->readonly)

set_disk_ro(gd, 1);

//mtd_info对应的块设备gendisk 添加到内核

add_disk(gd);

return 0;

}

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