mtdchar.c 28.3 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
 * Copyright © 1999-2010 David Woodhouse <dwmw2@infradead.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
L
Linus Torvalds 已提交
17 18 19
 *
 */

20 21
#include <linux/device.h>
#include <linux/fs.h>
A
Andrew Morton 已提交
22
#include <linux/mm.h>
23
#include <linux/err.h>
24
#include <linux/init.h>
L
Linus Torvalds 已提交
25 26
#include <linux/kernel.h>
#include <linux/module.h>
27 28
#include <linux/slab.h>
#include <linux/sched.h>
29
#include <linux/mutex.h>
30
#include <linux/backing-dev.h>
31
#include <linux/compat.h>
32
#include <linux/mount.h>
33
#include <linux/blkpg.h>
34
#include <linux/magic.h>
L
Linus Torvalds 已提交
35
#include <linux/mtd/mtd.h>
36
#include <linux/mtd/partitions.h>
37
#include <linux/mtd/map.h>
L
Linus Torvalds 已提交
38

39
#include <asm/uaccess.h>
40

41
static DEFINE_MUTEX(mtd_mutex);
L
Linus Torvalds 已提交
42

43
/*
44
 * Data structure to hold the pointer to the mtd device as well
B
Brian Norris 已提交
45
 * as mode information of various use cases.
46
 */
47 48
struct mtd_file_info {
	struct mtd_info *mtd;
49
	struct inode *ino;
50 51
	enum mtd_file_modes mode;
};
52

53
static loff_t mtdchar_lseek(struct file *file, loff_t offset, int orig)
L
Linus Torvalds 已提交
54
{
55 56
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
L
Linus Torvalds 已提交
57 58

	switch (orig) {
59
	case SEEK_SET:
L
Linus Torvalds 已提交
60
		break;
61
	case SEEK_CUR:
62
		offset += file->f_pos;
L
Linus Torvalds 已提交
63
		break;
64
	case SEEK_END:
65
		offset += mtd->size;
L
Linus Torvalds 已提交
66 67 68 69 70
		break;
	default:
		return -EINVAL;
	}

71
	if (offset >= 0 && offset <= mtd->size)
72
		return file->f_pos = offset;
L
Linus Torvalds 已提交
73

74
	return -EINVAL;
L
Linus Torvalds 已提交
75 76
}

77 78 79
static int count;
static struct vfsmount *mnt;
static struct file_system_type mtd_inodefs_type;
L
Linus Torvalds 已提交
80

81
static int mtdchar_open(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
82 83 84
{
	int minor = iminor(inode);
	int devnum = minor >> 1;
85
	int ret = 0;
L
Linus Torvalds 已提交
86
	struct mtd_info *mtd;
87
	struct mtd_file_info *mfi;
88
	struct inode *mtd_ino;
L
Linus Torvalds 已提交
89

90
	pr_debug("MTD_open\n");
L
Linus Torvalds 已提交
91 92

	/* You can't open the RO devices RW */
93
	if ((file->f_mode & FMODE_WRITE) && (minor & 1))
L
Linus Torvalds 已提交
94 95
		return -EACCES;

96 97 98 99
	ret = simple_pin_fs(&mtd_inodefs_type, &mnt, &count);
	if (ret)
		return ret;

100
	mutex_lock(&mtd_mutex);
L
Linus Torvalds 已提交
101
	mtd = get_mtd_device(NULL, devnum);
102

103 104 105 106
	if (IS_ERR(mtd)) {
		ret = PTR_ERR(mtd);
		goto out;
	}
107

108
	if (mtd->type == MTD_ABSENT) {
109
		ret = -ENODEV;
110
		goto out1;
L
Linus Torvalds 已提交
111 112
	}

113
	mtd_ino = iget_locked(mnt->mnt_sb, devnum);
114 115
	if (!mtd_ino) {
		ret = -ENOMEM;
116
		goto out1;
117 118 119 120 121 122 123 124
	}
	if (mtd_ino->i_state & I_NEW) {
		mtd_ino->i_private = mtd;
		mtd_ino->i_mode = S_IFCHR;
		mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info;
		unlock_new_inode(mtd_ino);
	}
	file->f_mapping = mtd_ino->i_mapping;
125

L
Linus Torvalds 已提交
126
	/* You can't open it RW if it's not a writeable device */
127
	if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
128
		ret = -EACCES;
129
		goto out2;
L
Linus Torvalds 已提交
130
	}
131

132 133
	mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
	if (!mfi) {
134
		ret = -ENOMEM;
135
		goto out2;
136
	}
137
	mfi->ino = mtd_ino;
138 139
	mfi->mtd = mtd;
	file->private_data = mfi;
140 141
	mutex_unlock(&mtd_mutex);
	return 0;
142

143 144 145 146
out2:
	iput(mtd_ino);
out1:
	put_mtd_device(mtd);
147
out:
148
	mutex_unlock(&mtd_mutex);
149
	simple_release_fs(&mnt, &count);
150
	return ret;
151
} /* mtdchar_open */
L
Linus Torvalds 已提交
152 153 154

