linux mtd 块设备,基于块设备子系统的MTD子系统(2.6.26)

基于块设备子系统的MTD子系统(2.6.26)

作者:guolele

blog:http://blog.csdn.net/guolele2010

a4c26d1e5885305701be709a3d33442f.png

这图说明了MTD子系统的架构,那么它跟块设备子系统有什么关系?其实MTD就是利用了块设备子系统,只是它中间多了一层MTD设备驱动层。

在drivers/mtd/mtdblock.c里,

static int

__init init_mtdblock(void)

{

return

register_mtd_blktrans(&mtdblock_tr);

}

只有一行,看看

int

register_mtd_blktrans(struct mtd_blktrans_ops

*tr)

{

int

ret, i;

if

(!blktrans_notifier.list.next)

register_mtd_user(&blktrans_notifier);

……

ret

= register_blkdev(tr->major,

tr->name);

……

tr->blkcore_priv->rq=blk_init_queue(mtd_blktrans_request,

&tr->blkcore_priv->queue_lock);

……

tr->blkcore_priv->rq->queuedata

= tr;

blk_queue_hardsect_size(tr->blkcore_priv->rq,

tr->blksize);

tr->blkshift

= ffs(tr->blksize) - 1;

tr->blkcore_priv->thread

= kthread_run(mtd_blktrans_thread,

tr,

"%sd",

tr->name);

……

INIT_LIST_HEAD(&tr->devs);

list_add(&tr->list,

&blktrans_majors);

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;

}

只要不是瞎的都知道这就是块设备驱动,那它没指定make_requset函数,那么就默认为__make_request,这就不分析了,然后就交给mtd_blktrans_request,这个函数只有两行:

static void

mtd_blktrans_request(struct request_queue

*rq)

{

struct

mtd_blktrans_ops *tr = rq->queuedata;

wake_up_process(tr->blkcore_priv->thread);

}

正常人也能猜到这是唤醒一个线程,但是唤醒哪个?在register_mtd_blktrans有一句. tr->blkcore_priv->thread =

kthread_run(mtd_blktrans_thread,

tr,

"%sd",

tr->name);

看看mtd_blktrans_thread

static int

mtd_blktrans_thread(void *arg)

{

……

req

= elv_next_request(rq);

……

res

= do_blktrans_request(tr, dev,

req);

…..

end_request(req,

res);

}

spin_unlock_irq(rq->queue_lock);

return

0;

}

req =

elv_next_request(rq);

多熟悉的身影,就是拿一个请求出来,然后处理res = do_blktrans_request(tr, dev,

req);

Static int

do_blktrans_request(struct mtd_blktrans_ops

*tr,

struct

mtd_blktrans_dev *dev,

struct

request *req)

{

……

switch(rq_data_dir(req))

{

case

READ:

for

(; nsect > 0; nsect--, block++, buf +=

tr->blksize)

if

(tr->readsect(dev, block,

buf))//它在mtdblock_tr里指定为mtdblock_readsect

return

0;

return

1;

case

WRITE:

if

(!tr->writesect)

return

0;

for

(; nsect > 0; nsect--, block++, buf +=

tr->blksize)

if

(tr->writesect(dev, block, buf))

return

0;

return

1;

default:

printk(KERN_NOTICE

"Unknown request %u/n", rq_data_dir(req));

return

0;

}

}

其实就是调用mtdblock_readsect,在mtdblock_tr上。

static int

mtdblock_readsect(struct mtd_blktrans_dev

*dev,

unsigned

long block, char *buf)

{

struct

mtdblk_dev *mtdblk =

mtdblks[dev->devnum];//最终从注册的mtddev中取出对应的tdblk_dev结构

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;//在注册的mtdblk中拿出mtd_info其中最里面包含操作的函数

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);//s3c2410.c指定为nand_read

while

(len > 0)

……

}

mtd->read(mtd, pos, len, &retlen,

buf);以s3c2410.c为例,在probe函数里的nand_scan_tail,在指定为nand_read.

Nand_read就属于drivers/mtd/nand/nand_base.c

这一层就是多加的一层,它下面就是直接的驱动程序

static int

nand_read(struct mtd_info *mtd, loff_t from, size_t

len,

size_t

*retlen, uint8_t *buf)

{

……

ret

= nand_do_read_ops(mtd, from,

&chip->ops);

……

}

nand_do_read_ops执行读操作

static int

nand_do_read_ops(struct mtd_info *mtd, loff_t

from,

struct

mtd_oob_ops *ops)

{

……

if

(unlikely(ops->mode == MTD_OOB_RAW))

ret

= chip->ecc.read_page_raw(mtd, chip,

bufpoi);//在s3c2410.c里的nand_scan_tail(&nmtd->mtd);指定了nand_read_page_raw

……

}

