diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index e00d424..7621449 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -547,6 +547,84 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } + case MEMWRITEPAGE: + { + struct mtd_page_buf buf; + struct mtd_oob_ops ops; + struct mtd_page_buf __user *user_buf = argp; + uint32_t retlen; + + if(!(file->f_mode & 2)) + return -EPERM; + + if (copy_from_user(&buf, argp, sizeof(struct mtd_page_buf))) + return -EFAULT; + + if (buf.length != mtd->writesize || buf.oob_length != mtd->oobsize) + return -EINVAL; + + if (!mtd->write_oob) + return -EOPNOTSUPP; + + ret = access_ok(VERIFY_READ, buf.ptr, + buf.length) ? 0 : EFAULT; + if (ret) + return ret; + + ret = access_ok(VERIFY_READ, buf.oob_ptr, + buf.oob_length) ? 0 : EFAULT; + if (ret) + return ret; + + ops.ooblen = mtd->oobsize; + ops.ooboffs = 0; + ops.len = mtd->writesize; + ops.mode = MTD_OOB_RAW; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.datbuf = kmalloc(buf.length, GFP_KERNEL); + if (!ops.datbuf) + return -ENOMEM; + + if (copy_from_user(ops.datbuf, buf.ptr, buf.length)) { + kfree(ops.datbuf); + return -EFAULT; + } + + ops.oobbuf = kmalloc(buf.oob_length, GFP_KERNEL); + if (!ops.oobbuf) { + kfree(ops.datbuf); + return -ENOMEM; + } + + if (copy_from_user(ops.oobbuf, buf.oob_ptr, buf.oob_length)) { + kfree(ops.datbuf); + kfree(ops.oobbuf); + return -EFAULT; + } + + ret = mtd->write_oob(mtd, buf.start, &ops); + + if (!ret) { + retlen = ops.retlen; + if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length))) + ret = -EFAULT; + } + + if (!ret) { + retlen = ops.oobretlen; + if (copy_to_user(&user_buf->oob_length, &retlen, sizeof(buf.oob_length))) + ret = -EFAULT; + } + + + kfree(ops.datbuf); + kfree(ops.oobbuf); + break; + + } case MEMREADOOB: { diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index c6c61cd..3d9e3bc 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -16,6 +16,13 @@ struct mtd_oob_buf { unsigned char __user *ptr; }; +struct mtd_page_buf { + uint32_t start; + uint32_t length; + unsigned char __user *ptr; + uint32_t oob_length; + unsigned char __user *oob_ptr; +}; #define MTD_ABSENT 0 #define MTD_RAM 1 #define MTD_ROM 2 @@ -93,6 +100,7 @@ struct otp_info { #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) #define MTDFILEMODE _IO('M', 19) +#define MEMWRITEPAGE _IOWR('M', 30, struct mtd_page_buf) /* * Obsolete legacy interface. Keep it in order not to break userspace