/*====================================================================*/

155
static int mtdchar_close(struct inode *inode, struct file *file)
L
Linus Torvalds 已提交
156
{
157 158
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
L
Linus Torvalds 已提交
159

160
	pr_debug("MTD_close\n");
L
Linus Torvalds 已提交
161

162
	/* Only sync if opened RW */
163
	if ((file->f_mode & FMODE_WRITE))
164
		mtd_sync(mtd);
165

166 167
	iput(mfi->ino);

L
Linus Torvalds 已提交
168
	put_mtd_device(mtd);
169 170
	file->private_data = NULL;
	kfree(mfi);
171
	simple_release_fs(&mnt, &count);
L
Linus Torvalds 已提交
172 173

	return 0;
174
} /* mtdchar_close */
L
Linus Torvalds 已提交
175

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
/* Back in June 2001, dwmw2 wrote:
 *
 *   FIXME: This _really_ needs to die. In 2.5, we should lock the
 *   userspace buffer down and use it directly with readv/writev.
 *
 * The implementation below, using mtd_kmalloc_up_to, mitigates
 * allocation failures when the system is under low-memory situations
 * or if memory is highly fragmented at the cost of reducing the
 * performance of the requested transfer due to a smaller buffer size.
 *
 * A more complex but more memory-efficient implementation based on
 * get_user_pages and iovecs to cover extents of those pages is a
 * longer-term goal, as intimated by dwmw2 above. However, for the
 * write case, this requires yet more complex head and tail transfer
 * handling when those head and tail offsets and sizes are such that
 * alignment requirements are not met in the NAND subdriver.
 */
L
Linus Torvalds 已提交
193

194 195
static ssize_t mtdchar_read(struct file *file, char __user *buf, size_t count,
			loff_t *ppos)
L
Linus Torvalds 已提交
196
{
197 198
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
199
	size_t retlen;
L
Linus Torvalds 已提交
200 201 202
	size_t total_retlen=0;
	int ret=0;
	int len;
203
	size_t size = count;
L
Linus Torvalds 已提交
204
	char *kbuf;
205

206
	pr_debug("MTD_read\n");
L
Linus Torvalds 已提交
207 208 209 210 211 212

	if (*ppos + count > mtd->size)
		count = mtd->size - *ppos;

	if (!count)
		return 0;
213

214
	kbuf = mtd_kmalloc_up_to(mtd, &size);
215 216 217
	if (!kbuf)
		return -ENOMEM;

L
Linus Torvalds 已提交
218
	while (count) {
219
		len = min_t(size_t, count, size);
L
Linus Torvalds 已提交
220

221
		switch (mfi->mode) {
222
		case MTD_FILE_MODE_OTP_FACTORY:
223 224
			ret = mtd_read_fact_prot_reg(mtd, *ppos, len,
						     &retlen, kbuf);
225
			break;
226
		case MTD_FILE_MODE_OTP_USER:
227 228
			ret = mtd_read_user_prot_reg(mtd, *ppos, len,
						     &retlen, kbuf);
229
			break;
230
		case MTD_FILE_MODE_RAW:
231 232 233
		{
			struct mtd_oob_ops ops;

234
			ops.mode = MTD_OPS_RAW;
235 236 237 238
			ops.datbuf = kbuf;
			ops.oobbuf = NULL;
			ops.len = len;

239
			ret = mtd_read_oob(mtd, *ppos, &ops);
240 241 242
			retlen = ops.retlen;
			break;
		}
243
		default:
244
			ret = mtd_read(mtd, *ppos, len, &retlen, kbuf);
245
		}
246
		/* Nand returns -EBADMSG on ECC errors, but it returns
L
Linus Torvalds 已提交
247
		 * the data. For our userspace tools it is important
248
		 * to dump areas with ECC errors!
249
		 * For kernel internal usage it also might return -EUCLEAN
L
Lucas De Marchi 已提交
250
		 * to signal the caller that a bitflip has occurred and has
251
		 * been corrected by the ECC algorithm.
L
Linus Torvalds 已提交
252 253 254
		 * Userspace software which accesses NAND this way
		 * must be aware of the fact that it deals with NAND
		 */
255
		if (!ret || mtd_is_bitflip_or_eccerr(ret)) {
L
Linus Torvalds 已提交
256 257
			*ppos += retlen;
			if (copy_to_user(buf, kbuf, retlen)) {
258
				kfree(kbuf);
L
Linus Torvalds 已提交
259 260 261 262 263 264 265
				return -EFAULT;
			}
			else
				total_retlen += retlen;

			count -= retlen;
			buf += retlen;
266 267
			if (retlen == 0)
				count = 0;
L
Linus Torvalds 已提交
268 269 270 271 272
		}
		else {
			kfree(kbuf);
			return ret;
		}
273

L
Linus Torvalds 已提交
274 275
	}

276
	kfree(kbuf);
L
Linus Torvalds 已提交
277
	return total_retlen;
278
} /* mtdchar_read */
L
Linus Torvalds 已提交
279

