2012年(44)
分类: LINUX
2012-08-08 17:23:17
2010-07-21 21:23:05| 分类: arm linux设备驱 | 标签: |字号大中小
/******************************************************************/
/******************************************************************/
/******************************************************************/
/******************************************************************/
一, 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_info:MTD设备模型建立时存储一些临时状态信息。
/*****************************************************************************/
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)
{
。
。
。
//读取该flash的id,并在数组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 */
//将slave的mtd添加到内核,而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_MAJOR为90。
(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_info。mtd_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);
//如果res是0表示不能成功完成请求,为非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_bdev在bio中的定义为struct block_device *bi_bdev;
bio->bi_bdev->bd_disk在block_device中的定义为struct gendisk * bd_disk;
rq->rq_disk在request中的定义为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;
}