MTD源代码分析(三)

来源:互联网 发布:中国人娶外国人知乎 编辑:程序博客网 时间:2024/05/18 00:24
 
mtdchar.c

MTD字符设备的文件

notifier

MTD字符设备的notifier

static struct mtd_notifier notifier = {          

       add: mtd_notify_add,

       remove:   mtd_notify_remove,

};

 

mtd_lseek

格式:

       static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)

注释:

      

功能:

       MTD字符设备的指针

说明:

      

参数:

       fileMTD字符设备文件

       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;

}

 

 

mtd_open

格式:

       static int mtd_open(struct inode *inode, struct file *file)

注释:

      

功能:

       打开一个MTD字符设备

说明:

       devnum=minor>>2(参看Documentations/devices.txt),

       进行安全性检查,

       调用get_mtd_device获取MTD设备,并将file->private_data指向它

参数:

       inodeFIXME

       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 */

 

 

 

 

mtd_close

格式:

       static int mtd_close(struct inode *inode, struct file *file)

注释:

      

功能:

       关闭一个MTD字符设备

说明:

       调用mtd_info->sync()同步MTD设备,

       调用put_mtd_device()返还MTD设备

参数:

       inodeFIXME

       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 */

 

MAX_KMALLOC_SIZE

/* 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

 

 

mtd_read

格式:

       static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)

注释:

      

功能:

       MTD字符设备的写操作

说明:

       count>0{

              裁减本次操作大小lenmin(MAX_KMALLOC_SIZE,count)

              申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf

              调用mtd_info->readMTD设备中的数据读入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 */

 

 

 

mtd_write

格式:

       static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)

注释:

      

功能:

       MTD字符设备的写操作

说明:

       count>0{

              裁减本次操作大小lenmin(MAX_KMALLOC_SIZE,count)

              申请一块大小为MAX_KMALLOC_SIZE的内核空间kbuf

              将用户空间buf中的数据拷贝到kbuf

              调用mtd_info->writekbuf中的数据读入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 */

 

 

mtd_erase_callback

格式:

       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);

}

 

 

mtd_ioctl

格式:

       static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)

注释:

      

功能:

       MTD字符设备的IO控制

说明:

       根据cmd分别处理

参数:

       inodeFIXME

       file:传递参数的结构

       cmdIO控制的命令

       argIO控制的参数

返回:

       成功:返回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 ur;

 

              if (copy_from_user(      &ur,

                                   (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_fops

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 */

};

 

 

 

init_mtdchar

格式:

       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(&notifier);

#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;

}

 

cleanup_mtdchar

格式:

       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(&notifier);

       devfs_unregister(devfs_dir_handle);

       devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");

#else

       unregister_chrdev(MTD_CHAR_MAJOR, "mtd");

#endif

}


原创粉丝点击