linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / mtd / mtdchar.c
index 5b6acfc..6f04458 100644 (file)
@@ -5,6 +5,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/device.h>
 #include <linux/fs.h>
 #include <linux/init.h>
@@ -48,33 +49,42 @@ static struct mtd_notifier notifier = {
 };
 
 /*
- * Data structure to hold the pointer to the mtd device as well
- * as mode information ofr various use cases.
+ * We use file->private_data to store a pointer to the MTDdevice.
+ * Since alighment is at least 32 bits, we have 2 bits free for OTP
+ * modes as well.
  */
-struct mtd_file_info {
-       struct mtd_info *mtd;
-       enum mtd_file_modes mode;
-};
+
+#define TO_MTD(file) (struct mtd_info *)((long)((file)->private_data) & ~3L)
+
+#define MTD_MODE_OTP_FACT      1
+#define MTD_MODE_OTP_USER      2
+#define MTD_MODE(file)         ((long)((file)->private_data) & 3)
+
+#define SET_MTD_MODE(file, mode) \
+       do { long __p = (long)((file)->private_data); \
+            (file)->private_data = (void *)((__p & ~3L) | mode); } while (0)
 
 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
 {
-       struct mtd_file_info *mfi = file->private_data;
-       struct mtd_info *mtd = mfi->mtd;
+       struct mtd_info *mtd = TO_MTD(file);
 
        switch (orig) {
-       case SEEK_SET:
+       case 0:
+               /* SEEK_SET */
                break;
-       case SEEK_CUR:
+       case 1:
+               /* SEEK_CUR */
                offset += file->f_pos;
                break;
-       case SEEK_END:
+       case 2:
+               /* SEEK_END */
                offset += mtd->size;
                break;
        default:
                return -EINVAL;
        }
 
-       if (offset >= 0 && offset <= mtd->size)
+       if (offset >= 0 && offset < mtd->size)
                return file->f_pos = offset;
 
        return -EINVAL;
@@ -87,7 +97,6 @@ static int mtd_open(struct inode *inode, struct file *file)
        int minor = iminor(inode);
        int devnum = minor >> 1;
        struct mtd_info *mtd;
-       struct mtd_file_info *mfi;
 
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
 
@@ -108,20 +117,14 @@ static int mtd_open(struct inode *inode, struct file *file)
                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;
        }
 
-       mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
-       if (!mfi) {
-               put_mtd_device(mtd);
-               return -ENOMEM;
-       }
-       mfi->mtd = mtd;
-       file->private_data = mfi;
-
        return 0;
 } /* mtd_open */
 
@@ -129,17 +132,16 @@ static int mtd_open(struct inode *inode, struct file *file)
 
 static int mtd_close(struct inode *inode, struct file *file)
 {
-       struct mtd_file_info *mfi = file->private_data;
-       struct mtd_info *mtd = mfi->mtd;
+       struct mtd_info *mtd;
 
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
 
+       mtd = TO_MTD(file);
+
        if (mtd->sync)
                mtd->sync(mtd);
 
        put_mtd_device(mtd);
-       file->private_data = NULL;
-       kfree(mfi);
 
        return 0;
 } /* mtd_close */
