*
*/
+#include <linux/config.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
};
/*
- * 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;
int minor = iminor(inode);
int devnum = minor >> 1;
struct mtd_info *mtd;
- struct mtd_file_info *mfi;
DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
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 */
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 */
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;
/* 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
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;
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:
}
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);
}
kfree(kbuf);
return ret;
}
+
+ kfree(kbuf);
}
- kfree(kbuf);
return total_retlen;
} /* mtd_write */
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");
}
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;
case MEMWRITEOOB:
{
struct mtd_oob_buf buf;
- struct mtd_oob_ops ops;
+ void *databuf;
+ ssize_t retlen;
if(!(file->f_mode & 2))
return -EPERM;
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)
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;
}
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)
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;
}
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;
}
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;
}
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;
if (mtd->get_user_prot_info)
ret = mtd->get_user_prot_info(mtd, buf, 4096);
break;
- default:
- break;
}
if (ret >= 0) {
if (cmd == OTPGETREGIONCOUNT) {
{
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;
}
#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;
}