前面已经详细讲述了MTD子系统原理知识,这一节讲述嵌入式Linux下对NorFlash的驱动程序设计。在MTD设备层上有MTD字符设备和MTD块设备。
1、MTD字符设备层的源码文件为/mtd/mtdchar.c。该源码文件为MTD原始设备提供了一个字符设备访问接口,使得上层应用程序可以以字符设备的方式来访问MTD原始设备。mtdchar.c主要定义了一个字符设备访问文件操作函数和向内核注册了一个字符设备,MTD字符设备的主设备号为90,源码如下:
#define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
static const struct file_operations mtd_fops = {
.owner = THIS_MODULE,
.llseek = mtd_lseek,
.read = mtd_read,
.write = mtd_write,
.ioctl = mtd_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mtd_compat_ioctl,
#endif
.open = mtd_open,
.release = mtd_close,
.mmap = mtd_mmap,
#ifndef CONFIG_MMU
.get_unmapped_area = mtd_get_unmapped_area,
#endif
};
接着在MTD字符设备驱动的模块初始化函数中注册MTD字符设备,代码如下:
static int __init init_mtdchar(void)
{
int status;
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;
}
2、MTD块设备
mtd/mtd_blkdevs.c源代码文件提供了MTD 转换层设备和块设备访问之间的接口。该文件定义了块设备访问接口函数,代码如下:
static const struct block_device_operations mtd_blktrans_ops = {
.owner = THIS_MODULE,
.open = blktrans_open,
.release = blktrans_release,
.locked_ioctl = blktrans_ioctl,
.getgeo = blktrans_getgeo,
};
add_mtd_blktrans_dev函数用来将一个MTD转换层设备注册到内核中,每一个MTD转换层设备都对应一个通用磁盘设备。add_mtd_blktrans_dev函数针对一个MTD转换层设备分配了一个磁盘设备结构体,并调用函数add_disk注册磁盘设备到内核中。
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;
if (mutex_trylock(&mtd_table_mutex)) {
mutex_unlock(&mtd_table_mutex);
BUG();
}
list_for_each_entry(d, &tr->devs, list) {
if (new->devnum == -1) {
/* Use first free number */
if (d->devnum != last_devnum+1) {
/* Found a free devnum. Plug it in here */
new->devnum = last_devnum+1;
list_add_tail(&new->list, &d->list);
goto added;
}
} else if (d->devnum == new->devnum) {
/* Required number taken */
return -EBUSY;
} else if (d->devnum > new->devnum) {
/* Required number was free */
list_add_tail(&new->list, &d->list);
goto added;
}
last_devnum = d->devnum;
}
if (new->devnum == -1)
new->devnum = last_devnum+1;
if ((new->devnum << tr->part_bits) > 256) {
return -EBUSY;
}
list_add_tail(&new->list, &tr->devs);
added:
mutex_init(&new->lock);
if (!tr->writesect)
new->readonly = 1;
gd = alloc_disk(1 << tr->part_bits);
if (!gd) {
list_del(&new->list);
return -ENOMEM;
}
gd->major = tr->major;
gd->first_minor = (new->devnum) << tr->part_bits;
gd->fops = &mtd_blktrans_ops;
if (tr->part_bits)
if (new->devnum < 26)
snprintf(gd->disk_name, sizeof(gd->disk_name),
"%s%c", tr->name, 'a' + new->devnum);
else
snprintf(gd->disk_name, sizeof(gd->disk_name),
"%s%c%c", tr->name,
'a' - 1 + new->devnum / 26,
'a' + new->devnum % 26);
else
snprintf(gd->disk_name, sizeof(gd->disk_name),
"%s%d", tr->name, new->devnum);
/* 2.5 has capacity in units of 512 bytes while still
having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
set_capacity(gd, (new->size * tr->blksize) >> 9);
gd->private_data = new;
new->blkcore_priv = gd;
gd->queue = tr->blkcore_priv->rq;
gd->driverfs_dev = &new->mtd->dev;
if (new->readonly)
set_disk_ro(gd, 1);
add_disk(gd);
return 0;
}
MTD原始设备>>转换层设备>>磁盘设备>>块设备(">>"是属于的意思)。
mtdblock.c源文件实现了MTD块设备层驱动,定义了一个转换层设备操作函数接口。
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,
};
然后调用register_mtd_blktrans(&mtdblock_tr)注册一个转换层设备。