pata_efar: fix PIO2 underclocking
[safe/jmp/linux-2.6] / drivers / mtd / mtdchar.c
index 129d429..763d3f0 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * $Id: mtdchar.c,v 1.76 2005/11/07 11:14:20 gleixner Exp $
- *
  * Character-device access to raw MTD devices.
  *
  */
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
+#include <linux/backing-dev.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
 
 #include <asm/uaccess.h>
 
-static struct class *mtd_class;
-
-static void mtd_notify_add(struct mtd_info* mtd)
-{
-       if (!mtd)
-               return;
-
-       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), "mtd%d", mtd->index);
-
-       device_create(mtd_class, NULL,
-                     MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), "mtd%dro", mtd->index);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-       if (!mtd)
-               return;
-
-       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
-       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
-}
-
-static struct mtd_notifier notifier = {
-       .add    = mtd_notify_add,
-       .remove = mtd_notify_remove,
-};
 
 /*
  * Data structure to hold the pointer to the mtd device as well
@@ -97,7 +70,7 @@ static int mtd_open(struct inode *inode, struct file *file)
                return -ENODEV;
 
        /* You can't open the RO devices RW */
-       if ((file->f_mode & 2) && (minor & 1))
+       if ((file->f_mode & FMODE_WRITE) && (minor & 1))
                return -EACCES;
 
        lock_kernel();
@@ -108,14 +81,17 @@ static int mtd_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       if (MTD_ABSENT == mtd->type) {
+       if (mtd->type == MTD_ABSENT) {
                put_mtd_device(mtd);
                ret = -ENODEV;
                goto out;
        }
 
+       if (mtd->backing_dev_info)
+               file->f_mapping->backing_dev_info = mtd->backing_dev_info;
+
        /* You can't open it RW if it's not a writeable device */
-       if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
+       if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
                put_mtd_device(mtd);
                ret = -EACCES;
                goto out;
@@ -145,7 +121,7 @@ static int mtd_close(struct inode *inode, struct file *file)
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
        /* Only sync if opened RW */
-       if ((file->f_mode & 2) && mtd->sync)
+       if ((file->f_mode & FMODE_WRITE) && mtd->sync)
                mtd->sync(mtd);
 
        put_mtd_device(mtd);
@@ -349,7 +325,7 @@ static void mtdchar_erase_callback (struct erase_info *instr)
        wake_up((wait_queue_head_t *)instr->priv);
 }
 
-#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
+#ifdef CONFIG_HAVE_MTD_OTP
 static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
 {
        struct mtd_info *mtd = mfi->mtd;
@@ -409,16 +385,20 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case MEMGETREGIONINFO:
        {
-               struct region_info_user ur;
+               uint32_t ur_idx;
+               struct mtd_erase_region_info *kr;
+               struct region_info_user *ur = (struct region_info_user *) argp;
 
-               if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
+               if (get_user(ur_idx, &(ur->regionindex)))
                        return -EFAULT;
 
-               if (ur.regionindex >= mtd->numeraseregions)
-                       return -EINVAL;
-               if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
-                               sizeof(struct mtd_erase_region_info)))
+               kr = &(mtd->eraseregions[ur_idx]);
+
+               if (put_user(kr->offset, &(ur->offset))
+                   || put_user(kr->erasesize, &(ur->erasesize))
+                   || put_user(kr->numblocks, &(ur->numblocks)))
                        return -EFAULT;
+
                break;
        }
 
@@ -440,23 +420,27 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                struct erase_info *erase;
 
-               if(!(file->f_mode & 2))
+               if(!(file->f_mode & FMODE_WRITE))
                        return -EPERM;
 
                erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
                if (!erase)
                        ret = -ENOMEM;
                else {
+                       struct erase_info_user einfo;
+
                        wait_queue_head_t waitq;
                        DECLARE_WAITQUEUE(wait, current);
 
                        init_waitqueue_head(&waitq);
 
-                       if (copy_from_user(&erase->addr, argp,
+                       if (copy_from_user(&einfo, argp,
                                    sizeof(struct erase_info_user))) {
                                kfree(erase);
                                return -EFAULT;
                        }
+                       erase->addr = einfo.start;
+                       erase->len = einfo.length;
                        erase->mtd = mtd;
                        erase->callback = mtdchar_erase_callback;
                        erase->priv = (unsigned long)&waitq;
@@ -491,9 +475,10 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                struct mtd_oob_buf buf;
                struct mtd_oob_ops ops;
+               struct mtd_oob_buf __user *user_buf = argp;
                uint32_t retlen;
 
-               if(!(file->f_mode & 2))
+               if(!(file->f_mode & FMODE_WRITE))
                        return -EPERM;
 
                if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
@@ -534,8 +519,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (ops.oobretlen > 0xFFFFFFFFU)
                        ret = -EOVERFLOW;
                retlen = ops.oobretlen;
-               if (copy_to_user(&((struct mtd_oob_buf *)argp)->length,
-                                &retlen, sizeof(buf.length)))
+               if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length)))
                        ret = -EFAULT;
 
                kfree(ops.oobbuf);
