转:MTD源代码分析(三)mtdchar.c
MTD字符设备的文件 MTD字符设备的notifier static struct mtd_notifier notifier =
{ add: mtd_notify_add, remove: mtd_notify_remove, }; 格式: static loff_t mtd_lseek (struct file
*file, loff_t offset, int orig) 注释: 无 功能: 置MTD字符设备的指针 说明: 无 参数: file:MTD字符设备文件 offset:偏移 orig:设置方式 返回: 成功:返回当前指针位置 设置方式无效:返回-EINVAL 调用: 无 被调用: 注册进mtd_fops结构 源代码: { struct mtd_info *mtd=(struct mtd_info
*)file->private_data; switch (orig) { case 0: /* SEEK_SET */ file->f_pos = offset; break; case 1: /* SEEK_CUR */ file->f_pos += offset; break; case 2: /* SEEK_END */ file->f_pos =mtd->size + offset; break; default: return -EINVAL; } if (file->f_pos < 0) file->f_pos = 0; else if (file->f_pos >=
mtd->size) file->f_pos = mtd->size - 1; return file->f_pos; } 格式: static int mtd_open(struct inode
*inode, struct file *file) 注释: 无 功能: 打开一个MTD字符设备 说明: devnum=minor>>2(参看Documentations/devices.txt), 进行安全性检查, 调用get_mtd_device获取MTD设备,并将file->private_data指向它 参数: inode:FIXME file:是系统提供给MTD字符设备用于传递参数的file结构,此函数中它的private_data 成员被指向原始设备层的MTD设备 返回: 成功:返回0 失败:返回错误码 调用: get_mtd_device()获得原始设备层的MTD设备 被调用: 注册进mtd_fops结构 源代码: { int minor = MINOR(inode->i_rdev); int devnum = minor >> 1; 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 = get_mtd_device(NULL, devnum); if (!mtd) return -ENODEV; if (MTD_ABSENT == mtd->type) { put_mtd_device(mtd); return -ENODEV; } file->private_data = mtd; /* 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 */ 格式: static int mtd_close(struct inode
*inode, struct file *file) 注释: 无 功能: 关闭一个MTD字符设备 说明: 调用mtd_info->sync()同步MTD设备, 调用put_mtd_device()返还MTD设备 参数: inode:FIXME file:无用 返回: 返回0 调用: mtd_info->sync()同步MTD设备 put_mtd_device()返还MTD设备 被调用: 被注册进mtd_fops结构 源代码: { struct mtd_info *mtd; DEBUG(MTD_DEBUG_LEVEL0,
"MTD_close/n"); mtd = (struct mtd_info
*)file->private_data; if (mtd->sync) mtd->sync(mtd); put_mtd_device(mtd); return 0; } /* mtd_close */ /* FIXME: This _really_ needs to die. In
2.5, we should lock the userspace buffer down and use it
directly with readv/writev. */ #define MAX_KMALLOC_SIZE 0x20000 格式: static ssize_t mtd_read(struct file
*file, char *buf, size_t count,loff_t *ppos) 注释: 无 功能: MTD字符设备的写操作 说明: 当count>0时{ 裁减本次操作大小len至min(MAX_KMALLOC_SIZE,count), 申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf, 调用mtd_info->read将MTD设备中的数据读入kbuf, 将kbuf中的数据拷贝到用户空间buf, count自减 释放kbuf } 参数: file:系统给MTD字符设备驱动程序用于传递参数的file结构,此函数通过file得到下 层的MTD设备 buf:用户空间的指针,用于存放读取的数据 count:被读数据的长度 ppos:被读数据在MTD设备中的位置 返回: 成功:返回实际读取数据的长度 失败:返回错误码 调用: mtd_info->read()用于从MTD设备中读取数据 被调用: 被注册进mtd_fops结构 源代码: { struct mtd_info *mtd = (struct
mtd_info *)file->private_data; 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) { if (count > MAX_KMALLOC_SIZE) len = MAX_KMALLOC_SIZE; else len = count; kbuf=kmalloc(len,GFP_KERNEL); if (!kbuf) return -ENOMEM; ret = MTD_READ(mtd, *ppos, len,
&retlen, kbuf); if (!ret) { *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) { kfree(kbuf); return -EFAULT; } else total_retlen += retlen; count -= retlen; buf += retlen; } else { kfree(kbuf); return ret; } kfree(kbuf); } return total_retlen; } /* mtd_read */ 格式: static ssize_t mtd_write(struct file
*file, const char *buf, size_t count,loff_t *ppos) 注释: 无 功能: 对MTD字符设备的写操作 说明: 当count>0时{ 裁减本次操作大小len至min(MAX_KMALLOC_SIZE,count), 申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf, 将用户空间buf中的数据拷贝到kbuf, 调用mtd_info->write将kbuf中的数据读入MTD设备, count自减 释放kbuf } 参数: file:系统给MTD字符设备驱动程序用于传递参数的file结构,此函数通过file得到下 层的MTD设备 buf:用户空间的指针,用于存放将要写入的数据 count:被写数据的长度 ppos:数据被写入MTD设备中的位置 返回: 成功:返回实际读取数据的长度 失败:返回错误码 调用: mtd_info->write用于写入MTD设备 被调用: 被注册进mtd_fops结构 源代码: { struct mtd_info *mtd = (struct
mtd_info *)file->private_data; char *kbuf; size_t retlen; size_t total_retlen=0; int ret=0; int len; DEBUG(MTD_DEBUG_LEVEL0,"MTD_write/n"); if (*ppos == mtd->size) return -ENOSPC; if (*ppos + count > mtd->size) count = mtd->size - *ppos; if (!count) return 0; while (count) { if (count > MAX_KMALLOC_SIZE) len = MAX_KMALLOC_SIZE; else len = count; kbuf=kmalloc(len,GFP_KERNEL); if (!kbuf) { printk("kmalloc is null/n"); return -ENOMEM; } if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); return -EFAULT; } ret = (*(mtd->write))(mtd, *ppos,
len, &retlen, kbuf); if (!ret) { *ppos += retlen; total_retlen += retlen; count -= retlen; buf += retlen; } else { kfree(kbuf); return ret; } kfree(kbuf); } return total_retlen; } /* mtd_write */ 格式: static void mtd_erase_callback (struct
erase_info *instr) 注释: 无 功能: 唤醒进程 说明: 在擦除进行完后被调用 参数: instr:进行的擦除的erase_info结构 返回: 无 调用: wake_up() 被调用: 在mtd_ioctl中被赋给erase_info->callback 源代码: { wake_up((wait_queue_head_t
*)instr->priv); } 格式: static int mtd_ioctl(struct inode
*inode, struct file *file, u_int cmd, u_long arg) 注释: 无 功能: MTD字符设备的IO控制 说明: 根据cmd分别处理 参数: inode:FIXME file:传递参数的结构 cmd:IO控制的命令 arg:IO控制的参数 返回: 成功:返回0 失败:返回错误码 调用: 无 被调用: 注册进mtd_fops结构 源代码: { struct mtd_info *mtd = (struct
mtd_info *)file->private_data; int ret = 0; u_long size; DEBUG(MTD_DEBUG_LEVEL0,
"MTD_ioctl/n"); size = (cmd & IOCSIZE_MASK)
>> IOCSIZE_SHIFT; if (cmd & IOC_IN) { ret = verify_area(VERIFY_READ, (char
*)arg, size); if (ret) return ret; } if (cmd & IOC_OUT) { ret = verify_area(VERIFY_WRITE, (char
*)arg, size); if (ret) return ret; } switch (cmd) { case MEMGETREGIONCOUNT: if (copy_to_user((int *) arg,
&(mtd->numeraseregions), sizeof(int))) return -EFAULT; break; case MEMGETREGIONINFO: { struct region_info_user if
(copy_from_user( & (struct region_info_user *)arg, sizeof(struct region_info_user))) { return -EFAULT; } if (ur.regionindex >=
mtd->numeraseregions) return -EINVAL; if (copy_to_user((struct
mtd_erase_region_info *) arg, &(mtd->eraseregions[ur.regionindex]), sizeof(struct mtd_erase_region_info))) return -EFAULT; break; } case MEMGETINFO: if (copy_to_user((struct mtd_info
*)arg, mtd, sizeof(struct mtd_info_user))) return -EFAULT; break; case MEMERASE: { struct erase_info
*erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); if (!erase) ret = -ENOMEM; else { wait_queue_head_t waitq; DECLARE_WAITQUEUE(wait, current); init_waitqueue_head(&waitq); memset (erase,0,sizeof(struct
erase_info)); if
(copy_from_user(&erase->addr, (u_long *)arg, 2 * sizeof(u_long))) { kfree(erase); return -EFAULT; } erase->mtd = mtd; erase->callback =
mtd_erase_callback; erase->priv = (unsigned
long)&waitq; /* FIXME: Allow INTERRUPTIBLE. Which
means not having the wait_queue head on the
stack. If the wq_head is on the stack, and we leave because we got interrupted, then
the wq_head is no longer there when the callback routine tries to wake us up. */ ret = mtd->erase(mtd, erase); if (!ret) { set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&waitq, &wait); if (erase->state != MTD_ERASE_DONE
&& erase->state !=
MTD_ERASE_FAILED) schedule(); remove_wait_queue(&waitq,
&wait); set_current_state(TASK_RUNNING); ret = (erase->state ==
MTD_ERASE_FAILED)?-EIO:0; } kfree(erase); } break; } case MEMWRITEOOB: { struct mtd_oob_buf buf; void *databuf; ssize_t retlen; if (copy_from_user(&buf, (struct
mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) return -EFAULT; if (buf.length > 0x4096) return -EINVAL; if (!mtd->write_oob) ret = -EOPNOTSUPP; else ret = verify_area(VERIFY_READ, (char
*)buf.ptr, buf.length); if (ret) return ret; databuf = kmalloc(buf.length,
GFP_KERNEL); if (!databuf) return -ENOMEM; if (copy_from_user(databuf, buf.ptr,
buf.length)) { kfree(databuf); return -EFAULT; } ret = (mtd->write_oob)(mtd,
buf.start, buf.length, &retlen, databuf); if (copy_to_user((void *)arg +
sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) ret = -EFAULT; kfree(databuf); break; } case MEMREADOOB: { struct mtd_oob_buf buf; void *databuf; ssize_t retlen; if (copy_from_user(&buf, (struct
mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) return -EFAULT; if (buf.length > 0x4096) return -EINVAL; if (!mtd->read_oob) ret = -EOPNOTSUPP; else ret = verify_area(VERIFY_WRITE, (char
*)buf.ptr, buf.length); if (ret) return ret; databuf = kmalloc(buf.length,
GFP_KERNEL); if (!databuf) return -ENOMEM; ret = (mtd->read_oob)(mtd,
buf.start, buf.length, &retlen, databuf); if (copy_to_user((void *)arg +
sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) ret = -EFAULT; else if (retlen &&
copy_to_user(buf.ptr, databuf, retlen)) ret = -EFAULT; kfree(databuf); break; } case MEMLOCK: { unsigned long adrs[2]; if (copy_from_user(adrs ,(void *)arg,
2* sizeof(unsigned long))) return -EFAULT; if (!mtd->lock) ret = -EOPNOTSUPP; else ret = mtd->lock(mtd, adrs[0],
adrs[1]); break; } case MEMUNLOCK: { unsigned long adrs[2]; if (copy_from_user(adrs, (void *)arg,
2* sizeof(unsigned long))) return -EFAULT; if (!mtd->unlock) ret = -EOPNOTSUPP; else ret = mtd->unlock(mtd, adrs[0],
adrs[1]); break; } default: DEBUG(MTD_DEBUG_LEVEL0, "Invalid
ioctl %x (MEMGETINFO = %x)/n", cmd, MEMGETINFO); ret = -ENOTTY; } return ret; } /* memory_ioctl */ MTD字符设备的操作函数结构 static struct file_operations mtd_fops =
{ owner: THIS_MODULE, llseek: mtd_lseek, /* lseek */ read: mtd_read, /* read */ write: mtd_write, /* write */ ioctl: mtd_ioctl, /* ioctl */ open: mtd_open, /* open */ release: mtd_close, /* release */ }; 格式: static int __init init_mtdchar(void) 注释: 无 功能: 初始化一个MTD字符设备(设备层) 说明: 如果定义了CONFIG_DEVFS_FS{ 调用devfs_register_chrdev()注册MTD字符设备 调用register_mtd_user()将设备层MTD字符设备的notifier注册进原始设备层 } 否则调用register_chrdev()注册MTD字符设备 参数: 无 返回: 成功:返回0 注册失败:返回-EAGAIN 调用: devfs_register_chrdev()或register_chrdev()注册字符设备 被调用: __init module_init 源代码: { #ifdef CONFIG_DEVFS_FS if
(devfs_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; } devfs_dir_handle = devfs_mk_dir(NULL,
"mtd", NULL); register_mtd_user(¬ifier); #else 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; } #endif return 0; } 格式: static void __exit
cleanup_mtdchar(void) 注释: 无 功能: 清除MTD字符设备 说明: 如果定义了CONFIG_DEVFS_FS{ 调用unregister_mtd_user()注销MTD字符设备的notifier, 调用devfs_unregister_chrdev()注销MTD字符设备 } 否则调用unregister_chrdev()注销MTD字符设备 参数: 无 返回: 无 调用: unregister_chrdev()或devfs_unregister_chrdev()注销字符设备 被调用: __exit module_exit 源代码: { #ifdef
CONFIG_DEVFS_FS unregister_mtd_user(¬ifier); devfs_unregister(devfs_dir_handle); devfs_unregister_chrdev(MTD_CHAR_MAJOR,
"mtd"); #else unregister_chrdev(MTD_CHAR_MAJOR,
"mtd"); #endif } |
|