系统调用add_mtd_partitions(&priv->mtd, p1020_partition_info, 3)函数创建新分区的调用关系如下所示:
->add_mtd_partitions(&priv->mtd, p1020_partition_info, 3)
->add_mtd_device(&slave->mtd)
->mtd_table[i] = mtd;
由此可知,每个分区的mtd_info结构都添加到了mtd_table[i]数组当中。
现在分析\drivers\mtd\mtdchar.c代码,如下所示:
mtdchar.c是字符设备驱动程序的实现,它只需调用nand硬件驱动层的接口,即可实现对nand flash的操作。
1、file_operations
static 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, //关闭设备
};
2、mtd_open
static int mtd_open(struct inode *inode, struct file *file)
{ //打开字符设备
int minor = iminor(inode); //得到打开字符设备的从设备号
int devnum = minor >> 1; //因为1个nand分区对应2个字符从设备,所以需要除以2来得到分区号
struct mtd_info *mtd;
DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
if (devnum >= MAX_MTD_DEVICES)
return -ENODEV;
/* You can't open the RO devices RW */
if ((file->f_mode & 2) && (minor & 1)) //当从设备号为奇数时,只能进行只读访问
return -EACCES;
/* 此函数将从mtd_table[i]数组中取得i=devnum时的mtd_info结构体 */
mtd = get_mtd_device(NULL, devnum); //根据分区号来得到对应分区的mtd_info结构体
if (!mtd)
return -ENODEV;
if (MTD_ABSENT == mtd->type) {
put_mtd_device(mtd);
return -ENODEV;
}
file->private_data = mtd; //将取得的mtd_info结构体赋给file的私有数据成员
/* You can't open it RW if it's not a writeable device */
if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
put_mtd_device(mtd);
return -EACCES;
}
return 0;
} /* mtd_open */
3、mtd_read
static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
{ //此函数用于实现字符设备读操作的用户层接口
struct mtd_info *mtd = file->private_data; //首先取得对应分区的file私有数据中的mtd_info结构
size_t retlen=0;
size_t total_retlen=0;
int ret=0;
int len;
char *kbuf; //定义一个字符指针,读出的数据首先存放于此
DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
if (*ppos + count > mtd->size) //判断要读取的数据总数是否超出了分区的存储空间
count = mtd->size - *ppos; //超出,修改要读取数据的总数
if (!count)
return 0;
/* FIXME: Use kiovec in 2.5 to lock down the user's buffers
and pass them directly to the MTD functions */
while (count) {
/* 判断所要读取的数据大小是否超出了所规定最大空间分配的大小(128K) */
if (count > MAX_KMALLOC_SIZE)
len = MAX_KMALLOC_SIZE; //是,则分配最大空间
else
len = count; //否,则按count来分配
kbuf=kmalloc(len,GFP_KERNEL); //分配内核空间,来存储读出的数据内容
if (!kbuf)
return -ENOMEM;
/* 进行读数据操作,这是一个宏,其等价于mdt->read */
ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); //len是准备读取数据的长度
/* Nand returns -EBADMSG on ecc errors, but it returns
* the data. For our userspace tools it is important
* to dump areas with ecc errors !
* Userspace software which accesses NAND this way
* must be aware of the fact that it deals with NAND
*/
if (!ret || (ret == -EBADMSG)) {
*ppos += retlen;
if (copy_to_user(buf, kbuf, retlen)) { //将内核空间的数据传递给用户空间,而此数据就是读出的nand数据
kfree(kbuf); //数据全部传出后,释放此内核空间
return -EFAULT;
}
else
total_retlen += retlen;
count -= retlen; //当所要读取的数据总数超出最大分配内存空间时,while循环将继续
buf += retlen;
}
else {
kfree(kbuf);
return ret;
}
kfree(kbuf);
}
return total_retlen;
} /* mtd_read */
其它的字符驱动程序用户层接口的实现就不再详细描述了,方法基本和上面mtd_read接口实现方法一致。
最后对字符设备进行注册后,就可以在用户空间通过应用程序来调用此nand flash的字符驱动了。代码如下:
static int __init init_mtdchar(void)
{
/* 注册字符驱动程序,其主设备号为90 */
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
mtdchar_devfs_init();
return 0;
}
至此nand flash驱动程序基本分析完毕,关于mtdblock.c的块设备驱动程序将不再进行分析。