@@ -589,29 +573,29 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case MEMLOCK:
        {
-               struct erase_info_user info;
+               struct erase_info_user einfo;
 
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&einfo, argp, sizeof(einfo)))
                        return -EFAULT;
 
                if (!mtd->lock)
                        ret = -EOPNOTSUPP;
                else
-                       ret = mtd->lock(mtd, info.start, info.length);
+                       ret = mtd->lock(mtd, einfo.start, einfo.length);
                break;
        }
 
        case MEMUNLOCK:
        {
-               struct erase_info_user info;
+               struct erase_info_user einfo;
 
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&einfo, argp, sizeof(einfo)))
                        return -EFAULT;
 
                if (!mtd->unlock)
                        ret = -EOPNOTSUPP;
                else
-                       ret = mtd->unlock(mtd, info.start, info.length);
+                       ret = mtd->unlock(mtd, einfo.start, einfo.length);
                break;
        }
 
@@ -662,7 +646,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
-#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
+#ifdef CONFIG_HAVE_MTD_OTP
        case OTPSELECT:
        {
                int mode;
@@ -711,15 +695,15 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
 
        case OTPLOCK:
        {
-               struct otp_info info;
+               struct otp_info oinfo;
 
                if (mfi->mode != MTD_MODE_OTP_USER)
                        return -EINVAL;
-               if (copy_from_user(&info, argp, sizeof(info)))
+               if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
                        return -EFAULT;
                if (!mtd->lock_user_prot_reg)
                        return -EOPNOTSUPP;
-               ret = mtd->lock_user_prot_reg(mtd, info.start, info.length);
+               ret = mtd->lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
                break;
        }
 #endif
@@ -774,6 +758,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        return ret;
 } /* memory_ioctl */
 
+/*
+ * try to determine where a shared mapping can be made
+ * - only supported for NOMMU at the moment (MMU can't doesn't copy private
+ *   mappings)
+ */
+#ifndef CONFIG_MMU
+static unsigned long mtd_get_unmapped_area(struct file *file,
+                                          unsigned long addr,
+                                          unsigned long len,
+                                          unsigned long pgoff,
+                                          unsigned long flags)
+{
+       struct mtd_file_info *mfi = file->private_data;
+       struct mtd_info *mtd = mfi->mtd;
+
+       if (mtd->get_unmapped_area) {
+               unsigned long offset;
+
+               if (addr != 0)
+                       return (unsigned long) -EINVAL;
+
+               if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
+                       return (unsigned long) -EINVAL;
+
+               offset = pgoff << PAGE_SHIFT;
+               if (offset > mtd->size - len)
+                       return (unsigned long) -EINVAL;
+
+               return mtd->get_unmapped_area(mtd, len, offset, flags);
+       }
+
+       /* can't map directly */
+       return (unsigned long) -ENOSYS;
+}
+#endif
+
+/*
+ * set up a mapping for shared memory segments
+ */
+static int mtd_mmap(struct file *file, struct vm_area_struct *vma)
+{
+#ifdef CONFIG_MMU
+       struct mtd_file_info *mfi = file->private_data;
+       struct mtd_info *mtd = mfi->mtd;
+
+       if (mtd->type == MTD_RAM || mtd->type == MTD_ROM)
+               return 0;
+       return -ENOSYS;
+#else
+       return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
+#endif
+}
+
 static const struct file_operations mtd_fops = {
        .owner          = THIS_MODULE,
        .llseek         = mtd_lseek,
@@ -782,39 +819,36 @@ static const struct file_operations mtd_fops = {
        .ioctl          = mtd_ioctl,
        .open           = mtd_open,
        .release        = mtd_close,
+       .mmap           = mtd_mmap,
+#ifndef CONFIG_MMU
+       .get_unmapped_area = mtd_get_unmapped_area,
+#endif
 };
 
 static int __init init_mtdchar(void)
 {
-       if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
+       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 -EAGAIN;
-       }
-
-       mtd_class = class_create(THIS_MODULE, "mtd");
-
-       if (IS_ERR(mtd_class)) {
-               printk(KERN_ERR "Error creating mtd class.\n");
-               unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
-               return PTR_ERR(mtd_class);
        }
 
-       register_mtd_user(&notifier);
-       return 0;
+       return status;
 }
 
 static void __exit cleanup_mtdchar(void)
 {
-       unregister_mtd_user(&notifier);
-       class_destroy(mtd_class);
        unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 }
 
 module_init(init_mtdchar);
 module_exit(cleanup_mtdchar);
 
+MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_DESCRIPTION("Direct character-device access to MTD devices");
+MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);