280 281
static ssize_t mtdchar_write(struct file *file, const char __user *buf, size_t count,
			loff_t *ppos)
L
Linus Torvalds 已提交
282
{
283 284
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
285
	size_t size = count;
L
Linus Torvalds 已提交
286 287 288 289 290 291
	char *kbuf;
	size_t retlen;
	size_t total_retlen=0;
	int ret=0;
	int len;

292
	pr_debug("MTD_write\n");
293

L
Linus Torvalds 已提交
294 295
	if (*ppos == mtd->size)
		return -ENOSPC;
296

L
Linus Torvalds 已提交
297 298 299 300 301 302
	if (*ppos + count > mtd->size)
		count = mtd->size - *ppos;

	if (!count)
		return 0;

303
	kbuf = mtd_kmalloc_up_to(mtd, &size);
304 305 306
	if (!kbuf)
		return -ENOMEM;

L
Linus Torvalds 已提交
307
	while (count) {
308
		len = min_t(size_t, count, size);
L
Linus Torvalds 已提交
309 310 311 312 313

		if (copy_from_user(kbuf, buf, len)) {
			kfree(kbuf);
			return -EFAULT;
		}
314

315
		switch (mfi->mode) {
316
		case MTD_FILE_MODE_OTP_FACTORY:
317 318
			ret = -EROFS;
			break;
319
		case MTD_FILE_MODE_OTP_USER:
320 321
			ret = mtd_write_user_prot_reg(mtd, *ppos, len,
						      &retlen, kbuf);
322
			break;
323

324
		case MTD_FILE_MODE_RAW:
325 326 327
		{
			struct mtd_oob_ops ops;

328
			ops.mode = MTD_OPS_RAW;
329 330
			ops.datbuf = kbuf;
			ops.oobbuf = NULL;
331
			ops.ooboffs = 0;
332 333
			ops.len = len;

334
			ret = mtd_write_oob(mtd, *ppos, &ops);
335 336 337 338
			retlen = ops.retlen;
			break;
		}

339
		default:
340
			ret = mtd_write(mtd, *ppos, len, &retlen, kbuf);
341
		}
L
Linus Torvalds 已提交
342 343 344 345 346 347 348 349 350 351 352 353
		if (!ret) {
			*ppos += retlen;
			total_retlen += retlen;
			count -= retlen;
			buf += retlen;
		}
		else {
			kfree(kbuf);
			return ret;
		}
	}

354
	kfree(kbuf);
L
Linus Torvalds 已提交
355
	return total_retlen;
356
} /* mtdchar_write */
L
Linus Torvalds 已提交
357 358 359 360 361 362 363 364 365 366 367

/*======================================================================

    IOCTL calls for getting device parameters.

======================================================================*/
static void mtdchar_erase_callback (struct erase_info *instr)
{
	wake_up((wait_queue_head_t *)instr->priv);
}

368
#ifdef CONFIG_HAVE_MTD_OTP
369 370 371
static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
{
	struct mtd_info *mtd = mfi->mtd;
372
	size_t retlen;
373 374
	int ret = 0;

375 376 377 378
	/*
	 * Make a fake call to mtd_read_fact_prot_reg() to check if OTP
	 * operations are supported.
	 */
379
	if (mtd_read_fact_prot_reg(mtd, -1, 0, &retlen, NULL) == -EOPNOTSUPP)
380 381
		return -EOPNOTSUPP;

382 383
	switch (mode) {
	case MTD_OTP_FACTORY:
384
		mfi->mode = MTD_FILE_MODE_OTP_FACTORY;
385 386
		break;
	case MTD_OTP_USER:
387
		mfi->mode = MTD_FILE_MODE_OTP_USER;
388 389 390 391 392 393 394 395 396 397 398 399
		break;
	default:
		ret = -EINVAL;
	case MTD_OTP_OFF:
		break;
	}
	return ret;
}
#else
# define otp_select_filemode(f,m)	-EOPNOTSUPP
#endif

400
static int mtdchar_writeoob(struct file *file, struct mtd_info *mtd,
401 402 403
	uint64_t start, uint32_t length, void __user *ptr,
	uint32_t __user *retp)
{
404
	struct mtd_file_info *mfi = file->private_data;
405 406 407 408 409 410 411 412 413 414
	struct mtd_oob_ops ops;
	uint32_t retlen;
	int ret = 0;

	if (!(file->f_mode & FMODE_WRITE))
		return -EPERM;

	if (length > 4096)
		return -EINVAL;

415
	if (!mtd->_write_oob)
416 417
		ret = -EOPNOTSUPP;
	else
418
		ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
419 420 421 422 423

	if (ret)
		return ret;

	ops.ooblen = length;
424
	ops.ooboffs = start & (mtd->writesize - 1);
425
	ops.datbuf = NULL;
426
	ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW :
427
		MTD_OPS_PLACE_OOB;
428 429 430 431

	if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
		return -EINVAL;

432 433 434
	ops.oobbuf = memdup_user(ptr, length);
	if (IS_ERR(ops.oobbuf))
		return PTR_ERR(ops.oobbuf);
435

436
	start &= ~((uint64_t)mtd->writesize - 1);
437
	ret = mtd_write_oob(mtd, start, &ops);
438 439 440 441 442 443 444 445 446 447 448

	if (ops.oobretlen > 0xFFFFFFFFU)
		ret = -EOVERFLOW;
	retlen = ops.oobretlen;
	if (copy_to_user(retp, &retlen, sizeof(length)))
		ret = -EFAULT;

	kfree(ops.oobbuf);
	return ret;
}

