diff options
Diffstat (limited to 'drivers/mtd/mtdchar.c')
| -rw-r--r-- | drivers/mtd/mtdchar.c | 162 | 
1 files changed, 120 insertions, 42 deletions
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 61086ea3cc6..e7dc732ddab 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -43,7 +43,7 @@ static struct vfsmount *mtd_inode_mnt __read_mostly;  /*   * Data structure to hold the pointer to the mtd device as well - * as mode information ofr various use cases. + * as mode information of various use cases.   */  struct mtd_file_info {  	struct mtd_info *mtd; @@ -86,7 +86,7 @@ static int mtd_open(struct inode *inode, struct file *file)  	struct mtd_file_info *mfi;  	struct inode *mtd_ino; -	DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); +	pr_debug("MTD_open\n");  	/* You can't open the RO devices RW */  	if ((file->f_mode & FMODE_WRITE) && (minor & 1)) @@ -151,7 +151,7 @@ static int mtd_close(struct inode *inode, struct file *file)  	struct mtd_file_info *mfi = file->private_data;  	struct mtd_info *mtd = mfi->mtd; -	DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n"); +	pr_debug("MTD_close\n");  	/* Only sync if opened RW */  	if ((file->f_mode & FMODE_WRITE) && mtd->sync) @@ -195,7 +195,7 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t  	size_t size = count;  	char *kbuf; -	DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); +	pr_debug("MTD_read\n");  	if (*ppos + count > mtd->size)  		count = mtd->size - *ppos; @@ -211,17 +211,17 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t  		len = min_t(size_t, count, size);  		switch (mfi->mode) { -		case MTD_MODE_OTP_FACTORY: +		case MTD_FILE_MODE_OTP_FACTORY:  			ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);  			break; -		case MTD_MODE_OTP_USER: +		case MTD_FILE_MODE_OTP_USER:  			ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);  			break; -		case MTD_MODE_RAW: +		case MTD_FILE_MODE_RAW:  		{  			struct mtd_oob_ops ops; -			ops.mode = MTD_OOB_RAW; +			ops.mode = MTD_OPS_RAW;  			ops.datbuf = kbuf;  			ops.oobbuf = NULL;  			ops.len = len; @@ -233,16 +233,16 @@ static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t  		default:  			ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);  		} -		/* Nand returns -EBADMSG on ecc errors, but it returns +		/* Nand returns -EBADMSG on ECC errors, but it returns  		 * the data. For our userspace tools it is important -		 * to dump areas with ecc errors ! +		 * to dump areas with ECC errors!  		 * For kernel internal usage it also might return -EUCLEAN  		 * to signal the caller that a bitflip has occurred 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 || mtd_is_bitflip_or_eccerr(ret)) {  			*ppos += retlen;  			if (copy_to_user(buf, kbuf, retlen)) {  				kfree(kbuf); @@ -278,7 +278,7 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count  	int ret=0;  	int len; -	DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); +	pr_debug("MTD_write\n");  	if (*ppos == mtd->size)  		return -ENOSPC; @@ -302,10 +302,10 @@ static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count  		}  		switch (mfi->mode) { -		case MTD_MODE_OTP_FACTORY: +		case MTD_FILE_MODE_OTP_FACTORY:  			ret = -EROFS;  			break; -		case MTD_MODE_OTP_USER: +		case MTD_FILE_MODE_OTP_USER:  			if (!mtd->write_user_prot_reg) {  				ret = -EOPNOTSUPP;  				break; @@ -313,13 +313,14 @@ 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: +		case MTD_FILE_MODE_RAW:  		{  			struct mtd_oob_ops ops; -			ops.mode = MTD_OOB_RAW; +			ops.mode = MTD_OPS_RAW;  			ops.datbuf = kbuf;  			ops.oobbuf = NULL; +			ops.ooboffs = 0;  			ops.len = len;  			ret = mtd->write_oob(mtd, *ppos, &ops); @@ -367,13 +368,13 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode)  		if (!mtd->read_fact_prot_reg)  			ret = -EOPNOTSUPP;  		else -			mfi->mode = MTD_MODE_OTP_FACTORY; +			mfi->mode = MTD_FILE_MODE_OTP_FACTORY;  		break;  	case MTD_OTP_USER:  		if (!mtd->read_fact_prot_reg)  			ret = -EOPNOTSUPP;  		else -			mfi->mode = MTD_MODE_OTP_USER; +			mfi->mode = MTD_FILE_MODE_OTP_USER;  		break;  	default:  		ret = -EINVAL; @@ -390,6 +391,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,  	uint64_t start, uint32_t length, void __user *ptr,  	uint32_t __user *retp)  { +	struct mtd_file_info *mfi = file->private_data;  	struct mtd_oob_ops ops;  	uint32_t retlen;  	int ret = 0; @@ -409,9 +411,10 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,  		return ret;  	ops.ooblen = length; -	ops.ooboffs = start & (mtd->oobsize - 1); +	ops.ooboffs = start & (mtd->writesize - 1);  	ops.datbuf = NULL; -	ops.mode = MTD_OOB_PLACE; +	ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW : +		MTD_OPS_PLACE_OOB;  	if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))  		return -EINVAL; @@ -420,7 +423,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,  	if (IS_ERR(ops.oobbuf))  		return PTR_ERR(ops.oobbuf); -	start &= ~((uint64_t)mtd->oobsize - 1); +	start &= ~((uint64_t)mtd->writesize - 1);  	ret = mtd->write_oob(mtd, start, &ops);  	if (ops.oobretlen > 0xFFFFFFFFU) @@ -433,9 +436,11 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,  	return ret;  } -static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start, -	uint32_t length, void __user *ptr, uint32_t __user *retp) +static int mtd_do_readoob(struct file *file, struct mtd_info *mtd, +	uint64_t start, uint32_t length, void __user *ptr, +	uint32_t __user *retp)  { +	struct mtd_file_info *mfi = file->private_data;  	struct mtd_oob_ops ops;  	int ret = 0; @@ -451,9 +456,10 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,  		return ret;  	ops.ooblen = length; -	ops.ooboffs = start & (mtd->oobsize - 1); +	ops.ooboffs = start & (mtd->writesize - 1);  	ops.datbuf = NULL; -	ops.mode = MTD_OOB_PLACE; +	ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW : +		MTD_OPS_PLACE_OOB;  	if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))  		return -EINVAL; @@ -462,7 +468,7 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,  	if (!ops.oobbuf)  		return -ENOMEM; -	start &= ~((uint64_t)mtd->oobsize - 1); +	start &= ~((uint64_t)mtd->writesize - 1);  	ret = mtd->read_oob(mtd, start, &ops);  	if (put_user(ops.oobretlen, retp)) @@ -472,13 +478,29 @@ static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,  		ret = -EFAULT;  	kfree(ops.oobbuf); + +	/* +	 * NAND returns -EBADMSG on ECC errors, but it returns the OOB +	 * 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. +	 * +	 * Note: currently the standard NAND function, nand_read_oob_std, +	 * does not calculate ECC for the OOB area, so do not rely on +	 * this behavior unless you have replaced it with your own. +	 */ +	if (mtd_is_bitflip_or_eccerr(ret)) +		return 0; +  	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 + * nand_ecclayout_user. This is necessary only to support 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). @@ -544,6 +566,55 @@ static int mtd_blkpg_ioctl(struct mtd_info *mtd,  	}  } +static int mtd_write_ioctl(struct mtd_info *mtd, +		struct mtd_write_req __user *argp) +{ +	struct mtd_write_req req; +	struct mtd_oob_ops ops; +	void __user *usr_data, *usr_oob; +	int ret; + +	if (copy_from_user(&req, argp, sizeof(req)) || +			!access_ok(VERIFY_READ, req.usr_data, req.len) || +			!access_ok(VERIFY_READ, req.usr_oob, req.ooblen)) +		return -EFAULT; +	if (!mtd->write_oob) +		return -EOPNOTSUPP; + +	ops.mode = req.mode; +	ops.len = (size_t)req.len; +	ops.ooblen = (size_t)req.ooblen; +	ops.ooboffs = 0; + +	usr_data = (void __user *)(uintptr_t)req.usr_data; +	usr_oob = (void __user *)(uintptr_t)req.usr_oob; + +	if (req.usr_data) { +		ops.datbuf = memdup_user(usr_data, ops.len); +		if (IS_ERR(ops.datbuf)) +			return PTR_ERR(ops.datbuf); +	} else { +		ops.datbuf = NULL; +	} + +	if (req.usr_oob) { +		ops.oobbuf = memdup_user(usr_oob, ops.ooblen); +		if (IS_ERR(ops.oobbuf)) { +			kfree(ops.datbuf); +			return PTR_ERR(ops.oobbuf); +		} +	} else { +		ops.oobbuf = NULL; +	} + +	ret = mtd->write_oob(mtd, (loff_t)req.start, &ops); + +	kfree(ops.datbuf); +	kfree(ops.oobbuf); + +	return ret; +} +  static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  {  	struct mtd_file_info *mfi = file->private_data; @@ -553,7 +624,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  	u_long size;  	struct mtd_info_user info; -	DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n"); +	pr_debug("MTD_ioctl\n");  	size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;  	if (cmd & IOC_IN) { @@ -601,8 +672,8 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  		info.erasesize	= mtd->erasesize;  		info.writesize	= mtd->writesize;  		info.oobsize	= mtd->oobsize; -		/* The below fields are obsolete */ -		info.ecctype	= -1; +		/* The below field is obsolete */ +		info.padding	= 0;  		if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))  			return -EFAULT;  		break; @@ -698,7 +769,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  		if (copy_from_user(&buf, argp, sizeof(buf)))  			ret = -EFAULT;  		else -			ret = mtd_do_readoob(mtd, buf.start, buf.length, +			ret = mtd_do_readoob(file, mtd, buf.start, buf.length,  				buf.ptr, &buf_user->start);  		break;  	} @@ -725,12 +796,19 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  		if (copy_from_user(&buf, argp, sizeof(buf)))  			ret = -EFAULT;  		else -			ret = mtd_do_readoob(mtd, buf.start, buf.length, +			ret = mtd_do_readoob(file, mtd, buf.start, buf.length,  				(void __user *)(uintptr_t)buf.usr_ptr,  				&buf_user->length);  		break;  	} +	case MEMWRITE: +	{ +		ret = mtd_write_ioctl(mtd, +		      (struct mtd_write_req __user *)arg); +		break; +	} +  	case MEMLOCK:  	{  		struct erase_info_user einfo; @@ -827,7 +905,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  		if (copy_from_user(&mode, argp, sizeof(int)))  			return -EFAULT; -		mfi->mode = MTD_MODE_NORMAL; +		mfi->mode = MTD_FILE_MODE_NORMAL;  		ret = otp_select_filemode(mfi, mode); @@ -843,11 +921,11 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  			return -ENOMEM;  		ret = -EOPNOTSUPP;  		switch (mfi->mode) { -		case MTD_MODE_OTP_FACTORY: +		case MTD_FILE_MODE_OTP_FACTORY:  			if (mtd->get_fact_prot_info)  				ret = mtd->get_fact_prot_info(mtd, buf, 4096);  			break; -		case MTD_MODE_OTP_USER: +		case MTD_FILE_MODE_OTP_USER:  			if (mtd->get_user_prot_info)  				ret = mtd->get_user_prot_info(mtd, buf, 4096);  			break; @@ -871,7 +949,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  	{  		struct otp_info oinfo; -		if (mfi->mode != MTD_MODE_OTP_USER) +		if (mfi->mode != MTD_FILE_MODE_OTP_USER)  			return -EINVAL;  		if (copy_from_user(&oinfo, argp, sizeof(oinfo)))  			return -EFAULT; @@ -882,7 +960,7 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  	}  #endif -	/* This ioctl is being deprecated - it truncates the ecc layout */ +	/* This ioctl is being deprecated - it truncates the ECC layout */  	case ECCGETLAYOUT:  	{  		struct nand_ecclayout_user *usrlay; @@ -915,17 +993,17 @@ static int mtd_ioctl(struct file *file, u_int cmd, u_long arg)  		mfi->mode = 0;  		switch(arg) { -		case MTD_MODE_OTP_FACTORY: -		case MTD_MODE_OTP_USER: +		case MTD_FILE_MODE_OTP_FACTORY: +		case MTD_FILE_MODE_OTP_USER:  			ret = otp_select_filemode(mfi, arg);  			break; -		case MTD_MODE_RAW: +		case MTD_FILE_MODE_RAW:  			if (!mtd->read_oob || !mtd->write_oob)  				return -EOPNOTSUPP;  			mfi->mode = arg; -		case MTD_MODE_NORMAL: +		case MTD_FILE_MODE_NORMAL:  			break;  		default:  			ret = -EINVAL; @@ -1011,7 +1089,7 @@ static long mtd_compat_ioctl(struct file *file, unsigned int cmd,  		if (copy_from_user(&buf, argp, sizeof(buf)))  			ret = -EFAULT;  		else -			ret = mtd_do_readoob(mtd, buf.start, +			ret = mtd_do_readoob(file, mtd, buf.start,  				buf.length, compat_ptr(buf.ptr),  				&buf_user->start);  		break;  |