@@ -151,8 +153,7 @@ static int mtd_close(struct inode *inode, struct file *file)
 
 static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
 {
-       struct mtd_file_info *mfi = file->private_data;
-       struct mtd_info *mtd = mfi->mtd;
+       struct mtd_info *mtd = TO_MTD(file);
        size_t retlen=0;
        size_t total_retlen=0;
        int ret=0;
@@ -169,58 +170,36 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
 
        /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
           and pass them directly to the MTD functions */
-
-       if (count > MAX_KMALLOC_SIZE)
-               kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
-       else
-               kbuf=kmalloc(count, GFP_KERNEL);
-
-       if (!kbuf)
-               return -ENOMEM;
-
        while (count) {
-
                if (count > MAX_KMALLOC_SIZE)
                        len = MAX_KMALLOC_SIZE;
                else
                        len = count;
 
-               switch (mfi->mode) {
-               case MTD_MODE_OTP_FACTORY:
+               kbuf=kmalloc(len,GFP_KERNEL);
+               if (!kbuf)
+                       return -ENOMEM;
+
+               switch (MTD_MODE(file)) {
+               case MTD_MODE_OTP_FACT:
                        ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
                        break;
                case MTD_MODE_OTP_USER:
                        ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
                        break;
-               case MTD_MODE_RAW:
-               {
-                       struct mtd_oob_ops ops;
-
-                       ops.mode = MTD_OOB_RAW;
-                       ops.datbuf = kbuf;
-                       ops.oobbuf = NULL;
-                       ops.len = len;
-
-                       ret = mtd->read_oob(mtd, *ppos, &ops);
-                       retlen = ops.retlen;
-                       break;
-               }
                default:
-                       ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
+                       ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
                }
                /* Nand returns -EBADMSG on ecc errors, but it returns
                 * the data. For our userspace tools it is important
                 * to dump areas with ecc errors !
-                * For kernel internal usage it also might return -EUCLEAN
-                * to signal the caller that a bitflip has occured and has
-                * been corrected by the ECC algorithm.
                 * Userspace software which accesses NAND this way
                 * must be aware of the fact that it deals with NAND
                 */
-               if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
+               if (!ret || (ret == -EBADMSG)) {
                        *ppos += retlen;
                        if (copy_to_user(buf, kbuf, retlen)) {
-                               kfree(kbuf);
+                               kfree(kbuf);
                                return -EFAULT;
                        }
                        else
@@ -236,16 +215,15 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t
                        return ret;
                }
 
+               kfree(kbuf);
        }
 
-       kfree(kbuf);
        return total_retlen;
 } /* mtd_read */
 
 static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
 {
-       struct mtd_file_info *mfi = file->private_data;
-       struct mtd_info *mtd = mfi->mtd;
+       struct mtd_info *mtd = TO_MTD(file);
        char *kbuf;
        size_t retlen;
        size_t total_retlen=0;
@@ -263,28 +241,25 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
        if (!count)
                return 0;
 
-       if (count > MAX_KMALLOC_SIZE)
-               kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
-       else
-               kbuf=kmalloc(count, GFP_KERNEL);
-
-       if (!kbuf)
-               return -ENOMEM;
-
        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;
                }
 
-               switch (mfi->mode) {
-               case MTD_MODE_OTP_FACTORY:
+               switch (MTD_MODE(file)) {
+               case MTD_MODE_OTP_FACT:
                        ret = -EROFS;
                        break;
                case MTD_MODE_OTP_USER:
@@ -294,21 +269,6 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
                        }
                        ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
                        break;
-
-               case MTD_MODE_RAW:
-               {
-                       struct mtd_oob_ops ops;
-
-                       ops.mode = MTD_OOB_RAW;
-                       ops.datbuf = kbuf;
-                       ops.oobbuf = NULL;
-                       ops.len = len;
-
-                       ret = mtd->write_oob(mtd, *ppos, &ops);
-                       retlen = ops.retlen;
-                       break;
-               }
-
                default:
                        ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
                }
@@ -322,9 +282,10 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count
                        kfree(kbuf);
                        return ret;
                }
+
+               kfree(kbuf);
        }
 
-       kfree(kbuf);
        return total_retlen;
 } /* mtd_write */
 
@@ -338,45 +299,13 @@ 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)
-static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
-{
-       struct mtd_info *mtd = mfi->mtd;
-       int ret = 0;
-
-       switch (mode) {
-       case MTD_OTP_FACTORY:
-               if (!mtd->read_fact_prot_reg)
-                       ret = -EOPNOTSUPP;
-               else
-                       mfi->mode = MTD_MODE_OTP_FACTORY;
-               break;
-       case MTD_OTP_USER:
-               if (!mtd->read_fact_prot_reg)
-                       ret = -EOPNOTSUPP;
-               else
-                       mfi->mode = MTD_MODE_OTP_USER;
-               break;
-       default:
-               ret = -EINVAL;
-       case MTD_OTP_OFF:
-               break;
-       }
-       return ret;
-}
-#else
-# define otp_select_filemode(f,m)      -EOPNOTSUPP
-#endif
-
 static int mtd_ioctl(struct inode *inode, struct file *file,
                     u_int cmd, u_long arg)
 {
-       struct mtd_file_info *mfi = file->private_data;
-       struct mtd_info *mtd = mfi->mtd;
+       struct mtd_info *mtd = TO_MTD(file);
        void __user *argp = (void __user *)arg;
        int ret = 0;
        u_long size;
-       struct mtd_info_user info;
 
        DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
 
@@ -412,15 +341,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        }
 
        case MEMGETINFO:
-               info.type       = mtd->type;
-               info.flags      = mtd->flags;
-               info.size       = mtd->size;
-               info.erasesize  = mtd->erasesize;
-               info.writesize  = mtd->writesize;
-               info.oobsize    = mtd->oobsize;
-               info.ecctype    = mtd->ecctype;
-               info.eccsize    = mtd->eccsize;
-               if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
+               if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user)))
                        return -EFAULT;
                break;
 
@@ -479,7 +400,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        case MEMWRITEOOB:
        {
                struct mtd_oob_buf buf;
-               struct mtd_oob_ops ops;
+               void *databuf;
+               ssize_t retlen;
 
                if(!(file->f_mode & 2))
                        return -EPERM;
@@ -487,7 +409,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
                        return -EFAULT;
 
-               if (buf.length > 4096)
+               if (buf.length > 0x4096)
                        return -EINVAL;
 
                if (!mtd->write_oob)
@@ -499,32 +421,21 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (ret)
                        return ret;
 
-               ops.len = buf.length;
-               ops.ooblen = buf.length;
-               ops.ooboffs = buf.start & (mtd->oobsize - 1);
-               ops.datbuf = NULL;
-               ops.mode = MTD_OOB_PLACE;
-
-               if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
-                       return -EINVAL;
-
-               ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
-               if (!ops.oobbuf)
+               databuf = kmalloc(buf.length, GFP_KERNEL);
+               if (!databuf)
                        return -ENOMEM;
 
-               if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) {
-                       kfree(ops.oobbuf);
+               if (copy_from_user(databuf, buf.ptr, buf.length)) {
+                       kfree(databuf);
                        return -EFAULT;
                }
 
-               buf.start &= ~(mtd->oobsize - 1);
-               ret = mtd->write_oob(mtd, buf.start, &ops);
+               ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 
-               if (copy_to_user(argp + sizeof(uint32_t), &ops.retlen,
-                                sizeof(uint32_t)))
+               if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
                        ret = -EFAULT;
 
-               kfree(ops.oobbuf);
+               kfree(databuf);
                break;
 
        }
@@ -532,12 +443,13 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        case MEMREADOOB:
        {
                struct mtd_oob_buf buf;
-               struct mtd_oob_ops ops;
+               void *databuf;
+               ssize_t retlen;
 
                if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
                        return -EFAULT;
 
-               if (buf.length > 4096)
+               if (buf.length > 0x4096)
                        return -EINVAL;
 
                if (!mtd->read_oob)
@@ -545,32 +457,22 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                else
                        ret = access_ok(VERIFY_WRITE, buf.ptr,
                                        buf.length) ? 0 : -EFAULT;
+
                if (ret)
                        return ret;
 
-               ops.len = buf.length;
-               ops.ooblen = buf.length;
-               ops.ooboffs = buf.start & (mtd->oobsize - 1);
-               ops.datbuf = NULL;
-               ops.mode = MTD_OOB_PLACE;
-
-               if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs))
-                       return -EINVAL;
-
-               ops.oobbuf = kmalloc(buf.length, GFP_KERNEL);
-               if (!ops.oobbuf)
+               databuf = kmalloc(buf.length, GFP_KERNEL);
+               if (!databuf)
                        return -ENOMEM;
 
-               buf.start &= ~(mtd->oobsize - 1);
-               ret = mtd->read_oob(mtd, buf.start, &ops);
+               ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 
-               if (put_user(ops.retlen, (uint32_t __user *)argp))
+               if (put_user(retlen, (uint32_t __user *)argp))
                        ret = -EFAULT;
-               else if (ops.retlen && copy_to_user(buf.ptr, ops.oobbuf,
-                                                   ops.retlen))
+               else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
                        ret = -EFAULT;
 
-               kfree(ops.oobbuf);
+               kfree(databuf);
                break;
        }
 