449
static int mtdchar_readoob(struct file *file, struct mtd_info *mtd,
450 451
	uint64_t start, uint32_t length, void __user *ptr,
	uint32_t __user *retp)
452
{
453
	struct mtd_file_info *mfi = file->private_data;
454 455 456 457 458 459
	struct mtd_oob_ops ops;
	int ret = 0;

	if (length > 4096)
		return -EINVAL;

460 461
	if (!access_ok(VERIFY_WRITE, ptr, length))
		return -EFAULT;
462 463

	ops.ooblen = length;
464
	ops.ooboffs = start & (mtd->writesize - 1);
465
	ops.datbuf = NULL;
466
	ops.mode = (mfi->mode == MTD_FILE_MODE_RAW) ? MTD_OPS_RAW :
467
		MTD_OPS_PLACE_OOB;
468 469 470 471 472 473 474 475

	if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
		return -EINVAL;

	ops.oobbuf = kmalloc(length, GFP_KERNEL);
	if (!ops.oobbuf)
		return -ENOMEM;

476
	start &= ~((uint64_t)mtd->writesize - 1);
477
	ret = mtd_read_oob(mtd, start, &ops);
478 479 480 481 482 483 484 485

	if (put_user(ops.oobretlen, retp))
		ret = -EFAULT;
	else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf,
					    ops.oobretlen))
		ret = -EFAULT;

	kfree(ops.oobbuf);
486 487 488 489 490 491 492 493 494

	/*
	 * 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.
	 *
495 496 497
	 * 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.
498
	 */
499
	if (mtd_is_bitflip_or_eccerr(ret))
500 501
		return 0;

502 503 504
	return ret;
}

505 506 507
/*
 * Copies (and truncates, if necessary) data from the larger struct,
 * nand_ecclayout, to the smaller, deprecated layout struct,
B
Brian Norris 已提交
508
 * nand_ecclayout_user. This is necessary only to support the deprecated
509 510 511 512 513 514 515 516 517 518 519 520 521 522
 * 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));

523
	to->eccbytes = min((int)from->eccbytes, MTD_MAX_ECCPOS_ENTRIES);
524 525 526 527 528 529 530 531 532 533 534 535 536 537
	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;
}

538
static int mtdchar_blkpg_ioctl(struct mtd_info *mtd,
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
			   struct blkpg_ioctl_arg __user *arg)
{
	struct blkpg_ioctl_arg a;
	struct blkpg_partition p;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	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:

556 557 558 559
		/* Only master mtd device must be used to add partitions */
		if (mtd_is_partition(mtd))
			return -EINVAL;

560 561 562 563 564 565 566 567 568 569 570 571 572 573
		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;
	}
}

574
static int mtdchar_write_ioctl(struct mtd_info *mtd,
B
Brian Norris 已提交
575 576 577 578 579 580 581 582 583 584 585
		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;
586
	if (!mtd->_write_oob)
B
Brian Norris 已提交
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
		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;
	}

615
	ret = mtd_write_oob(mtd, (loff_t)req.start, &ops);
B
Brian Norris 已提交
616 617 618 619 620 621 622

	kfree(ops.datbuf);
	kfree(ops.oobbuf);

	return ret;
}