chip->ecc.read_page_raw这个函数指针呢,还是在nand_scan_tail(&nmtd->mtd);指定了nand_read_page_raw,这要在默认不没有的时候内核才填的,当然也可以自己实现。

static int

nand_read_page_raw(struct mtd_info *mtd, struct nand_chip

*chip,

uint8_t

*buf)

{

chip->read_buf(mtd,

buf, mtd->writesize);//最终调用的是nand_ chip里的read_buf函数指针,s3c2410为s3c2440_nand_read_buf

chip->read_buf(mtd,

chip->oob_poi, mtd->oobsize);

return

0;

}

最终,调用的是我们要写的驱动程序里的读flash函数。

还有一边是为了提供给用户空间的一个接口,所以采用了一个以字符设备驱动为主的一层。

在drivers/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,

};

只看打开跟读。

static int

mtd_open(struct inode *inode, struct file

*file)

{

……

mtd

= get_mtd_device(NULL, devnum);

……

mfi->mtd

= mtd;

file->private_data

= mfi;

return

0;

}

打开就为了获得struct

mtd_info这个结构体,有个读操作进来。

static

ssize_t mtd_read(struct file *file, char __user *buf, size_t

count,loff_t *ppos)

{

……

ret

= mtd->read_oob(mtd, *ppos,

&ops);//在nand_scan_tail里指定为nand_read_oob

retlen

= ops.retlen;

break;

}

default:

ret

= mtd->read(mtd, *ppos, len, &retlen,

kbuf);//在nand_scan_tail里指定为nand_read

}

……

}

就是采用我们要写的驱动程序里的读函数,前面几个什么read_oob这些要指定才有,我们没指定,所以就直接到mtd->read也就是nand_read,也就重复上面说的内容了。

再看看上面的图,清楚的明白了这个机制了吧?然后呢,就是要谈我们怎么写的时候了。内核已经给我们做完了大部分工作,我们要实现的就是s3c2410_nand.c这个驱动程序,这个驱动程序的主要工作是什么?

说这之前要先说几个结构体:

struct

mtd_info {

u_char

type;

u_int32_t

flags;

u_int32_t

size; //

Total size of the MTD

u_int32_t

erasesize;

u_int32_t

writesize;

u_int32_t

oobsize; //

Amount of OOB data per block (e.g. 16)

u_int32_t

oobavail; // Available OOB bytes

per block

//

Kernel-only stuff starts here.

char

*name;

int

index;

struct

nand_ecclayout *ecclayout;

int

numeraseregions;

struct

mtd_erase_region_info *eraseregions;

int

(*erase) (struct mtd_info *mtd, struct erase_info

*instr);

int

(*point) (struct mtd_info *mtd, loff_t from, size_t

len,

size_t

*retlen, void **virt, resource_size_t

*phys);

void

(*unpoint) (struct mtd_info *mtd, loff_t from, size_t

len);

int

(*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t

*retlen, u_char *buf);

int

(*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t

*retlen, const u_char *buf);

int

(*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t

*retlen, const u_char *buf);

int

(*read_oob) (struct mtd_info *mtd, loff_t

from,

struct

mtd_oob_ops *ops);

int

(*write_oob) (struct mtd_info *mtd, loff_t

to,

struct

mtd_oob_ops *ops);

int

(*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,

size_t len);

int

(*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len, size_t *retlen, u_char *buf);

int

(*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,

size_t len);

int

(*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len, size_t *retlen, u_char *buf);

int

(*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len, size_t *retlen, u_char *buf);

int

(*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len);

int

(*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned

long count, loff_t to, size_t *retlen);

void

(*sync) (struct mtd_info *mtd);

int

(*lock) (struct mtd_info *mtd, loff_t ofs, size_t

len);

int

(*unlock) (struct mtd_info *mtd, loff_t ofs, size_t

len);

int

(*suspend) (struct mtd_info *mtd);

void

(*resume) (struct mtd_info *mtd);

int

(*block_isbad) (struct mtd_info *mtd, loff_t

ofs);

int

(*block_markbad) (struct mtd_info *mtd, loff_t

ofs);

struct

notifier_block

reboot_notifier;

struct

mtd_ecc_stats ecc_stats;

int

subpage_sft;

void

*priv;

struct

module *owner;

int

usecount;

int

(*get_device) (struct mtd_info *mtd);

void

(*put_device) (struct mtd_info *mtd);

};

Mtd_info这个结构体是描述每一个mtd操作

还有一个就是struct

nand_chip,觉得太长,就只复制个内核描述就可以了。

Struct

nand_chip是底层操作的,接到命令,就把数据写到mtd_info里,然后处理后再转为struct

nand_chip,才真正执行。

1、实现底层硬件操作,struct

nand_chip

2、填充struct

mtd_info

整个驱动程序就这么简单!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值