以at91_nand.c为例,分析在块设备驱动中,nand_flash 的读写过程:
1.分配nand_chip结构体
2.设置nand_chip
3.添加分区
从init开始分析:
static int __init at91_nand_init(void)
{
return platform_driver_register(&at91_nand_driver);
}
static struct platform_driver at91_nand_driver = {
.probe = at91_nand_probe,
.remove = at91_nand_remove,
.driver = {
.name = "at91_nand",
.owner = THIS_MODULE,
},
};
static int __init at91_nand_probe(struct platform_device *pdev)
{
struct at91_nand_host *host;
struct mtd_info *mtd;
struct nand_chip *nand_chip; //分配nand_chip结构体
.......
nand_chip = &host->nand_chip;
host->board = pdev->dev.platform_data;
nand_chip->priv = host; /* link the private data structures */
mtd->priv = nand_chip;
mtd->owner = THIS_MODULE;
/* Set address of NAND IO lines */
nand_chip->IO_ADDR_R = host->io_base;
nand_chip->IO_ADDR_W = host->io_base;
nand_chip->cmd_ctrl = at91_nand_cmd_ctrl;
nand_chip->dev_ready = at91_nand_device_ready;
nand_chip->ecc.mode = NAND_ECC_SOFT; /* enable ECC */
nand_chip->chip_delay = 20; /* 20us command delay time */
.........
nand_scan(mtd, 1) //扫描设备,根据nand的信息设置mtd_info结构体
.........
parse_mtd_partitions(mtd, part_probes, &partitions, 0);
.........
res = add_mtd_partitions(mtd, partitions, num_partitions);
#else
res = add_mtd_device(mtd); //添加分区
对于nand_scan函数:
int nand_scan(struct mtd_info *mtd, int maxchips)
{
int ret;
/* Many callers got this wrong, so check for it for a while... */
if (!mtd->owner && caller_is_module()) {
printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
BUG();
}
ret = nand_scan_ident(mtd, maxchips);
if (!ret)
ret = nand_scan_tail(mtd);
return ret;
}
nand_scan_ident函数主要就是nand类型就行分析,获取厂家以及设备ID,得到flash的大小等信息。
nand_scan_tail函数主要就是根据nand 的信息对mtd_infp结构体进行设置
对于add_mtd_device:将上面设置的mtd_info添加到mtd-table中
int add_mtd_device(struct mtd_info *mtd)
{
int i;
BUG_ON(mtd->writesize == 0);
mutex_lock(&mtd_table_mutex);
for (i=0; i < MAX_MTD_DEVICES; i++)
if (!mtd_table[i]) {
struct list_head *this;
mtd_table[i] = mtd;
mtd->index = i;
mtd->usecount = 0;
.............
DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
list_for_each(this, &mtd_notifiers) {
struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
not->add(mtd); //对链表进行遍历,每个mtd_notifiers调用add函数
}
mutex_unlock(&mtd_table_mutex);
__module_get(THIS_MODULE);
return 0;
}
mutex_unlock(&mtd_table_mutex);
return 1;
}
mtd_notifiers是在register_mtd_user函数中被初始化的
void register_mtd_user (struct mtd_notifier *new)
{
int i;
mutex_lock(&mtd_table_mutex);
list_add(&new->list, &mtd_notifiers);
__module_get(THIS_MODULE);
for (i=0; i< MAX_MTD_DEVICES; i++)
if (mtd_table[i])
new->add(mtd_table[i]);
mutex_unlock(&mtd_table_mutex);
}
而register_mtd_user 是被谁调用的了?--->被mtdchar.c以及mtd_blkdevs.c调用,前者注册一个字符设备,后者注册一个块设备。
重点分析mtd_blkdevs.c
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
........
register_mtd_user(&blktrans_notifier);
.............
}
static struct mtd_notifier blktrans_notifier = {
.add = blktrans_notify_add,
.remove = blktrans_notify_remove,
};
static void blktrans_notify_add(struct mtd_info *mtd)
{
struct list_head *this;
if (mtd->type == MTD_ABSENT)
return;
list_for_each(this, &blktrans_majors) {
struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
tr->add_mtd(tr, mtd); //链表的遍历,对于每个blktrans_majors调用add_mtd函数
}
}
对于blktrans_majors是在register_mtd_blktrans函数中初始化的
int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
.......
list_add(&tr->list, &blktrans_majors);
.........
}
register_mtd_blktrans函数被谁调用的?--->被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,
};
static int __init init_mtdblock(void)
{
return register_mtd_blktrans(&mtdblock_tr);
}
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); //此函数实现了块设备注册的流程:
// 1.分配gendisk结构体 2.设置 3.add_disk
}
int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
{
........
gd = alloc_disk(1 << tr->part_bits);
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);
gd->private_data = new;
new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq;
add_disk(gd);
}
//tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
对于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);
}
// tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,"%sd", tr->name);
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 | PF_NOFREEZE;
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) { //如果队列为空,则休眠,否则do_blktrans_request
set_current_state(TASK_INTERRUPTIBLE);
spin_unlock_irq(rq->queue_lock);
schedule();
spin_lock_irq(rq->queue_lock);
continue;
}
dev = req->rq_disk->private_data;
tr = dev->tr;
spin_unlock_irq(rq->queue_lock);
mutex_lock(&dev->lock);
res = do_blktrans_request(tr, dev, req);
mutex_unlock(&dev->lock);
spin_lock_irq(rq->queue_lock);
end_request(req, res);
}
spin_unlock_irq(rq->queue_lock);
return 0;
}
static int do_blktrans_request(struct mtd_blktrans_ops *tr,
struct mtd_blktrans_dev *dev,
struct request *req)
{
unsigned long block, nsect;
char *buf;
block = req->sector << 9 >> tr->blkshift;
nsect = req->current_nr_sectors << 9 >> tr->blkshift;
buf = req->buffer;
if (!blk_fs_request(req))
return 0;
if (req->sector + req->current_nr_sectors > get_capacity(req->rq_disk))
return 0;
switch(rq_data_dir(req)) {
case READ:
for (; nsect > 0; nsect--, block++, buf += tr->blksize)
if (tr->readsect(dev, block, buf))
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;
}
}
对于readsect以及writesect,在mtdblock.c中调用register_mtd_blktrans时,注册了static struct mtd_blktrans_ops mtdblock_tr结构体
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,
};
static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
unsigned long block, char *buf)
{
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;
/* -EINTR is not really correct, but it is the best match
* documented in man 2 write for all cases. We could also
* return -EAGAIN sometimes, but why bother?
*/
}
return do_cached_write(mtdblk, block<<9, 512, buf);
}
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;
DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: write on \"%s\" at 0x%lx, size 0x%x\n",
mtd->name, pos, len);
if (!sect_size)
return mtd->write(mtd, pos, len, &retlen, buf);
while (len > 0) {
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) {
/*
* We are covering a whole sector. Thus there is no
* need to bother with the cache while it may still be
* useful for other partial writes.
*/
ret = erase_write (mtd, pos, size, buf);
if (ret)
return ret;
} else {
/* Partial sector: need to use the cache */
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) {
/* fill the cache with the current sector */
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;
}
/* write data to our local cache */
memcpy (mtdblk->cache_data + offset, buf, size);
mtdblk->cache_state = STATE_DIRTY;
}
buf += size;
pos += size;
len -= size;
}
return 0;