623
static int mtdchar_ioctl(struct file *file, u_int cmd, u_long arg)
L
Linus Torvalds 已提交
624
{
625 626
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
L
Linus Torvalds 已提交
627 628 629
	void __user *argp = (void __user *)arg;
	int ret = 0;
	u_long size;
630
	struct mtd_info_user info;
631

632
	pr_debug("MTD_ioctl\n");
L
Linus Torvalds 已提交
633 634 635 636 637 638 639 640 641 642

	size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
	if (cmd & IOC_IN) {
		if (!access_ok(VERIFY_READ, argp, size))
			return -EFAULT;
	}
	if (cmd & IOC_OUT) {
		if (!access_ok(VERIFY_WRITE, argp, size))
			return -EFAULT;
	}
643

L
Linus Torvalds 已提交
644 645 646 647 648 649 650 651
	switch (cmd) {
	case MEMGETREGIONCOUNT:
		if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
			return -EFAULT;
		break;

	case MEMGETREGIONINFO:
	{
652 653
		uint32_t ur_idx;
		struct mtd_erase_region_info *kr;
654
		struct region_info_user __user *ur = argp;
L
Linus Torvalds 已提交
655

656
		if (get_user(ur_idx, &(ur->regionindex)))
L
Linus Torvalds 已提交
657 658
			return -EFAULT;

659 660 661
		if (ur_idx >= mtd->numeraseregions)
			return -EINVAL;

662 663 664 665 666
		kr = &(mtd->eraseregions[ur_idx]);

		if (put_user(kr->offset, &(ur->offset))
		    || put_user(kr->erasesize, &(ur->erasesize))
		    || put_user(kr->numblocks, &(ur->numblocks)))
L
Linus Torvalds 已提交
667
			return -EFAULT;
668

L
Linus Torvalds 已提交
669 670 671 672
		break;
	}

	case MEMGETINFO:
673
		memset(&info, 0, sizeof(info));
674 675 676 677 678 679
		info.type	= mtd->type;
		info.flags	= mtd->flags;
		info.size	= mtd->size;
		info.erasesize	= mtd->erasesize;
		info.writesize	= mtd->writesize;
		info.oobsize	= mtd->oobsize;
680 681
		/* The below field is obsolete */
		info.padding	= 0;
682
		if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
L
Linus Torvalds 已提交
683 684 685 686
			return -EFAULT;
		break;

	case MEMERASE:
687
	case MEMERASE64:
L
Linus Torvalds 已提交
688 689 690
	{
		struct erase_info *erase;

691
		if(!(file->f_mode & FMODE_WRITE))
L
Linus Torvalds 已提交
692 693
			return -EPERM;

694
		erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
L
Linus Torvalds 已提交
695 696 697 698 699 700 701 702
		if (!erase)
			ret = -ENOMEM;
		else {
			wait_queue_head_t waitq;
			DECLARE_WAITQUEUE(wait, current);

			init_waitqueue_head(&waitq);

703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
			if (cmd == MEMERASE64) {
				struct erase_info_user64 einfo64;

				if (copy_from_user(&einfo64, argp,
					    sizeof(struct erase_info_user64))) {
					kfree(erase);
					return -EFAULT;
				}
				erase->addr = einfo64.start;
				erase->len = einfo64.length;
			} else {
				struct erase_info_user einfo32;

				if (copy_from_user(&einfo32, argp,
					    sizeof(struct erase_info_user))) {
					kfree(erase);
					return -EFAULT;
				}
				erase->addr = einfo32.start;
				erase->len = einfo32.length;
L
Linus Torvalds 已提交
723 724 725 726
			}
			erase->mtd = mtd;
			erase->callback = mtdchar_erase_callback;
			erase->priv = (unsigned long)&waitq;
727

L
Linus Torvalds 已提交
728 729 730
			/*
			  FIXME: Allow INTERRUPTIBLE. Which means
			  not having the wait_queue head on the stack.
731

L
Linus Torvalds 已提交
732 733 734 735 736
			  If the wq_head is on the stack, and we
			  leave because we got interrupted, then the
			  wq_head is no longer there when the
			  callback routine tries to wake us up.
			*/
737
			ret = mtd_erase(mtd, erase);
L
Linus Torvalds 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756
			if (!ret) {
				set_current_state(TASK_UNINTERRUPTIBLE);
				add_wait_queue(&waitq, &wait);
				if (erase->state != MTD_ERASE_DONE &&
				    erase->state != MTD_ERASE_FAILED)
					schedule();
				remove_wait_queue(&waitq, &wait);
				set_current_state(TASK_RUNNING);

				ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
			}
			kfree(erase);
		}
		break;
	}

	case MEMWRITEOOB:
	{
		struct mtd_oob_buf buf;
757
		struct mtd_oob_buf __user *buf_user = argp;
L
Linus Torvalds 已提交
758

759 760
		/* NOTE: writes return length to buf_user->length */
		if (copy_from_user(&buf, argp, sizeof(buf)))
L
Linus Torvalds 已提交
761
			ret = -EFAULT;
762
		else
763
			ret = mtdchar_writeoob(file, mtd, buf.start, buf.length,
764
				buf.ptr, &buf_user->length);
L
Linus Torvalds 已提交
765 766 767 768 769 770
		break;
	}

	case MEMREADOOB:
	{
		struct mtd_oob_buf buf;
771
		struct mtd_oob_buf __user *buf_user = argp;
772

773 774
		/* NOTE: writes return length to buf_user->start */
		if (copy_from_user(&buf, argp, sizeof(buf)))
L
Linus Torvalds 已提交
775
			ret = -EFAULT;
776
		else
777
			ret = mtdchar_readoob(file, mtd, buf.start, buf.length,
778
				buf.ptr, &buf_user->start);
L
Linus Torvalds 已提交
779 780 781
		break;
	}

782 783 784 785 786 787 788 789
	case MEMWRITEOOB64:
	{
		struct mtd_oob_buf64 buf;
		struct mtd_oob_buf64 __user *buf_user = argp;

		if (copy_from_user(&buf, argp, sizeof(buf)))
			ret = -EFAULT;
		else
790
			ret = mtdchar_writeoob(file, mtd, buf.start, buf.length,
791 792 793 794 795 796 797 798 799 800 801 802 803
				(void __user *)(uintptr_t)buf.usr_ptr,
				&buf_user->length);
		break;
	}

	case MEMREADOOB64:
	{
		struct mtd_oob_buf64 buf;
		struct mtd_oob_buf64 __user *buf_user = argp;

		if (copy_from_user(&buf, argp, sizeof(buf)))
			ret = -EFAULT;
		else
804
			ret = mtdchar_readoob(file, mtd, buf.start, buf.length,
805 806 807 808 809
				(void __user *)(uintptr_t)buf.usr_ptr,
				&buf_user->length);
		break;
	}

B
Brian Norris 已提交
810 811
	case MEMWRITE:
	{
812
		ret = mtdchar_write_ioctl(mtd,
B
Brian Norris 已提交
813 814 815 816
		      (struct mtd_write_req __user *)arg);
		break;
	}

L
Linus Torvalds 已提交
817 818
	case MEMLOCK:
	{
819
		struct erase_info_user einfo;
L
Linus Torvalds 已提交
820

821
		if (copy_from_user(&einfo, argp, sizeof(einfo)))
L
Linus Torvalds 已提交
822 823
			return -EFAULT;

824
		ret = mtd_lock(mtd, einfo.start, einfo.length);
L
Linus Torvalds 已提交
825 826 827 828 829
		break;
	}

	case MEMUNLOCK:
	{
830
		struct erase_info_user einfo;
L
Linus Torvalds 已提交
831

832
		if (copy_from_user(&einfo, argp, sizeof(einfo)))
L
Linus Torvalds 已提交
833 834
			return -EFAULT;

835
		ret = mtd_unlock(mtd, einfo.start, einfo.length);
L
Linus Torvalds 已提交
836 837 838
		break;
	}

839 840 841 842 843 844 845
	case MEMISLOCKED:
	{
		struct erase_info_user einfo;

		if (copy_from_user(&einfo, argp, sizeof(einfo)))
			return -EFAULT;

846
		ret = mtd_is_locked(mtd, einfo.start, einfo.length);
847 848 849
		break;
	}

850
	/* Legacy interface */
L
Linus Torvalds 已提交
851 852
	case MEMGETOOBSEL:
	{
853 854 855 856 857 858 859 860 861 862 863
		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));
864
		oi.eccbytes = mtd->ecclayout->eccbytes;
865 866

		if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
L
Linus Torvalds 已提交
867 868 869 870 871 872 873
			return -EFAULT;
		break;
	}

	case MEMGETBADBLOCK:
	{
		loff_t offs;
874

L
Linus Torvalds 已提交
875 876
		if (copy_from_user(&offs, argp, sizeof(loff_t)))
			return -EFAULT;
877
		return mtd_block_isbad(mtd, offs);
L
Linus Torvalds 已提交
878 879 880 881 882 883 884 885 886
		break;
	}

	case MEMSETBADBLOCK:
	{
		loff_t offs;

		if (copy_from_user(&offs, argp, sizeof(loff_t)))
			return -EFAULT;
887
		return mtd_block_markbad(mtd, offs);
L
Linus Torvalds 已提交
888 889 890
		break;
	}