@@ -602,22 +504,16 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
-       /* Legacy interface */
-       case MEMGETOOBSEL:
+       case MEMSETOOBSEL:
        {
-               struct nand_oobinfo oi;
-
-               if (!mtd->ecclayout)
-                       return -EOPNOTSUPP;
-               if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
-                       return -EINVAL;
-
-               oi.useecc = MTD_NANDECC_AUTOPLACE;
-               memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
-               memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
-                      sizeof(oi.oobfree));
+               if (copy_from_user(&mtd->oobinfo, argp, sizeof(struct nand_oobinfo)))
+                       return -EFAULT;
+               break;
+       }
 
-               if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
+       case MEMGETOOBSEL:
+       {
+               if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
                        return -EFAULT;
                break;
        }
@@ -648,17 +544,31 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                break;
        }
 
-#if defined(CONFIG_MTD_OTP) || defined(CONFIG_MTD_ONENAND_OTP)
+#ifdef CONFIG_MTD_OTP
        case OTPSELECT:
        {
                int mode;
                if (copy_from_user(&mode, argp, sizeof(int)))
                        return -EFAULT;
-
-               mfi->mode = MTD_MODE_NORMAL;
-
-               ret = otp_select_filemode(mfi, mode);
-
+               SET_MTD_MODE(file, 0);
+               switch (mode) {
+               case MTD_OTP_FACTORY:
+                       if (!mtd->read_fact_prot_reg)
+                               ret = -EOPNOTSUPP;
+                       else
+                               SET_MTD_MODE(file, MTD_MODE_OTP_FACT);
+                       break;
+               case MTD_OTP_USER:
+                       if (!mtd->read_fact_prot_reg)
+                               ret = -EOPNOTSUPP;
+                       else
+                               SET_MTD_MODE(file, MTD_MODE_OTP_USER);
+                       break;
+               default:
+                       ret = -EINVAL;
+               case MTD_OTP_OFF:
+                       break;
+               }
                file->f_pos = 0;
                break;
        }
@@ -670,8 +580,8 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                if (!buf)
                        return -ENOMEM;
                ret = -EOPNOTSUPP;
-               switch (mfi->mode) {
-               case MTD_MODE_OTP_FACTORY:
+               switch (MTD_MODE(file)) {
+               case MTD_MODE_OTP_FACT:
                        if (mtd->get_fact_prot_info)
                                ret = mtd->get_fact_prot_info(mtd, buf, 4096);
                        break;
@@ -679,8 +589,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
                        if (mtd->get_user_prot_info)
                                ret = mtd->get_user_prot_info(mtd, buf, 4096);
                        break;
-               default:
-                       break;
                }
                if (ret >= 0) {
                        if (cmd == OTPGETREGIONCOUNT) {
@@ -699,7 +607,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        {
                struct otp_info info;
 
-               if (mfi->mode != MTD_MODE_OTP_USER)
+               if (MTD_MODE(file) != MTD_MODE_OTP_USER)
                        return -EINVAL;
                if (copy_from_user(&info, argp, sizeof(info)))
                        return -EFAULT;
@@ -710,49 +618,6 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        }
 #endif
 
-       case ECCGETLAYOUT:
-       {
-               if (!mtd->ecclayout)
-                       return -EOPNOTSUPP;
-
-               if (copy_to_user(argp, &mtd->ecclayout,
-                                sizeof(struct nand_ecclayout)))
-                       return -EFAULT;
-               break;
-       }
-
-       case ECCGETSTATS:
-       {
-               if (copy_to_user(argp, &mtd->ecc_stats,
-                                sizeof(struct mtd_ecc_stats)))
-                       return -EFAULT;
-               break;
-       }
-
-       case MTDFILEMODE:
-       {
-               mfi->mode = 0;
-
-               switch(arg) {
-               case MTD_MODE_OTP_FACTORY:
-               case MTD_MODE_OTP_USER:
-                       ret = otp_select_filemode(mfi, arg);
-                       break;
-
-               case MTD_MODE_RAW:
-                       if (!mtd->read_oob || !mtd->write_oob)
-                               return -EOPNOTSUPP;
-                       mfi->mode = arg;
-
-               case MTD_MODE_NORMAL:
-                       break;
-               default:
-                       ret = -EINVAL;
-               }
-               file->f_pos = 0;
-               break;
-       }
-
        default:
                ret = -ENOTTY;
        }