Merge git://git.infradead.org/mtd-2.6
[pandora-kernel.git] / drivers / mtd / mtdchar.c
index a34a0fe..4759d82 100644 (file)
@@ -30,8 +30,9 @@
 #include <linux/backing-dev.h>
 #include <linux/compat.h>
 #include <linux/mount.h>
-
+#include <linux/blkpg.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
 #include <linux/mtd/map.h>
 
 #include <asm/uaccess.h>
@@ -478,6 +479,78 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
        return ret;
 }
 
+/*
+ * Copies (and truncates, if necessary) data from the larger struct,
+ * nand_ecclayout, to the smaller, deprecated layout struct,
+ * nand_ecclayout_user. This is necessary only to suppport the deprecated
+ * API ioctl ECCGETLAYOUT while allowing all new functionality to use
+ * nand_ecclayout flexibly (i.e. the struct may change size in new
+ * releases without requiring major rewrites).
+ */
+static int shrink_ecclayout(const struct nand_ecclayout *from,
+               struct nand_ecclayout_user *to)
+{
+       int i;
+
+       if (!from || !to)
+               return -EINVAL;
+
+       memset(to, 0, sizeof(*to));
+
+       to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
+       for (i = 0; i < to->eccbytes; i++)
+               to->eccpos[i] = from->eccpos[i];
+
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES; i++) {
+               if (from->oobfree[i].length == 0 &&
+                               from->oobfree[i].offset == 0)
+                       break;
+               to->oobavail += from->oobfree[i].length;
+               to->oobfree[i] = from->oobfree[i];
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static int mtd_blkpg_ioctl(struct mtd_info *mtd,
+                          struct blkpg_ioctl_arg __user *arg)
+{
+       struct blkpg_ioctl_arg a;
+       struct blkpg_partition p;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       /* Only master mtd device must be used to control partitions */
+       if (!mtd_is_master(mtd))
+               return -EINVAL;
+
+       if (copy_from_user(&a, arg, sizeof(struct blkpg_ioctl_arg)))
+               return -EFAULT;
+
+       if (copy_from_user(&p, a.data, sizeof(struct blkpg_partition)))
+               return -EFAULT;
+
+       switch (a.op) {
+       case BLKPG_ADD_PARTITION:
+
+               return mtd_add_partition(mtd, p.devname, p.start, p.length);
+
+       case BLKPG_DEL_PARTITION:
+
+               if (p.pno < 0)
+                       return -EINVAL;
+
+               return mtd_del_partition(mtd, p.pno);
+
+       default:
+               return -EINVAL;
+       }
+}
+#endif
+
+
 static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
 {
        struct mtd_file_info *mfi = file->private_data;
@@ -514,6 +587,9 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
                if (get_user(ur_idx, &(ur->regionindex)))
                        return -EFAULT;
 
+               if (ur_idx >= mtd->numeraseregions)
+                       return -EINVAL;
+
                kr = &(mtd->eraseregions[ur_idx]);
 
                if (put_user(kr->offset, &(ur->offset))
@@ -813,14 +889,23 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
        }
 #endif
 
+       /* This ioctl is being deprecated - it truncates the ecc layout */
        case ECCGETLAYOUT:
        {
+               struct nand_ecclayout_user *usrlay;
+
                if (!mtd->ecclayout)
                        return -EOPNOTSUPP;
 
-               if (copy_to_user(argp, mtd->ecclayout,
-                                sizeof(struct nand_ecclayout)))
-                       return -EFAULT;
+               usrlay = kmalloc(sizeof(*usrlay), GFP_KERNEL);
+               if (!usrlay)
+                       return -ENOMEM;
+
+               shrink_ecclayout(mtd->ecclayout, usrlay);
+
+               if (copy_to_user(argp, usrlay, sizeof(*usrlay)))
+                       ret = -EFAULT;
+               kfree(usrlay);
                break;
        }
 
@@ -856,6 +941,22 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)
                break;
        }
 
+#ifdef CONFIG_MTD_PARTITIONS
+       case BLKPG:
+       {
+               ret = mtd_blkpg_ioctl(mtd,
+                     (struct blkpg_ioctl_arg __user *)arg);
+               break;
+       }
+
+       case BLKRRPART:
+       {
+               /* No reread partition feature. Just return ok */
+               ret = 0;
+               break;
+       }
+#endif
+
        default:
                ret = -ENOTTY;
        }
@@ -1033,7 +1134,7 @@ static const struct file_operations mtd_fops = {
 static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
                                int flags, const char *dev_name, void *data)
 {
-        return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
+       return mount_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC);
 }
 
 static struct file_system_type mtd_inodefs_type = {