891
#ifdef CONFIG_HAVE_MTD_OTP
892 893 894 895 896
	case OTPSELECT:
	{
		int mode;
		if (copy_from_user(&mode, argp, sizeof(int)))
			return -EFAULT;
897

898
		mfi->mode = MTD_FILE_MODE_NORMAL;
899 900 901

		ret = otp_select_filemode(mfi, mode);

902
		file->f_pos = 0;
903 904 905 906 907 908 909 910 911
		break;
	}

	case OTPGETREGIONCOUNT:
	case OTPGETREGIONINFO:
	{
		struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
		if (!buf)
			return -ENOMEM;
912
		switch (mfi->mode) {
913
		case MTD_FILE_MODE_OTP_FACTORY:
914
			ret = mtd_get_fact_prot_info(mtd, buf, 4096);
915
			break;
916
		case MTD_FILE_MODE_OTP_USER:
917
			ret = mtd_get_user_prot_info(mtd, buf, 4096);
918
			break;
919
		default:
920
			ret = -EINVAL;
921
			break;
922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937
		}
		if (ret >= 0) {
			if (cmd == OTPGETREGIONCOUNT) {
				int nbr = ret / sizeof(struct otp_info);
				ret = copy_to_user(argp, &nbr, sizeof(int));
			} else
				ret = copy_to_user(argp, buf, ret);
			if (ret)
				ret = -EFAULT;
		}
		kfree(buf);
		break;
	}

	case OTPLOCK:
	{
938
		struct otp_info oinfo;
939

940
		if (mfi->mode != MTD_FILE_MODE_OTP_USER)
941
			return -EINVAL;
942
		if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
943
			return -EFAULT;
944
		ret = mtd_lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
945 946 947 948
		break;
	}
#endif

949
	/* This ioctl is being deprecated - it truncates the ECC layout */
