jffs2: Update copyright notices
[deliverable/linux.git] / drivers / mtd / mtdchar.c
CommitLineData
1da177e4 1/*
1da177e4
LT
2 * Character-device access to raw MTD devices.
3 *
4 */
5
15fdc52f
TG
6#include <linux/device.h>
7#include <linux/fs.h>
0c1eafdb 8#include <linux/mm.h>
9c74034f 9#include <linux/err.h>
15fdc52f 10#include <linux/init.h>
1da177e4
LT
11#include <linux/kernel.h>
12#include <linux/module.h>
15fdc52f
TG
13#include <linux/slab.h>
14#include <linux/sched.h>
6071239e 15#include <linux/smp_lock.h>
402d3265 16#include <linux/backing-dev.h>
97718540 17#include <linux/compat.h>
cd874237 18#include <linux/mount.h>
15fdc52f 19
1da177e4 20#include <linux/mtd/mtd.h>
dd02b67d 21#include <linux/mtd/map.h>
1da177e4 22#include <linux/mtd/compatmac.h>
1da177e4 23
15fdc52f 24#include <asm/uaccess.h>
9bc7b387 25
cd874237
KS
26#define MTD_INODE_FS_MAGIC 0x11307854
27static struct vfsmount *mtd_inode_mnt __read_mostly;
1da177e4 28
045e9a5d 29/*
f1a28c02
TG
30 * Data structure to hold the pointer to the mtd device as well
31 * as mode information ofr various use cases.
045e9a5d 32 */
f1a28c02
TG
33struct mtd_file_info {
34 struct mtd_info *mtd;
cd874237 35 struct inode *ino;
f1a28c02
TG
36 enum mtd_file_modes mode;
37};
31f4233b 38
1da177e4
LT
39static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
40{
f1a28c02
TG
41 struct mtd_file_info *mfi = file->private_data;
42 struct mtd_info *mtd = mfi->mtd;
1da177e4
LT
43
44 switch (orig) {
ea59830d 45 case SEEK_SET:
1da177e4 46 break;
ea59830d 47 case SEEK_CUR:
8b491d75 48 offset += file->f_pos;
1da177e4 49 break;
ea59830d 50 case SEEK_END:
8b491d75 51 offset += mtd->size;
1da177e4
LT
52 break;
53 default:
54 return -EINVAL;
55 }
56
1887f517 57 if (offset >= 0 && offset <= mtd->size)
8b491d75 58 return file->f_pos = offset;
1da177e4 59
8b491d75 60 return -EINVAL;
1da177e4
LT
61}
62
63
64
65static int mtd_open(struct inode *inode, struct file *file)
66{
67 int minor = iminor(inode);
68 int devnum = minor >> 1;
6071239e 69 int ret = 0;
1da177e4 70 struct mtd_info *mtd;
f1a28c02 71 struct mtd_file_info *mfi;
cd874237 72 struct inode *mtd_ino;
1da177e4
LT
73
74 DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n");
75
1da177e4 76 /* You can't open the RO devices RW */
aeb5d727 77 if ((file->f_mode & FMODE_WRITE) && (minor & 1))
1da177e4
LT
78 return -EACCES;
79
6071239e 80 lock_kernel();
1da177e4 81 mtd = get_mtd_device(NULL, devnum);
97894cda 82
6071239e
JC
83 if (IS_ERR(mtd)) {
84 ret = PTR_ERR(mtd);
85 goto out;
86 }
97894cda 87
402d3265 88 if (mtd->type == MTD_ABSENT) {
1da177e4 89 put_mtd_device(mtd);
6071239e
JC
90 ret = -ENODEV;
91 goto out;
1da177e4
LT
92 }
93
cd874237
KS
94 mtd_ino = iget_locked(mtd_inode_mnt->mnt_sb, devnum);
95 if (!mtd_ino) {
96 put_mtd_device(mtd);
97 ret = -ENOMEM;
98 goto out;
99 }
100 if (mtd_ino->i_state & I_NEW) {
101 mtd_ino->i_private = mtd;
102 mtd_ino->i_mode = S_IFCHR;
103 mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info;
104 unlock_new_inode(mtd_ino);
105 }
106 file->f_mapping = mtd_ino->i_mapping;
402d3265 107
1da177e4 108 /* You can't open it RW if it's not a writeable device */
aeb5d727 109 if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
cd874237 110 iput(mtd_ino);
1da177e4 111 put_mtd_device(mtd);
6071239e
JC
112 ret = -EACCES;
113 goto out;
1da177e4 114 }
97894cda 115
f1a28c02
TG
116 mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);
117 if (!mfi) {
cd874237 118 iput(mtd_ino);
f1a28c02 119 put_mtd_device(mtd);
6071239e
JC
120 ret = -ENOMEM;
121 goto out;
f1a28c02 122 }
cd874237 123 mfi->ino = mtd_ino;
f1a28c02
TG
124 mfi->mtd = mtd;
125 file->private_data = mfi;
126
6071239e
JC
127out:
128 unlock_kernel();
129 return ret;
1da177e4
LT
130} /* mtd_open */
131
132/*====================================================================*/
133
134static int mtd_close(struct inode *inode, struct file *file)
135{
f1a28c02
TG
136 struct mtd_file_info *mfi = file->private_data;
137 struct mtd_info *mtd = mfi->mtd;
1da177e4
LT
138
139 DEBUG(MTD_DEBUG_LEVEL0, "MTD_close\n");
140
7eafaed5 141 /* Only sync if opened RW */
aeb5d727 142 if ((file->f_mode & FMODE_WRITE) && mtd->sync)
1da177e4 143 mtd->sync(mtd);
97894cda 144
cd874237
KS
145 iput(mfi->ino);
146
1da177e4 147 put_mtd_device(mtd);
f1a28c02
TG
148 file->private_data = NULL;
149 kfree(mfi);
1da177e4
LT
150
151 return 0;
152} /* mtd_close */
153
154/* FIXME: This _really_ needs to die. In 2.5, we should lock the
155 userspace buffer down and use it directly with readv/writev.
156*/
157#define MAX_KMALLOC_SIZE 0x20000
158
159static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
160{
f1a28c02
TG
161 struct mtd_file_info *mfi = file->private_data;
162 struct mtd_info *mtd = mfi->mtd;
1da177e4
LT
163 size_t retlen=0;
164 size_t total_retlen=0;
165 int ret=0;
166 int len;
167 char *kbuf;
97894cda 168
1da177e4
LT
169 DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
170
171 if (*ppos + count > mtd->size)
172 count = mtd->size - *ppos;
173
174 if (!count)
175 return 0;
97894cda 176
1da177e4
LT
177 /* FIXME: Use kiovec in 2.5 to lock down the user's buffers
178 and pass them directly to the MTD functions */
b802c074
TG
179
180 if (count > MAX_KMALLOC_SIZE)
181 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
182 else
183 kbuf=kmalloc(count, GFP_KERNEL);
184
185 if (!kbuf)
186 return -ENOMEM;
187
1da177e4 188 while (count) {
b802c074 189
97894cda 190 if (count > MAX_KMALLOC_SIZE)
1da177e4
LT
191 len = MAX_KMALLOC_SIZE;
192 else
193 len = count;
194
f1a28c02
TG
195 switch (mfi->mode) {
196 case MTD_MODE_OTP_FACTORY:
31f4233b
NP
197 ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf);
198 break;
199 case MTD_MODE_OTP_USER:
200 ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
201 break;
f1a28c02
TG
202 case MTD_MODE_RAW:
203 {
204 struct mtd_oob_ops ops;
205
206 ops.mode = MTD_OOB_RAW;
207 ops.datbuf = kbuf;
208 ops.oobbuf = NULL;
209 ops.len = len;
210
211 ret = mtd->read_oob(mtd, *ppos, &ops);
212 retlen = ops.retlen;
213 break;
214 }
31f4233b 215 default:
f4a43cfc 216 ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);
31f4233b 217 }
1da177e4
LT
218 /* Nand returns -EBADMSG on ecc errors, but it returns
219 * the data. For our userspace tools it is important
97894cda 220 * to dump areas with ecc errors !
9a1fcdfd
TG
221 * For kernel internal usage it also might return -EUCLEAN
222 * to signal the caller that a bitflip has occured and has
223 * been corrected by the ECC algorithm.
1da177e4
LT
224 * Userspace software which accesses NAND this way
225 * must be aware of the fact that it deals with NAND
226 */
9a1fcdfd 227 if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {
1da177e4
LT
228 *ppos += retlen;
229 if (copy_to_user(buf, kbuf, retlen)) {
f4a43cfc 230 kfree(kbuf);
1da177e4
LT
231 return -EFAULT;
232 }
233 else
234 total_retlen += retlen;
235
236 count -= retlen;
237 buf += retlen;
31f4233b
NP
238 if (retlen == 0)
239 count = 0;
1da177e4
LT
240 }
241 else {
242 kfree(kbuf);
243 return ret;
244 }
97894cda 245
1da177e4
LT
246 }
247
b802c074 248 kfree(kbuf);
1da177e4
LT
249 return total_retlen;
250} /* mtd_read */
251
252static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
253{
f1a28c02
TG
254 struct mtd_file_info *mfi = file->private_data;
255 struct mtd_info *mtd = mfi->mtd;
1da177e4
LT
256 char *kbuf;
257 size_t retlen;
258 size_t total_retlen=0;
259 int ret=0;
260 int len;
261
262 DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
97894cda 263
1da177e4
LT
264 if (*ppos == mtd->size)
265 return -ENOSPC;
97894cda 266
1da177e4
LT
267 if (*ppos + count > mtd->size)
268 count = mtd->size - *ppos;
269
270 if (!count)
271 return 0;
272
b802c074
TG
273 if (count > MAX_KMALLOC_SIZE)
274 kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL);
275 else
276 kbuf=kmalloc(count, GFP_KERNEL);
277
278 if (!kbuf)
279 return -ENOMEM;
280
1da177e4 281 while (count) {
b802c074 282
97894cda 283 if (count > MAX_KMALLOC_SIZE)
1da177e4
LT
284 len = MAX_KMALLOC_SIZE;
285 else
286 len = count;
287
1da177e4
LT
288 if (copy_from_user(kbuf, buf, len)) {
289 kfree(kbuf);
290 return -EFAULT;
291 }
97894cda 292
f1a28c02
TG
293 switch (mfi->mode) {
294 case MTD_MODE_OTP_FACTORY:
31f4233b
NP
295 ret = -EROFS;
296 break;
297 case MTD_MODE_OTP_USER:
298 if (!mtd->write_user_prot_reg) {
299 ret = -EOPNOTSUPP;
300 break;
301 }
302 ret = mtd->write_user_prot_reg(mtd, *ppos, len, &retlen, kbuf);
303 break;
f1a28c02
TG
304
305 case MTD_MODE_RAW:
306 {
307 struct mtd_oob_ops ops;
308
309 ops.mode = MTD_OOB_RAW;
310 ops.datbuf = kbuf;
311 ops.oobbuf = NULL;
312 ops.len = len;
313
314 ret = mtd->write_oob(mtd, *ppos, &ops);
315 retlen = ops.retlen;
316 break;
317 }
318
31f4233b
NP
319 default:
320 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
321 }
1da177e4
LT
322 if (!ret) {
323 *ppos += retlen;
324 total_retlen += retlen;
325 count -= retlen;
326 buf += retlen;
327 }
328 else {
329 kfree(kbuf);
330 return ret;
331 }
1da177e4
LT
332 }
333
b802c074 334 kfree(kbuf);
1da177e4
LT
335 return total_retlen;
336} /* mtd_write */
337
338/*======================================================================
339
340 IOCTL calls for getting device parameters.
341
342======================================================================*/
343static void mtdchar_erase_callback (struct erase_info *instr)
344{
345 wake_up((wait_queue_head_t *)instr->priv);
346}
347
34a82443 348#ifdef CONFIG_HAVE_MTD_OTP
f1a28c02
TG
349static int otp_select_filemode(struct mtd_file_info *mfi, int mode)
350{
351 struct mtd_info *mtd = mfi->mtd;
352 int ret = 0;
353
354 switch (mode) {
355 case MTD_OTP_FACTORY:
356 if (!mtd->read_fact_prot_reg)
357 ret = -EOPNOTSUPP;
358 else
359 mfi->mode = MTD_MODE_OTP_FACTORY;
360 break;
361 case MTD_OTP_USER:
362 if (!mtd->read_fact_prot_reg)
363 ret = -EOPNOTSUPP;
364 else
365 mfi->mode = MTD_MODE_OTP_USER;
366 break;
367 default:
368 ret = -EINVAL;
369 case MTD_OTP_OFF:
370 break;
371 }
372 return ret;
373}
374#else
375# define otp_select_filemode(f,m) -EOPNOTSUPP
376#endif
377
97718540
KC
378static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd,
379 uint64_t start, uint32_t length, void __user *ptr,
380 uint32_t __user *retp)
381{
382 struct mtd_oob_ops ops;
383 uint32_t retlen;
384 int ret = 0;
385
386 if (!(file->f_mode & FMODE_WRITE))
387 return -EPERM;
388
389 if (length > 4096)
390 return -EINVAL;
391
392 if (!mtd->write_oob)
393 ret = -EOPNOTSUPP;
394 else
0040476b 395 ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT;
97718540
KC
396
397 if (ret)
398 return ret;
399
400 ops.ooblen = length;
401 ops.ooboffs = start & (mtd->oobsize - 1);
402 ops.datbuf = NULL;
403 ops.mode = MTD_OOB_PLACE;
404
405 if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
406 return -EINVAL;
407
df1f1d1c
JL
408 ops.oobbuf = memdup_user(ptr, length);
409 if (IS_ERR(ops.oobbuf))
410 return PTR_ERR(ops.oobbuf);
97718540
KC
411
412 start &= ~((uint64_t)mtd->oobsize - 1);
413 ret = mtd->write_oob(mtd, start, &ops);
414
415 if (ops.oobretlen > 0xFFFFFFFFU)
416 ret = -EOVERFLOW;
417 retlen = ops.oobretlen;
418 if (copy_to_user(retp, &retlen, sizeof(length)))
419 ret = -EFAULT;
420
421 kfree(ops.oobbuf);
422 return ret;
423}
424
425static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start,
426 uint32_t length, void __user *ptr, uint32_t __user *retp)
427{
428 struct mtd_oob_ops ops;
429 int ret = 0;
430
431 if (length > 4096)
432 return -EINVAL;
433
434 if (!mtd->read_oob)
435 ret = -EOPNOTSUPP;
436 else
437 ret = access_ok(VERIFY_WRITE, ptr,
438 length) ? 0 : -EFAULT;
439 if (ret)
440 return ret;
441
442 ops.ooblen = length;
443 ops.ooboffs = start & (mtd->oobsize - 1);
444 ops.datbuf = NULL;
445 ops.mode = MTD_OOB_PLACE;
446
447 if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs))
448 return -EINVAL;
449
450 ops.oobbuf = kmalloc(length, GFP_KERNEL);
451 if (!ops.oobbuf)
452 return -ENOMEM;
453
454 start &= ~((uint64_t)mtd->oobsize - 1);
455 ret = mtd->read_oob(mtd, start, &ops);
456
457 if (put_user(ops.oobretlen, retp))
458 ret = -EFAULT;
459 else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf,
460 ops.oobretlen))
461 ret = -EFAULT;
462
463 kfree(ops.oobbuf);
464 return ret;
465}
466
1da177e4
LT
467static int mtd_ioctl(struct inode *inode, struct file *file,
468 u_int cmd, u_long arg)
469{
f1a28c02
TG
470 struct mtd_file_info *mfi = file->private_data;
471 struct mtd_info *mtd = mfi->mtd;
1da177e4
LT
472 void __user *argp = (void __user *)arg;
473 int ret = 0;
474 u_long size;
73c619ea 475 struct mtd_info_user info;
97894cda 476
1da177e4
LT
477 DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl\n");
478
479 size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
480 if (cmd & IOC_IN) {
481 if (!access_ok(VERIFY_READ, argp, size))
482 return -EFAULT;
483 }
484 if (cmd & IOC_OUT) {
485 if (!access_ok(VERIFY_WRITE, argp, size))
486 return -EFAULT;
487 }
97894cda 488
1da177e4
LT
489 switch (cmd) {
490 case MEMGETREGIONCOUNT:
491 if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
492 return -EFAULT;
493 break;
494
495 case MEMGETREGIONINFO:
496 {
b67c5f87
ZW
497 uint32_t ur_idx;
498 struct mtd_erase_region_info *kr;
bcc98a46 499 struct region_info_user __user *ur = argp;
1da177e4 500
b67c5f87 501 if (get_user(ur_idx, &(ur->regionindex)))
1da177e4
LT
502 return -EFAULT;
503
b67c5f87
ZW
504 kr = &(mtd->eraseregions[ur_idx]);
505
506 if (put_user(kr->offset, &(ur->offset))
507 || put_user(kr->erasesize, &(ur->erasesize))
508 || put_user(kr->numblocks, &(ur->numblocks)))
1da177e4 509 return -EFAULT;
b67c5f87 510
1da177e4
LT
511 break;
512 }
513
514 case MEMGETINFO:
73c619ea
JE
515 info.type = mtd->type;
516 info.flags = mtd->flags;
517 info.size = mtd->size;
518 info.erasesize = mtd->erasesize;
519 info.writesize = mtd->writesize;
520 info.oobsize = mtd->oobsize;
64f60710
AB
521 /* The below fields are obsolete */
522 info.ecctype = -1;
523 info.eccsize = 0;
73c619ea 524 if (copy_to_user(argp, &info, sizeof(struct mtd_info_user)))
1da177e4
LT
525 return -EFAULT;
526 break;
527
528 case MEMERASE:
0dc54e9f 529 case MEMERASE64:
1da177e4
LT
530 {
531 struct erase_info *erase;
532
aeb5d727 533 if(!(file->f_mode & FMODE_WRITE))
1da177e4
LT
534 return -EPERM;
535
95b93a0c 536 erase=kzalloc(sizeof(struct erase_info),GFP_KERNEL);
1da177e4
LT
537 if (!erase)
538 ret = -ENOMEM;
539 else {
540 wait_queue_head_t waitq;
541 DECLARE_WAITQUEUE(wait, current);
542
543 init_waitqueue_head(&waitq);
544
0dc54e9f
KC
545 if (cmd == MEMERASE64) {
546 struct erase_info_user64 einfo64;
547
548 if (copy_from_user(&einfo64, argp,
549 sizeof(struct erase_info_user64))) {
550 kfree(erase);
551 return -EFAULT;
552 }
553 erase->addr = einfo64.start;
554 erase->len = einfo64.length;
555 } else {
556 struct erase_info_user einfo32;
557
558 if (copy_from_user(&einfo32, argp,
559 sizeof(struct erase_info_user))) {
560 kfree(erase);
561 return -EFAULT;
562 }
563 erase->addr = einfo32.start;
564 erase->len = einfo32.length;
1da177e4
LT
565 }
566 erase->mtd = mtd;
567 erase->callback = mtdchar_erase_callback;
568 erase->priv = (unsigned long)&waitq;
97894cda 569
1da177e4
LT
570 /*
571 FIXME: Allow INTERRUPTIBLE. Which means
572 not having the wait_queue head on the stack.
97894cda 573
1da177e4
LT
574 If the wq_head is on the stack, and we
575 leave because we got interrupted, then the
576 wq_head is no longer there when the
577 callback routine tries to wake us up.
578 */
579 ret = mtd->erase(mtd, erase);
580 if (!ret) {
581 set_current_state(TASK_UNINTERRUPTIBLE);
582 add_wait_queue(&waitq, &wait);
583 if (erase->state != MTD_ERASE_DONE &&
584 erase->state != MTD_ERASE_FAILED)
585 schedule();
586 remove_wait_queue(&waitq, &wait);
587 set_current_state(TASK_RUNNING);
588
589 ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
590 }
591 kfree(erase);
592 }
593 break;
594 }
595
596 case MEMWRITEOOB:
597 {
598 struct mtd_oob_buf buf;
97718540 599 struct mtd_oob_buf __user *buf_user = argp;
1da177e4 600
97718540
KC
601 /* NOTE: writes return length to buf_user->length */
602 if (copy_from_user(&buf, argp, sizeof(buf)))
1da177e4 603 ret = -EFAULT;
97718540
KC
604 else
605 ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
606 buf.ptr, &buf_user->length);
1da177e4 607 break;
1da177e4
LT
608 }
609
610 case MEMREADOOB:
611 {
612 struct mtd_oob_buf buf;
97718540 613 struct mtd_oob_buf __user *buf_user = argp;
8593fbc6 614
97718540
KC
615 /* NOTE: writes return length to buf_user->start */
616 if (copy_from_user(&buf, argp, sizeof(buf)))
1da177e4 617 ret = -EFAULT;
97718540
KC
618 else
619 ret = mtd_do_readoob(mtd, buf.start, buf.length,
620 buf.ptr, &buf_user->start);
1da177e4
LT
621 break;
622 }
623
aea7cea9
KC
624 case MEMWRITEOOB64:
625 {
626 struct mtd_oob_buf64 buf;
627 struct mtd_oob_buf64 __user *buf_user = argp;
628
629 if (copy_from_user(&buf, argp, sizeof(buf)))
630 ret = -EFAULT;
631 else
632 ret = mtd_do_writeoob(file, mtd, buf.start, buf.length,
633 (void __user *)(uintptr_t)buf.usr_ptr,
634 &buf_user->length);
635 break;
636 }
637
638 case MEMREADOOB64:
639 {
640 struct mtd_oob_buf64 buf;
641 struct mtd_oob_buf64 __user *buf_user = argp;
642
643 if (copy_from_user(&buf, argp, sizeof(buf)))
644 ret = -EFAULT;
645 else
646 ret = mtd_do_readoob(mtd, buf.start, buf.length,
647 (void __user *)(uintptr_t)buf.usr_ptr,
648 &buf_user->length);
649 break;
650 }
651
1da177e4
LT
652 case MEMLOCK:
653 {
175428b2 654 struct erase_info_user einfo;
1da177e4 655
175428b2 656 if (copy_from_user(&einfo, argp, sizeof(einfo)))
1da177e4
LT
657 return -EFAULT;
658
659 if (!mtd->lock)
660 ret = -EOPNOTSUPP;
661 else
175428b2 662 ret = mtd->lock(mtd, einfo.start, einfo.length);
1da177e4
LT
663 break;
664 }
665
666 case MEMUNLOCK:
667 {
175428b2 668 struct erase_info_user einfo;
1da177e4 669
175428b2 670 if (copy_from_user(&einfo, argp, sizeof(einfo)))
1da177e4
LT
671 return -EFAULT;
672
673 if (!mtd->unlock)
674 ret = -EOPNOTSUPP;
675 else
175428b2 676 ret = mtd->unlock(mtd, einfo.start, einfo.length);
1da177e4
LT
677 break;
678 }
679
9938424f
RC
680 case MEMISLOCKED:
681 {
682 struct erase_info_user einfo;
683
684 if (copy_from_user(&einfo, argp, sizeof(einfo)))
685 return -EFAULT;
686
687 if (!mtd->is_locked)
688 ret = -EOPNOTSUPP;
689 else
690 ret = mtd->is_locked(mtd, einfo.start, einfo.length);
691 break;
692 }
693
5bd34c09 694 /* Legacy interface */
1da177e4
LT
695 case MEMGETOOBSEL:
696 {
5bd34c09
TG
697 struct nand_oobinfo oi;
698
699 if (!mtd->ecclayout)
700 return -EOPNOTSUPP;
701 if (mtd->ecclayout->eccbytes > ARRAY_SIZE(oi.eccpos))
702 return -EINVAL;
703
704 oi.useecc = MTD_NANDECC_AUTOPLACE;
705 memcpy(&oi.eccpos, mtd->ecclayout->eccpos, sizeof(oi.eccpos));
706 memcpy(&oi.oobfree, mtd->ecclayout->oobfree,
707 sizeof(oi.oobfree));
d25ade71 708 oi.eccbytes = mtd->ecclayout->eccbytes;
5bd34c09
TG
709
710 if (copy_to_user(argp, &oi, sizeof(struct nand_oobinfo)))
1da177e4
LT
711 return -EFAULT;
712 break;
713 }
714
715 case MEMGETBADBLOCK:
716 {
717 loff_t offs;
97894cda 718
1da177e4
LT
719 if (copy_from_user(&offs, argp, sizeof(loff_t)))
720 return -EFAULT;
721 if (!mtd->block_isbad)
722 ret = -EOPNOTSUPP;
723 else
724 return mtd->block_isbad(mtd, offs);
725 break;
726 }
727
728 case MEMSETBADBLOCK:
729 {
730 loff_t offs;
731
732 if (copy_from_user(&offs, argp, sizeof(loff_t)))
733 return -EFAULT;
734 if (!mtd->block_markbad)
735 ret = -EOPNOTSUPP;
736 else
737 return mtd->block_markbad(mtd, offs);
738 break;
739 }
740
34a82443 741#ifdef CONFIG_HAVE_MTD_OTP
31f4233b
NP
742 case OTPSELECT:
743 {
744 int mode;
745 if (copy_from_user(&mode, argp, sizeof(int)))
746 return -EFAULT;
f1a28c02
TG
747
748 mfi->mode = MTD_MODE_NORMAL;
749
750 ret = otp_select_filemode(mfi, mode);
751
81dba488 752 file->f_pos = 0;
31f4233b
NP
753 break;
754 }
755
756 case OTPGETREGIONCOUNT:
757 case OTPGETREGIONINFO:
758 {
759 struct otp_info *buf = kmalloc(4096, GFP_KERNEL);
760 if (!buf)
761 return -ENOMEM;
762 ret = -EOPNOTSUPP;
f1a28c02
TG
763 switch (mfi->mode) {
764 case MTD_MODE_OTP_FACTORY:
31f4233b
NP
765 if (mtd->get_fact_prot_info)
766 ret = mtd->get_fact_prot_info(mtd, buf, 4096);
767 break;
768 case MTD_MODE_OTP_USER:
769 if (mtd->get_user_prot_info)
770 ret = mtd->get_user_prot_info(mtd, buf, 4096);
771 break;
f1a28c02
TG
772 default:
773 break;
31f4233b
NP
774 }
775 if (ret >= 0) {
776 if (cmd == OTPGETREGIONCOUNT) {
777 int nbr = ret / sizeof(struct otp_info);
778 ret = copy_to_user(argp, &nbr, sizeof(int));
779 } else
780 ret = copy_to_user(argp, buf, ret);
781 if (ret)
782 ret = -EFAULT;
783 }
784 kfree(buf);
785 break;
786 }
787
788 case OTPLOCK:
789 {
175428b2 790 struct otp_info oinfo;
31f4233b 791
f1a28c02 792 if (mfi->mode != MTD_MODE_OTP_USER)
31f4233b 793 return -EINVAL;
175428b2 794 if (copy_from_user(&oinfo, argp, sizeof(oinfo)))
31f4233b
NP
795 return -EFAULT;
796 if (!mtd->lock_user_prot_reg)
797 return -EOPNOTSUPP;
175428b2 798 ret = mtd->lock_user_prot_reg(mtd, oinfo.start, oinfo.length);
31f4233b
NP
799 break;
800 }
801#endif
802
f1a28c02
TG
803 case ECCGETLAYOUT:
804 {
805 if (!mtd->ecclayout)
806 return -EOPNOTSUPP;
807
d25ade71 808 if (copy_to_user(argp, mtd->ecclayout,
f1a28c02
TG
809 sizeof(struct nand_ecclayout)))
810 return -EFAULT;
811 break;
812 }
813
814 case ECCGETSTATS:
815 {
816 if (copy_to_user(argp, &mtd->ecc_stats,
817 sizeof(struct mtd_ecc_stats)))
818 return -EFAULT;
819 break;
820 }
821
822 case MTDFILEMODE:
823 {
824 mfi->mode = 0;
825
826 switch(arg) {
827 case MTD_MODE_OTP_FACTORY:
828 case MTD_MODE_OTP_USER:
829 ret = otp_select_filemode(mfi, arg);
830 break;
831
832 case MTD_MODE_RAW:
833 if (!mtd->read_oob || !mtd->write_oob)
834 return -EOPNOTSUPP;
835 mfi->mode = arg;
836
837 case MTD_MODE_NORMAL:
838 break;
839 default:
840 ret = -EINVAL;
841 }
842 file->f_pos = 0;
843 break;
844 }
845
1da177e4
LT
846 default:
847 ret = -ENOTTY;
848 }
849
850 return ret;
851} /* memory_ioctl */
852
97718540
KC
853#ifdef CONFIG_COMPAT
854
855struct mtd_oob_buf32 {
856 u_int32_t start;
857 u_int32_t length;
858 compat_caddr_t ptr; /* unsigned char* */
859};
860
861#define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32)
862#define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32)
863
864static long mtd_compat_ioctl(struct file *file, unsigned int cmd,
865 unsigned long arg)
866{
668ff9ab 867 struct inode *inode = file->f_path.dentry->d_inode;
97718540
KC
868 struct mtd_file_info *mfi = file->private_data;
869 struct mtd_info *mtd = mfi->mtd;
0b6585ce 870 void __user *argp = compat_ptr(arg);
97718540
KC
871 int ret = 0;
872
873 lock_kernel();
874
875 switch (cmd) {
876 case MEMWRITEOOB32:
877 {
878 struct mtd_oob_buf32 buf;
879 struct mtd_oob_buf32 __user *buf_user = argp;
880
881 if (copy_from_user(&buf, argp, sizeof(buf)))
882 ret = -EFAULT;
883 else
884 ret = mtd_do_writeoob(file, mtd, buf.start,
885 buf.length, compat_ptr(buf.ptr),
886 &buf_user->length);
887 break;
888 }
889
890 case MEMREADOOB32:
891 {
892 struct mtd_oob_buf32 buf;
893 struct mtd_oob_buf32 __user *buf_user = argp;
894
895 /* NOTE: writes return length to buf->start */
896 if (copy_from_user(&buf, argp, sizeof(buf)))
897 ret = -EFAULT;
898 else
899 ret = mtd_do_readoob(mtd, buf.start,
900 buf.length, compat_ptr(buf.ptr),
901 &buf_user->start);
902 break;
903 }
904 default:
0b6585ce 905 ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp);
97718540
KC
906 }
907
908 unlock_kernel();
909
910 return ret;
911}
912
913#endif /* CONFIG_COMPAT */
914
402d3265
DH
915/*
916 * try to determine where a shared mapping can be made
917 * - only supported for NOMMU at the moment (MMU can't doesn't copy private
918 * mappings)
919 */
920#ifndef CONFIG_MMU
921static unsigned long mtd_get_unmapped_area(struct file *file,
922 unsigned long addr,
923 unsigned long len,
924 unsigned long pgoff,
925 unsigned long flags)
926{
927 struct mtd_file_info *mfi = file->private_data;
928 struct mtd_info *mtd = mfi->mtd;
929
930 if (mtd->get_unmapped_area) {
931 unsigned long offset;
932
933 if (addr != 0)
934 return (unsigned long) -EINVAL;
935
936 if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
937 return (unsigned long) -EINVAL;
938
939 offset = pgoff << PAGE_SHIFT;
940 if (offset > mtd->size - len)
941 return (unsigned long) -EINVAL;
942
943 return mtd->get_unmapped_area(mtd, len, offset, flags);
944 }
945
946 /* can't map directly */
947 return (unsigned long) -ENOSYS;
948}
949#endif
950
951/*
952 * set up a mapping for shared memory segments
953 */
954static int mtd_mmap(struct file *file, struct vm_area_struct *vma)
955{
956#ifdef CONFIG_MMU
957 struct mtd_file_info *mfi = file->private_data;
958 struct mtd_info *mtd = mfi->mtd;
dd02b67d
AG
959 struct map_info *map = mtd->priv;
960 unsigned long start;
961 unsigned long off;
962 u32 len;
963
964 if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
965 off = vma->vm_pgoff << PAGE_SHIFT;
966 start = map->phys;
967 len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
968 start &= PAGE_MASK;
969 if ((vma->vm_end - vma->vm_start + off) > len)
970 return -EINVAL;
971
972 off += start;
973 vma->vm_pgoff = off >> PAGE_SHIFT;
974 vma->vm_flags |= VM_IO | VM_RESERVED;
975
976#ifdef pgprot_noncached
977 if (file->f_flags & O_DSYNC || off >= __pa(high_memory))
978 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
979#endif
980 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
981 vma->vm_end - vma->vm_start,
982 vma->vm_page_prot))
983 return -EAGAIN;
402d3265 984
402d3265 985 return 0;
dd02b67d 986 }
402d3265
DH
987 return -ENOSYS;
988#else
989 return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
990#endif
991}
992
d54b1fdb 993static const struct file_operations mtd_fops = {
1da177e4
LT
994 .owner = THIS_MODULE,
995 .llseek = mtd_lseek,
996 .read = mtd_read,
997 .write = mtd_write,
998 .ioctl = mtd_ioctl,
97718540
KC
999#ifdef CONFIG_COMPAT
1000 .compat_ioctl = mtd_compat_ioctl,
1001#endif
1da177e4
LT
1002 .open = mtd_open,
1003 .release = mtd_close,
402d3265
DH
1004 .mmap = mtd_mmap,
1005#ifndef CONFIG_MMU
1006 .get_unmapped_area = mtd_get_unmapped_area,
1007#endif
1da177e4
LT
1008};
1009
cd874237
KS
1010static int mtd_inodefs_get_sb(struct file_system_type *fs_type, int flags,
1011 const char *dev_name, void *data,
1012 struct vfsmount *mnt)
1013{
1014 return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC,
1015 mnt);
1016}
1017
1018static struct file_system_type mtd_inodefs_type = {
1019 .name = "mtd_inodefs",
1020 .get_sb = mtd_inodefs_get_sb,
1021 .kill_sb = kill_anon_super,
1022};
1023
1024static void mtdchar_notify_add(struct mtd_info *mtd)
1025{
1026}
1027
1028static void mtdchar_notify_remove(struct mtd_info *mtd)
1029{
1030 struct inode *mtd_ino = ilookup(mtd_inode_mnt->mnt_sb, mtd->index);
1031
1032 if (mtd_ino) {
1033 /* Destroy the inode if it exists */
1034 mtd_ino->i_nlink = 0;
1035 iput(mtd_ino);
1036 }
1037}
1038
1039static struct mtd_notifier mtdchar_notifier = {
1040 .add = mtdchar_notify_add,
1041 .remove = mtdchar_notify_remove,
1042};
1043
1da177e4
LT
1044static int __init init_mtdchar(void)
1045{
cd874237 1046 int ret;
1f24b5a8 1047
cd874237 1048 ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
dad0db31 1049 "mtd", &mtd_fops);
cd874237
KS
1050 if (ret < 0) {
1051 pr_notice("Can't allocate major number %d for "
1052 "Memory Technology Devices.\n", MTD_CHAR_MAJOR);
1053 return ret;
9bc7b387
TP
1054 }
1055
cd874237
KS
1056 ret = register_filesystem(&mtd_inodefs_type);
1057 if (ret) {
1058 pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret);
1059 goto err_unregister_chdev;
1060 }
1061
1062 mtd_inode_mnt = kern_mount(&mtd_inodefs_type);
1063 if (IS_ERR(mtd_inode_mnt)) {
1064 ret = PTR_ERR(mtd_inode_mnt);
1065 pr_notice("Error mounting mtd_inodefs filesystem: %d\n", ret);
1066 goto err_unregister_filesystem;
1067 }
1068 register_mtd_user(&mtdchar_notifier);
1069
1070 return ret;
1071
1072err_unregister_filesystem:
1073 unregister_filesystem(&mtd_inodefs_type);
1074err_unregister_chdev:
1075 __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
1076 return ret;
1da177e4
LT
1077}
1078
1079static void __exit cleanup_mtdchar(void)
1080{
cd874237
KS
1081 unregister_mtd_user(&mtdchar_notifier);
1082 mntput(mtd_inode_mnt);
1083 unregister_filesystem(&mtd_inodefs_type);
dad0db31 1084 __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
1da177e4
LT
1085}
1086
1087module_init(init_mtdchar);
1088module_exit(cleanup_mtdchar);
1089
1f24b5a8 1090MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
1da177e4
LT
1091
1092MODULE_LICENSE("GPL");
1093MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
1094MODULE_DESCRIPTION("Direct character-device access to MTD devices");
90160e13 1095MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
This page took 0.537891 seconds and 5 git commands to generate.