基于块设备子系统的MTD子系统(2.6.26)
作者:guolele
blog:http://blog.csdn.net/guolele2010
这图说明了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
整个驱动程序就这么简单!