950 951
	case ECCGETLAYOUT:
	{
952 953
		struct nand_ecclayout_user *usrlay;

954 955 956
		if (!mtd->ecclayout)
			return -EOPNOTSUPP;

957 958 959 960 961 962 963 964 965
		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);
966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981
		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) {
982 983
		case MTD_FILE_MODE_OTP_FACTORY:
		case MTD_FILE_MODE_OTP_USER:
984 985 986
			ret = otp_select_filemode(mfi, arg);
			break;

987
		case MTD_FILE_MODE_RAW:
988
			if (!mtd_has_oob(mtd))
989 990 991
				return -EOPNOTSUPP;
			mfi->mode = arg;

992
		case MTD_FILE_MODE_NORMAL:
993 994 995 996 997 998 999 1000
			break;
		default:
			ret = -EINVAL;
		}
		file->f_pos = 0;
		break;
	}

1001 1002
	case BLKPG:
	{
1003
		ret = mtdchar_blkpg_ioctl(mtd,
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
		      (struct blkpg_ioctl_arg __user *)arg);
		break;
	}

	case BLKRRPART:
	{
		/* No reread partition feature. Just return ok */
		ret = 0;
		break;
	}

L
Linus Torvalds 已提交
1015 1016 1017 1018 1019 1020 1021
	default:
		ret = -ENOTTY;
	}

	return ret;
} /* memory_ioctl */

1022
static long mtdchar_unlocked_ioctl(struct file *file, u_int cmd, u_long arg)
1023 1024 1025
{
	int ret;

1026
	mutex_lock(&mtd_mutex);
1027
	ret = mtdchar_ioctl(file, cmd, arg);
1028
	mutex_unlock(&mtd_mutex);
1029 1030 1031 1032

	return ret;
}

1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
#ifdef CONFIG_COMPAT

struct mtd_oob_buf32 {
	u_int32_t start;
	u_int32_t length;
	compat_caddr_t ptr;	/* unsigned char* */
};

#define MEMWRITEOOB32		_IOWR('M', 3, struct mtd_oob_buf32)
#define MEMREADOOB32		_IOWR('M', 4, struct mtd_oob_buf32)

1044
static long mtdchar_compat_ioctl(struct file *file, unsigned int cmd,
1045 1046 1047 1048
	unsigned long arg)
{
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
1049
	void __user *argp = compat_ptr(arg);
1050 1051
	int ret = 0;

1052
	mutex_lock(&mtd_mutex);
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062

	switch (cmd) {
	case MEMWRITEOOB32:
	{
		struct mtd_oob_buf32 buf;
		struct mtd_oob_buf32 __user *buf_user = argp;

		if (copy_from_user(&buf, argp, sizeof(buf)))
			ret = -EFAULT;
		else
1063
			ret = mtdchar_writeoob(file, mtd, buf.start,
1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
				buf.length, compat_ptr(buf.ptr),
				&buf_user->length);
		break;
	}

	case MEMREADOOB32:
	{
		struct mtd_oob_buf32 buf;
		struct mtd_oob_buf32 __user *buf_user = argp;

		/* NOTE: writes return length to buf->start */
		if (copy_from_user(&buf, argp, sizeof(buf)))
			ret = -EFAULT;
		else
1078
			ret = mtdchar_readoob(file, mtd, buf.start,
1079 1080 1081 1082 1083
				buf.length, compat_ptr(buf.ptr),
				&buf_user->start);
		break;
	}
	default:
1084
		ret = mtdchar_ioctl(file, cmd, (unsigned long)argp);
1085 1086
	}

1087
	mutex_unlock(&mtd_mutex);
1088 1089 1090 1091 1092 1093

	return ret;
}

#endif /* CONFIG_COMPAT */

1094 1095 1096 1097 1098 1099
/*
 * try to determine where a shared mapping can be made
 * - only supported for NOMMU at the moment (MMU can't doesn't copy private
 *   mappings)
 */
#ifndef CONFIG_MMU
1100
static unsigned long mtdchar_get_unmapped_area(struct file *file,
1101 1102 1103 1104 1105 1106 1107
					   unsigned long addr,
					   unsigned long len,
					   unsigned long pgoff,
					   unsigned long flags)
{
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
1108 1109
	unsigned long offset;
	int ret;
1110

1111 1112
	if (addr != 0)
		return (unsigned long) -EINVAL;
1113

1114 1115
	if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
		return (unsigned long) -EINVAL;
1116

1117 1118 1119
	offset = pgoff << PAGE_SHIFT;
	if (offset > mtd->size - len)
		return (unsigned long) -EINVAL;
1120

1121 1122
	ret = mtd_get_unmapped_area(mtd, len, offset, flags);
	return ret == -EOPNOTSUPP ? -ENOSYS : ret;
1123 1124 1125
}
#endif

1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
static inline unsigned long get_vm_size(struct vm_area_struct *vma)
{
	return vma->vm_end - vma->vm_start;
}

static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
{
	return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
}

/*
 * Set a new vm offset.
 *
 * Verify that the incoming offset really works as a page offset,
 * and that the offset and size fit in a resource_size_t.
 */
