X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmtd%2Fmtdchar.c;h=6f044584bdc6ac3b38c4a20949bfcbabb5f292ae;hb=9464c7cf61b9433057924c36e6e02f303a00e768;hp=5b6acfcb2b880be00cc77be7abafbf0c71b264da;hpb=41689045f6a3cbe0550e1d34e9cc20d2e8c432ba;p=linux-2.6.git diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b6acfcb2..6f044584b 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -5,6 +5,7 @@ * */ +#include #include #include #include @@ -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; }