static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
{
	pgoff_t pgoff = off >> PAGE_SHIFT;
	if (off != (resource_size_t) pgoff << PAGE_SHIFT)
		return -EINVAL;
	if (off + get_vm_size(vma) - 1 < off)
		return -EINVAL;
	vma->vm_pgoff = pgoff;
	return 0;
}

1153 1154 1155
/*
 * set up a mapping for shared memory segments
 */
1156
static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
1157 1158 1159 1160
{
#ifdef CONFIG_MMU
	struct mtd_file_info *mfi = file->private_data;
	struct mtd_info *mtd = mfi->mtd;
1161
	struct map_info *map = mtd->priv;
1162 1163
	resource_size_t start, off;
	unsigned long len, vma_len;
1164

1165 1166 1167 1168 1169
        /* This is broken because it assumes the MTD device is map-based
	   and that mtd->priv is a valid struct map_info.  It should be
	   replaced with something that uses the mtd_get_unmapped_area()
	   operation properly. */
	if (0 /*mtd->type == MTD_RAM || mtd->type == MTD_ROM*/) {
1170
		off = get_vm_offset(vma);
1171 1172 1173
		start = map->phys;
		len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
		start &= PAGE_MASK;
1174 1175 1176 1177 1178 1179 1180
		vma_len = get_vm_size(vma);

		/* Overflow in off+len? */
		if (vma_len + off < off)
			return -EINVAL;
		/* Does it fit in the mapping? */
		if (vma_len + off > len)
1181 1182 1183
			return -EINVAL;

		off += start;
1184 1185 1186 1187 1188
		/* Did that overflow? */
		if (off < start)
			return -EINVAL;
		if (set_vm_offset(vma, off) < 0)
			return -EINVAL;
1189
		vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
1190 1191 1192 1193 1194 1195 1196 1197 1198

#ifdef pgprot_noncached
		if (file->f_flags & O_DSYNC || off >= __pa(high_memory))
			vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
#endif
		if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
				       vma->vm_end - vma->vm_start,
				       vma->vm_page_prot))
			return -EAGAIN;
1199 1200

		return 0;
1201
	}
1202 1203 1204 1205 1206 1207
	return -ENOSYS;
#else
	return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
#endif
}

1208
static const struct file_operations mtd_fops = {
L
Linus Torvalds 已提交
1209
	.owner		= THIS_MODULE,
1210 1211 1212 1213
	.llseek		= mtdchar_lseek,
	.read		= mtdchar_read,
	.write		= mtdchar_write,
	.unlocked_ioctl	= mtdchar_unlocked_ioctl,
1214
#ifdef CONFIG_COMPAT
1215
	.compat_ioctl	= mtdchar_compat_ioctl,
1216
#endif
1217 1218 1219
	.open		= mtdchar_open,
	.release	= mtdchar_close,
	.mmap		= mtdchar_mmap,
1220
#ifndef CONFIG_MMU
1221
	.get_unmapped_area = mtdchar_get_unmapped_area,
1222
#endif
L
Linus Torvalds 已提交
1223 1224
};

1225 1226 1227 1228 1229
static const struct super_operations mtd_ops = {
	.drop_inode = generic_delete_inode,
	.statfs = simple_statfs,
};

A
Al Viro 已提交
1230 1231
static struct dentry *mtd_inodefs_mount(struct file_system_type *fs_type,
				int flags, const char *dev_name, void *data)
1232
{
1233
	return mount_pseudo(fs_type, "mtd_inode:", &mtd_ops, NULL, MTD_INODE_FS_MAGIC);
1234 1235 1236 1237
}

static struct file_system_type mtd_inodefs_type = {
       .name = "mtd_inodefs",
A
Al Viro 已提交
1238
       .mount = mtd_inodefs_mount,
1239 1240 1241
       .kill_sb = kill_anon_super,
};

L
Linus Torvalds 已提交
1242 1243
static int __init init_mtdchar(void)
{
1244
	int ret;
1245

1246
	ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
1247
				   "mtd", &mtd_fops);
1248 1249 1250 1251
	if (ret < 0) {
		pr_notice("Can't allocate major number %d for "
				"Memory Technology Devices.\n", MTD_CHAR_MAJOR);
		return ret;
1252 1253
	}

1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
	ret = register_filesystem(&mtd_inodefs_type);
	if (ret) {
		pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret);
		goto err_unregister_chdev;
	}
	return ret;

err_unregister_chdev:
	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
	return ret;
L
Linus Torvalds 已提交
1264 1265 1266 1267
}

static void __exit cleanup_mtdchar(void)
{
1268
	unregister_filesystem(&mtd_inodefs_type);
1269
	__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
L
Linus Torvalds 已提交
1270 1271 1272 1273 1274
}

module_init(init_mtdchar);
module_exit(cleanup_mtdchar);

1275
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
L
Linus Torvalds 已提交
1276 1277 1278 1279

MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Direct character-device access to MTD devices");
1280
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
新手
引导
客服 返回
顶部